How to create ABR content with FFmpeg in one pass

I was once informed that it would be nigh impossible to create ABR content in one pass using FFmpeg.  Challenge accepted!

I remembered that statement when I wanted to calculate a bit per pixel density encoding matrix for different video resolutions.  The problem with doing that is that every source video is different, even throughout the entire video, and I did not want to encode multiple different video files multiple times to generate this matrix.  Desperation, being the mother of invention, decided to intervene on my behalf.  Once I figured out how to perform single pass ABR encoding I decided to perform some ABR encoding using CRF 21 to find an approximate bit per pixel density.  As expected this yielded a fair amount of data as each one minute section of the source video I encoded was different.  From that I validated a trend that I had been seeing for a while.

To retain a relatively sane visual quality, the lower the resolution of the video the higher the bit per pixel density should be.  I did some work and came up with some data.  If you run a one minute CRF encode against each minute in your source 1080p video you will be provided with a bit per pixel density matrix.  Using the matrix below, assuming you do not want to make one of your own, you should be able to find an approximate bitrate to use for your other resolutions. Please note that the 1080p bit per pixel density shown below is post encode and is not the source file. Best practice for a full encode is to perform a single pass encode against your 1080p source to generate a bit per pixel density that you can then use to encode your ABR content.

RES     BPP      RES     BPP      RES     BPP      RES      BPP
360p    0.277    480p    0.226    720p    0.185    1080p    0.161
360p    0.244    480p    0.207    720p    0.176    1080p    0.155
360p    0.208    480p    0.169    720p    0.139    1080p    0.120
360p    0.215    480p    0.164    720p    0.128    1080p    0.111
360p    0.194    480p    0.158    720p    0.131    1080p    0.117
360p    0.164    480p    0.136    720p    0.115    1080p    0.106
360p    0.136    480p    0.110    720p    0.091    1080p    0.082
360p    0.152    480p    0.117    720p    0.092    1080p    0.080
360p    0.160    480p    0.120    720p    0.093    1080p    0.079
360p    0.134    480p    0.108    720p    0.089    1080p    0.079
360p    0.126    480p    0.100    720p    0.081    1080p    0.071
360p    0.125    480p    0.097    720p    0.078    1080p    0.069
360p    0.118    480p    0.091    720p    0.074    1080p    0.065
360p    0.103    480p    0.084    720p    0.070    1080p    0.065
360p    0.103    480p    0.083    720p    0.068    1080p    0.063
360p    0.110    480p    0.085    720p    0.068    1080p    0.062
360p    0.105    480p    0.082    720p    0.066    1080p    0.061
360p    0.094    480p    0.074    720p    0.061    1080p    0.057
360p    0.100    480p    0.075    720p    0.059    1080p    0.054
360p    0.077    480p    0.062    720p    0.051    1080p    0.049
360p    0.078    480p    0.060    720p    0.050    1080p    0.048
360p    0.077    480p    0.061    720p    0.049    1080p    0.044
360p    0.072    480p    0.055    720p    0.044    1080p    0.041
360p    0.056    480p    0.045    720p    0.038    1080p    0.041
360p    0.063    480p    0.052    720p    0.043    1080p    0.040
360p    0.051    480p    0.042    720p    0.035    1080p    0.038
360p    0.046    480p    0.038    720p    0.033    1080p    0.037

Yes, entropy encoding is interesting.  You probably recognized in the matrix above that the numbers are not as uniform across the resolutions as we would like.  Using that matrix as a guideline I came up with some calculations that I have provided below and did some rounding of the bit per pixel density and then rounded the bitrate.

1920*1080*23.976/1024*0.070  ==  3398.598kbps
1280*720*23.976/1024*0.080   ==  1726.272kbps
854*480*23.976/1024*0.100    ==  959.78925kbps
480*360*23.976/1024*0.125    ==  505.74375kbps
426*240*23.976/1024*0.133    ==  318.38254875kbps
284*160*23.976/1024*0.150    ==  159.59025kbps

With all of that said, 1080p comes out to about 3400kbps, 720p comes out to about 1725kbps, 480p comes out to about 960kbps, 360p comes out to about, 510kbps, 240p comes out to about 320kbps, and 160p comes out to about 160kbps.

Now, how did I do it?  With likely the longest FFmpeg command line I have ever assembled.  I have broken out the different outputs to their own separate lines for readability.

ffmpeg.exe -i sourcefile.mp4

-pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf "scale=1920:1080" -b:v 3400k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af "aresample=async=1:min_hard_comp=0.100000:first_pts=0" -map_metadata -1 -f mp4 1080p.mp4

-pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf "scale=1280:720" -b:v 1725k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af "aresample=async=1:min_hard_comp=0.100000:first_pts=0" -map_metadata -1 -f mp4 720p.mp4

-pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf "scale=854:480" -b:v 960k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af "aresample=async=1:min_hard_comp=0.100000:first_pts=0" -map_metadata -1 -f mp4 480p.mp4

-pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf "scale=480:360" -b:v 510k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af "aresample=async=1:min_hard_comp=0.100000:first_pts=0" -map_metadata -1 -f mp4 360p.mp4

-pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf "scale=426:240" -b:v 320k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af "aresample=async=1:min_hard_comp=0.100000:first_pts=0" -map_metadata -1 -f mp4 240p.mp4

-pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf "scale=284:160" -b:v 160k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af "aresample=async=1:min_hard_comp=0.100000:first_pts=0" -map_metadata -1 -f mp4 160p.mp4

Or the long version if you prefer:

ffmpeg.exe -i sourcefile.mp4 -pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf “scale=1920:1080” -b:v 3400k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af “aresample=async=1:min_hard_comp=0.100000:first_pts=0” -map_metadata -1 -f mp4 1080p.mp4 -pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf “scale=1280:720” -b:v 1725k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af “aresample=async=1:min_hard_comp=0.100000:first_pts=0” -map_metadata -1 -f mp4 720p.mp4 -pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf “scale=854:480” -b:v 960k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af “aresample=async=1:min_hard_comp=0.100000:first_pts=0” -map_metadata -1 -f mp4 480p.mp4 -pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf “scale=480:360” -b:v 510k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af “aresample=async=1:min_hard_comp=0.100000:first_pts=0” -map_metadata -1 -f mp4 360p.mp4 -pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf “scale=426:240” -b:v 320k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af “aresample=async=1:min_hard_comp=0.100000:first_pts=0” -map_metadata -1 -f mp4 240p.mp4 -pix_fmt yuv420p -r 23.976 -vcodec libx264 -vf “scale=284:160” -b:v 160k -preset veryfast -profile:v baseline -keyint_min 24 -g 48 -x264opts no-scenecut -strict experimental -acodec aac -b:a 96k -af “aresample=async=1:min_hard_comp=0.100000:first_pts=0” -map_metadata -1 -f mp4 160p.mp4 -strict experimental -acodec aac -vn -b:a 96k -af “aresample=async=1:min_hard_comp=0.100000:first_pts=0” -f mp4 AudioOnly.mp4

Now go forth and streamline your production environment, unless you plan on performing two pass encoding then you are on your own, or not if you build your own bit per pixel density encoding matrix from your unique and varied content.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s