338

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?

llogan
  • 121,796
  • 28
  • 232
  • 243
Andy Hin
  • 30,345
  • 42
  • 99
  • 142
  • @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?..thanks – varmashrivastava Oct 27 '18 at 08:42
  • 2
    @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 Oct 27 '18 at 14:04
  • @varmashrivastava I've gone ahead and fixed the first answer. Hopefully it doesn't get vandalized by the mods. – Aleksandr Dubinsky Oct 27 '18 at 14:15
  • @AleksandrDubinsky thanks..and user can use `"scale="` instead of `"pad="` if he/she doesn't want colured padding pixels? – varmashrivastava Nov 03 '18 at 07:27
  • Related answer: https://superuser.com/questions/571141/ffmpeg-avconv-force-scaled-output-to-be-divisible-by-2 – Nav Jan 27 '23 at 14:46

7 Answers7

447

The answer to the original question should not scale the video but instead fix the height not divisible by 2 error. This can be achieve using this filter:

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

Full command:

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

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.

Teocci
  • 7,189
  • 1
  • 50
  • 48
Andy Hin
  • 30,345
  • 42
  • 99
  • 142
  • 3
    It's not a bug. It does not matter that you're not performing scaling since the output will inherit the frame size of the input. – llogan Dec 30 '13 at 23:12
  • 1
    @LordNeckbeard right, but why does either dimension need to be divisible by 2? – Andy Hin Dec 31 '13 at 08:05
  • 1
    Because that's what libx264 needs. I don't think it's a limitation of H.264 itself. Other encoders might enforce 8. In H.264 macroblocks can be split to 4⨉4 at minimum, so I don't know why 2 is the limit. – slhck Dec 31 '13 at 15:14
  • 6
    For the record, I was just doing something where I created a video out of an image, and it used yuvj444p as the pixel format; it didn't care about the video size. Then I needed to convert it to yuv420p, and then it cared about the video size. I looked up yuv420p on wikipedia, I think it's a multi-pixel color format, that needs the image to be a specific size. Not sure why it matters compressed, though. – lahwran Oct 18 '15 at 23:20
  • 7
    You're probably better off using pad rather than scale, to add a black row/column. Scaling an image up by one pixel will blur it. – Glenn Maynard Nov 14 '15 at 22:20
  • 1
    @GlennMaynard Could you care to give an example of `pad` for this problem? – Nicke Manarin Apr 17 '17 at 14:44
  • 1
    It will distort the video. – vbence May 16 '17 at 11:56
  • 7
    @NickeManarin, this filter should work to add 1 pixel of white padding to the vertical dimension, with the video positioned upper left: `-vf pad="width=iw:height=ih+1:x=0:y=0:color=white"`. The ffmpeg pad documentation is here: https://ffmpeg.org/ffmpeg-filters.html#pad-1. – Mark Berry Jul 18 '17 at 22:23
  • 5
    Here's a solution that only adds a pixel of padding to dimensions that are odd: `-vf pad="width=ceil(iw/2)*2:height=ceil(ih/2)*2"`. – danneu Nov 28 '17 at 02:50
  • 1
    There is an old ffmpeg bug report that says it is a limitation of libx264: https://trac.ffmpeg.org/ticket/309 – Jay Prall Jan 30 '18 at 00:34
  • 3
    @danneu this is the right answer. scaling will make the video look bad, padding is much better.. – BjornW Apr 07 '18 at 10:59
  • Alright, I've added an answer and tried to add some commentary based on my memory of the problems I had here. It's been a while. :) I've been using my solution in a large production pipeline since then and it's always had great results. – danneu Oct 27 '18 at 18:30
  • 5
    Instead of padding, if you prefer to crop. This is the formula `crop=floor(iw/2)*2:floor(ih/2)*2` – Ahmet Dec 11 '18 at 13:43
  • 2
    the command '-vf pad=..." should be placed before the output video.mp4, otherwise no padding is executed. My example: ffmpeg -i test.h264 -vcodec libx264 -vf pad="width=iw+16:height=ih+16:color=white" -frames 10 -y -an test.mp4 – Shevach Riabtsev Sep 29 '19 at 02:57
  • 2
    Height being divisible by 2 is not a requirement of h264 but of the pixel format used: yuv420p. If you use yuv422p you won't have to deal with this limitation. – Aalex Gabi Dec 01 '21 at 10:07
395

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.

llogan
  • 121,796
  • 28
  • 232
  • 243
  • 23
    I think tihis should be marked as the right answer because of no "tricks" involved. Whish to upvote more than one time – LucaM Jun 19 '15 at 10:14
  • 1
    Why does `-vf scale=-2:-2` not work? In my case I want to preserve the original file size as much as possible. What worked for me was `-vf scale=-2:ih`. But it doesn't work if both h/w are uneven. – Pascal Jun 30 '15 at 08:27
  • 3
    @tuner The resulting value of `-2` depends on the declared value of the other dimension. – llogan Jun 30 '15 at 17:10
  • 3
    in my case this gave me the following error: `Size values less than -1 are not acceptable.` but the answer from @Zbyszek worked perfectly. – Julien Sep 30 '16 at 17:31
  • @Julien Never seen that before. Can you show your actual command and the complete console output? You can use a pastebin site and provide the link here. – llogan Sep 30 '16 at 17:33
  • 2
    @Julien [That's not `ffmpeg`](http://stackoverflow.com/a/9477756/1109017). You can [download a static build](http://johnvansickle.com/ffmpeg/). – llogan Oct 02 '16 at 18:12
  • Unfortunately doesn't answer the original question: how to preserve the original width and height without rescaling – Georgii Oleinikov Aug 20 '18 at 11:41
  • @llogan what is the difference between passing -1 or -2 in w or h ? – Rahul Vyas Nov 24 '20 at 06:05
  • @RahulVyas It's explained in the [scale filter documentation](https://ffmpeg.org/ffmpeg-filters.html#scale) under *width*, *height* sections. – llogan Nov 26 '20 at 03:34
  • This `-2:480` saved my day ;) `ffmpeg -i input.mp4 -vf scale=-2:480 output.mp4`. Also `-preset fast` gave a bit bigger file size, but `-preset ultrafast` gave almost 2x bigger file size. – Vladimir Vukanac Mar 21 '21 at 15:16
76

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)

Zbyszek
  • 1,420
  • 18
  • 23
36

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.

danneu
  • 9,244
  • 3
  • 35
  • 63
  • The scale solutions alter the pixel count by 1 at most. That hardly distorts the picture. If you're worried about filtering speed, use `scale=iw+mod(iw,2):ih+mod(ih,2):flags=neighbor`. This can only increase each dimension by 1, if needed, and will duplicate the last row/column. – Gyan Feb 04 '19 at 15:59
  • 2
    @Gyan It's been too long since I had the problem that this solved (my answer was extracted from a comment I made long ago), but I remember that scaling by a single pixel *did* introduce noticeable visual artifacts under some conditions which is why I bothered in the first place. I don't remember exactly, maybe disproportionate amount of blurring from a single pixel change? Maybe only on some vid/image formats? All I can say is that I processed thousands of vids with this fix and it was the favorable transform. – danneu Feb 06 '19 at 00:35
  • 2
    Can confirm ; padding a pixel adds no artifacts. Stretching, even if by only a single pixel, adds small artifacts due to aliasing. – zaTricky Oct 26 '20 at 14:18
  • 3
    Just came across this question, and can confirm this answer works. Alternatively if you are using complex filters, a solution like this will do the trick `-filter_complex "[0:v] {some filters you have} [outv]; [outv]pad='width=ceil(iw/2)*2:height=ceil(ih/2)*2'[outvpad]" -map "[outvpad]"` – ffarhour Jan 27 '22 at 21:40
  • 1
    @Gyan I did scale 720:-1 and I can't even read the text anymore. – nurettin May 12 '22 at 07:26
20

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.

Adisak
  • 6,708
  • 38
  • 46
  • 2
    Another interesting fact... when you decode with Microsoft Media Foundation stuff, you need to use multiples of 16 for H264. So 1080P video actually decodes into a buffer that is 1088 high (although you ignore the last 8 lines). – Adisak Jan 14 '15 at 17:50
4

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.

endigo
  • 69
  • 3
3

LordNeckbeard has the right answer, very fast

-vf scale=1280:-2

For android, dont forget add

"-preset ultrafast" and|or "-threads n"
fallouter
  • 47
  • 1
  • You don't need to declare threads: that's dealt with automatically. I believe the Andriod slowness when encoding to H.264 is due to people using the popular "WritingMinds/ffmpeg-android" which uses `--disable-asm` in its [x264 build script](https://github.com/WritingMinds/ffmpeg-android/blob/master/x264_build.sh#L25). This results in unecessary and significant slowness (you can check the ffmpeg log and if it shows `using cpu capabilties: none!` then that's bad). I'm not sure why they added that, but I'm not an Android developer. – llogan Nov 30 '17 at 21:37