DASH再生用にストリームを調整する場合、ランダムアクセスポイントはすべてのストリームの中でまったく同じソースストリーム時間になければなりません。これを行う通常の方法は、固定フレームレートおよび固定GOP長(すなわち、Nフレームごとにキーフレーム)を強制することである。
FFmpegでは、固定フレームレートは簡単です(-r NUMBER)。
しかし、固定キーフレーム位置(GOP長)の場合、3つの方法があります...どれが「正しい」のでしょうか? FFmpegのドキュメンテーションはこれについてイライラして曖昧です。
-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1
シーンカットが発生したときにキーフレームの「カウンタ」が再開されるかどうかは不明であるため、シーンカットをオフにするかどうかについてはある程度の議論があるようです。
-g GOP_LEN_IN_FRAMES
残念ながら、これはFFMPEGドキュメンテーションの引き渡しにおいてのみ文書化されているため、この議論の影響は非常に不明瞭です。
-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
これは明示的に文書化されています。しかし、「時間カウンタ」がすべてのキーフレームの後に再開するかどうかはまだすぐには明らかではありません。たとえば、5秒間のGOPで、libx264によって3秒以内にscenecut
キーフレームが挿入された場合、次のキーフレームは5秒後になるのでしょうか、それとも2秒後になるのでしょうか。
実際、FFmpegのドキュメントではこれと-g
オプションを区別していますが、上記の2つのオプションの違いが最も少ないということは実際にはわかりません(明らかに、-g
には固定フレームレートが必要です)。
-force_key_frames
は固定フレームレートを必要としないため、優れているように思われます。 ただし、これにはが必要です。
scenecut
のキーフレームに関係なく、固定のリズムでキーフレームがあることを保証します。また、-g
は固定フレームレート(-r
)を強制しないと機能しない可能性があります。これは、異なるコーデック引数を持つffmpeg
を複数回実行しても同じ瞬間値が得られるという保証はないためです。各解像度のフレームレート。固定フレームレートは、圧縮性能を低下させる可能性があります(DASHシナリオでは重要です)。
最後に、keyint
メソッドはハックのようです。私はこれが正しい答えではないことを願って反対しています。
参考文献:
そのため、答えは次のようになります。
libx264
特有であり、libx264
の非常に便利なscenecut
オプションを削除するという犠牲を払っています。-g
は推奨されていないようです動作しないようで、ドキュメントで明示的に定義されていることも、ドキュメントで定義されていることもありません。また、コード内で使用されているようには見えません。コード検査により、-g
オプションはおそらくMPEG-2ストリーム用であることがわかります(PALおよびNTSCを参照するコードスタンザさえあります)。また:
-force_key_frames
オプション用のスクリプトこれはslhckのffprobe提案の出力に基づいてIフレームケイデンスを検証するために使った短いPerlプログラムです。 -force_key_frames
メソッドも機能することを確認しているようで、scenecut
フレームを許容するという追加の利点があります。 FFMPEGがどのように機能するのか、あるいは私のストリームがうまく調整されているために何らかの形でうまく動かなかったのかどうか、私にはまったくわかりません。
私の場合は、6秒の予想GOPサイズ、つまり180フレームで30fpsでエンコードしました。このプログラムのgopsize引数として180を使用して、180の倍数ごとにIフレームを検証しましたが、181(または180の倍数ではない他の任意の数)に設定すると文句を言われました。
#!/usr/bin/Perl
use strict;
my $gopsize = shift(@ARGV);
my $file = shift(@ARGV);
print "GOPSIZE = $gopsize\n";
my $linenum = 0;
my $expected = 0;
open my $pipe, "ffprobe -i $file -select_streams v -show_frames -of csv -show_entries frame=pict_type |"
or die "Blah";
while (<$pipe>) {
if ($linenum > $expected) {
# Won't catch all the misses. But even one is good enough to fail.
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\n";
} else {
#print "IFrame HIT at $expected\n";
$expected += $gopsize;
}
}
$linenum += 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フレーム/キーフレームを定義する必要があります。
ストリーミングの場合は、次のことを行います。
エンコーダを設定するために、キーフレームパラメータが何をするのか理解しなければなりません。 FFmpegの3つのエンコーダlibx264
、libx265
およびlibvpx-vp9
について、いくつかテストを行い、次のことを発見しました。
libx264
:
-g
はキーフレーム間隔を設定します。-keyint_min
は最小キーフレーム間隔を設定します。-x264-params "keyint=x:min-keyint=y"
は-g x -keyint_min y
と同じです。注:両方を同じ値に設定すると、最小値は内部的に half 最大間隔プラスx264
コードに見られるように、1つ:
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に関しては、私は深刻な品質変動に気づいたので、この場合それを使うことはお勧めできません。
これは私の場合の50セントです。
方法1:
libx264の引数をいじる
-c:v libx264 -x264opts keyint = GOPSIZE:最小keyint = GOPSIZE:シーンカット= -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秒ごとにキーフレームを挿入する(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 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
ご覧のとおり、2秒ごとにiframeが配置され、シーンカット(浮動部分のある秒数)に配置されます。これは、ビデオストリームの複雑さにとって重要です。
生成されたファイルサイズはほぼ同じです。非常に奇妙なことに、方法3でより多くのキーフレームがあっても、標準のx264ライブラリアルゴリズムよりも少ないファイルしか生成されないことがあります。
HLSストリーム用に複数のビットレートファイルを生成するには、方法3を選択します。それはチャンクの間の2秒と完全に一致しています、彼らはあらゆるチャンクの始めにiframeを持っています、そして彼らは時代遅れの装置を持っていてx264ハイプロファイルを再生できないユーザーにとってより良い経験を提供します.
誰かに役立つことを願っています。
私のグーグルが私のDASHエンコーディングを自分の望む方法でセグメント化する方法を見つけるための情報を探すために私のグーグルがこの議論をかなり引き上げたので、ここに情報を追加したいと思いました。
取り除くための最初のいくつかの誤解:
すべての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について(たくさん読むことから)考え出すまで。
目的のGOPサイズを2倍にするためにkeyint
を設定する必要があることがわかりました。これはmin-keyint
を私の望むGOPサイズに設定することができることを意味します(内部コードは半分にそれをカットしません)。常にmin-keyinit
より小さい。
そして最後にforce_key_frame
オプションを設定すると、倍サイズのkeyint
がオーバーライドされます。それで、ここでうまくいくのです:
私は2秒単位のセグメントを好むので、私のGOPSIZE = Framerate * 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
その結果、固定GOPSIZE
の間隔でのみIDR I-Frameが表示されるはずですが、他のすべてのIフレームはシーンカット検出で必要に応じて挿入された非IDR I-frameです。
私たちのVODコンテンツやライブコンテンツ(ファイルダンプ)についてはかなりテストされていますが、シーンカットがうまく動作せず、中間のiframeが発生することもあります。
I50-> p50アップコンバージョン、2秒gop/segment、開始時のIDR、必要ならばその間のiframeの構文
ffmpeg.exe -loglevel verbose -i avc_50i.ts -pix_fmt yuv420p -filter_complex yadif=1,scale=1920:1080 -vcodec libx264 -preset fast -x264-params "rc-lookahead=100:keyint=200:min-keyint=100:hrd=1:vbv_maxrate=12000:vbv_bufsize=12000:no-open-gop=1" -r 50 -crf 22 -force_key_frames "expr:eq(mod(n,100),0)" -codec:a aac -b:a 128k -y target.ts
Twitchがこれについて投稿しています。彼らは、彼らがいくつかの理由で彼ら自身のプログラムを使うことに決めたと説明します。そのうちの1つは、ffmpegでは異なるx264インスタンスを異なるスレッドで実行することを許可せず、代わりに次の出力に進む前に1つの出力の1つのフレームにすべての指定されたスレッドを当てるということでした。
あなたがリアルタイムストリーミングをしていないなら、あなたはもっと贅沢を持っています。 「正しい」方法は、-gで指定されたGOPサイズだけで1つの解像度でエンコードしてから、他の解像度を同じ場所に強制的にエンコードすることです。
それをしたい場合は、ffprobeを使用してキーフレーム時間を取得してから、シェルスクリプトまたは実際のプログラミング言語を使用してそれをffmpegコマンドに変換することができます。
しかし、ほとんどのコンテンツでは、5秒ごとに1つのキーフレームを使用することと5秒ごとに2つのキーフレームを使用することの違いはほとんどありません(1つは強制的、もう1つはシーンカットから)。これは、Iフレームの平均サイズとPフレームおよびBフレームのサイズの差です。一般的な設定でx264を使用する場合(これらに影響を与えるために何かをすべきだと思う唯一の理由は、x264が簡単なコンテンツでビットレートを使用しないようにする悪い方法として-qminを設定する場合です。 、私は思う)と46キロバイトのIフレームの平均サイズ、Pフレームの24キロバイト、Bフレームの17キロバイト(Pフレームの半分の頻度)、そして30 fpsで毎秒余分なIフレームのような結果を得るファイルサイズのわずか3%の増加です。 h264とh263の違いは3%減少したものであるかもしれませんが、1つでもそれほど重要ではありません。
他の種類のコンテンツでは、フレームサイズが異なります。公平に言えば、これは時間的な複雑さに関するものであり、空間的な複雑さに関するものではないので、それは単なるコンテンツ対ハードコンテンツではありません。しかし、一般的に、ストリーミングビデオサイトにはビットレートの制限があり、比較的大きなIフレームを含むコンテンツは、追加のキーフレームがいくつ追加されても、高品質でエンコードされる簡単なコンテンツです。それは無駄ですが、この無駄は通常気づかれないでしょう。最も無駄なケースは、おそらく、各キーフレームがまったく同じである、曲に付随する単なる静止画であるビデオです。
よくわからないことの1つは、強制キーフレームが-maxrateおよび-bufsizeで設定されたレートリミッタとどのように相互作用するかです。 YouTubeでも、一貫した品質を得るためにバッファ設定を正しく構成することに最近問題があったと思います。いくつかのサイトで見られるような平均ビットレート設定を使っているだけなら(hex /エディタでheader/mov atom?の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フィルターを使用し、何らかの形でselectフィルターを使用します。あるいは、テストビデオを入力として使用して、そのffmpegオプションが機能する場合はキーフレームのみをデコードするか、選択フィルタを使用してキーフレームを選択することもできます。それからそれを正しいサイズにスケーリングし(これにはscale2refフィルタさえあります)、その上にオリジナルのビデオを重ねます。次に、インターリーブフィルタを使用して、これらの強制キーフレームを元のビデオと結合します。これがインターリーブフィルタが防ぐことができない0.001秒間隔である2つのフレームという結果に終る場合、そして別の選択フィルタでこの問題に自分自身で対処してください。ここでの主な問題は、インターリーブフィルタのフレームバッファ制限を処理することです。これらはすべてうまくいく可能性があります。高密度のストリームをバッファリングするためにある種のフィルタを使用します(fifo filter?)。入力ファイルを複数回参照するので、複数回デコードされ、フレームを保存する必要はありません。私がこれまで一度も行ったことのない「streamselect」フィルタを、キーフレームのタイミングで使用します。デフォルトの振る舞いを変更するか、またはフレームを落とす代わりに最も古いフレームをバッファに出力するオプションを追加することによって、インターリーブフィルタを改善します。