FFMPEG (libx264) "Höhe nicht durch 2 teilbar"

185

Ich versuche, ein .mp4-Video aus einer Reihe von Frames mit FFMPEG unter Verwendung des libx264-Codecs zu codieren.

Dies ist der Befehl, den ich ausführe:

/usr/local/bin/ffmpeg -r 24 -i frame_%05d.jpg -vcodec libx264 -y -an video.mp4

Ich erhalte manchmal den folgenden Fehler:

[libx264 @ 0xa3b85a0] height not divisible by 2 (520x369)

Nach einigem Hin und Her scheint das Problem etwas mit dem Skalierungsalgorithmus zu tun zu haben und kann durch Hinzufügen eines -vf-Arguments behoben werden.

In meinem Fall möchte ich jedoch keine Skalierung durchführen. Idealerweise möchte ich die Abmessungen genau so halten wie die Rahmen. Irgendein Rat? Gibt es ein Seitenverhältnis, das h264 erzwingt?

Andy Hin
quelle
@AleksandrDubinsky Aber LordNeckbeards Antwort behält die ursprüngliche Breite und Höhe nicht bei. Hier müssen wir entweder Breite oder Höhe manuell angeben. Wenn wir -vf scale = -2: ih oder -vf scale = iw: -2 verwenden, wird dies nicht der Fall sein arbeiten, wenn sowohl Höhe als auch Breite ungleichmäßig sind. Bitte erklären Sie, wie diese Antwort optimaler ist? .. danke
varmashrivastava
1
@varmashrivastava Nun, die Art und Weise, wie SO funktioniert, ist, dass es ursprünglich eine Frage gegeben haben könnte, und dann sendet Google eine Reihe von Personen mit einer anderen Frage, die dann die Seite entführen. Es ist was es ist, versuche es nicht zu bekämpfen. Die richtige Antwort auf die ursprüngliche Frage lautet -vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2": Dies ist nicht einmal eine der Antworten. Die richtige Antwort auf die Frage aller anderen ist die von LordNeckbeard.
Aleksandr Dubinsky
@varmashrivastava Ich habe die erste Antwort korrigiert. Hoffentlich wird es nicht durch die Mods zerstört.
Aleksandr Dubinsky
@AleksandrDubinsky danke..und Benutzer kann "scale="stattdessen verwenden, "pad="wenn er / sie keine farbigen Füllpixel möchte?
Varmashrivastava

Antworten:

267

Die Antwort auf die ursprüngliche Frage, die das Video nicht skalieren möchte, lautet:

-vf "pad=ceil(iw/2)*2:ceil(ih/2)*2"

Befehl:

ffmpeg -r 24 -i frame_%05d.jpg -vcodec libx264 -y -an video.mp4 -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2"

Grundsätzlich benötigt .h264 gerade Abmessungen, damit dieser Filter:

  1. Teilen Sie die ursprüngliche Höhe und Breite durch 2
  2. Runden Sie es auf das nächste Pixel auf
  3. Multiplizieren Sie es erneut mit 2, um eine gerade Zahl zu erhalten
  4. Fügen Sie dieser Zahl schwarze Füllpixel hinzu

Sie können die Farbe der Polsterung ändern, indem Sie Filterparameter hinzufügen :color=white. Siehe die Dokumentation des Pads .

Andy Hin
quelle
3
Es ist kein Fehler. Es spielt keine Rolle, dass Sie keine Skalierung durchführen, da die Ausgabe die Frame-Größe der Eingabe erbt.
Llogan
5
Für die Aufzeichnung habe ich gerade etwas gemacht, bei dem ich ein Video aus einem Bild erstellt habe und das yuvj444p als Pixelformat verwendet hat. Die Videogröße war ihm egal. Dann musste ich es in yuv420p konvertieren, und dann kümmerte es mich um die Videogröße. Ich habe yuv420p auf Wikipedia nachgeschlagen. Ich denke, es ist ein Multi-Pixel-Farbformat, bei dem das Bild eine bestimmte Größe haben muss. Ich bin mir jedoch nicht sicher, warum es wichtig ist, komprimiert zu sein.
Lahwran
7
Sie sind wahrscheinlich besser dran, Pad als Skalierung zu verwenden, um eine schwarze Zeile / Spalte hinzuzufügen. Wenn Sie ein Bild um ein Pixel vergrößern, wird es unscharf.
Glenn Maynard
5
@NickeManarin, dieser Filter sollte funktionieren, um 1 Pixel weiße Polsterung zur vertikalen Dimension hinzuzufügen, wobei das Video oben links positioniert ist : -vf pad="width=iw:height=ih+1:x=0:y=0:color=white". Die Dokumentation zum ffmpeg-Pad finden Sie hier: ffmpeg.org/ffmpeg-filters.html#pad-1 .
Mark Berry
4
Hier ist eine Lösung, die nur ungeraden Dimensionen ein Pixel Polster hinzufügt : -vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2".
Danneu
247

Benutz einfach -2

Aus der Dokumentation zum Skalenfilter :

Wenn einer der Werte -nmit ist n > 1, verwendet der Skalierungsfilter auch einen Wert, der das Seitenverhältnis des Eingabebilds beibehält, das aus der anderen angegebenen Dimension berechnet wird. Danach wird jedoch sichergestellt, dass die berechnete Abmessung durch teilbar ist, nund der Wert gegebenenfalls angepasst.

Beispiele

Set Breite bis 1280, und die Höhe wird automatisch das Seitenverhältnis zu erhalten , berechnet werden, und die Höhe , die durch 2 teilbar sein wird:

-vf scale=1280:-2

Wie oben, jedoch mit einer angegebenen Höhe. Die Breite muss vom Filter behandelt werden:

-vf scale=-2:720

"teilbar durch 2"

Wie von x264 gefordert, wird für YUV 4: 2: 0-Chroma-Subsampled-Ausgänge das "durch 2 teilbare für Breite und Höhe" benötigt. 4: 2: 2 würde "durch 2 durch Breite teilbar" benötigen, und 4: 4: 4 hat diese Einschränkungen nicht. Die meisten nicht auf FFmpeg basierenden Player können jedoch nur 4: 2: 0 ordnungsgemäß dekodieren. Daher ffmpegwerden bei der -pix_fmt yuv420pAusgabe von H.264-Videos häufig Befehle mit dieser Option angezeigt.

Vorbehalt

Leider können Sie nicht -2sowohl für die Breite als auch für die Höhe verwenden. Wenn Sie jedoch bereits eine Dimension angegeben haben, -2ist die Verwendung eine einfache Lösung.

llogan
quelle
14
Ich denke, dies sollte als die richtige Antwort markiert werden, da keine "Tricks" involviert sind. Ich möchte mehr als einmal
upvoten
1
Warum funktioniert das -vf scale=-2:-2nicht? In meinem Fall möchte ich die ursprüngliche Dateigröße so weit wie möglich beibehalten. Was für mich funktioniert hat war -vf scale=-2:ih. Aber es funktioniert nicht, wenn beide h / w ungleichmäßig sind.
Pascal
2
@tuner Der resultierende Wert von -2hängt vom deklarierten Wert der anderen Dimension ab.
Llogan
3
In meinem Fall gab mir dies den folgenden Fehler: Size values less than -1 are not acceptable.Aber die Antwort von @Zbyszek funktionierte perfekt.
Julien
64

Wenn Sie eine Ausgabebreite einstellen möchten und eine Ausgabe mit demselben Verhältnis wie das Original haben möchten

scale=720:-1 

und um nicht mit diesem Problem zu fallen, können Sie verwenden

scale="720:trunc(ow/a/2)*2"

(Nur für Leute, die suchen, wie man das mit Skalierung macht)

Zbyszek
quelle
16
Und für eine feste Höhe ist esscale="trunc(oh*a/2)*2:720"
Tom
20

Das Problem bei den scaleLösungen hier ist, dass sie das Quellbild / -video verzerren, was fast nie das ist, was Sie wollen.

Stattdessen habe ich festgestellt, dass die beste Lösung darin besteht, der ungeraden Dimension ein 1-Pixel-Pad hinzuzufügen. (Standardmäßig ist das Pading schwarz und schwer zu bemerken.)

Das Problem mit den anderen padLösungen ist, dass sie nicht über beliebige Dimensionen verallgemeinern, weil sie immer auffüllen.

Diese Lösung fügt Höhe und / oder Breite nur dann ein 1-Pixel-Pad hinzu, wenn sie ungerade sind:

-vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2"

Dies ist ideal, weil es immer das Richtige tut, auch wenn keine Polsterung erforderlich ist.

danneu
quelle
Die Skalierungslösungen ändern die Pixelanzahl um höchstens 1. Das verzerrt das Bild kaum. Wenn Sie sich Sorgen über die Filtergeschwindigkeit machen, verwenden Sie scale=iw+mod(iw,2):ih+mod(ih,2):flags=neighbor. Dies kann jede Dimension bei Bedarf nur um 1 erhöhen und die letzte Zeile / Spalte duplizieren.
Gyan
@Gyan Es ist zu lange her, dass ich das Problem hatte, das dadurch gelöst wurde (meine Antwort wurde aus einem Kommentar extrahiert, den ich vor langer Zeit abgegeben habe), aber ich erinnere mich, dass die Skalierung um ein einzelnes Pixel unter bestimmten Bedingungen zu merklichen visuellen Artefakten führte, weshalb ich mich darum kümmerte an erster Stelle. Ich erinnere mich nicht genau, vielleicht unverhältnismäßig viel Unschärfe durch einen einzelnen Pixelwechsel? Vielleicht nur bei einigen Video- / Bildformaten? Ich kann nur sagen, dass ich mit diesem Fix Tausende von Videos verarbeitet habe und es war die günstige Transformation.
Danneu
19

Dies liegt wahrscheinlich an der Tatsache, dass H264-Videos vor dem Anwenden der Komprimierung normalerweise als 4: 2: 0 von RGB in YUV-Speicher konvertiert werden (obwohl die Formatkonvertierung selbst ein verlustbehafteter Komprimierungsalgorithmus ist, der zu 50% Speicherplatzersparnis führt).

YUV-420 beginnt mit einem RGB-Bild (Rot, Grün, Blau) und konvertiert es in YUV (im Grunde ein Intensitätskanal und zwei "Farbton" -Kanäle). Die Farbtonkanäle werden dann unterabgetastet, indem für jedes 2 × 2-Quadrat dieses Farbtons eine Farbtonabtastung erstellt wird.

Wenn Sie eine ungerade Anzahl von RGB-Pixeln entweder horizontal oder vertikal haben, haben Sie unvollständige Daten für die letzte Pixelspalte oder -zeile im unterabgetasteten Farbtonraum des YUV-Rahmens.

Adisak
quelle
2
Eine weitere interessante Tatsache ... Wenn Sie mit Microsoft Media Foundation dekodieren, müssen Sie für H264 ein Vielfaches von 16 verwenden. 1080P-Video wird also tatsächlich in einen Puffer dekodiert, der 1088 hoch ist (obwohl Sie die letzten 8 Zeilen ignorieren).
Adisak
2

LordNeckbeard hat sehr schnell die richtige Antwort

-vf scale=1280:-2

Vergessen Sie für Android nicht hinzufügen

"-preset ultrafast" and|or "-threads n"
Fallouter
quelle
Sie müssen keine Threads deklarieren: Dies wird automatisch behandelt. Ich glaube, die Langsamkeit von Andriod beim Codieren in H.264 ist darauf zurückzuführen, dass Benutzer das beliebte "WritingMinds / ffmpeg-android" verwenden, das --disable-asmin seinem x264-Build-Skript verwendet wird . Dies führt zu unnötiger und erheblicher Langsamkeit (Sie können das ffmpeg-Protokoll überprüfen und wenn es anzeigt using cpu capabilties: none!, ist das schlecht). Ich bin mir nicht sicher, warum sie das hinzugefügt haben, aber ich bin kein Android-Entwickler.
Llogan
1

Sie können auch die bitandFunktion anstelle von trunc:

Bitand (x, 65534)

werde das gleiche tun wie trunc(x/2)*2und es ist meiner Meinung nach transparenter.
(Betrachten Sie 65534 hier als magische Zahl;))


Meine Aufgabe war es, viele Videodateien automatisch auf die halbe Auflösung zu skalieren .

scale=-2,ih/2führen zu leicht unscharfen Bildern

Grund:

  • Für Eingangsvideos wurde das Seitenverhältnis (DAR) eingestellt
  • scale skaliert die tatsächlichen Rahmenabmessungen
  • Während der Vorschau müssen die Größen der neuen Videos mit DAR korrigiert werden, was bei Videos mit relativ geringer Auflösung (360 x 288, DAR 16: 9) zu Unschärfe führen kann

Lösung:

-vf "scale='bitand(oh*dar, 65534)':'bitand(ih/2, 65534)', setsar=1"

Erläuterung:

  • output_height = input_height / 2
  • output_width = output_height * original_display_aspect_ratio
  • Sowohl output_width als auch output_height werden jetzt auf die nächste kleinere Zahl gerundet, die durch 2 teilbar ist
  • setsar=1bedeutet, dass output_dimensions jetzt endgültig sind und keine Korrektur des Seitenverhältnisses angewendet werden sollte

Jemand könnte dies hilfreich finden.

Endigo
quelle