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 (dh 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.
Methode 1: Mit den Argumenten von libx264 herumspielen
-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 es unklar ist, ob der Keyframe- "Counter" neu gestartet wird, wenn eine Szene geschnitten wird.
Methode 2: Festlegen einer festen GOP-Größe:
-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.
Methode 3: Fügen Sie alle N Sekunden einen Keyframe ein ( Möglicherweise? ):
-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
Dies ist ausdrücklich dokumentiert. Es ist aber immer noch nicht sofort klar, ob der "Zeitzähler" nach jedem Schlüsselbild neu startet. Wenn zum Beispiel in einer erwarteten 5-Sekunden-GOP ein scenecut
Keyframe 3 Sekunden von libx264 injiziert wird, ist der nächste Keyframe 5 Sekunden später oder 2 Sekunden später?
In der FFmpeg-Dokumentation wird tatsächlich zwischen dieser und der -g
Option unterschieden, aber es wird nicht wirklich angegeben, wie die beiden oben genannten Optionen sich am wenigsten unterscheiden (dies setzt natürlich -g
eine feste Bildrate voraus).
Welches ist richtig?
Es scheint, dass das -force_key_frames
überlegen wäre , da es keine feste Bildrate erfordern würde. Dies setzt jedoch voraus, dass
- es entspricht den GOP-Spezifikationen in H.264 ( falls vorhanden )
- es GARANTIERT, dass ein Keyframe in fester Trittfrequenz vorhanden ist, unabhängig von libx264-
scenecut
Keyframes.
Es scheint auch, dass -g
dies nicht funktionieren könnte, ohne eine feste Bildrate ( -r
) zu erzwingen , da nicht garantiert werden kann, dass mehrere Durchläufe ffmpeg
mit unterschiedlichen Codec-Argumenten in jeder Auflösung die gleiche augenblickliche Bildrate liefern. Feste Bildraten können die Komprimierungsleistung verringern (WICHTIG in einem DASH-Szenario!).
Schließlich scheint die keyint
Methode 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
quelle
ffprobe -i input.mp4 -select_streams v -show_frames -of csv -show_entries frame=pict_type
und dann die Zellen eingefärbt habe. Ich fürchte, es gibt keine öffentlichen Diskussionen, aber ich werde sehen, ob ich einige der Links ausgraben kann, die ich damals gefunden habe.-force_key_frames expr:gte(t,n_forced*GOP_LEN_IN_SECONDS)
Formular wiederholen ? Ich habe es gerade ausprobiert und festgestellt, dass ich zwar zusätzliche Frames im Stream hatte, aber meine Regel nicht eingehalten zu haben schien. Ein PERL-Programm wird als "Antwort" folgen, da Sie Markup anscheinend nicht in Kommentaren verwenden können.-force_key_frames
hat bei mir nicht funktioniert, und deshalb habe ich es nie wieder versucht. Das war vor mehr als einem Jahr. Vielleicht war es ein Fehler. Ich werde es bald wieder versuchen.Hier sind meine fünfzig Cent für den Fall.
Generieren Sie iframes nur in den gewünschten Intervallen.
Beispiel 1:
Generiere iframes wie erwartet so:
Methode 2 wird abgeschrieben. Weggelassen
Beispiel 2
Generieren Sie ein iframes auf eine etwas andere Art:
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 als mit dem standardmäßigen x264-Bibliotheksalgorithmus generiert werden.
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.
quelle
Die Antwort scheint daher zu sein:
libx264
-spezifisch und geht zu Lasten der Eliminierung der sehr nützlichenscenecut
Option inlibx264
.-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 Codeinspektion zeigt, dass die-g
Option wahrscheinlich für MPEG-2-Streams gedacht ist (es gibt sogar Codezeilen, die sich auf PAL und NTSC beziehen!).Ebenfalls:
Skript für die
-force_key_frames
OptionHier ist ein kurzes PERL-Programm, mit dem ich die I-Frame-Trittfrequenz basierend auf der Ausgabe von slhcks ffprobe-Vorschlag verifiziert habe. Es scheint zu bestätigen, dass die
-force_key_frames
Methode auch funktioniert, und hat den zusätzlichen Vorteil, dassscenecut
Frames zugelassen werden. 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 überprüft, ob bei jedem Vielfachen von 180 ein I-Frame vorhanden ist. Wenn ich es jedoch auf 181 (oder eine andere Zahl, die kein Vielfaches von 180 ist) einsetze, beschwert sich das Programm.
quelle
force_key_frames
wird der x264-Bitzuweisungsalgorithmus durcheinander gebracht, sodass möglicherweise eine schlechtere Qualität erzielt wird, als wenn lediglich ein festes Keyframe-Intervall festgelegt wird.-g
Sie sagen ungefähr: "Es scheint weder zu funktionieren, noch scheint es im Code verwendet zu werden." Ich überprüfte und die die Eingabe vong
gespeichert istavctx->gop_size
und dass libx264 nutzt es:x4->params.i_keyint_max = avctx->gop_size;
. Wenn ich diese generierte Testdatei prüfeffmpeg -i a-test-file.mp4 -g 37 -t 15 gtest.mp4
, erhalte ich Keyframes genau um0,37,74,111,148,185,222,259,296,333,370
. Eine GOP könnte gekürzt werden, wenn ein Szenenwechsel ausgelöst wird, und dafür-sc_threshold
könnte eingestellt werden, was auch von x264 aufgenommen wird.Ich wollte hier einige Informationen hinzufügen, da mein Googeln diese Diskussion zu einem großen Teil in meiner Suche nach Informationen darüber, wie ich meine DASH-Codierung so segmentieren kann, wie ich es wollte, ausgelöst hat, und keine der Informationen, die ich gefunden habe, war vollständig korrekt.
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
← Das macht nicht das, was Sie denken. Ich brauchte eine Weile, um das herauszufinden. Es stellt sich heraus, dass dasmin-keyint
im Code begrenzt ist. Es darf nicht größer sein als(keyint / 2) + 1
.min-keyint
Wenn Sie also diesen beiden Variablen denselben Wert zuweisen, wird der Wert beim Codieren halbiert.Hier ist die Sache: Szenenschnitt ist wirklich großartig, besonders in Videos mit schnellen harten Schnitten. Es bleibt schön und knackig, so dass 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 die
keyint
gewünschte GOP-Größe verdoppeln musste. Dies bedeutet, dassmin-keyint
die gewünschte GOP-Größe eingestellt werden kann (ohne dass der interne Code sie halbiert), wodurch verhindert wird, dass die Szenenschnitterkennung IDR-I-Frames innerhalb der GOP-Größe verwendet, da die Frame-Anzahl seit dem letzten IDR-I-Frame beträgt immer weniger alsmin-keyinit
.Und schließlich
force_key_frame
überschreibt das Setzen der Option die doppelte Größekeyint
. Das funktioniert also so:Ich bevorzuge Segmente in 2 Sekundenblöcken, daher ist meine GOPSIZE = Framerate * 2
Sie können mit ffprobe Folgendes überprüfen:
In der generierten CSV-Datei wird in jeder Zeile Folgendes angegeben
frame, [is_an_IDR_?], [frame_type], [frame_number]
: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.quelle
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
quelle
Twitch hat einen Beitrag dazu. Sie erklären, dass sie aus mehreren Gründen beschlossen haben, ihr eigenes Programm zu verwenden. Eine davon war, dass Sie mit ffmpeg nicht verschiedene x264-Instanzen in verschiedenen Threads ausführen können, sondern alle angegebenen Threads einem Frame in einer Ausgabe zuweisen, bevor Sie zur nächsten Ausgabe übergehen.
Wenn Sie kein Echtzeit-Streaming durchführen, haben Sie mehr Luxus. Der 'richtige' Weg ist wahrscheinlich, bei einer Auflösung nur die mit -g angegebene GOP-Größe zu codieren und dann die anderen Auflösungen zu codieren, die Keyframes an denselben Stellen erzwingen.
Wenn Sie das tun möchten, können Sie ffprobe verwenden, um die Keyframe-Zeiten abzurufen, und dann ein Shell-Skript oder eine tatsächliche Programmiersprache verwenden, um das in einen ffmpeg-Befehl zu konvertieren.
Bei den meisten Inhalten besteht jedoch kaum ein Unterschied zwischen einem Keyframe alle 5 Sekunden und zwei Keyframes alle 5 Sekunden (eines erzwungen und eines aus Scenecut). Dies ist ungefähr die durchschnittliche Größe von I-Frames im Vergleich zur Größe von P-Frames und B-Frames. Wenn Sie x264 mit typischen Einstellungen verwenden (der einzige Grund, weshalb Sie meines Erachtens irgendetwas tun sollten, um dies zu ändern, ist die Einstellung von -qmin, um zu verhindern, dass x264 bei einfachen Inhalten die Bitrate verwendet), wodurch alle Rahmentypen auf den gleichen Wert begrenzt werden , Denke ich) und erhalte ein Ergebnis wie I-Frame-Durchschnittsgröße von 46 kB, P-Frame 24 kB, B-Frame 17 kB (halb so häufig wie P-Frames), dann ein zusätzliches I-Frame pro Sekunde bei 30 fps ist nur eine 3% ige Zunahme der Dateigröße. Der Unterschied zwischen h264 und h263 könnte aus einer Menge von 3% Abnahmen bestehen, aber eine einzige ist nicht sehr wichtig.
Bei anderen Inhaltstypen unterscheiden sich die Bildgrößen. Fairerweise geht es hier um zeitliche und nicht um räumliche Komplexität. Es geht also nicht nur um einfachen Inhalt, sondern auch um harten Inhalt. Im Allgemeinen ist die Bitrate für Streaming-Video-Websites jedoch begrenzt, und Inhalte mit relativ großen I-Frames sind einfache Inhalte, die unabhängig von der Anzahl der hinzugefügten zusätzlichen Keyframes in hoher Qualität codiert werden. Es ist verschwenderisch, aber diese Verschwendung wird normalerweise nicht bemerkt. Der verschwenderischste Fall ist wahrscheinlich ein Video, das nur ein statisches Bild eines Songs ist, bei dem jeder Keyframe genau gleich ist.
Ich bin mir nicht sicher, wie erzwungene Keyframes mit dem mit -maxrate und -bufsize festgelegten Ratenbegrenzer interagieren. Ich denke, sogar YouTube hatte kürzlich Probleme, die Puffereinstellungen richtig zu konfigurieren, um eine gleichbleibende Qualität zu erzielen. Wenn Sie nur durchschnittliche Bitrateneinstellungen verwenden, wie dies von einigen Sites angezeigt wird (da Sie die x264-Optionen im Header / Mov-Atom mit einem Hex-Editor überprüfen können), ist das Puffermodell kein Problem Durch die durchschnittliche Bitrate wird der Nutzer dazu aufgefordert, am Ende seines Videos einen schwarzen Bildschirm einzufügen.
Die Option -g von Ffmpeg oder eine andere von Ihnen verwendete Encoder-Option wird der encoderspezifischen Option zugeordnet. Also ist '-x264-params keyint = GOPSIZE' gleichbedeutend mit '-g GOPSIZE'.
Ein Problem bei der Szenenerkennung besteht darin, dass Sie aus irgendeinem Grund Keyframes in der Nähe bestimmter Zahlen bevorzugen. Wenn Sie alle 5 Sekunden Keyframes angeben und die Szenenerkennung verwenden und bei 4.5 eine Szenenänderung auftritt, sollte diese erkannt werden, der nächste Keyframe jedoch bei 9.5. Wenn die Zeit so schnell wird, könnten Sie Keyframes bei 42,5, 47,5, 52,5 usw. anstelle von 40, 45, 50, 55 erhalten. Wenn sich die Szene bei 5,5 ändert, ist dies umgekehrt der Fall Ein Keyframe mit 5 und 5.5 ist für einen anderen zu früh. In Ffmpeg können Sie nicht angeben, dass hier ein Keyframe erstellt werden soll, wenn innerhalb der nächsten 30 Frames keine Szenenänderung erfolgt. Jemand, der C versteht, könnte diese Option jedoch hinzufügen.
Wenn Sie bei Videos mit variabler Bildrate nicht wie Twitch Live-Streaming betreiben, sollten Sie in der Lage sein, Szenenänderungen zu verwenden, ohne diese dauerhaft in eine konstante Bildrate zu konvertieren. Wenn Sie den Filter 'select' in ffmpeg verwenden und die Konstante 'scene' im Ausdruck verwenden, wird in der Debug-Ausgabe (-v debug oder drücken Sie mehrmals '+' während der Codierung) die Szenenänderungsnummer angezeigt. Dies unterscheidet sich wahrscheinlich von der von x264 verwendeten Nummer und ist nicht so nützlich, könnte aber dennoch nützlich sein.
Das Verfahren würde dann wahrscheinlich darin bestehen, ein Testvideo zu erstellen, das nur für Keyframe-Änderungen gedacht ist, bei Verwendung von 2-Pass jedoch möglicherweise für Daten zur Ratensteuerung verwendet werden kann. (Sie sind sich nicht sicher, ob die generierten Daten für unterschiedliche Auflösungen und Einstellungen überhaupt nützlich sind; die Makroblock-Baumdaten sind es nicht.) Konvertieren Sie sie in ein Video mit konstanter Framerate, aber sehen Sie diesen Fehler, wenn Sie die Framerate halbieren, falls Sie sich jemals entscheiden um den fps filter für andere zwecke zu verwenden. Führen Sie es über x264 mit den gewünschten Keyframe- und GOP-Einstellungen aus.
Verwenden Sie dann einfach diese Keyframe-Zeiten mit dem Originalvideo mit variabler Bildrate.
Wenn Sie völlig verrückte benutzergenerierte Inhalte mit einer Lücke von 20 Sekunden zwischen Frames zulassen, können Sie für die Codierung mit variabler Framerate die Ausgabe aufteilen, den fps-Filter verwenden und irgendwie einen Auswahlfilter verwenden (möglicherweise einen wirklich langen Ausdruck erstellen, der hat) every keyframe time) ... oder Sie könnten das Testvideo als Eingabe verwenden und entweder nur Keyframes decodieren, wenn diese ffmpeg-Option funktioniert, oder den Auswahlfilter verwenden, um Keyframes auszuwählen. Skalieren Sie es dann auf die richtige Größe (es gibt sogar einen scale2ref-Filter dafür) und überlagern Sie es mit dem Originalvideo. Verwenden Sie dann den Interleave-Filter, um diese zu erzwingenden Keyframes mit dem Originalvideo zu kombinieren. Wenn dies zu zwei Frames mit einem Abstand von 0,001 Sekunden führt, den der Interleave-Filter nicht verhindert, können Sie dieses Problem mit einem anderen Auswahlfilter selbst beheben. Das Hauptproblem könnte hier der Umgang mit Frame Buffer Limits für das Interleave-Filter sein. Diese könnten alle funktionieren: Verwenden Sie eine Art Filter, um den dichteren Strom zu puffern (FIFO-Filter?); Verweisen Sie mehrmals auf die Eingabedatei, damit sie mehrmals dekodiert wird und keine Frames gespeichert werden müssen. Verwende den 'Streamselect'-Filter, den ich noch nie gemacht habe, genau zu den Zeiten der Keyframes. Verbessern Sie den Interleave-Filter, indem Sie sein Standardverhalten ändern oder eine Option hinzufügen, um den ältesten Frame in einem Puffer auszugeben, anstatt einen Frame zu löschen. was ich noch nie gemacht habe, genau zu den Zeiten der Keyframes; Verbessern Sie den Interleave-Filter, indem Sie sein Standardverhalten ändern oder eine Option hinzufügen, um den ältesten Frame in einem Puffer auszugeben, anstatt einen Frame zu löschen. was ich noch nie gemacht habe, genau zu den Zeiten der Keyframes; Verbessern Sie den Interleave-Filter, indem Sie sein Standardverhalten ändern oder eine Option hinzufügen, um den ältesten Frame in einem Puffer auszugeben, anstatt einen Frame zu löschen.
quelle