Skip to content

Instantly share code, notes, and snippets.

@mikoim
Last active September 24, 2024 12:41
Show Gist options
  • Save mikoim/27e4e0dc64e384adbcb91ff10a2d3678 to your computer and use it in GitHub Desktop.
Save mikoim/27e4e0dc64e384adbcb91ff10a2d3678 to your computer and use it in GitHub Desktop.
[Updated! Aug 14 2020] YouTube recommended encoding settings on ffmpeg (+ libx264)

Parameters

Container: MP4

Parameter YouTube recommends setting
-movflags faststart moov atom at the front of the file (Fast Start)

Video codec: H.264

Parameter YouTube recommends setting Note
-c:v libx264 H.264
-profile:v high High Profile
-bf 2 2 consecutive B frames
Closed GOP x264 produces closed gop as default
-g 30 GOP of half the frame rate.
CABAC Normally, x264 uses CABAC as an entropy encoder.
-crf 18 Variable bitrate. Currently, YouTube does not have a bitrate limit. Refer official document: https://support.google.com/youtube/answer/1722171?hl=en
-pix_fmt yuv420p Chroma subsampling: 4:2:0 If you like to encode HDR video, this comment might be helpful. https://gist.github.com/mikoim/27e4e0dc64e384adbcb91ff10a2d3678#gistcomment-2859601

Audio codec: AAC-LC

Parameter YouTube recommends setting Note
-c:a libfdk_aac -profile:a aac_low AAC-LC Fraunhofer FDK AAC is a high-quality codec library, but it does not compatible GPL. So your FFmpeg most likely does not contain it. Build FFmpeg manually or use a built-in AAC encoder instead of libfdk_aac.
-b:a 384k Mono 128 kbps, Stereo 384 kbps, 5.1 512 kbps

Sample video

References

ffmpeg version N-98725-gcfc6552032 Copyright (c) 2000-2020 the FFmpeg developers
built with gcc 10 (GCC)
configuration: --prefix=xxx --enable-nonfree --enable-gpl --enable-libfdk-aac --enable-libx264 --enable-libx265
libavutil 56. 58.100 / 56. 58.100
libavcodec 58.100.100 / 58.100.100
libavformat 58. 50.100 / 58. 50.100
libavdevice 58. 11.101 / 58. 11.101
libavfilter 7. 87.100 / 7. 87.100
libswscale 5. 8.100 / 5. 8.100
libswresample 3. 8.100 / 3. 8.100
libpostproc 55. 8.100 / 55. 8.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'source.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf58.20.100
Duration: 00:00:23.60, start: 0.000000, bitrate: 13088 kb/s
Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 3840x2160, 12970 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)
Metadata:
handler_name : ?Mainconcept Video Media Handler
Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 129 kb/s (default)
Metadata:
handler_name : #Mainconcept MP4 Sound Media Handler
Stream mapping:
Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
Stream #0:1 -> #0:1 (aac (native) -> aac (libfdk_aac))
Press [q] to stop, [?] for help
[libx264 @ 0x2337f40] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2 AVX512
[libx264 @ 0x2337f40] profile High, level 5.1, 4:2:0, 8-bit
[libx264 @ 0x2337f40] 264 - core 159 r2999 296494a - H.264/MPEG-4 AVC codec - Copyleft 2003-2020 - http://www.videolan.org/x264.html - options: cabac=1 ref=5 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=8 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=2 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=27 lookahead_threads=4 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=2 b_pyramid=2 b_adapt=1 b_bias=0 direct=3 weightb=1 open_gop=0 weightp=2 keyint=12 keyint_min=1 scenecut=40 intra_refresh=0 rc_lookahead=12 rc=crf mbtree=1 crf=18.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to 'output.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf58.50.100
Stream #0:0(eng): Video: h264 (libx264) (avc1 / 0x31637661), yuv420p, 3840x2160, q=-1--1, 23.98 fps, 24k tbn, 23.98 tbc (default)
Metadata:
handler_name : ?Mainconcept Video Media Handler
encoder : Lavc58.100.100 libx264
Side data:
cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
Stream #0:1(eng): Audio: aac (libfdk_aac) (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, s16, 384 kb/s (default)
Metadata:
handler_name : #Mainconcept MP4 Sound Media Handler
encoder : Lavc58.100.100 libfdk_aac
[mp4 @ 0x2346280] Starting second pass: moving the moov atom to the beginning of the file0.881x
frame= 565 fps= 20 q=-1.0 Lsize= 50496kB time=00:00:23.57 bitrate=17548.0kbits/s speed=0.828x
video:49380kB audio:1107kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.017619%
[libx264 @ 0x2337f40] frame I:54 Avg QP:12.52 size:145222
[libx264 @ 0x2337f40] frame P:508 Avg QP:15.73 size: 83671
[libx264 @ 0x2337f40] frame B:3 Avg QP:15.72 size: 72715
[libx264 @ 0x2337f40] consecutive B-frames: 98.9% 1.1% 0.0%
[libx264 @ 0x2337f40] mb I I16..4: 32.3% 61.1% 6.6%
[libx264 @ 0x2337f40] mb P I16..4: 18.7% 32.0% 0.7% P16..4: 21.4% 3.8% 1.2% 0.0% 0.0% skip:22.1%
[libx264 @ 0x2337f40] mb B I16..4: 4.4% 10.4% 0.4% B16..8: 48.1% 7.4% 0.4% direct: 3.5% skip:25.4% L0:65.6% L1:33.4% BI: 1.0%
[libx264 @ 0x2337f40] 8x8 transform intra:62.0% inter:85.3%
[libx264 @ 0x2337f40] direct mvs spatial:33.3% temporal:66.7%
[libx264 @ 0x2337f40] coded y,uvDC,uvAC intra: 25.4% 34.5% 3.6% inter: 5.2% 15.3% 0.0%
[libx264 @ 0x2337f40] i16 v,h,dc,p: 11% 21% 8% 60%
[libx264 @ 0x2337f40] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 30% 18% 23% 4% 5% 5% 6% 5% 5%
[libx264 @ 0x2337f40] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 32% 16% 18% 5% 8% 7% 6% 4% 4%
[libx264 @ 0x2337f40] i8c dc,h,v,p: 56% 22% 17% 5%
[libx264 @ 0x2337f40] Weighted P-Frames: Y:0.0% UV:0.0%
[libx264 @ 0x2337f40] ref P L0: 72.7% 1.1% 15.9% 6.0% 2.6% 1.7%
[libx264 @ 0x2337f40] ref B L0: 89.1% 7.4% 3.5%
[libx264 @ 0x2337f40] kb/s:17165.92
#!/bin/bash
AAC_ENCODER=libfdk_aac
# AAC_ENCODER=aac
AUDIO_PARAMS="-c:a $AAC_ENCODER -profile:a aac_low -b:a 384k"
VIDEO_PARAMS="-pix_fmt yuv420p -c:v libx264 -profile:v high -preset slow -crf 18 -g 30 -bf 2"
CONTAINER_PARAMS="-movflags faststart"
# You need to adjust the GOP length to fit your source video.
# 60 fps -> -g 30
# 23.976 (24000/1001) -> -g 24000/1001/2 (???) <- plz comment
ffmpeg -i "$1" $AUDIO_PARAMS $VIDEO_PARAMS $CONTAINER_PARAMS "$2"
General
Complete name : output.mp4
Format : MPEG-4
Format profile : Base Media
Codec ID : isom (isom/iso2/avc1/mp41)
File size : 49.3 MiB
Duration : 23 s 616 ms
Overall bit rate : 17.5 Mb/s
Writing application : Lavf58.50.100
Video
ID : 1
Format : AVC
Format/Info : Advanced Video Codec
Format profile : High@L5.1
Format settings : CABAC / 5 Ref Frames
Format settings, CABAC : Yes
Format settings, Reference frames : 5 frames
Format settings, GOP : M=1, N=11
Codec ID : avc1
Codec ID/Info : Advanced Video Coding
Duration : 23 s 566 ms
Bit rate : 17.2 Mb/s
Width : 3 840 pixels
Height : 2 160 pixels
Display aspect ratio : 16:9
Frame rate mode : Constant
Frame rate : 23.976 (24000/1001) FPS
Color space : YUV
Chroma subsampling : 4:2:0
Bit depth : 8 bits
Scan type : Progressive
Bits/(Pixel*Frame) : 0.086
Stream size : 48.2 MiB (98%)
Writing library : x264 core 159 r2999 296494a
Encoding settings : cabac=1 / ref=5 / deblock=1:0:0 / analyse=0x3:0x113 / me=hex / subme=8 / psy=1 / psy_rd=1.00:0.00 / mixed_ref=1 / me_range=16 / chroma_me=1 / trellis=2 / 8x8dct=1 / cqm=0 / deadzone=21,11 / fast_pskip=1 / chroma_qp_offset=-2 / threads=27 / lookahead_threads=4 / sliced_threads=0 / nr=0 / decimate=1 / interlaced=0 / bluray_compat=0 / constrained_intra=0 / bframes=2 / b_pyramid=2 / b_adapt=1 / b_bias=0 / direct=3 / weightb=1 / open_gop=0 / weightp=2 / keyint=12 / keyint_min=1 / scenecut=40 / intra_refresh=0 / rc_lookahead=12 / rc=crf / mbtree=1 / crf=18.0 / qcomp=0.60 / qpmin=0 / qpmax=69 / qpstep=4 / ip_ratio=1.40 / aq=1:1.00
Language : English
Codec configuration box : avcC
Audio
ID : 2
Format : AAC LC
Format/Info : Advanced Audio Codec Low Complexity
Codec ID : mp4a-40-2
Duration : 23 s 616 ms
Bit rate mode : Constant
Bit rate : 384 kb/s
Channel(s) : 2 channels
Channel layout : L R
Sampling rate : 48.0 kHz
Frame rate : 46.875 FPS (1024 SPF)
Compression mode : Lossy
Stream size : 1.08 MiB (2%)
Language : English
Default : Yes
Alternate group : 1
@genuinefafa
Copy link

you can also use -pix_fmt yuv420p10le for HDR content (or even improve compression quality on h.264)

@ipatch
Copy link

ipatch commented Nov 24, 2019

thank you so much for sharing your encoder settings and taking the time to explain the sub commands and flags.

for the sake of readability, consider breaking your command across multiple lines to prevent side scrolling, ie.

ffmpeg -i input \
-c:v libx264 -preset slow -profile:v high -crf 18 -coder 1 -pix_fmt yuv420p -movflags +faststart -g 30 -bf 2 \
-c:a aac -b:a 384k -profile:a aac_low \ 
output

just a thought.

@mikoim
Copy link
Author

mikoim commented Aug 13, 2020

Thank you for a lot of stars and comments. I've rewrite parameters and descriptions.

@varenc
Copy link

varenc commented Sep 4, 2020

For anyone else on macOS looking to use ffmpeg with the libfdk_aac encoder, check out this 3rd party homebrew formula. Tap it and then brew install/compile ffmpeg with the --with-fdk-aac option.

That said, ffmpeg's own aac encoder has gotten way better than it used to be and works fine for me.

@alou-S
Copy link

alou-S commented Oct 19, 2020

I would highly suggest using -preset veryfast because it both produces a smaller file size and is twice as fast as -preset slow. (Can be even faster when using lesser threads)

If you want file sizes smaller than -preset veryfast you will have use -preset veryslow instead which is painstakingly slow and not worth the time.

I have tested this on my own and it's even shown in detail here.

Note: I think that the lower file size is due to a small loss in video quality but again a rate control is already being used so the loss is minimal.

@bassel999
Copy link

I would highly suggest using -preset veryfast because it both produces a smaller file size and is twice as fast as -preset slow. (Can be even faster when using lesser threads)

If you want file sizes smaller than -preset veryfast you will have use -preset veryslow instead which is painstakingly slow and not worth the time.

I have tested this on my own and it's even shown in detail here.

Note: I think that the lower file size is due to a small loss in video quality but again a rate control is already being used so the loss is minimal.

@alou-S
Man you did great job, I was impreased by your test.

@varenc
Copy link

varenc commented Dec 19, 2020

I would highly suggest using -preset veryfast because it both produces a smaller file size and is twice as fast as -preset slow. (Can be even faster when using lesser threads)

What about the encoded video's quality? A smaller file doesn't mean it's better compressed. For something I only have to upload once, I still say go with the slow preset. Even with the same crf, a slower preset likely produce a higher quality encoding. Though I agree it's weird and confusing how -preset veryfast seems to be the odd one out when compared with the other presets.

I found this great answer on Stack Overflow that does a nice job explaining this paradoxical veryfast preset behavior: https://superuser.com/a/1557090/692789

@alou-S
Copy link

alou-S commented Dec 19, 2020 via email

@rodrigopolo
Copy link

rodrigopolo commented Apr 23, 2021

A simple shell script, call it encode.sh, do the typical chmod +x encode.sh and then execute it with ./encode.sh input.mp4, enjoy.

#!/usr/bin/env bash

PRESET=faster
CRF=21

IFS=$'\t\n'

real_path () {
	TARGET_FILE=$1
	cd `dirname $TARGET_FILE`
	TARGET_FILE=`basename $TARGET_FILE`

	while [ -L "$TARGET_FILE" ]
	do
		TARGET_FILE=`readlink $TARGET_FILE`
		cd `dirname $TARGET_FILE`
		TARGET_FILE=`basename $TARGET_FILE`
	done
	
	PHYS_DIR=`pwd -P`
	RESULT=$PHYS_DIR/$TARGET_FILE
	echo $RESULT
}

rp_fullpath_noext () {
	echo "${1%.*}"
}

SOURCE="`real_path \"$1\"`"
OUTPUT="`rp_fullpath_noext \"$SOURCE\"`"
OUTPUT=$OUTPUT.$CRF.$PRESET.mp4

ffmpeg \
-y \
-hide_banner \
-i "$1" \
-pix_fmt yuv420p \
-c:v libx264 \
-preset $PRESET \
-crf $CRF \
-c:a aac \
-b:a 128k \
-ar 44100 \
-ac 2 \
-movflags +faststart \
$OUTPUT

@varenc
Copy link

varenc commented Dec 11, 2021

@eladkarako

prevent pyramidal B-frames with -bf_ref 0.

Do things like this matter if the intention is to upload these video to YouTube? YouTube will re-encode all videos uploaded to it so if that's the use case I don't think there's much point in tweaking encoding params to improve player compatibility. (I've even used YouTube as a free video transcoding service by uploading a buggy video, then downloading YouTube's fixed version of it). If the intention is to upload to YouTube, and you don't mind uploading a lot, I'd just encoded with -preset superfast -crf 15, or even lossless -crf 0, and trust YouTube will re-encode my video 20 different ways for player compatibility.

That said if I'm serving my encoded .mp4 directly to users, this is valuable info! Had no idea about pyramidal b-frames

@wanghaisheng
Copy link

@varenc for long duration video, when upload it to studio, it got so much time to finish upload,processing and finally copyright check process.
I just want to know

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment