Jak dopasować film do określonego rozmiaru, ale nie przeskalowywać go za pomocą FFmpeg?

13

Muszę dopasować filmy do 640x360 (maksimum, jakie może obsłużyć odtwarzacz mojego telefonu), jednocześnie zachowując proporcje , ale chcę też, aby wideo pozostało niezmienione, jeśli jest mniejsze niż 640x360 (w końcu nie ma sensu przeskalowywać go).

Czy istnieje sposób na uzyskanie tego zachowania za pomocą wiersza polecenia ffmpeg?

sashoalm
źródło
Nie sądzę, że można tego dokonać wyłącznie w ffmpeg, ale jeśli chcesz to zrobić, to na pewno można to zrobić.
evilsoup
Już go napisałem, ale chciałem wyczyścić mój kod na wypadek, gdyby nie był potrzebny.
sashoalm
Prawdopodobnie jest to możliwe dzięki filtrowi skali, który korzysta z funkcji takich jak, min(…)ale zdecydowanie łatwiejszy dzięki prostemu skryptowi, który analizuje wymiary. Zobacz moje polecenie tutaj, aby zobaczyć przykład tego, co można zrobić: superuser.com/questions/547296/…
slhck

Odpowiedzi:

10

W nowszych wersjach ffmpeg możesz użyć opcji scalefiltra force_original_aspect_ratio. Na przykład, aby zmieścić wideo w rozdzielczości 1280 × 720, bez skalowania (zobacz ten wpis, aby uzyskać więcej informacji):

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

Tutaj scalefiltr jest skalowany do 1280 × 720, jeśli wejściowe wideo jest większe. Jeśli jest mniejszy, nie będzie powiększany. padFiltr konieczny, aby wyjścia wideo do 1280 x 720, w razie jego proporcji lub wielkości różni się od wielkości docelowej jest.


W starszych wersjach ffmpeg istnieje dość hackujące obejście. Najpierw określ szerokość, wysokość i proporcje wydruku. Pozwoli nam to zaoszczędzić trochę pisania.

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

Teraz zastosujmy polecenie super złożonego filtru, które napisał Jim Worrall :

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

Naprawdę nie będę wyjaśniać, co to wszystko robi, ale w zasadzie możesz go nakarmić dowolnym filmem, i to będzie tylko zmniejszanie, a nie zwiększanie. Jeśli masz na to ochotę, możesz podzielić filtr na poszczególne wyrażenia. Można to skrócić, ale tak też działa.

slhck
źródło
1
+1, ale to naprawdę przerażające polecenie: P
evilsoup
Wiem dobrze Spędziłem dziesięć minut, próbując rozbić go na logiczne części, a następnie wstawić wartości, ale poddałem się. Jest trochę stary i być może byłoby możliwe napisanie go bardziej zwięźle niż to.
slhck
14

Bardziej czytelna wersja może wyglądać następująco:

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

640 / iw jest współczynnikiem skalowania w poziomie, a 360 / ih jest współczynnikiem skalowania w pionie

Chcesz zmieścić skalowany obraz w polu wyjściowym i zachować proporcje (przechowywania). Robisz to, wybierając najmniejszy współczynnik skalowania przy minimalnej funkcji: min (640 / iw, 360 / ih)

Chcesz uniknąć jakiegokolwiek skalowania (tj. Współczynnika skalowania> 1,0), więc dodajesz kolejną funkcję minimalną: min (1, min (640 / iw, 360 / ih))

Następnym krokiem jest obliczenie rozdzielczości wyjściowej przez pomnożenie współczynnika skalowania przez szerokość wejściową i wysokość wejściową:
szerokość wyjściowa = iw * min (1, min (640 / iw, 360 / ih))
wysokość wyjściowa = ih * min (1, min (640 / iw, 360 / ih))

Ostatnim krokiem jest zbudowanie polecenia filter. Nie ma potrzeby określania wysokości wyjściowej, możesz podać -1, a ffmpeg zachowa proporcje, stosując taki sam współczynnik skalowania jak dla szerokości.

immerzl
źródło
A + działa od razu po wyjęciu z pudełka. Wybrane rozwiązanie nie zachowało współczynnika proporcji. Ścisnął ramy.
287352
5

Miałem też ten sam problem, ale rozwiązałem go, umieszczając wideo w kwadracie 640x640 (z powodu pionowych filmów zrobionych smartfonami).

Używając logiki immerzi i niektórych badań, kończę na tym:

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

ostatnia część dotyczy wysokości podzielnej przez 2, która jest potrzebna przez wiele koderów.

Shebuka
źródło