Wie kann ich ein Video auf eine bestimmte Größe anpassen, es aber nicht mit FFmpeg hochskalieren?

13

Ich muss Videos auf 640 x 360 anpassen (das Maximum, das der Player meines Telefons verarbeiten kann) und gleichzeitig das Seitenverhältnis beibehalten , aber ich möchte auch, dass das Video unverändert bleibt, wenn es kleiner als 640 x 360 ist (es macht keinen Sinn, es zu vergrößern).

Gibt es eine Möglichkeit, dieses Verhalten über die Befehlszeile von ffmpeg zu ermitteln?

Sashoalm
quelle
Ich denke nicht, dass dies nur in ffmpeg möglich ist, aber wenn Sie bereit sind, ein Skript zu schreiben, ist dies definitiv möglich.
Evilsoup
Ich habe es bereits geschrieben, aber ich wollte meinen Code bereinigen, falls er nicht benötigt wird.
Sashoalm
Es ist wahrscheinlich mit einem Skalierungsfilter möglich, der Funktionen wie verwendet, min(…)aber mit einem einfachen Skript, das die Dimensionen analysiert, definitiv einfacher ist. Sehen Sie meinen Befehl hier für ein Beispiel von dem, was getan werden kann: superuser.com/questions/547296/...
slhck

Antworten:

9

Bei neueren ffmpeg-Versionen können Sie scaledie force_original_aspect_ratioFilteroption verwenden. So passen Sie beispielsweise ein Video ohne Upscaling in 1280 × 720 ein ( weitere Informationen finden Sie in diesem Beitrag ):

ffmpeg -i input.mp4 -filter:v "scale='min(1280,iw)':min'(720,ih)':force_original_aspect_ratio=decrease,pad=1280:720:(ow-iw)/2:(oh-ih)/2" output.mp4

Hier scaleskaliert der Filter auf 1280 × 720, wenn das Eingangsvideo größer ist. Wenn es kleiner ist, wird es nicht hochskaliert. Der padFilter ist erforderlich, um das ausgegebene Video auf 1280 × 720 zu bringen, falls sein Seitenverhältnis oder seine Größe von der Zielgröße abweicht.


Bei älteren ffmpeg-Versionen gibt es eine etwas hackige Problemumgehung. Definieren Sie zunächst die Breite, Höhe und das Seitenverhältnis Ihrer Ausgabe. Dies erspart uns einige Eingaben.

width=640; height=360
aspect=$( bc <<< "scale=3; $width / $height") # <= floating point division

Wenden wir nun den super komplexen Filterbefehl an, den Jim Worrall geschrieben hat :

ffmpeg -i input.mp4 -vf "scale = min(1\,gt(iw\,$width)+gt(ih\,$height)) * (gte(a\,$aspect)*$width + \
lt(a\,$aspect)*(($height*iw)/ih)) + not(min(1\,gt(iw\,$width)+gt(ih\,$height)))*iw : \
min(1\,gt(iw\,$width)+gt(ih\,$height)) * (lte(a\,$aspect)*$height + \
gt(a\,$aspect)*(($width*ih)/iw)) + not(min(1\,gt(iw\,$width)+gt(ih\,$height)))*ih" \
output.mp4

Ich werde nicht wirklich erklären, was das alles macht, aber im Grunde kann man jedes Video füttern, und es wird nur verkleinert, nicht hochskaliert. Wenn Sie dazu bereit sind, können Sie den Filter in seine einzelnen Ausdrücke zerlegen. Es könnte möglich sein, dies zu verkürzen, aber es funktioniert auch so.

slhck
quelle
1
+1 aber das ist ein wirklich schrecklicher Befehl: P
Evilsoup
Ich weiß es schon gut? Ich habe zehn Minuten damit verbracht, es in logische Teile zu zerlegen und dann einige Werte einzufügen, aber ich habe aufgegeben. Es ist ein bisschen alt und vielleicht wäre es möglich, es viel prägnanter zu schreiben.
Slhck
13

Eine besser lesbare Version kann wie folgt aussehen:

-filter_complex "scale=iw*min(1\,min(640/iw\,360/ih)):-1"

640 / iw ist der horizontale Skalierungsfaktor und 360 / ih ist der vertikale Skalierungsfaktor

Sie möchten das skalierte Bild in das Ausgabefeld einpassen und das (Speicher-) Seitenverhältnis beibehalten. Dazu wählen Sie den kleinsten Skalierungsfaktor mit der Mindestfunktion aus: min (640 / iw, 360 / ih)

Sie möchten eine Hochskalierung verhindern (dh einen Skalierungsfaktor> 1,0), also fügen Sie eine weitere Mindestfunktion hinzu: min (1, min (640 / iw, 360 / ih))

Der nächste Schritt besteht darin, die Ausgabeauflösung zu berechnen, indem der Skalierungsfaktor mit der Eingabebreite und der Eingabehöhe multipliziert wird:
Ausgabebreite = iw * min (1, min (640 / iw, 360 / ih))
Ausgabehöhe = ih * min (1, min (640 / iw, 360 / ih))

Der letzte Schritt besteht darin, den Filterbefehl zu erstellen. Die Ausgabehöhe muss nicht angegeben werden. Sie können -1 angeben, und ffmpeg behält das Seitenverhältnis bei, indem der gleiche Skalierungsfaktor wie für die Breite angewendet wird.

immerzl
quelle
A + Funktioniert sofort. Die Lösung der ausgewählten Antwort bewahrte das Seitenverhältnis nicht . Es drückte die Rahmen.
287352
5

Ich hatte das gleiche Problem auch, aber gelöst, indem ich das Video in ein Quadrat 640x640 passte (wegen vertikaler Videos, die mit Smartphones erstellt wurden).

Mit immerzi Logik und etwas Forschung komme ich zu folgendem Ergebnis :

-vf "scale=iw*min(1\,if(gt(iw\,ih)\,640/iw\,(640*sar)/ih)):(floor((ow/dar)/2))*2"

Der letzte Teil dient dazu, eine durch 2 teilbare Höhe zu haben, die von vielen Encodern benötigt wird.

Shebuka
quelle