如何使用ffmpeg准确地从视频文件中导出图像?

如何使用ffmpeg准确地从视频文件中导出图像?

我的目标;使用 ffmpeg 从视频文件中导出一系列图像。我猜我的问题与每秒帧数和比特率有关。

我尝试了以下命令: ffmpeg -i myVideo.mp4 -r 1 images_%04d.jpg 但不知何故,在 45 秒的视频中,我最终得到了 47 张图片。准确获取这些信息非常重要。我无法提前知道适当的 FPS 或比特率是多少,如果我在运行命令时需要这些值,我需要能够以某种方式从 ffmpeg 中提取它们。

我还考虑过导出视频中的所有图像ffmpeg -i myVideo images_%04d.jpg,然后将图像数量除以视频的总秒数。这样我会得到 24.97 左右的结果,然后我会四舍五入到 25 并删除每 25 帧中的 24 帧。我担心的是,如果文件是 VBR,并且高比特率部分位于视频的开头,那么我完成的帧数将不会精确匹配每秒 1 帧。例如,第 30 张图像实际上可能出现在视频中更接近第 31 秒的位置。

运行 ffmpeg -> ffmpeg -i "/videos/11.mp4" -s "352x264" "/images/image%06d.jpg"
FFmpeg 版本 0.6-4:0.6-2ubuntu6.1,版权所有 (c) 2000-2010 FFmpeg 开发人员
  于 2011 年 3 月 31 日 18:43:47 使用 gcc 4.4.5 构建
  配置:--extra-version=4:0.6-2ubuntu6.1 --prefix=/usr --enable-avfilter --enable-avfilter-lavf
--启用-vdpau --启用-bzlib --启用-libgsm --启用-libschroedinger --启用-libspeex
--启用-libtheora --启用-libvorbis --启用-vaapi --启用-pthreads --启用-zlib --启用-libvpx
--disable-stripping--enable-runtime-cpudetect--enable-gpl--enable-postproc--enable-x11grab
--启用-libdc1394 --启用共享--禁用静态
  libavutil 50.15。 1 / 50.15。 1
  libavcodec 52.72.2 / 52.72.2
  libavformat 52.64. 2 / 52.64. 2
  libavdevice 52.2.0 / 52.2.0
  libavfilter 1.19.0 / 1.19.0
  libswscale 0.11. 0 / 0.11. 0
  libpostproc 51. 2. 0 / 51. 2. 0

似乎流 1 编解码器帧速率与容器帧速率不同:49938.00 (49938/1) -> 24.97 (24969/1000)
输入 #0,mov,mp4,m4a,3gp,3g2,mj2,来自'/videos/11.mp4':
  元数据:
    主要品牌:mp42
    次要版本:0
    兼容品牌: isomavc1mp42
  时长:00:00:32.60,开始:0.000000,比特率:433 kb/s
    流 #0.0(und):音频:aac,44100 Hz,立体声,s16,127 kb/s
    流 #0.1(und):视频:h264、yuv420p、352x264 [PAR 1:1 DAR 4:3]、303 kb/s、24.97 fps、24.97 tbr、24969 tbn、49938 tbc
输出 #0,图像 2,至‘/images/image%06d.jpg’:
  元数据:
    编码器:Lavf52.64.2
    流 #0.0(und):视频:mjpeg、yuvj420p、352x264 [PAR 1:1 DAR 4:3]、q=2-31、200 kb/s、90k tbn、24.97 tbc
流映射:
  流 #0.1 -> #0.0
按 [q] 停止编码帧= 176 fps= 0 q=24.8 大小= -0kB 时间=7.05 比特率=  
-0.0kbits/s ^Mframe= 312 fps=236 q=24.8 大小= -0kB 时间=12.50 比特率= -0.0kbits/s    
^Mframe= 316 fps=112 q=24.8 大小= -0kB 时间=12.66 比特率= -0.0kbits/s ^Mframe= 322
fps= 55 q=24.8 大小= -0kB 时间=12.90 比特率= -0.0kbits/s ^Mframe= 327 fps= 39 q=24.8
大小= -0kB 时间=13.10 比特率= -0.0kbits/s ^Mframe= 331 fps= 33 q=24.8 大小=      
-0kB 时间=13.26 比特率= -0.0kbits/s ^Mframe= 336 fps= 31 q=24.8 大小= -0kB 时间=13.46
比特率= -0.0kbits/s ^M帧= 339 fps= 27 q=24.8 大小= -0kB
时间=13.58 比特率= -0.0kbits/s ^M帧= 344 fps= 22 q=24.8 大小= -0kB
时间=13.78 比特率= -0.0kbits/s

有人知道如何使用 ffmpeg 从视频中导出图像并获得时间准确的结果吗?谢谢!

答案1

您需要手动指定一个-vsync值。差异很可能是由于可变/恒定帧速率和帧时间戳的差异造成的。如果您有 VFR 源,ffmpeg 默认会尝试以估计速率(值tbr)为目标,并会丢弃或复制帧以“平滑”运动。将帧导出为图像也遵循此行为。文档中的一些信息:

-垂直同步范围

视频同步方法。出于兼容性原因,旧值可以指定为数字。新添加的值必须始终指定为字符串。

0,直通

每个帧都带有其时间戳,从解复用器传递到复用器。

1、到岸价

将复制并删除帧以准确实现所要求的恒定帧速率。

2、vfr

帧连同其时间戳一起传递或者被丢弃,以防止两帧具有相同的时间戳。

降低

作为直通但销毁所有时间戳,使得多路复用器根据帧速率生成新的时间戳。

-1,自动

根据多路复用器功能在 1 和 2 之间进行选择。这是默认方法。

正如这里所说,ffmpeg 将-vsync -1默认选择(用于视频编码和图像导出)。您想要的是直接通过而没有任何变化。-vsync 0如果您想保持每一帧原样,没有丢失或重复,请使用。您可能会收到有关非单调 DTS 的消息,但这可以基本忽略。以下是用于从视频中准确输出所有帧的示例命令:

ffmpeg -i video.nut -f image2 -vsync 0 frame-%03d.tiff

答案2

你确定你丢失了帧吗?你的输入视频是 h264,支持可变帧速率。要获取有关每个帧的详细信息,我建议使用以下命令:

ffmpeg -i inputvideo -vf showinfo -acodec copy -vcodec mpeg2video temp.mp4

这样你就会知道你的电影有多少帧。

您可以通过将电影转换为非可变帧速率视频、使用参数-r float,然后导出照片(就像在命令中所做的那样)来避免可变帧速率。

答案3

我观察到使用 -r 参数提取图像时也出现了类似的不稳定行为。

我找到了一个对我有用的解决方案,即改用 -vf fps=X 选项(下面有一些重要说明)。信不信由你,这里的行为似乎比使用 -r 的行为更可预测。

  1. 要从输入视频每 X 秒生成一张图像,请计算 1/X 的浮点值并运行:

ffmpeg -i input.mp4 -vf fps=1.0/x image-%04d.jpg

例如每秒获取 2 张图像:

ffmpeg -i input.mp4 fps=0.5 image-%04d.jpg

这几乎可以完成正确的事,请注意:

  1. 生成了额外的第一帧,应将其忽略。
  2. 每 X 秒生成一帧,其中 X 是 的参数-vf fps=X,从 X/2 秒开始。

因此,运行:

ffmpeg -i input.mp4 fps=1 image-%04d.jpg

5 秒的视频将产生 6 个文件,第一个文件应该被忽略,第 2 到第 6 个文件将对应时间戳:0.5、1.5、2.5、3.5 和 4.5。

这是一个对于调试此类事情有用的测试视频:

https://drive.google.com/file/d/0B56RokrDs3xabS1TS19wRjlBaVk/view?usp=sharing

相关内容