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.
OH mine , you man did it perfectly , I don’t know if it suite to your code , but I tried it on my live broadcast site and the picture become less pixelated (since I broadcast on low quality video)
LikeLike