FFmpegを使用してN秒ごとにフレームを正確に抽出する

FFmpegを使用してN秒ごとにフレームを正確に抽出する

使用法

私は を使用してビデオから画像を抽出しますffmpeg

私は 10 秒ごとに 1 つの縮小された画像をダンプし、 でモンタージュに組み合わせます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ビデオの開始時に余分な画像が 1 つ生成されることです。たとえば、ファイルは次のようになります。

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
...

場合によっては、1 番目と 2 番目が数ミリ秒ずれることがあります。

私の知る限りでは(今のところ)、最初の画像を削除して残りの作業を続行するだけで済みますが、なぜこれが起こるのか、バグなのか、それとも何か他の原因なのかはわかりません。

言い換えれば、最初の2フレームの「効果」が信頼性のある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
...

回答に基づいて現在使用しているコマンド(シェル):

# 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、フレームが「選択」され、出力に渡されます。
  • bitorisnan(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 秒ごとです。

関連情報