FFmpeg:从给定时间戳列表的视频中提取多个单独的帧?

fdm*_*ion 7 ffmpeg

假设我有一个时间戳列表,例如1.45.610.5等。

对于每个时间戳,我想提取最近的帧并将其保存到 jpeg 文件中。

我可以一次执行一帧:

ffmpeg -i video.mp4 -ss 1.4 -frames 1 frame_1_4.jpg
ffmpeg -i video.mp4 -ss 5.6 -frames 1 frame_5_6.jpg
ffmpeg -i video.mp4 -ss 10.5 -frames 1 frame_10_5.jpg
...
Run Code Online (Sandbox Code Playgroud)

这种方法的问题在于,由于以下几个原因,它似乎是资源密集型的:一遍又一遍地运行 ffmpeg 会产生进程开销,并且如果视频具有遥远的关键帧(或者在最坏的情况下根本没有),则解码器必须解码多个帧对于每次调用。如果您假设最坏的情况(没有可用的关键帧),那么提取帧的最有效方法是对流进行一次解码并在出现每个帧时拾取它。对于只有 3 帧的情况可能还不错,但如果我想提取 100 或 200 或更多帧的列表,即使对于具有良好关键帧的文件,它也会很快变得难以处理。

一个非常基本的测试表明,对于一个长五分钟、每 5 秒一次关键帧的测试文件,每次调用ffmpeg平均需要运行约 0.22 秒,最快为 0.11 秒,最慢为 0.34 秒。同样的文件只需6秒即可解码为空。因此,如果我想从这个文件中提取 40 帧,我最多需要等待 12 秒,而整个文件的解码时间只是这个时间的一半。这告诉我很多开销只是在ffmpeg初始化、打开文件等方面。

一种非常幼稚的方法可能是将所有帧提取到磁盘,然后挑选所需的帧。当然,这比多次运行 ffmpeg 还要慢,因为 JPEG 编码器一遍又一遍地运行,并且所需的磁盘空间很快就会变得疯狂。

是否有一个vf select或相关的过滤器可以选择我提供的特定时间戳列表并仅提取那些帧?

(为了澄清,带有时间戳的文件名不必由 ffmpeg 生成;只需顺序号就可以了,因为调用应用程序/脚本可以根据相同的时间戳列表重命名文件本身)

fur*_*ras 4

如果您可以将时间戳转换为帧号 - 即。100, 184, 213- 那么你可以这样做:

ffmpeg -i video.mp4 -vf select='eq(n\,100)+eq(n\,184)+eq(n\,213)' -vsync 0 frame_%d.jpg
Run Code Online (Sandbox Code Playgroud)

(看来-vsync 0获得正确的帧很重要)

它需要一些脚本来获取列表并生成字符串select=

脚本甚至可以获取时间戳和 FPS 来计算帧数。


基于来自以下问题Stack Overflowshell - Extract list of certainframes using ffmpeg


最终我会使用Python模块cvOpenCV),因为它可以从视频文件(或网络摄像头或流)中逐帧读取(可能使用ffmpeg)并将帧保存为图像。


编辑:

select的文档显示您可以使用变量t,因此timestamp
它可能可以直接与1.4, 5.6,10.5

ffmpeg -i video.mp4 -vf select='eq(t\,1.4)+eq(t\,5.6)+eq(t\,10.5)' -vsync 0 frame_%d.jpg
Run Code Online (Sandbox Code Playgroud)