237
votes

I am trying to encode a .mp4 video from a set of frames using FFMPEG using the libx264 codec.

This is the command I am running:

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

I sometimes get the following error:

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

After searching around a bit it seems that the issue has something to do with the scaling algorithm and can be fixed by adding a -vf argument.

However, in my case I don't want to do any scaling. Ideally, I want to keep the dimensions exactly the same as the frames. Any advice? Is there some sort of aspect ratio that h264 enforces?

7
@AleksandrDubinsky But LordNeckbeard's answer doesn't preserve original width and height.Here we need to manually specify either width or height..and if w use -vf scale=-2:ih or -vf scale=iw:-2 this will not work if both height and width are uneven..Please explain how that answer is more optimal?..thanksvarmashrivastava
@varmashrivastava Well, the way SO works is that there may originally have been one question, and then Google sends over a bunch of people with a different question who then hijack the page. It is what it is, try not to fight it. The correct answer to the original question is -vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2", which isn't even one of the answers. The correct answer to everyone else's question is LordNeckbeard's.Aleksandr Dubinsky
@varmashrivastava I've gone ahead and fixed the first answer. Hopefully it doesn't get vandalized by the mods.Aleksandr Dubinsky
@AleksandrDubinsky thanks..and user can use "scale=" instead of "pad=" if he/she doesn't want colured padding pixels?varmashrivastava

7 Answers

319
votes

The answer to the original question which does not want to scale the video is:

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

Command:

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

Basically, .h264 needs even dimensions so this filter will:

  1. Divide the original height and width by 2
  2. Round it up to the nearest pixel
  3. Multiply it by 2 again, thus making it an even number
  4. Add black padding pixels up to this number

You can change the color of the padding by adding filter parameter :color=white. See the documentation of pad.

307
votes

For width and height

Make width and height divisible by 2 with the crop filter:

ffmpeg -i input.mp4 -vf "crop=trunc(iw/2)*2:trunc(ih/2)*2" output.mp4

If you want to scale instead of crop change crop to scale.

For width or height

Using the scale filter. This will make width 1280. Height will be automatically calculated to preserve the aspect ratio, and the width will be divisible by 2:

ffmpeg -i input.mp4 -vf scale=1280:-2 output.mp4

Similar to above, but make height 720 and automatically calculate width:

ffmpeg -i input.mp4 -vf scale=-2:720 output.mp4

You can't use -2 for both width and height, but if you already specified one dimension then using -2 is a simple solution.

68
votes

If you want to set some output width and have output with the same ratio as original

scale=720:-1 

and not to fall with this problem then you can use

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

(Just for people searching how to do that with scaling)

27
votes

The problem with the scale solutions here is that they distort the source image/video which is almost never what you want.

Instead, I've found the best solution is to add a 1-pixel pad to the odd dimension. (By default, the pading is black and hard to notice.)

The problem with the other pad solutions is that they do not generalize over arbitrary dimensions because they always pad.

This solution only adds a 1-pixel pad to height and/or width if they are odd:

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

This is ideal because it always does the right thing even when no padding is necessary.

19
votes

It's likely due to the the fact that H264 video is usually converted from RGB to YUV space as 4:2:0 prior to applying compression (although the format conversion itself is a lossy compression algorithm resulting in 50% space savings).

YUV-420 starts with an RGB (Red Green Blue) picture and converts it into YUV (basically one intensity channel and two "hue" channels). The Hue channels are then subsampled by creating one hue sample for every 2X2 square of that hue.

If you have an odd number of RGB pixels either horizontally or vertically, you will have incomplete data for the last pixel column or row in the subsampled hue space of the YUV frame.

3
votes

You may also use bitand function instead of trunc:

bitand(x, 65534)

will do the same as trunc(x/2)*2 and it is more transparent in my opinion.
(Consider 65534 a magical number here ;) )


My task was to scale automatically a lot of video files to half resolution.

scale=-2,ih/2 lead to slightly blurred images

reason:

  • input videos had their display aspect ratio (DAR) set
  • scale scales the real frame dimensions
  • during preview the new videos' sizes have to be corrected using DAR which in case of quite low-resoution video (360x288, DAR 16:9) may lead to blurring

solution:

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

explanation:

  • output_height = input_height / 2
  • output_width = output_height * original_display_aspect_ratio
  • both output_width and output_height are now rounded to nearest smaller number divisible by 2
  • setsar=1 means output_dimensions are now final, no aspect ratio correction should be applied

Someone might find this helpful.

2
votes

LordNeckbeard has the right answer, very fast

-vf scale=1280:-2

For android, dont forget add

"-preset ultrafast" and|or "-threads n"