
DASH 再生用にストリームを調整する場合、ランダム アクセス ポイントはすべてのストリームでソース ストリーム時間と同じである必要があります。これを行う通常の方法は、固定フレーム レートと固定 GOP 長 (つまり、N フレームごとにキーフレーム) を強制することです。
FFmpeg では、フレーム レートを固定するのは簡単です (-r NUMBER)。
しかし、固定キーフレーム位置 (GOP 長さ) の場合、3 つの方法があります...どれが「正しい」のでしょうか? FFmpeg のドキュメントは、この点に関してイライラするほど曖昧です。
方法1: libx264の引数を変更する
-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1
シーンカットが発生したときにキーフレーム「カウンター」が再開されるかどうかが不明であるため、シーンカットをオフにする必要があるかどうかについては議論があるようです。
方法2: 固定GOPサイズの設定:
-g GOP_LEN_IN_FRAMES
残念ながら、これは FFMPEG ドキュメントに簡単に記載されているだけなので、この議論の効果は非常に不明確です。
方法3: N秒ごとにキーフレームを挿入する(多分?):
-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
これは明示的に文書化されています。しかし、すべてのキー フレームの後に「時間カウンター」が再開されるかどうかは、まだすぐにはわかりません。たとえば、予想される 5 秒の GOP で、scenecut
libx264 によって 3 秒後にキーフレームが挿入された場合、次のキーフレームは 5 秒後になるのでしょうか、それとも 2 秒後になるのでしょうか。
実際、FFmpeg のドキュメントでは、このオプションと-g
オプションを区別していますが、上記の 2 つのオプションがどのように少しでも異なるのかは実際には説明されていません (明らかに、-g
固定フレーム レートが必要になります)。
どちらが正しいでしょうか?
-force_key_frames
が優れているように思われる固定フレームレートを必要としないためです。しかし、そのためには
- H.264のGOP仕様に準拠しています(もしあれば)
- libx264 キーフレームに関係なく、キーフレームが固定リズムで存在することを保証します
scenecut
。
-g
また、固定フレームレートを強制しないと動作しないようです( -r
)異なるコーデック引数でを複数回実行しても、各解像度で同じ瞬間フレーム レートが得られるという保証はありませんffmpeg
。フレーム レートを固定すると、圧縮パフォーマンスが低下する可能性があります (DASH シナリオでは重要)。
ついに、このkeyint
方法は単なるハックのようだ望みは薄いですが、これが正解ではないことを願っています。
参考文献:
答え1
要約
以下のことをお勧めします:
libx264
: (オプションで を追加)-g X -keyint_min X
-force_key_frames "expr:gte(t,n_forced*N)"
libx265
:-x265-params "keyint=X:min-keyint=X"
libvpx-vp9
:-g X
ここで、X
はフレーム単位の間隔、N
は秒単位の間隔です。たとえば、30fps のビデオで 2 秒間隔の場合、X
= 60、N
= 2 になります。
さまざまなフレームタイプに関する注意
このトピックを適切に説明するには、まず 2 種類の I フレーム / キーフレームを定義する必要があります。
- 瞬時デコーダーリフレッシュ (IDR) フレーム: IDR フレームの前のフレームにアクセスすることなく、後続のフレームを独立してデコードできます。
- 非 IDR フレーム: デコードが機能するには、前の IDR フレームが必要です。非 IDR フレームは、GOP (画像グループ) の途中のシーン カットに使用できます。
ストリーミングには何がおすすめですか?
ストリーミングの場合は、次のようになります。
- ビデオを同じ長さのセグメントに分割できるように、すべての IDR フレームが通常の位置 (例: 2 秒、4 秒、6 秒、… 秒) にあることを確認します。
- シーン カット検出を有効にして、コーディングの効率と品質を向上させます。つまり、IDR フレームの間に I フレームを配置できるようになります。シーン カット検出を無効にした状態でも作業できます (これは多くのガイドで依然として取り上げられています) が、必須ではありません。
パラメータは何をするのですか?
エンコーダーを構成するには、キーフレーム パラメーターが何をするかを理解する必要があります。いくつかテストを行った結果、3 つのエンコーダーと FFmpeg で次のことがわかりlibx264
ましたlibx265
。libvpx-vp9
libx264
:-g
キーフレーム間隔を設定します。-keyint_min
最小キーフレーム間隔を設定します。-x264-params "keyint=x:min-keyint=y"
と同じです-g x -keyint_min y
。注記:両方を同じ値に設定すると、最小値は内部的に次のように設定されます。半分コードに示されているように、最大間隔に 1 を加えた値です
x264
。h->param.i_keyint_min = x264_clip3( h->param.i_keyint_min, 1, h->param.i_keyint_max/2+1 );
libx265
:-g
実装されていません。-x265-params "keyint=x:min-keyint=y"
動作します。
libvpx-vp9
:-g
キーフレーム間隔を設定します。-keyint_min
最小キーフレーム間隔を設定します注記:FFmpeg の動作により、 は
-keyint_min
と同じ場合にのみエンコーダーに転送されます-g
。FFmpeg の のコードには次の内容libvpxenc.c
が記載されています。if (avctx->keyint_min >= 0 && avctx->keyint_min == avctx->gop_size) enccfg.kf_min_dist = avctx->keyint_min; if (avctx->gop_size >= 0) enccfg.kf_max_dist = avctx->gop_size;
これはバグ (または機能不足?) である可能性があります。 は、
libvpx
に異なる値を設定することを確実にサポートしていますkf_min_dist
。
を使うべきでしょうか-force_key_frames
?
この-force_key_frames
オプションは、指定された間隔 (式) でキーフレームを強制的に挿入します。これはすべてのエンコーダーで機能しますが、レート制御メカニズムに影響を及ぼす可能性があります。特に VP9 の場合、品質の大幅な変動が見られるため、この場合は使用をお勧めしません。
答え2
これがこの件に対する私の50セントです。
方法1:
libx264の引数をいじる
-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1
必要な間隔でのみ iframe を生成します。
例1:
ffmpeg -i test.mp4 -codec:v libx264 \
-r 23.976 \
-x264opts "keyint=48:min-keyint=48:no-scenecut" \
-c:a copy \
-y test_keyint_48.mp4
次のように期待どおりに iframe を生成します。
Iframes Seconds
1 0
49 2
97 4
145 6
193 8
241 10
289 12
337 14
385 16
433 18
481 20
529 22
577 24
625 26
673 28
721 30
769 32
817 34
865 36
913 38
961 40
1009 42
1057 44
1105 46
1153 48
1201 50
1249 52
1297 54
1345 56
1393 58
方法2は減価償却されます。省略。
方法3:
N 秒ごとにキーフレームを挿入します (おそらく):
-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
例2
ffmpeg -i test.mp4 -codec:v libx264 \
-r 23.976 \
-force_key_frames "expr:gte(t,n_forced*2)"
-c:a copy \
-y test_fkf_2.mp4
少し異なる方法で iframe を生成します。
Iframes Seconds
1 0
49 2
97 4
145 6
193 8
241 10
289 12
337 14
385 16
433 18
481 20
519 21.58333333
529 22
577 24
625 26
673 28
721 30
769 32
817 34
865 36
913 38
931 38.75
941 39.16666667
961 40
1008 42
1056 44
1104 46
1152 48
1200 50
1248 52
1296 54
1305 54.375
1344 56
1367 56.95833333
1392 58
1430 59.58333333
1440 60
1475 61.45833333
1488 62
1536 64
1544 64.33333333
1584 66
1591 66.29166667
1632 68
1680 70
1728 72
1765 73.54166667
1776 74
1811 75.45833333
1824 75.95833333
1853 77.16666667
1872 77.95833333
1896 78.95833333
1920 79.95833333
1939 80.75
1968 81.95833333
ご覧のとおり、iframe は 2 秒ごとに、またシーンカット (浮動部分のある秒数) に配置されますが、これはビデオ ストリームの複雑さにとって重要であると私は考えています。
生成されたファイルサイズはほぼ同じです。キーフレームを増やしても、方法3標準の x264 ライブラリ アルゴリズムよりも少ないファイルを生成する場合があります。
HLS ストリーム用の複数のビットレート ファイルを生成するために、方法 3 を選択します。チャンク間の間隔が 2 秒で完全に調整され、各チャンクの先頭に iframe があり、複雑なシーンには追加の iframe があるため、古いデバイスを使用していて x264 ハイ プロファイルを再生できないユーザーには、より優れたエクスペリエンスが提供されます。
誰かの役に立つことを願います。
答え3
DASH エンコーディングを希望どおりにセグメント化する方法を見つけるための情報を探す中で、Google 検索でこのディスカッションがかなり多く表示されたのですが、見つかった情報はどれも完全に正しいものではなかったため、ここに情報を追加したいと思いました。
まず最初に取り除くべきいくつかの誤解:
すべての I フレームが同じというわけではありません。大きな「I」フレームと小さな「i」フレームがあります。または、正確な用語を使用すると、IDR I フレームと非 IDR I フレームがあります。IDR I フレーム (「キーフレーム」と呼ばれることもあります) は新しい GOP を作成します。非 IDR フレームは作成しません。シーンが変わる GOP 内に置くと便利です。
-x264opts keyint=GOPSIZE:min-keyint=GOPSIZE
← これは、あなたが考えているような動作をしません。これを理解するのに少し時間がかかりました。 はmin-keyint
コード内で制限されていることがわかりました。 より大きい値は許可されていません(keyint / 2) + 1
。そのため、これら 2 つの変数に同じ値を割り当てると、エンコード時に の値がmin-keyint
半分に減らされます。
問題は、シーン カットが本当に素晴らしいことです。特に、高速なハード カットがあるビデオでは、その効果は絶大です。シーン カットは、映像を鮮明に保ってくれるので、無効にしたくありませんが、有効にしたままでは、GOP サイズを固定することができませんでした。シーン カットを有効にして、非 IDR I フレームのみを使用するようにしたかったのです。しかし、うまくいきませんでした。誤解 2 について (たくさん読んで) 理解するまでは。
結局、keyint
希望する GOP サイズの 2 倍に設定する必要がありました。つまり、min-keyint
を希望する GOP サイズに設定できるということです (内部コードで半分にカットされることなく)。これにより、最後の IDR I フレーム以降のフレーム数は常に 未満であるため、シーン カット検出が GOP サイズ内で IDR I フレームを使用することを防止できますmin-keyinit
。
そして最後に、force_key_frame
オプションを設定すると、 double size が上書きされますkeyint
。 動作は次のとおりです。
私は2秒単位のセグメントを好むので、GOPSIZE = フレームレート * 2
ffmpeg <other_options> -force_key_frames "expr:eq(mod(n,<GOPSIZE>),0)" -x264opts rc-lookahead=<GOPSIZE>:keyint=<GOPSIZE * 2>:min-keyint=<GOPSIZE> <other_options>
ffprobe を使用して確認できます:
ffprobe <SRC_FLE> -select_streams v -show_frames -of csv -show_entries frame=coded_picture_number,key_frame,pict_type > frames.csv
生成された CSV ファイルの各行には次の内容が表示されますframe, [is_an_IDR_?], [frame_type], [frame_number]
。
frame,1,I,60 <-- frame 60, is I frame, 1 means is an IDR I-frame (aka KeyFrame)
frame,0,I,71 <-- frame 71, is I frame, 0 means not an IDR I_frame
その結果、IDR I フレームは固定GOPSIZE
間隔でのみ表示され、その他のすべての I フレームはシーン カット検出によって必要に応じて挿入される非 IDR I フレームになります。
答え4
Twitch にはこれに関する投稿があります。彼らは、いくつかの理由から独自のプログラムを使用することにしたと説明しています。その 1 つは、ffmpeg では異なるスレッドで異なる x264 インスタンスを実行できず、代わりに指定されたすべてのスレッドを 1 つの出力の 1 つのフレームに割り当ててから次の出力に進むためです。
リアルタイム ストリーミングを行わない場合は、より贅沢な方法があります。おそらく「正しい」方法は、-g で指定された GOP サイズのみを使用して 1 つの解像度でエンコードし、次に同じ場所にキーフレームを強制して他の解像度をエンコードすることです。
これを実行したい場合は、ffprobe を使用してキーフレーム時間を取得し、シェル スクリプトまたは実際のプログラミング言語を使用してそれを ffmpeg コマンドに変換します。
しかし、ほとんどのコンテンツでは、5 秒ごとに 1 つのキーフレームと 5 秒ごとに 2 つのキーフレーム (1 つは強制、もう 1 つはシーンカットから) の間にほとんど違いはありません。これは、平均 I フレーム サイズと P フレームおよび B フレームのサイズに関するものです。x264 を一般的な設定で使用し (これらに影響を与える必要がある唯一の理由は、-qmin を設定する場合です。これは、x264 が簡単なコンテンツでビットレートを使用しないようにするための不適切な方法です。これにより、すべてのフレーム タイプが同じ値に制限されると思います)、I フレームの平均サイズが 46 kB、P フレームが 24 kB、B フレームが 17 kB (P フレームの半分の頻度) のような結果になった場合、30 fps で毎秒 1 つの余分な I フレームはファイル サイズが 3% 増加するだけです。h264 と h263 の違いは、3% の減少の集まりである可能性がありますが、1 つだけはあまり重要ではありません。
他の種類のコンテンツでは、フレーム サイズは異なります。公平を期すために言うと、これは時間的な複雑さに関するもので、空間的な複雑さに関するものではないため、簡単なコンテンツと難しいコンテンツの違いだけではありません。ただし、一般的に、ストリーミング ビデオ サイトにはビットレート制限があり、比較的大きな I フレームを持つコンテンツは、キーフレームがいくつ追加されても高品質でエンコードされる簡単なコンテンツです。これは無駄ですが、この無駄は通常は気付かれません。最も無駄なケースは、各キーフレームがまったく同じで、曲に付随する静止画像だけのビデオでしょう。
よくわからないのは、強制キーフレームが -maxrate および -bufsize で設定されたレート リミッターとどのように相互作用するかです。YouTube でも最近、一貫した品質を提供するためにバッファー設定を正しく構成できない問題が発生していると思います。一部のサイトで見られるように、平均ビットレート設定のみを使用している場合 (16 進エディターを使用して、ヘッダー/mov アトムの x264 のオプションを調べることができるため)、バッファー モデルは問題になりませんが、ユーザー生成コンテンツを提供している場合、平均ビットレートは、ユーザーがビデオの最後に黒い画面を追加することを促します。
Ffmpeg の -g オプション、または使用するその他のエンコーダー オプションは、エンコーダー固有のオプションにマップされます。したがって、「-x264-params keyint=GOPSIZE」は「-g GOPSIZE」と同等です。
シーン検出を使用する際の 1 つの問題は、何らかの理由で特定の数字に近いキーフレームを希望する場合です。5 秒ごとにキーフレームを指定してシーン検出を使用すると、4.5 でシーン変更があった場合、それは検出されるはずですが、次のキーフレームは 9.5 になります。時間がこのようにステップアップし続けると、40、45、50、55 ではなく、42.5、47.5、52.5 などのキーフレームになる可能性があります。逆に、5.5 でシーン変更があった場合、5 にキーフレームがあり、5.5 は次のキーフレームには早すぎます。Ffmpeg では、「次の 30 フレーム以内にシーン変更がない場合は、ここでキーフレームを作成する」という指定はできません。ただし、C を理解できる人なら、そのオプションを追加できます。
可変フレームレートのビデオの場合、Twitch のようにライブストリーミングしていないときは、固定フレームレートに永続的に変換せずにシーン変更を使用できるはずです。ffmpeg で 'select' フィルターを使用し、式で 'scene' 定数を使用すると、デバッグ出力 (-v debug またはエンコード中に '+' を数回押す) にシーン変更番号が表示されます。これはおそらく x264 で使用される番号とは異なり、それほど便利ではありませんが、それでも役に立つ可能性があります。
手順としては、おそらくキーフレームの変更のみのテストビデオを作成することになりますが、2パスを使用する場合はレート制御データに使用できる可能性があります。(生成されたデータがさまざまな解像度や設定に役立つかどうかはわかりません。マクロブロックツリーデータは役に立ちません。)これを固定フレームレートビデオに変換しますが、このバグfps フィルターを他の目的で使用することにした場合、フレームレートを半分にすると出力が途切れることがあります。希望するキーフレームと GOP 設定で x264 で実行してください。
次に、これらのキーフレーム時間を元の可変フレームレート ビデオで使用します。
フレーム間に 20 秒のギャップがある、完全にクレイジーなユーザー生成コンテンツを許可する場合、可変フレーム レート エンコードでは、出力を分割し、fps フィルターを使用し、何らかの方法で選択フィルターを使用します (すべてのキーフレーム時間を含む非常に長い式を構築するなど)。または、テスト ビデオを入力として使用し、ffmpeg オプションが機能する場合はキーフレームのみをデコードするか、選択フィルターを使用してキーフレームを選択することもできます。次に、正しいサイズに拡大縮小し (このための scale2ref フィルターもあります)、元のビデオをその上にオーバーレイします。次に、インターリーブ フィルターを使用して、強制される予定のこれらのキーフレームを元のビデオと結合します。これにより、インターリーブ フィルターでは防止できない 0.001 秒離れた 2 つのフレームが生成された場合は、別の選択フィルターを使用してこの問題に対処してください。インターリーブ フィルターのフレーム バッファー制限に対処することが、ここでの主な問題である可能性があります。これらはすべて機能する可能性があります。何らかのフィルターを使用して、より密度の高いストリームをバッファーします (fifo フィルターなど)。入力ファイルを複数回参照して複数回デコードし、フレームを保存する必要がないようにします。キーフレームの正確な時間に「streamselect」フィルターを使用します (私はこれまで一度も使用していません)。インターリーブ フィルターの既定の動作を変更するか、フレームをドロップする代わりにバッファー内の最も古いフレームを出力するオプションを追加して、インターリーブ フィルターを改善します。