DASH用のFFmpegでキーフレームを修正する正しい方法は何ですか?



What Is Correct Way Fix Keyframes Ffmpeg



解決:

TL; DR

私は次のことをお勧めします:

  • libx264:-NS NS -keyint_min NS (およびオプションで追加-force_key_frames'expr:gte(t、n_forced * NS ) ')
  • libx265:-x265-params'keyint = NS :min-keyint = NS '
  • libvpx-vp9:-NS NS

どこXはフレーム単位の間隔であり、Nは秒単位の間隔です。たとえば、30 fpsのビデオで2秒間隔の場合、X = 60お​​よびN = 2。



さまざまなフレームタイプに関する注意

このトピックを適切に説明するには、最初に2種類のIフレーム/キーフレームを定義する必要があります。

  • 瞬時デコーダリフレッシュ(IDR)フレーム:これらは、IDRフレームより前のフレームにアクセスすることなく、次のフレームの独立したデコードを可能にします。
  • 非IDRフレーム:デコードを機能させるには、前のIDRフレームが必要です。非IDRフレームは、GOP(写真のグループ)の途中でのシーンカットに使用できます。

ストリーミングには何が推奨されますか?

ストリーミングの場合、次のことを行います。



  • ビデオを同じ長さのセグメントに分割できるように、すべてのIDRフレームが通常の位置(2、4、6、…秒など)にあることを確認してください。
  • シーンカット検出を有効にして、コーディングの効率/品質を向上させます。これは、IフレームをIDRフレームの間に配置できるようにすることを意味します。シーンカット検出を無効にしても作業できますが(これは多くのガイドの一部です)、必須ではありません。

パラメータは何をしますか?

エンコーダーを構成するには、キーフレームパラメーターの機能を理解する必要があります。私はいくつかのテストを行い、3つのエンコーダーについて次のことを発見しましたlibx264、libx265およびFFmpegのlibvpx-vp9:

  • libx264:

    • -gはキーフレーム間隔を設定します。
    • -keyint_minは、最小キーフレーム間隔を設定します。
    • -x264-params'keyint = x:min-keyint = y 'はと同じです-g x -keyint_miny。
    • ノート: 両方を同じ値に設定すると、最小値は内部的に次のように設定されます。 半分 に見られるように、最大​​間隔プラス1x264コード:



      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は、それがと同じである場合にのみエンコーダに転送されます-NS。からのコードで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?

NS-force_key_framesオプションは、指定された間隔(式)でキーフレームを強制的に挿入します。これはすべてのエンコーダーで機能しますが、レート制御メカニズムが混乱する可能性があります。特にVP9の場合、品質の変動が激しいので、今回の使用はお勧めできません。


これが私の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 4145 6193 8241 10289 12337 14385 16433 18481 20529 22577 24625 26673 28721 30769 32817 34865 36 913 38961 401009 42 1057 44 1105 46 1153 48 1201 50 1249 52 1297 54 1345 56 1393 58

方法2は減価償却されます。省略。

方法3:

N秒ごとにキーフレームを挿入します(MAYBE):

-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 4145 6193 8241 10289 12337 14385 16433 18481 20519 21.58333333 529 22577 24625 26673 28721 30769 32817 34865 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 1824 75.95833333 1853 77.16666667 1872 77.95833333 1896 78.95833333 1920 79.95833333 1939 80.75 1968 81.95833333

ご覧のとおり、2秒ごとにiframeを配置し、シーンカット(フローティング部分のある秒)に配置します。これは、私の意見ではビデオストリームの複雑さにとって重要です。

生成されるファイルサイズはほとんど同じです。より多くのキーフレームがあっても非常に奇妙です 方法3 標準のx264ライブラリアルゴリズムよりも少ないファイルを生成する場合があります。

HLSストリームの複数のビットレートファイルを生成するには、方法3を選択します。チャンク間の2秒と完全に一致し、すべてのチャンクの先頭にiframeがあり、複雑なシーンに追加のiframeがあり、古いデバイスを使用していてx264ハイプロファイルを再生できないユーザーに優れたエクスペリエンスを提供します。

それが誰かを助けることを願っています。


したがって、答えは次のように思われます。

  • 方法1は機能することが確認されていますが、libx264固有であり、非常に便利なものを排除するという犠牲を払って提供されますのシーンカットオプションlibx264。
  • 方法3は2015年4月のFFMPEGバージョンの時点で機能しますが、FFMPEGのドキュメントではオプションの効果が不明であるため、この投稿の下部に含まれているスクリプトを使用して結果を確認する必要があります。それが機能する場合、それは2つのオプションの優れています。
  • 方法2は使用しないでください。-gは非推奨のようです。 動作しているようには見えず、ドキュメントで明示的に定義されておらず、ヘルプにも記載されておらず、コードで使用されているようにも見えません。コード検査は、-gオプションは、MPEG-2ストリームを対象としている可能性があります(PALおよびNTSCを参照するコードスタンザもあります!)。

また:

  • インタースティシャルIフレーム(キーフレーム)が許可されているため、方法3で生成されたファイルは方法1よりもわずかに大きくなる場合があります。
  • 方法3では、またはの次のフレームスロットにIフレームを配置しますが、どちらの場合も「-r」フラグを明示的に設定する必要があります。 指定された時間。 '-r'フラグを設定しないと、フレームレートが変動する可能性があるため、ソースファイルに翻弄されます。互換性のないDASH遷移が発生する可能性があります。
  • FFMPEGドキュメントの警告にもかかわらず、方法3は いいえ 他よりも効率が悪い。実際、テストでは、方法1よりもわずかに効率的である可能性があることが示されています。

のスクリプト-force_key_framesオプション

これは、slhckのffprobe提案の出力に基づいてIフレームのリズムを検証するために使用した短いPERLプログラムです。確認しているようです-force_key_framesメソッドも機能し、次のことができるという追加の利点があります。シーンカットフレーム。私はFFMPEGがこれをどのように機能させるのか、あるいは私のストリームがたまたまよく調整されているのでどういうわけか運が良かったのかどうか全くわかりません。

私の場合、予想されるGOPサイズが6秒、つまり180フレームの30fpsでエンコードしました。このプログラムのgopsize引数として180を使用して、180の倍数ごとにIフレームを検証しましたが、181(または180の倍数以外の他の数値)に設定すると文句が表示されました。

#!/ usr / bin / perl use strict;私の$ gopsize = shift(@ARGV);私の$ file = shift(@ARGV); print'GOPSIZE = $ gopsize  n ';私の$ linenum = 0;私の$ expected = 0; $ pipeを開き、 'ffprobe -i $ file -select_streams v -show_frames -of csv -show_entries frame = pict_type |'または死ぬ 'ブラ'; while(){if($ linenum> $ expected){#すべてのミスをキャッチしません。しかし、1つでも失敗するのに十分です。 print'Missed IFrame at $ expected  n '; $ expected =(int($ linenum / $ gopsize)+ 1)* $ gopsize; } if(m /、I  s * $ /){if($ linenum<$expected) { # Don't care term, just an extra I frame. Snore. #print 'Free IFrame at $linenum
'; } else { #print 'IFrame HIT at $expected
'; $expected += $gopsize; } } $linenum += 1; }