53

I need to convert a bunch of video files using FFmpeg. I run a Bash file that converts all the files nicely, however there is a problem if a file converted is not in 16:9 format.

As I am fixing the size of the screen to -s 720x400, if the aspect ratio of the original is 4:3, FFmpeg creates a 16:9 output file, screwing up the aspect ratio.

Is there a setting that allows setting an aspect ratio as the main parameter, with size being adjusted (for example, by fixing an X or Y dimension only)?

one noa
  • 336
  • 1
  • 3
  • 9
0-alpha
  • 2,421
  • 4
  • 17
  • 12

8 Answers8

76
-vf "scale=640:-1"

works great until you will encounter error

[libx264 @ 0x2f08120] height not divisible by 2 (640x853)

So most generic approach is use filter expressions:

scale=640:trunc(ow/a/2)*2

It takes output width (ow), divides it by aspect ratio (a), divides by 2, truncates digits after decimal point and multiplies by 2. It guarantees that resulting height is divisible by 2.

Credits to ffmpeg trac

UPDATE

As comments pointed out simpler way would be to use -vf "scale=640:-2". Credits to @BradWerth for elegant solution

mente
  • 2,476
  • 1
  • 26
  • 31
  • 9
    Or, if you want to specify the height and let ffmpeg set the width proportionally, `scale=trunc(oh*a/2)*2:min(480\,iw)`. – Christian Davén Jun 12 '13 at 12:25
  • 1
    @BradWerth is this documented anywhere? – Adam Apr 04 '18 at 15:18
  • 1
    @Adam not that I can recall. I believe I was just reading through the options and playing with the settings while implementing this excellent answer. – Brad Werth Apr 04 '18 at 15:35
  • Minor note: `scale=640:trunc(ow/a/2)*2` and `scale=640:-2` are both sufficient answers but not necessarily equivalent. The truncation in the former can give a value that is, e.g. 2 pixels, smaller depending on the size of the input source. – systemexit Jun 06 '18 at 21:16
  • What is this requirement for the height to be divisible by 2? Does it only apply to x264 codec or is it all codecs? And does it also apply to width? – Anders Emil Oct 24 '18 at 14:13
  • 1
    @AndersEmil from what I know it is only to x264 and relevant to both width and height – mente Oct 30 '18 at 09:32
31

For example:

1920x1080 aspect ratio 16:9 => 640x480 aspect 4:3:

ffmpeg -y -i import.media -aspect 16:9 scale=640x360,pad=640:480:0:60:black output.media

aspect ratio 16:9 , size width 640pixel => height 360pixel:
With final output size 640x480, and pad 60pixel black image (top and bottom):

"-vf scale=640x360,pad=640:480:0:60:black"
Brock Adams
  • 82,642
  • 19
  • 207
  • 268
cool2ikou
  • 311
  • 3
  • 3
  • 2
    I've just needed to go from 640x480 to 1280x720 and used `-aspect 16:9 -vf scale=960x720,pad=1280:720:160:0:white` (I needed white padding instead of black, too). Thanks! – Peter K. Mar 18 '16 at 14:20
  • 1
    I needed to go the opposite way, from 640x480 to 1920x1080 with pillar boxing: `-vf "scale=1440x1080,pad=1920:1080:240:0:black"` – whereisalext Apr 12 '17 at 22:41
  • I got it to work with this: `ffmpeg -y -i 01.mp4 -vf "scale=640x360,pad=640:480:0:60:black" 02.mp4` – John Trichereau Jul 11 '17 at 14:21
24

I've asked this a long time ago, but I've actually got a solution which was not known to me at the time -- in order to keep the aspect ratio, you should use the video filter scale, which is a very powerful filter.

You can simply use it like this:

-vf "scale=640:-1" 

Which will fix the width and supply the height required to keep the aspect ratio. But you can also use many other options and even mathematical functions, check the documentation here - http://ffmpeg.org/ffmpeg.html#scale

0-alpha
  • 2,421
  • 4
  • 17
  • 12
  • 3
    this worked out well but I still found that I could run into problems if the height was not divisible by 2. Any idea how to get around this problem? – DevDave Oct 03 '12 at 10:51
  • 13
    note the `-vf "scale=640:-2"` answers here as well. – coco Sep 01 '15 at 03:55
14

Although most of these answers are great, I was looking for a command that could resize to a target dimension (width or height) while maintaining aspect ratio. I was able to accomplish this using ffmpeg's Expression Evaluation.

Here's the relevant video filter, with a target dimension of 512:

-vf "thumbnail,scale='if(gt(iw,ih),512,trunc(oh*a/2)*2)':'if(gt(iw,ih),trunc(ow/a/2)*2,512)'"


For the output width:

'if(gt(iw,ih),512,trunc(oh*a/2)*2)'

If width is greater than height, return the target, otherwise, return the proportional width.


For the output height:

'if(gt(iw,ih),trunc(ow/a/2)*2,512)'

If width is greater than height, return the proportional height, otherwise, return the target.

Charlie
  • 14,603
  • 3
  • 58
  • 64
12

Use force_original_aspect_ratio, from the ffmpeg trac:

ffmpeg -i input.mp4 -vf scale=720:400:force_original_aspect_ratio=decrease output.mp4
xmedeko
  • 5,855
  • 5
  • 44
  • 75
  • This fails when your video has a weird size like for example 1438x800 then the result will be 719x400 and gives an error because width is not divisible by 2... – Daniel Carrasco Marín Feb 04 '17 at 13:57
  • @DanielCarrascoMarín Yep, when you have no control over the input video size, then the problem of divisibility by 2 is not possible to solve by `ffmpeg` itself (unfortunately). I have solved it by computing the aspect ratio in the Python script and then pass the `ffmpeg scale=` params I want. – xmedeko Feb 06 '17 at 09:14
  • 2
    I just tested it with ffmpeg 3.4 and 3.2.9, and it works as a charm. Video become: `719x400` but there were no errors. I could play it with VLC media player (saw the extra pixel on the right). Ubuntu native video player also played it well. And ffmpeg could read the file after, and convert to another one – Kostanos Nov 02 '17 at 17:37
10

If you are trying to fit a bounding box, then using force_original_aspect_ratio as per xmedeko's answer is a good starting point.

However, this does not work if your input video has a weird size and you are encoding to a format that requires the dimensions to be divisible by 2, resulting in an error.

In this case, you can use expression evaluation in the scale function, like that used in Charlie's answer.

Assuming an output bounding box of 720x400:

-vf "scale='trunc(min(1,min(720/iw,400/ih))*iw/2)*2':'trunc(min(1,min(720/iw,400/ih))*ih/2)*2'"

To break this down:

  • min(1,min(720/iw,400/ih) finds the scaling factor to fit within the bounding box (from here), constraining it to a maximum of 1 to ensure it only downscales, and
  • trunc(<scaling factor>*iw/2)*2 and trunc(<scaling factor>*iw/2)*2 ensure that the dimensions are divisible by 2 by dividing by 2, making the result an integer, then multiplying it back by 2.

This eliminates the need for finding the dimensions of the input video prior to encoding.

tvStatic
  • 801
  • 1
  • 7
  • 25
1

If '-aspect x:y' is present and output file format is ISO Media File Format (mp4) then ffmpeg adds pasp-atom (PixelAspectRatioBox) into stsd-box in the video track to indicate to players the expected aspect ratio. Players should scale video frames respectively. Not needed to scale video before encoding or transcoding to fit it to the aspect ratio, it should be performed by a player.

1

you can use ffmpeg -i to get the dimensions of the original file, and use that in your commands for the encode. What platform are you using ffmpeg on?

box86rowh
  • 3,285
  • 2
  • 22
  • 37
  • Hi, I am using it on Linux. I would ideally like to have a bash script that works regardless of aspect ratio. I guess I can adjust it manually for non-widescreen resolution, but with the myriad of options in ffmpeg I thought I just didn't know a trick setting... – 0-alpha Nov 22 '11 at 19:09
  • 2
    Yes, it is an answer because by using the original dimensions and scaling them down (or up) by a simple computation, OP can solve the problem "manually". – Alfe Feb 28 '14 at 09:56
  • I found it useful in any case. Personally I am glad this answer was posted. – TripleAntigen Aug 29 '14 at 05:34