使用 FFmpeg 每 N 秒准确提取帧

使用 FFmpeg 每 N 秒准确提取帧

用法

我使用 从视频中提取图像ffmpeg

我每 10 秒(含)转储一张缩小的图像,然后将其组合成蒙太奇imagemagick。这些蒙太奇再次用于在基于 Web 的视频播放器中将滑块悬停时显示视频预览。(计算要显示蒙太奇中的哪幅图像)。

命令

经过一番尝试后,我最终得到了以下命令,其理念是速度胜过质量:

ffmpeg \
    -loglevel error \
    -hwaccel cuvid \
    -hwaccel_output_format cuda \
    -c:v h264_cuvid \
    -i "$video_file" \
    -r 0.1 \
    -filter:v "scale_cuda=w=-1:h=100,thumbnail_cuda=2,hwdownload,format=nv12" \
    -color_range 2 \
    f%09d.jpg

效果似乎不错。镜头偶尔会有 ± 0.5 - 1 秒的误差,但可以接受。

问题

问题是ffmpeg在视频开始时会产生一个额外的图像。例如文件是:

file             time
f000000001.jpg   00:00:00
f000000002.jpg   00:00:00
f000000003.jpg   00:00:10
f000000004.jpg   00:00:20
f000000005.jpg   00:00:30
...

有时第一和第二个时间会相差几毫秒。

据我所知(现在),我可以简单地删除第一张图像并继续处理其余图像,但不确定为什么会发生这种情况,以及这是否是一个错误或其他什么。

换句话说:我需要知道前两帧的“效果”是否可靠的以便我ffmpeg也可以在其他版本中删除它。

当我使用图像来显示特定时间的视频中的 10 秒快照时它关闭了 10 秒如果我不删除生成的第一个图像。如果出于某种原因,它应该不是在开始时创建一个重复版本或其他版本,删除第一个图像会产生同样的问题。

剪辑

(如果感兴趣的话,蒙太奇的创作方式如下):

montage -tile 5x -geometry +0+0 -background none [file1  - file50 ]  montage01.jpg
montage -tile 5x -geometry +0+0 -background none [file51 - file100]  montage02.jpg
...

我现在根据答案使用的命令(shell):

# Set on call or global:
file_in=sample.mp4
pix_fmt=yuvj420p
sec_snap_interval=10
nr_start=1
pfx_out=snap


ffmpeg \
    -loglevel warning \
    -hwaccel cuvid \
    -hwaccel_output_format cuda \
    -c:v h264_cuvid \
    -i "$file_in" \
    -pix_fmt "$pix_fmt" \
    -filter:v "
        scale_cuda=
            w = -1 :
            h = 100,
        thumbnail_cuda = 2,
        hwdownload,
        format = nv12,
        select = 'bitor(
            gte(t - prev_selected_t, $sec_snap_interval),
            isnan(prev_selected_t)
        )'
    " \
    -vsync passthrough \
    -color_range 2 \
    -start_number "$nr_start" \
    "$pfx_out%09d.jpg"

答案1

使用-r 0.1将输出帧速率设置为 0.1Hz,但不能保证每 10 秒准确地从输入视频中获取帧(我不确定为什么)。

解决这个问题的一种方法是使用选择过滤器

示例(不使用 GPU 加速):

ffmpeg -i input.mp4 -vf "select=bitor(gte(t-prev_selected_t\,10)\,isnan(prev_selected_t))" -vsync 0 f%09d.jpg

  • bitor(gte(t-prev_selected_t\,10)1当“已传递”时间戳之间的差异大于等于 10 秒时。
    当表达式的计算结果为 时1,该帧被“选定”并传递到输出。
  • bitorwithisnan(prev_selected_t)传递第一帧,其中prev_selected_tNaN(没有值)。
  • -vsync 0应用“直通”——每个帧都带有其时间戳,从解复用器传递到复用器。

scale_cuda以下是使用和 的示例thumbnail_cuda

ffmpeg \
    -loglevel error \
    -hwaccel cuvid \
    -hwaccel_output_format cuda \
    -c:v h264_cuvid \
    -i "$video_file" \
    -filter:v "scale_cuda=w=-1:h=100,thumbnail_cuda=2,hwdownload,format=nv12,select=bitor(gte(t-prev_selected_t\,10)\,isnan(prev_selected_t))" \
    -vsync 0 \
    -color_range 2 \
    f%09d.jpg   
  • 由于使用了thumbnail_cuda过滤器,我们必须将select过滤器放在最后。

测试:
以 10fps 的帧计数器构建合成视频:

ffmpeg -y -f lavfi -r 10 -i testsrc=size=128x72:rate=1:duration=1000 -vf setpts=N/10/TB -vcodec libx264 -pix_fmt yuv420p input.mp4

执行上述命令后的输出帧:

在此处输入图片描述在此处输入图片描述在此处输入图片描述在此处输入图片描述在此处输入图片描述
在此处输入图片描述在此处输入图片描述在此处输入图片描述在此处输入图片描述在此处输入图片描述

如您所见,选定的帧恰好每 10 秒一次。

相关内容