Convert a video by setting the final size

Is there a way to set the maximum final size for video converting using ffmpeg (or any other CLI based video converter)?

Like if I have an 100mb video and want to convert it to a 10mb video with the highest quality possible, considering the final format isn't important.


Solution 1:

Going from 100mb to 10mb is a slightly unrealistic 90% drop in size but I will give an example of reducing to 50mb creating an H.264 video, which is a more reasonable 50% reduction in size as well as reducing to 40mb using HEVC.

I am using the following sample file:

wget https://web.archive.org/web/20190412004321/http://dl3.h265files.com/TearsOfSteel_720p_h265.mkv

You can use this sample file to confirm the following results that I have given and perhaps experiment a little further yourself. MediaInfo reveals the following for this downloaded file:

mediainfo \
--Inform="General;Duration=%Duration/String3%\nFile size=%FileSize/String1%" \
TearsOfSteel_720p_h265.mkv
Duration=00:12:14.058   <-----
File size=101 MiB       <-----

The arrows of course are my own! You then perhaps have 2 really good choices:

  1. Re-Encode to H.264
  2. Re-encode to HEVC

1. Re-Encode to H.264 (50mb)

H.264 is widely accepted now and would be an excellent choice for your output video file. The formula to calculate the output bitrate for the desired 50mb would then be:

(50 MiB * 8192 [converts MiB to kBit]) / 734 seconds = ~558 kBit/s total bitrate
558 - 128 kBit/s (desired audio bitrate) = 430 kBit/s video bitrate

To accomplish this use the following FFmpeg 2 pass command:

ffmpeg -y -i TearsOfSteel_720p_h265.mkv \
      -c:v libx264 -b:v 430k -pass 1 \
      -c:a libmp3lame -b:a 128k -f mp4 /dev/null && \
ffmpeg -i TearsOfSteel_720p_h265.mkv \
       -c:v libx264 -b:v 430k -pass 2 \
       -c:a libmp3lame -b:a 128k TearsOfSteel_smaller.mp4

The resulting file size is 49.4MiB with quite reasonable, but not amazing, viewing quality. Pretty good for a 50% reduction in file size and a huge drop in video bitrate actually!

2. Re-encode to HEVC (40mb)

Another choice (as suggested by emk2203) is to re-encode the existing HEVC stream with a lower bitrate, this time aiming for 40mb as HEVC claims better quality at a lower bitrate.

This time the formula would be:

(40 MiB * 8192 [converts MiB to kBit]) / 734 seconds = ~446 kBit/s total bitrate
446 - 128 kBit/s (desired audio bitrate) = 318 kBit/s video bitrate

and the FFmpeg 2 pass command line is:

ffmpeg -y -i TearsOfSteel_720p_h265.mkv \
      -c:v libx265 -x265-params pass=1 -b:v 318k \
      -c:a libmp3lame -b:a 128k -f mp4 /dev/null && \
ffmpeg -i TearsOfSteel_720p_h265.mkv \
       -c:v libx265 -x265-params pass=2 -b:v 318k \
       -c:a libmp3lame -b:a 128k TearsOfSteel_smaller.mp4

And this provides a 40mb file with quite reasonable quality.

In conclusion:

From these example you can experiment further by decreasing the required MiB in the formula and observing the subsequent viewing quality. Have Fun!!

References:

  • H.264 Video Encoding Guide: 2 Pass Encoding FFmpeg trac article which gives definitive and easily followed information on 2 pass encoding to H.264.
  • FFmpeg and H.265 Encoding Guide FFmpeg trac gudelines for encoding to HEVC using the x265 wrapper.
  • FFmpeg and VP9 Encoding Guide Another option to look at is encoding to VP9 and all of the details are on this FFmpeg trac page. Again you are after the 2 pass encoding section...