Wenn Sie einen Stream für die DASH-Wiedergabe aufbereiten, müssen sich die Direktzugriffspunkte in allen Streams zur exakt gleichen Quellstreamzeit befinden. Der übliche Weg, dies zu tun, besteht darin, eine feste Bildrate und eine feste GOP-Länge (d. H. Ein Schlüsselbild alle N Bilder) zu erzwingen.
Bei FFmpeg ist die feste Bildrate einfach (-r NUMBER).
Aber für feste Keyframe-Positionen (GOP-Länge) gibt es drei Methoden ... welche ist "korrekt"? Die FFmpeg-Dokumentation ist diesbezüglich frustrierend vage.
-c:v libx264 -x264opts keyint=GOPSIZE:min-keyint=GOPSIZE:scenecut=-1
Es scheint eine Debatte darüber zu geben, ob Scenecut deaktiviert werden soll oder nicht, da unklar ist, ob der Keyframe- "Zähler" neu gestartet wird, wenn ein Szenenschnitt stattfindet.
-g GOP_LEN_IN_FRAMES
Dies ist leider nur beiläufig in der FFMPEG-Dokumentation dokumentiert, und daher ist die Wirkung dieses Arguments sehr unklar.
-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
Dieses is ist explizit dokumentiert. Es ist aber immer noch nicht sofort klar, ob der "Zeitzähler" nach jedem Schlüsselbild neu startet. Wenn beispielsweise in einer erwarteten 5-Sekunden-GOP ein Keyframe scenecut
3 Sekunden von libx264 eingefügt wird, ist der nächste Keyframe 5 Sekunden später oder 2 Sekunden später?
In der FFmpeg-Dokumentation wird zwischen dieser und der -g
-Option unterschieden, aber es wird nicht wirklich angegeben, wie diese beiden Optionen sich am wenigsten unterscheiden (offensichtlich erfordert -g
eine feste Bildrate).
Der -force_key_frames
scheint überlegen zu sein , da keine feste Framerate erforderlich ist. Dies setzt jedoch voraus, dass
scenecut
name__-Keyframes. Es scheint auch, dass -g
nicht funktionieren könnte, ohne eine feste Bildrate (-r
) zu erzwingen , da nicht garantiert werden kann, dass mehrere Durchläufe von ffmpeg
mit unterschiedlichen Codec-Argumenten in jeder Auflösung die gleiche momentane Bildrate liefern. Feste Bildraten können die Komprimierungsleistung verringern (WICHTIG in einem DASH-Szenario!).
Schließlich scheint die Methode keyint
nur ein Hack zu sein . Ich hoffe gegen die Hoffnung, dass dies nicht die richtige Antwort ist.
Verweise:
Ein Beispiel mit der -force_key_frames
Methode
Die Antwort scheint daher zu sein:
libx264
spezifisch und geht zu Lasten der Eliminierung der sehr nützlichen Option scenecut
in libx264
.-g
scheint veraltet zu sein. Es scheint weder zu funktionieren, noch ist es explizit in der Dokumentation definiert, noch ist es in der Hilfe zu finden, noch scheint es im Code verwendet zu werden. Die Überprüfung des Codes zeigt, dass die Option -g
wahrscheinlich für MPEG-2-Streams gedacht ist (es gibt sogar Codezeilen, die sich auf PAL und NTSC beziehen!).Ebenfalls:
-force_key_frames
Hier ist ein kurzes Perl-Programm, mit dem ich die I-Frame-Trittfrequenz basierend auf der Ausgabe von slhcks ffprobe-Vorschlag überprüft habe. Es scheint zu überprüfen, ob die -force_key_frames
-Methode auch funktioniert, und hat den zusätzlichen Vorteil, dass scenecut
-Frames zulässig sind. Ich habe absolut keine Ahnung, wie FFMPEG diese Arbeit macht, oder ob ich irgendwie Glück gehabt habe, weil meine Streams zufällig gut konditioniert sind.
In meinem Fall habe ich mit 30 fps mit einer erwarteten GOP-Größe von 6 Sekunden oder 180 Bildern codiert. Ich habe 180 als gopsize-Argument für dieses Programm verwendet und bei jedem Vielfachen von 180 ein I-Frame verifiziert. Wenn ich es jedoch auf 181 (oder eine andere Zahl, die kein Vielfaches von 180 ist) einstelle, beschwert sich das Programm.
#!/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;
}
Ich würde folgendes empfehlen:
libx264
: -g X -keyint_min X
(und optional -force_key_frames "expr:gte(t,n_forced*N)"
hinzufügen)libx265
: -x265-params "keyint=X:min-keyint=X"
libvpx-vp9
: -g X
dabei ist X
das Intervall in Frames und N
das Intervall in Sekunden. Beispiel: Für ein 2-Sekunden-Intervall mit einem 30-fps-Video ist X
= 60 und N
= 2.
Um dieses Thema richtig zu erklären, müssen wir zuerst die beiden Arten von I-Frames/Keyframes definieren:
Für den Streaming-Fall möchten Sie:
Um den Encoder konfigurieren zu können, müssen wir verstehen, was die Keyframe-Parameter bewirken. Ich habe einige Tests durchgeführt und Folgendes für die drei Encoder libx264
, libx265
und libvpx-vp9
in FFmpeg entdeckt:
libx264
:
-g
legt das Keyframe-Intervall fest.-keyint_min
legt das minimale Keyframe-Intervall fest.-x264-params "keyint=x:min-keyint=y"
ist dasselbe wie -g x -keyint_min y
./ - Hinweis: Wenn beide auf den gleichen Wert gesetzt werden, wird das Minimum intern auf halb das maximale Intervall plus eins gesetzt, wie im Code x264
zu sehen ist:
h->param.i_keyint_min = x264_clip3( h->param.i_keyint_min, 1, h->param.i_keyint_max/2+1 );
libx265
:
-g
ist nicht implementiert.-x265-params "keyint=x:min-keyint=y"
funktioniert.libvpx-vp9
:
-g
legt das Keyframe-Intervall fest.-keyint_min
legt das minimale Keyframe-Intervall fest Hinweis: Aufgrund der Funktionsweise von FFmpeg wird -keyint_min
nur an den Encoder weitergeleitet, wenn er mit -g
identisch ist. Im Code von libvpxenc.c
in FFmpeg finden wir:
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;
Dies kann ein Fehler sein (oder ein Mangel an Funktionen?), Da libvpx
definitiv das Festlegen eines anderen Werts für kf_min_dist
unterstützt.
-force_key_frames
verwenden?Mit der Option -force_key_frames
werden im angegebenen Intervall (Ausdruck) zwangsweise Keyframes eingefügt. Dies funktioniert bei allen Encodern, kann jedoch zu Problemen mit der Geschwindigkeitskontrolle führen. Insbesondere bei VP9 sind mir starke Qualitätsschwankungen aufgefallen, daher kann ich die Verwendung in diesem Fall nicht empfehlen.
Hier sind meine fünfzig Cent für den Fall.
Methode 1:
mit den Argumenten von libx264 herumspielen
-c: v libx264 -x264opts keyint = GOPSIZE: min-keyint = GOPSIZE: scenecut = -1
Generieren Sie iframes nur in den gewünschten Intervallen.
Beispiel 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
Generiere iframes wie erwartet so:
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
Methode 2 wird abgeschrieben. Weggelassen
Methode 3:
füge alle N Sekunden einen Keyframe ein (MÖGLICHERWEISE):
-force_key_frames expr: gte (t, n_forced * GOP_LEN_IN_SECONDS)
Beispiel 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
Generieren Sie ein iframes auf eine etwas andere Art:
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
Wie Sie sehen, werden alle 2 Sekunden IFrames UND auf Scenecut (Sekunden mit schwebendem Teil) platziert, was meiner Meinung nach für die Komplexität des Videostreams wichtig ist.
Genearated Dateigrößen sind ziemlich gleich. Sehr seltsam, dass selbst mit mehr Keyframes in Methode 3 manchmal weniger Dateien erzeugt werden als mit dem normalen x264-Bibliotheksalgorithmus.
Um mehrere Bitrate-Dateien für den HLS-Stream zu generieren, wählen wir Methode drei. Es ist perfekt ausgerichtet mit 2 Sekunden zwischen den Chunks, sie haben Iframe am Anfang jedes Chunks und sie haben zusätzliche Iframes in komplexen Szenen, was eine bessere Erfahrung für Benutzer bietet, die veraltete Geräte haben und keine x264-High-Profile wiedergeben können.
Hoffe es hilft jemandem.
Ich wollte ein paar Informationen hier hinzufügen, da meine googeln diese Diskussion ganz infos ein bisschen in meiner Suche zu finden nach oben gezogen zu versuchen, einen Weg zu segmentieren meine DASH kodiert, wie ich wollte, und keine der Informationen, die ich war völlig richtig gefunden zu finden.
Zuerst einige Missverständnisse, die beseitigt werden müssen:
Nicht alle I-Frames sind gleich. Es gibt große "I" -Frames und kleine "I" -Frames. Oder um die korrekte Terminologie zu verwenden, IDR-I-Frames und Nicht-IDR-I-Frames. IDR-I-Frames (manchmal als "Keyframes" bezeichnet) erzeugen eine neue GOP. Die Nicht-IDR-Frames werden nicht. Sie sind praktisch, um sich in einer GOP zu befinden, in der sich die Szene ändert.
-x264opts keyint=GOPSIZE:min-keyint=GOPSIZE
← Dies entspricht nicht Ihren Vorstellungen. Ich brauchte eine Weile, um das herauszufinden. Es stellt sich heraus, dass der min-keyint
im Code begrenzt ist. Es darf nicht größer als (keyint / 2) + 1
sein. Wenn Sie diesen beiden Variablen denselben Wert zuweisen, wird der Wert für min-keyint
beim Codieren um die Hälfte reduziert.
Hier ist die Sache: Szenenschnitt ist wirklich großartig, besonders in Videos mit schnellen harten Schnitten. Es bleibt schön und knackig, sodass ich es nicht deaktivieren möchte, aber gleichzeitig konnte ich keine feste GOP-Größe erhalten, solange es aktiviert war. Ich wollte den Szenenschnitt aktivieren, aber nur Nicht-IDR-I-Frames verwenden. Aber es hat nicht funktioniert. Bis ich (aus vielen Lektüren) herausgefunden habe, dass ich falsch gedacht habe # 2.
Es stellte sich heraus, dass ich keyint
einstellen musste, um meine gewünschte GOP-Größe zu verdoppeln. Dies bedeutet, dass min-keyint
kann meine gewünschten GOP-Größe eingestellt werden (ohne den internen Code in Halbschnitt), der Szene-Cut- Erkennungs verwenden IDR I-Frames innerhalb der GOP-Größe verhindert, weil der Rahmen seit dem letzten IDR zählt I-Frame ist immer kleiner als min-keyinit
.
Und schließlich überschreibt das Setzen der Option force_key_frame
die doppelte Größe keyint
. Das funktioniert also so:
Ich bevorzuge Segmente in 2 Sekundenblöcken, daher ist meine 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>
Sie können mit ffprobe Folgendes überprüfen:
ffprobe <SRC_FLE> -select_streams v -show_frames -of csv -show_entries frame=coded_picture_number,key_frame,pict_type > frames.csv
In der generierten CSV-Datei wird in jeder Zeile Folgendes angegeben: 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
Das Ergebnis ist, dass Sie IDR-I-Frames nur in festgelegten GOPSIZE
-Intervallen sehen sollten, während alle anderen I-Frames Nicht-IDR-I-Frames sind, die bei Bedarf von der Szenenschnitterkennung eingefügt werden.
Es scheint, dass diese Syntax nicht immer funktioniert. Ich habe ziemlich viel auf unseren VOD-Inhalten sowie auf Live-Inhalten (Datei-Dumps) getestet und manchmal funktioniert scenecut nicht und löst einen Zwischen-Iframe aus:
Syntax für eine i50-> p50-Aufwärtskonvertierung, 2 Sek. Gop/Segment, IDR am Anfang, iFrames dazwischen, falls erforderlich
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