Ich habe ein MKV-Video (h264) mit 23,976 fps (24000/1001), aber ich möchte es in 25 fps umwandeln, ohne es neu zu kodieren und an Qualität zu verlieren. Ich weiß, dass mkvmerge das kann (mit der Option --default-duration '0:25fps'), aber ich würde es, wenn möglich, gerne direkt von ffmpeg aus machen. Laut der Dokumentation sollte das funktionieren:
ffmpeg -i input.mkv -r 25 -vcodec copy output.mkv
aber wenn ich es ausführe, erhalte ich nur die gleichen Video-FPS. Was ist die richtige Methode dafür (falls vorhanden) in ffmpeg?
Antwort1
Hier ist die Methode mit aktuellen Versionen von FFmpeg. Sie basiert darauf, dass der Concat-Demuxer die PTS der Eingaben nach der ersten Datei nicht neu skaliert, sondern einfach einen festen Offset anwendet. Nehmen wir an, Sie haben einen 30-fps-Stream mit einer Zeitskala von 15360
(typisch für FFmpeg-Ausgabe). Das bedeutet, dass Frame 0
PTS 0
und Frame 30
PTS hat 15360
. Dies würde zu einem 45-fps-Stream werden, wenn wir die Zeitskala auf ändern könnten, 23040
ohne die PTS-Werte zu beeinflussen.
Im Wesentlichen ist es das, was die folgende Methode macht.
1. Identifizieren Sie die Quelleigenschaften.
Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s,
30 fps, 30 tbr, 15360 tbn (default)
Sie möchten die Quelleigenschaften notieren, insbesondere Auflösung und tbn
.
2a(Optional) Ändern Sie die Zeitskala auf einen geeigneten Wert, um die Berechnungen zu vereinfachen.
ffmpeg -i in.mp4 -c copy -an -video_track_timescale 30 in-v30.mp4
Das bringt uns
Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s, \
30 fps, 30 tbr, 30 tbn (default
Wenn Sie diesen Schritt ausführen, sollte die neue Zeitskala gleich oder ein ganzzahliges Vielfaches der ursprünglichen Bildrate sein.
2b. Berechnen Sie die erforderliche Zeitskala, sodass für die Ziel-Framerate x
PTS von Frame # x
in der Quelle den gleichen Wert wie der neue hat . Wenn Sie Schritt 2a ausgeführt haben, ist dies sehr einfach und es ist einfach die neue Framerate. Für die Ziel-FPS sollte der neue Wert tbn
also sein .45
tbn
45
3. Dummy-Video generieren.
ffmpeg -f lavfi -i color=s=1280x720:r=45:d=1 -profile:v main -video_track_timescale 45 0.mp4
Für optimale Ergebnisse sollten alle Eigenschaften wie Auflösung, H.264-Profil, Pixelformat, Referenzanzahl usw. gleich sein.
4Verketten Sie die Videos.
Erstellen Sie zunächst eine Textdatei
file '0.mp4'
file 'in-v30.mp4'
Dann wird die Konkat
ffmpeg -f concat -i list.txt -c copy -video_track_timescale 45 45fps.mp4
In der Ausgabedatei wird das zweite Video mit 45 fps abgespielt.
5. Nun spalten Sie den Dummy-Preroll ab
ffmpeg -ss 1.1 -i 45fps.mp4 -c copy -avoid_negative_ts make_zero in45.mp4
und du hast
Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1757 kb/s, \
45 fps, 45 tbr, 11520 tbn (default)
Ich habe ja gesagt, dass das kompliziert ist!
Antwort2
ffmpeg -itsscale 1.0427083 -i input.mp4 -codec copy output.mp4
Dies verlangsamt eine von Handbrake erstellte MP4 mit 25 fps aus einer PAL-DVD-Quelle korrekt auf 23,974 fps. Die Originalsendung ist NTSC. Der Ton bleibt während der gesamten Laufzeit von nun 47 Minuten synchron. Es ist sehr schnell, da keine Dekodierung/Kodierung durchgeführt wird. Es gibt jedoch durchgehend Audiostörungen (Aussetzer) in Abständen von etwa 3 Sekunden. Dasselbe Ergebnis mit einem vcodec
Ersatzcodec, außer dass das Video zwar nicht neu kodiert wird, der Ton jedoch mit der halben ursprünglichen Bitrate neu kodiert wird und immer noch die Aussetzer aufweist.
ffmpeg -itsscale 1.0427083 -i input.mp4 -vcodec copy -filter:a "atempo=0.959041" output.mp4
Dadurch werden die Audioaussetzer eliminiert, Audio wird jedoch neu kodiert. Das ist viel schneller als die Neukodierung von Videos. Der verbleibende Nachteil ist, dass standardmäßig die Hälfte der ursprünglichen Audiobitrate verwendet wird. Sie müssen herausfinden, wie Sie die Audiobitrate für die Neukodierung einstellen.
Antwort3
Verwenden-itsscale
auf dem Eingangsvideo, um eine effektive Framerate-Änderung zu erreichen. Funktioniert gut mit -vcodec copy
.
Antwort4
Sie können dies mit dem Bitstream-Filter tun setts
. Dadurch entfällt auch der Aufwand, einen Rohdatenstrom in eine Datei zu schreiben und ihn dann erneut zu remuxen. Dies funktioniert, weil Sie mit Codec Copy ( -c:v copy
) zwar keine normalen Filter verwenden können, da diese auf dem dekodierten Videostrom funktionieren, Sie aber Bitstream-Filter verwenden können, die auf dem kodierten Strom funktionieren.
Um beispielsweise die Bildrate auf 60 fps zu ändern, fügen Sie ein
-bsf:v setts=ts=STARTPTS+N/TB_OUT/60
Dies sollte sowohl als auch ohne Dekodierung des Streams einstellen pts
. dts
Wenn Sie ein Video mit variabler Bildrate haben, benötigen Sie möglicherweise stattdessen so etwas
-bsf:v setts=ts='if(PREV_OUTPTS+9223372036854775808\,PREV_OUTPTS\,STARTPTS)+PREV_OUTDURATION*2'
Dies ändert die Dauer des Frames, anstatt anzunehmen, dass jeder Frame eine konstante Dauer hat. Der seltsam aussehende if
Ausdruck soll sicherstellen, dass der erste Frame richtig versetzt ist, da während des ersten Frames PREV_OUTPTS
auf (Mindestwert einer 64-Bit-Ganzzahl) eingestellt ist -9223372036854775808
und wir es durch Null ersetzen müssen.
Wenn Sie B-Frames haben, kann dies die Dekodierung durcheinander bringen, da dts
kleiner als sein muss pts
, aber bei mir scheint es trotzdem gut zu funktionieren. Sie können versuchen, setts=ts
durch zu ersetzen setts=pts
, wenn Sie auf Probleme stoßen. Siehehttps://ffmpeg.org/ffmpeg-bitstream-filters.html#settsfür Details