web-dev-qa-db-ja.com

各フレームがキーフレームで始まるようにFFMPEGを使用してビデオを分割する方法は?

大きなライブWMVビデオフィードをすべて同じサイズの小さなチャンクに分割する必要があります。ひとつだけを除いて、これをうまく動作させるスクリプトを作成しました:ビデオチャンクはキーフレームで始まらないため、ほとんどのビデオチャンクを再生するときに、元のビデオのキーフレームが最終的に表示されるまで画像を表示しません達した。

出力ビデオをキーフレームで開始するようにffmpegに指示する方法はありませんか?

コマンドラインの外観は次のとおりです。

ffmpeg.exe -i "C:\test.wmv" -ss 00:00:00 -t 00:00:05 -acodec copy -vcodec copy -async 1 -y  "0000.wmv"
ffmpeg.exe -i "C:\test.wmv" -ss 00:00:05 -t 00:00:05 -acodec copy -vcodec copy -async 1 -y  "0001.wmv"

等々...

32
sboisse

FFMPEGの最新ビルドには、新しいオプション「セグメント」が含まれています。

ffmpeg -i INPUT.mp4 -acodec copy -f segment -vcodec copy -reset_timestamps 1 -map 0 OUTPUT%d.mp4

これにより、キーフレームに基づいてセグメントに分割される一連の番号付き出力ファイルが生成されます。私自身のテストでは、数分以上MP4形式のみで使用したことはありませんが、うまく機能しています。

29
Tim Bull

公式の FFMPEG Docs で述べたように、-ss timestartbefore-i input_file.ext。これは、生成されたビデオの先頭を、指定されたタイムスタンプの前に見つかった最近接キーフレームに設定する(または理解している)ためです。

例を次のように変更します。

ffmpeg.exe -ss 00:00:00 -i "C:\test.wmv" -t 00:00:05 -acodec copy -vcodec copy -async 1 -y  "0000.wmv"

.flvおよび.mp4ファイル。

29

ここに私が働くことができる解決策があります:

Av501とd33pikaで示唆されているように、キーフレームの場所を見つけるためにffprobeを使用しました。 ffprobeは非常に冗長で、すべてのキーフレームを出力するのに数秒から数分かかる場合があり、長いビデオから必要なフレーム範囲をスコープする方法がないため、5つのステップに進みます。

  1. 目的のチャンクサイズの約2倍で、元のファイルからビデオチャンクをエクスポートします。

    ffmpeg -i source.wmv -ss 00:00:00 -t 00:00:06 -acodec copy -vcodec copy -async 1 -y  0001.wmv
    
  2. Ffprobeを使用して、キーフレームの場所を見つけます。目的のチャンクサイズに最も近いキーフレームを選択します。

    ffprobe -show_frames -select_streams v -print_format json=c=1 0001.wmv
    
  3. Ffprobeの出力から、そのキーフレームの直前のフレームのpkt_dts_timeを取得します。

  4. ステップ1のエクスポートされたチャンクのffmpeg。同じ入力ファイルと出力ファイルを指定し、-ss 00:00:00および-t [ステップ3で見つかった値]を指定します。

    ffmpeg -i 0001.wmv -ss 00:00:00 -t 00:00:03.1350000 -acodec copy -vcodec copy -async 1 -y 0001.wmv
    
  5. -ss [繰り返しにわたってステップ3で見つかった値の累積合計]を使用して、ステップ1から再開します。

このように進めると、ビデオをキーフレームで分割するための効率的で堅牢な方法を得ることができました。

19
sboisse

Ffmpegの新しいビルドを使用すると、ffprobeとffmpegセグメントマルチプレクサを使用してこれを実現できます。

1. ffprobeとawkを使用して、希望するチャンク長にできるだけ近いキーフレームを識別します。

ffprobe -show_frames -select_streams v:0 -print_format csv **[SOURCE_VIDEO]** 2>&1 | grep -n frame,video,1 | awk 'BEGIN { FS="," } { print $1 " " $5 }' | sed 's/:frame//g' | awk 'BEGIN { previous=0; frameIdx=0; size=0; } { split($2,time,"."); current=time[1]; if (current-previous >= **[DURATION_IN_SECONDS]**){ a[frameIdx]=$1; frameIdx++; size++; previous=current;} } END { str=a[0]; for(i=1;i<size;i++) { str = str "," a[i]; } print str;}'

どこで

  • [SOURCE_VIDEO] =セグメント化するビデオへのパス
  • [DURATION_IN_SECONDS] =秒単位の希望のセグメント長

出力は、キーフレームのカンマ区切り文字列です。

2. ffmpegへの入力として上記のキーフレーム出力を使用します。

ffmpeg -i [SOURCE_VIDEO] -codec copy -map 0 -f segment -segment_frames [OUTPUT_OF_STEP_1] [SEGMENT_PREFIX] _%03d .[SOURCE_VIDEO_EXTENSION] =

どこで

  • [SOURCE_VIDEO] =セグメント化するビデオへのパス
  • [OUTPUT_OF_STEP_1] =キーフレームのカンマ区切り文字列
  • [SEGMENT_PREFIX] =セグメント出力の名前
  • [SOURCE_VIDEO_EXTENSION] =ソースビデオの拡張子(mp4、mkvなど)
12
Steek

つかいます ffprobe -show_frames -pretty <stream>キーフレームを識別します。

6
d33pika

スクリプトを実行する意思があり、特定の間隔でフレームを作成する場合は、1つの方法があります

  1. Ffprobeを実行し、出力からIフレームの位置を収集します

    ffprobe -show_streams

  2. 同じスクリプトを使用して一連の-ss -tコマンドを実行し、必要なチャンクを取得します。

その後、スクリプトに最小フレーム数を決定させることができます(たとえば、10フレーム以内に2つのI画像がある場合、そこにチャンクすることは望ましくありません)。

もう1つの方法は、gstreamerのmultisfilesinkを使用して、モードをキーフレームに設定することです(ただし、すべてのキーフレームでチャンクされるため、理想的ではない場合があります)。

3
av501