我注意到如果我用这个命令提取帧:
ffmpeg -i sample_nosound.mp4 $filename%03d.jpg
默认情况下,它将提取取决于 fps。ffmpeg -i sample_nosound.mp4
显示此视频有 6 fps,因此它提取了 1630 个 jpg 帧文件,其中 1630/6 = 271.6 秒相当于 4:32 的总视频时长。
但是 1630 jpg 帧的总大小为 13 MB:
$ du -h extracted_jpg_folder
13M extracted_jpg_folder
,而 mp4 的文件大小为 1.8 MB,远低于总帧大小:
$ ls -la sample_nosound.mp4
-rw-rw-r-- 1 xiaobai xiaobai 1814889 Feb 13 15:42 'sample_nosound.mp4'
这意味着 ffmpeg 通过引用具有重复帧的 fps 信息来提取帧。
因此我的问题是,如何在不依赖 fps 的情况下通过“存储帧”使 ffmpeg 提取帧?
我希望我可以获得与 mp4 文件大小几乎相等的总帧大小。
我不希望完全匹配文件大小,因为 mp4 可以包含一些元数据。
输出ffprobe -i sample_nosound.mp4
:
ffprobe version 3.4.4-0ubuntu0.18.04.1 Copyright (c) 2007-2018 the FFmpeg developers
built with gcc 7 (Ubuntu 7.3.0-16ubuntu3)
configuration: --prefix=/usr --extra-version=0ubuntu0.18.04.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --enable-gpl --disable-stripping --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librubberband --enable-librsvg --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-omx --enable-openal --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libopencv --enable-libx264 --enable-shared
WARNING: library configuration mismatch
avcodec configuration: --prefix=/usr --extra-version=0ubuntu0.18.04.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --enable-gpl --disable-stripping --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librubberband --enable-librsvg --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-omx --enable-openal --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libopencv --enable-libx264 --enable-shared --enable-version3 --disable-doc --disable-programs --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libtesseract --enable-libvo_amrwbenc
libavutil 55. 78.100 / 55. 78.100
libavcodec 57.107.100 / 57.107.100
libavformat 57. 83.100 / 57. 83.100
libavdevice 57. 10.100 / 57. 10.100
libavfilter 6.107.100 / 6.107.100
libavresample 3. 7. 0 / 3. 7. 0
libswscale 4. 8.100 / 4. 8.100
libswresample 2. 9.100 / 2. 9.100
libpostproc 54. 7.100 / 54. 7.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'sample_nosound.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf57.83.100
Duration: 00:04:32.00, start: 0.000000, bitrate: 53 kb/s
Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(tv, bt470bg/bt709/bt709), 640x330 [SAR 1:1 DAR 64:33], 53 kb/s, 6 fps, 6 tbr, 12288 tbn, 12 tbc (default)
Metadata:
handler_name : VideoHandler
据我了解这个问题,您想从视频中提取帧。每个帧都应该存储一个单独的文件。所有文件大小的总和应与视频的文件大小相匹配。这仅适用于某些特定视频。我将尝试广泛地解释事情。
TL;博士
从具有相同视觉质量和文件大小的 h264 编码视频中提取帧是不可能的。
视频容器格式令人困惑
本例中的视频文件是 MP4 文件。MP4 是视频数据的容器。然而,容器的类型并没有真正说明实际内容。事实上,许多不同类型的视频格式可以驻留在 MP4 文件中——就像 zip 存档(或 PDF 文件)一样。
有不同类型的视频
视频是一系列图像。有很多方法可以将这些图像存储到视频流中(编码)以及之后如何读取它们(解码)。这些算法通常称为编解码器。
请记住,并非所有编解码器都进行压缩。在此示例中,h264 是编解码器。默认情况下,h264 编码器计算从一帧到下一帧的差异。如果差异很小,编码器只存储差异。实际帧被丢弃。只有第一个¹帧被存储为完整的图像。这节省了很多空间,是压缩策略之一。h264 解码器会将存储的差异应用于前一帧,重新创建原始帧。
如您所见,视频中的帧相互依赖。如果你想要单个文件,你希望它们是独立的。这意味着您始终需要存储每个单帧的完整信息。这意味着,您不能简单地将现有数据复制到文件中,而必须重新编码视频。在此过程中,文件大小的总和必须增加。
您可以阅读视频压缩中的各种图片类型,特别是“基于差异的”帧间或一般视频压缩的概述。
h264 不是 JPEG
即使我们谈论的是单个图像。JPEG 使用一种称为DCT的压缩方法。 H.264使用类似但改进的版本。这意味着 JPEG 不可能像 h264 那样有效地压缩。顺便说一句,您可以使用HEIF将 h264 压缩图像放入文件中(这本质上就像一帧视频)。
¹这并不完全正确,但我现在想保持简单。它实际上更像是“场景的第一帧”。如果您想了解细节:
编码器会注意到各个场景的开始(在电影摄影中,这通常称为“剪辑”)。从一帧到另一帧的差异非常大,因此不好压缩。编码器决定不使用“基于差异的”帧间。相反,它使用完整的图片(这称为“帧内”,也称为“关键帧”)。
还有一个技术原因:只有在寻找视频时,您可能会快速跳转到帧内。因此,帧内帧也会不时地放入流中(不管实际视频内容如何)。通常,
现在我们学到了很多关于视频压缩的知识。该视频演示了一些事情: 由于文件损坏,该视频丢失了帧内。解码器或多或少地成功地播放了它。丢失的帧可能显示女人在看一边。现在她转过头来,解码器只有来自帧间的数据,其中包括一些运动信息。看起来那个女人最终的脸在她的头侧。与此同时,一个人从背景中走过。这个人没有出现在丢失的帧内,因此看起来还不错。