如何提取MVIMG的照片/视频组件?

问题描述 投票:0回答:5

Google Pixel 2 以及其他可能的手机都具有覆盖“动态照片”的功能。这些保存为MVIMG并且比较大。

我正在寻找一种删除/提取视频的方法。

到目前为止我发现了一个有前途的 exif 标签

$ exiftool -xmp:all MVIMG_123.jpg
XMP Toolkit                     : Adobe XMP Core 5.1.0-jc003
Micro Video                     : 1
Micro Video Version             : 1
Micro Video Offset              : 4032524

我认为视频可能会出现在指定的偏移处,但这不起作用:

$ dd if=MVIMG_123.jpg of=video.mp4 bs=4032524 skip=1
$ file video.mp4
video.mp4: data

是否有任何资源记录嵌入?有没有任何工具可以删除/提取视频?

image exif exiftool
5个回答
18
投票

我确实找到了 https://github.com/cliveontoast/GoMoPho,它扫描 mp4 标头,然后转储视频。

我们可以做同样的事情,从 MP4 标头扫描

ftypmp4
(实际文件提前 4 个字节开始):

因此提取视频:

for i in MVIMG*.jpg; do \
  ofs=$(grep -F --byte-offset --only-matching --text ftypmp4 "$i"); \
  ofs=${ofs%:*}; \
  [[ $ofs ]] && dd "if=$i" "of=${i%.jpg}.mp4" bs=$((ofs-4)) skip=1; \
done

删除视频:

for i in MVIMG*.jpg; do \
  ofs=$(grep -F --byte-offset --only-matching --text ftypmp4 "$i"); \
  ofs=${ofs%:*}; \
  [[ $ofs ]] && truncate -s $((ofs-4)) "$i"; \
done

6
投票

EXIF 标签很有用,但偏移量是相对于文件末尾的。 mp4 文件嵌入在:

[file_size-micro_video_offset, file_size)

例如:

$ exiftool -xmp:all MVIMG_123.jpg
XMP Toolkit                     : Adobe XMP Core 5.1.0-jc003
Micro Video                     : 1
Micro Video Version             : 1
Micro Video Offset              : 2107172
Micro Video Presentation Timestamp Us: 966280
$ python -c 'import os; print os.path.getsize("MVIMG_123.jpg") - 2107172'
3322791
$ dd if=MVIMG_123.jpg of=video.mp4 bs=3322791 skip=1
$ file video.mp4 
video.mp4: ISO Media, MP4 v2 [ISO 14496-14]

6
投票

帖子顶部的非 Perl shell 脚本可以在我的 Linux 系统上运行。我将它们合并到一个 shell 脚本中,该脚本保留输入文件(如 MVIMG_20191216_153039.jpg)并创建两个输出文件(如 IMG_20191216_153039.jpg 和 IMG_20191216_153039.mp4):

#!/bin/bash
# extract-mvimg: Extract .mp4 video and .jpg still image from a Pixel phone
# camera "motion video" file with a name like MVIMG_20191216_153039.jpg
# to make files like IMG_20191216_153039.jpg and IMG_20191216_153039.mp4
#
# Usage: extract-mvimg MVIMG*.jpg [MVIMG*.jpg...]

for srcfile
do
  case "$srcfile" in
  MVIMG_*_*.jpg) ;;
  *)
    echo "extract-mvimg: skipping '$srcfile': not an MVIMG*.jpg file?" 2>&1
    continue
    ;;
  esac

  # Get base filename: strip leading MV and trailing .jpg
  # Example: MVIMG_20191216_153039.jpg becomes IMG_20191216_153039
  basefile=${srcfile#MV}
  basefile=${basefile%.jpg}

  # Get byte offset. Example output: 2983617:ftypmp4
  offset=$(grep -F --byte-offset --only-matching --text ftypmp4 "$srcfile")
  # Strip trailing text. Example output: 2983617
  offset=${offset%:*}

  # If $offset isn't an empty string, create .mp4 file and
  # truncate a copy of input file to make .jpg file.
  if [[ $offset ]]
  then
    dd status=none "if=$srcfile" "of=${basefile}.mp4" bs=$((offset-4)) skip=1
    cp -ip "$srcfile" "${basefile}.jpg" || exit 1
    truncate -s $((offset-4)) "${basefile}.jpg"
  else
    echo "extract-mvimg: can't find ftypmp4 in $srcfile; skipping..." 2>&1
  fi
done

status=none 抑制 dd 的“1+1 记录输入”和“1+1 记录输出”状态输出。如果你的dd不明白,你可以删除它。


4
投票

上述使用

grep -F --byte-offset ...
dd
的建议在 macOS High Sierra 上对我不起作用,因为
/usr/bin/grep
输出错误的偏移量 - 我猜它会产生包含单词
ftypmp4
的“线”的偏移量,即前一个 LF 字符的位置加一。我可能猜错了,但无论如何,这是我的解决方案:

for i in MVIMG*.jpg; do \
    perl -0777 -ne 's/^.*(....ftypmp4.*)$/$1/s && print' "$i" >"${i%.jpg}.mp4"; \
done

这使用了

perl
的能力来一次吞入整个文件并将其视为一个大字符串。如果不存在至少有四个前导字节的
ftypmp4
,则创建一个空文件,如果存在多个,则提取最后一个。

同样,要从所有文件中删除视频:

for i in MVIMG*.jpg; do \
    perl -0777 -pi -e 's/^(.*?)....ftypmp4.*$/$1/s' "$i"; \
done

这使用了

perl
的就地编辑功能。第一次出现
ftypmp4
及其四个前导字节之后的所有内容都将被切断。如果没有出现,则文件将被重写,其内容不变。

(可能需要或不需要在环境中设置 PERLIO=raw 和/或取消设置与语言环境相关的变量以避免 UTF-8 解释,这对于恰好包含违反 UTF-8 组成的字节序列的二进制文件可能会失败但在我对各种 MVIMG 文件的测试中没有出现这样的问题。)


0
投票

适用于摩托罗拉 G8

需要ffmpeg

提取视频: 运动-Photo_rotate.py

import os
import re
import subprocess

def convert_jpg_to_mp4():
    for filename in os.listdir("."):
        if filename.endswith(".jpg"):
            with open(filename, "rb") as file:
                data = file.read()
                
                # look for 'ftypmp4' in JPG
                match = re.search(b'ftypmp4', data)
                
                if match:
                    ofs = match.start()
                    
                    # create MP4 file
                    mp4_filename = filename.replace(".jpg", ".mp4")
                    with open(mp4_filename, "wb") as mp4_file:
                        mp4_file.write(data[ofs-4:])
                    
                    
                    rotated_mp4_filename = filename.replace(".jpg", "_fixed.mp4")
                    subprocess.run(['ffmpeg', '-display_rotation', '-90', '-i', mp4_filename, rotated_mp4_filename])
                    print(f"Converted and fix rotate: {filename} -> {rotated_mp4_filename}")
                else:
                    print(f" 'ftypmp4' not found: {filename}")

convert_jpg_to_mp4()
© www.soinside.com 2019 - 2024. All rights reserved.