I'm testing out converting some videos to HEVC encoding utilizing ffmpeg and libx265 and I've discovered that the the input width must be divisible by 8 in order to successfully re-encode. I would like to determine whether a re-encode will be successful without attempting the encode and then examining the output. Ideally I'd like to do this in a bash script. My skills are somewhat limited in this regard and much of what I have come up with thus far is far less than elegant.
For example I know that I can do the math with bc as in:
echo 'scale=2;576/8' | bc
72.00and I can obtain the width via
width=$(mediainfo $filename | grep "Width" | sed 's/[^0-9]*//g')but the former doesn't exactly answer the question is the width divisible by 8 and the latter is ugly and uses too many pipes which research indicates is less then efficient.
I've got the ffmpeg command line for re-encoding sorted to my satisfaction using:
ffmpeg -i "$f" -c:a copy -c:v libx265 -preset "$preset" -crf 25 "$target"
and I intend to scale the video to a width divisible by 8 if it isn't already but I'm looking for better solutions than I've come up with thus far for determining if scaling is required and how to determine the closest width divisible by 8 to scale to.
12 Answers
The following will correct the width to a number divisible by 8 and then correctly rescale the height to a number divisible by 8 while maintaining correct aspect ratio:
ffmpeg -i <input> \ -c:a copy \ -c:v libx265 -preset medium -x265-params crf=28 \ -vf scale="trunc(iw/8)*8:-8" \ <output>As a bonus if the width is already divisible by 8 there will be no change in the output width and no error from FFmpeg.
An explanation for the scale syntax, remembering that the syntax is -vf scale=width:height:
1. Output Width Calculation:
iw/8: The width of the input video stream will be divided by 8. So an original width of 690 would create a number 86.25trunc: The number 86.25 would be 'truncated' to 86*8: 86 would be multiplied by 8 to give a final width of 688 which of course is divisible by 8!
2. Output Height Calculation:
-8: FFmpeg will calculate a height that is divisible by 8 but which also maintains the correct aspect ratio of the original file
In a perfect world hevc encoding is run with encoding units of 8x8, 16x16, 32x32 etc and this syntax guarantees this. More details of this here...
References:
- FFmpeg and H.265 Encoding Guide
- Scaling (resizing) with ffmpeg
- FFmpeg: 3. Expression Evaluation
- HEVC – What are CTU, CU, CTB, CB, PB, and TB?
Obtaining the width is relatively simple with mediainfo. If you don't have it you can install it with sudo apt-get install mediainfo
width=$(mediainfo '--Inform=Video;%Width%' $filename)
Determining divisibility by 8 can be done via
if [ $(( $width % 8 )) -eq 0 ] ; then echo "Your number is divisible by 8 – you may convert it”
else echo "Video width is not divisible by 8 – it needs scaling to re-encode it."
fiA far simpler and faster approach inspired by @andrew.46 and a bit more research and testing is to let ffmpeg do the work with
ffmpeg -i $inputfilename -c:a copy -c:v libx265 -preset veryfast -x265-params crf=25 -vf scale=-8:ih $outputfilename
the -vf scale=-8:ih setting insures that the width is divisible by 8 (-8) and uses the input height (ih) accordingly to maintain aspect ratio.
Sources: man mediainfo
How can I determine if a video can be encoded successfully with HEVC (x265) encoding
1