Impact of adding additional keyframes on video size

The following is a quick test on looking at the impact on filesize of adding additional keyframes to an encode for the purpose of making it more suitable for segmentation in adaptive bitrate delivery.

The following shows that on a very hard to encode source file (reflections on water) that adding a keyframe every 50 frames (2 seconds on 25 fps source) that the overall increase in size was only 96k (approx 1%).

Note that the video is 1153 frames long so we would expect to have at least 23 key frames given a requested keyframe interval of 50. The source also had very few traditional scene changes which is reflected by the x264 generated number of keyframes of only 6 when left to only adding keyframes on scenecut.

Sample command to show GOP / I frame structure using ffprobe:

ffprobe -select_streams v:0 -show_frames goptest.mov |grep key_frame|less

File with x264 selected key frames

Encode settings

/usr/local/bin/ffmpeg -y -i goptest.mov -codec:v libx264 -b:v 6000K -s 1920x1080 -preset slower -tune film -me_range 24 -bufsize 50000K -maxrate 50000K -refs 4 -profile:v high -level 4.1 -threads 0 -sn 'goptest_1080p.mp4'

ffmpeg encode results

video:34160kB audio:538kB subtitle:0 global headers:0kB muxing overhead 0.077031%
[libx264 @ 0x7fc873846800] frame I:6     Avg QP:16.50  size: 25800
[libx264 @ 0x7fc873846800] frame P:1131  Avg QP:23.22  size: 30785
[libx264 @ 0x7fc873846800] frame B:16    Avg QP: 6.54  size:   354
[libx264 @ 0x7fc873846800] consecutive B-frames: 98.0%  0.3%  0.3%  1.4%
[libx264 @ 0x7fc873846800] mb I  I16..4: 66.5% 32.0%  1.5%
[libx264 @ 0x7fc873846800] mb P  I16..4: 49.4% 40.7%  1.3%  P16..4:  6.4%  0.4%  0.1%  0.0%  0.0%    skip: 1.6%
[libx264 @ 0x7fc873846800] mb B  I16..4:  0.2%  0.1%  0.0%  B16..8:  9.5%  0.0%  0.0%  direct: 0.1%  skip:90.2%  L0:68.0% L1:31.9% BI: 0.1%
[libx264 @ 0x7fc873846800] final ratefactor: 22.37
[libx264 @ 0x7fc873846800] 8x8 transform intra:44.5% inter:93.6%
[libx264 @ 0x7fc873846800] direct mvs  spatial:62.5% temporal:37.5%
[libx264 @ 0x7fc873846800] coded y,uvDC,uvAC intra: 21.3% 67.9% 13.0% inter: 17.0% 39.8% 0.6%
[libx264 @ 0x7fc873846800] i16 v,h,dc,p: 35% 25% 18% 22%
[libx264 @ 0x7fc873846800] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 14% 19% 36%  3%  5%  5% 10%  3%  5%
[libx264 @ 0x7fc873846800] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 18% 19% 44%  1%  6%  4%  6%  1%  2%
[libx264 @ 0x7fc873846800] i8c dc,h,v,p: 43% 33% 19%  5%
[libx264 @ 0x7fc873846800] Weighted P-Frames: Y:0.3% UV:0.2%
[libx264 @ 0x7fc873846800] ref P L0: 62.8%  3.6% 17.0%  9.3%  7.3%
[libx264 @ 0x7fc873846800] ref B L0: 99.4%  0.6%
[libx264 @ 0x7fc873846800] ref B L1: 99.7%  0.3%
[libx264 @ 0x7fc873846800] kb/s:6067.44

File with keyint=50

Encode settings

Note that just using keyint on it’s own doesn’t necessarily mean that keyframes will be forced every 50 frames, see below for alternative option by switching x264 scenecut off

/usr/local/bin/ffmpeg -y -i goptest.mov -codec:v libx264 -b:v 6000K -s 1920x1080 -preset slower -tune film -me_range 24 -bufsize 50000K -maxrate 50000K -refs 4 -profile:v high -level 4.1 -x264opts keyint=50 -threads 0 -sn 'goptest_1080p_keyint50.mp4'

ffmpeg encode results

video:34256kB audio:538kB subtitle:0 global headers:0kB muxing overhead 0.077043%
[libx264 @ 0x7fe631846800] frame I:26    Avg QP:19.07  size: 41326
[libx264 @ 0x7fe631846800] frame P:1112  Avg QP:23.33  size: 30574
[libx264 @ 0x7fe631846800] frame B:15    Avg QP: 6.27  size:   300
[libx264 @ 0x7fe631846800] consecutive B-frames: 98.1%  0.3%  0.5%  1.0%
[libx264 @ 0x7fe631846800] mb I  I16..4: 52.5% 43.3%  4.2%
[libx264 @ 0x7fe631846800] mb P  I16..4: 49.8% 40.4%  1.3%  P16..4:  6.3%  0.4%  0.1%  0.0%  0.0%    skip: 1.8%
[libx264 @ 0x7fe631846800] mb B  I16..4:  0.2%  0.1%  0.0%  B16..8:  2.8%  0.0%  0.0%  direct: 0.0%  skip:96.8%  L0:12.8% L1:87.0% BI: 0.2%
[libx264 @ 0x7fe631846800] final ratefactor: 22.35
[libx264 @ 0x7fe631846800] 8x8 transform intra:44.1% inter:94.0%
[libx264 @ 0x7fe631846800] direct mvs  spatial:60.0% temporal:40.0%
[libx264 @ 0x7fe631846800] coded y,uvDC,uvAC intra: 21.3% 67.2% 13.0% inter: 16.4% 38.5% 0.6%
[libx264 @ 0x7fe631846800] i16 v,h,dc,p: 35% 25% 18% 22%
[libx264 @ 0x7fe631846800] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 14% 19% 37%  3%  5%  5% 10%  3%  5%
[libx264 @ 0x7fe631846800] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 19% 20% 43%  1%  5%  4%  6%  1%  2%
[libx264 @ 0x7fe631846800] i8c dc,h,v,p: 43% 33% 19%  5%
[libx264 @ 0x7fe631846800] Weighted P-Frames: Y:0.3% UV:0.2%
[libx264 @ 0x7fe631846800] ref P L0: 63.5%  3.6% 16.9%  9.1%  7.0%
[libx264 @ 0x7fe631846800] ref B L0: 89.3% 10.7%
[libx264 @ 0x7fe631846800] kb/s:6084.54

Whith scene cut disabled

Note that with scenecut disable there are actually less key frames than with scenecut enabled, which makes sense as opposed to just dropping a key frame in every 50 frames it would also add them when a scene change is detetecd

/usr/local/bin/ffmpeg -y -i goptest.mov -codec:v libx264 -b:v 6000K -s 1920x1080 -preset slower -tune film -me_range 24 -bufsize 50000K -maxrate 50000K -refs 4 -profile:v high -level 4.1 -x264opts keyint=50:no-scenecut -threads 0 -sn 'goptest_1080p_keyint50_noscenecut.mp4'
frame= 1153 fps=1.1 q=-1.0 Lsize= 34821kB time=00:00:46.04 bitrate=6195.7kbits/s dup=15 drop=0 
video:34255kB audio:538kB subtitle:0 global headers:0kB muxing overhead 0.077056%
[libx264 @ 0x7f9b9b846800] frame I:24 Avg QP:19.70 size: 45538
[libx264 @ 0x7f9b9b846800] frame P:1112 Avg QP:23.33 size: 30551
[libx264 @ 0x7f9b9b846800] frame B:17 Avg QP: 7.33 size: 643
[libx264 @ 0x7f9b9b846800] consecutive B-frames: 97.9% 0.2% 0.5% 1.4%
[libx264 @ 0x7f9b9b846800] mb I I16..4: 51.0% 45.6% 3.4%
[libx264 @ 0x7f9b9b846800] mb P I16..4: 49.8% 40.4% 1.3% P16..4: 6.3% 0.4% 0.1% 0.0% 0.0% skip: 1.7%
[libx264 @ 0x7f9b9b846800] mb B I16..4: 0.2% 0.1% 0.0% B16..8: 8.3% 0.0% 0.0% direct: 0.5% skip:90.8% L0:73.7% L1:26.2% BI: 0.0%
[libx264 @ 0x7f9b9b846800] final ratefactor: 22.35
[libx264 @ 0x7f9b9b846800] 8x8 transform intra:44.2% inter:94.3%
[libx264 @ 0x7f9b9b846800] direct mvs spatial:64.7% temporal:35.3%
[libx264 @ 0x7f9b9b846800] coded y,uvDC,uvAC intra: 21.2% 67.2% 12.9% inter: 16.5% 38.6% 0.5%
[libx264 @ 0x7f9b9b846800] i16 v,h,dc,p: 35% 25% 18% 22%
[libx264 @ 0x7f9b9b846800] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 14% 19% 37% 3% 5% 5% 10% 3% 5%
[libx264 @ 0x7f9b9b846800] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 18% 20% 43% 1% 5% 4% 6% 1% 2%
[libx264 @ 0x7f9b9b846800] i8c dc,h,v,p: 43% 33% 19% 5%
[libx264 @ 0x7f9b9b846800] Weighted P-Frames: Y:0.3% UV:0.2%
[libx264 @ 0x7f9b9b846800] ref P L0: 63.3% 3.6% 16.9% 9.2% 7.0%
[libx264 @ 0x7f9b9b846800] ref B L0: 98.5% 1.5% 0.0%
[libx264 @ 0x7f9b9b846800] ref B L1: 99.6% 0.4%
[libx264 @ 0x7f9b9b846800] kb/s:6084.43

 

 

Useful script for multi-rate HLS output from ffmpeg

#!/bin/bash
VIDSOURCE=”$1RESOLUTION=”854x480”
BITRATE1=”800000BITRATE2=”600000BITRATE3=”400000”
 
AUDIO_OPTS=”-c:a libfaac -b:a 160000 -ac 2VIDEO_OPTS1=”-s $RESOLUTION -c:v libx264 -b:v $BITRATE1 -vprofilebaseline -preset medium -x264opts level=41VIDEO_OPTS2=”-s $RESOLUTION -c:v libx264 -b:v $BITRATE2 -vprofile
baseline -preset medium -x264opts level=41VIDEO_OPTS3=”-s $RESOLUTION -c:v libx264 -b:v $BITRATE3 -vprofile
baseline -preset medium -x264opts level=41OUTPUT_HLS=”-hls_time 3 -hls_list_size 10 -hls_wrap 30 -start_number 1ffmpeg -i$VIDSOURCE-y -threads 4
              $AUDIO_OPTS $VIDEO_OPTS1 $OUTPUT_HLS stream_hi.m3u8
              $AUDIO_OPTS $VIDEO_OPTS2 $OUTPUT_HLS stream_med.m3u8
              $AUDIO_OPTS $VIDEO_OPTS3 $OUTPUT_HLS stream_low.m3u8

Credit to: jeisom@gmail.com