如何提取 MVIMG 的 Photo/Video 组件?
How to extract the Photo/Video component of a MVIMG?
Google Pixel 2 和可能的其他手机都有能力覆盖 "Motion Photos"。这些保存为MVIMG,比较大
我正在寻找 remove/extract 视频的方法。
到目前为止我找到了一个有前途的 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
是否有记录嵌入的任何资源?甚至有任何工具可以 remove/extract 视频吗?
我确实找到了 https://github.com/cliveontoast/GoMoPho 扫描 mp4 header 然后转储视频。
我们可以这样做,从 MP4 header 中扫描 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
上述使用 grep -F --byte-offset ...
和 dd
的建议在 macOS High Sierra 上对我不起作用,因为 /usr/bin/grep
输出了错误的偏移量——我猜它会产生 "line" 其中包含单词 ftypmp4
,即前一个 LF 字符的位置加一。我可能猜错了,但无论如何,这是我的解决方案:
for i in MVIMG*.jpg; do \
perl -0777 -ne 's/^.*(....ftypmp4.*)$//s && print' "$i" >"${i%.jpg}.mp4"; \
done
这使用了 perl
的能力,可以一次吞入整个文件并将其视为一个大字符串。如果不存在具有至少四个前导字节的 ftypmp4
,则创建一个空文件,如果存在多个,则提取最后一个。
同样,要从所有文件中删除视频:
for i in MVIMG*.jpg; do \
perl -0777 -pi -e 's/^(.*?)....ftypmp4.*$//s' "$i"; \
done
这使用了 perl
的就地编辑功能。在第一次出现 ftypmp4
及其四个前导字节之后的所有内容都将被截断。如果没有出现,文件将被重写,其内容不变。
(可能需要也可能不需要在环境中设置 PERLIO=raw and/or 取消设置与语言环境相关的变量以避免 UTF-8 解释,这对于恰好包含字节序列的二进制文件可能会失败违反了 UTF-8 组合规则。但在我对各种 MVIMG 文件的测试中,没有出现此类问题。)
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]
post 顶部的非 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 不明白,你可以删除它。
Google Pixel 2 和可能的其他手机都有能力覆盖 "Motion Photos"。这些保存为MVIMG,比较大
我正在寻找 remove/extract 视频的方法。
到目前为止我找到了一个有前途的 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
是否有记录嵌入的任何资源?甚至有任何工具可以 remove/extract 视频吗?
我确实找到了 https://github.com/cliveontoast/GoMoPho 扫描 mp4 header 然后转储视频。
我们可以这样做,从 MP4 header 中扫描 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
上述使用 grep -F --byte-offset ...
和 dd
的建议在 macOS High Sierra 上对我不起作用,因为 /usr/bin/grep
输出了错误的偏移量——我猜它会产生 "line" 其中包含单词 ftypmp4
,即前一个 LF 字符的位置加一。我可能猜错了,但无论如何,这是我的解决方案:
for i in MVIMG*.jpg; do \
perl -0777 -ne 's/^.*(....ftypmp4.*)$//s && print' "$i" >"${i%.jpg}.mp4"; \
done
这使用了 perl
的能力,可以一次吞入整个文件并将其视为一个大字符串。如果不存在具有至少四个前导字节的 ftypmp4
,则创建一个空文件,如果存在多个,则提取最后一个。
同样,要从所有文件中删除视频:
for i in MVIMG*.jpg; do \
perl -0777 -pi -e 's/^(.*?)....ftypmp4.*$//s' "$i"; \
done
这使用了 perl
的就地编辑功能。在第一次出现 ftypmp4
及其四个前导字节之后的所有内容都将被截断。如果没有出现,文件将被重写,其内容不变。
(可能需要也可能不需要在环境中设置 PERLIO=raw and/or 取消设置与语言环境相关的变量以避免 UTF-8 解释,这对于恰好包含字节序列的二进制文件可能会失败违反了 UTF-8 组合规则。但在我对各种 MVIMG 文件的测试中,没有出现此类问题。)
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]
post 顶部的非 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 不明白,你可以删除它。