This is meant as a personal note to myself as I constantly keep forgetting about encoding parameters. Use with caution, your mileage may vary.

Note: All recommendations below target 1080p content as I don't own any UHD BluRays.

H.265

CPU based (x265)

$ ffmpeg -i input.mkv \
        -c:a copy \
        -c:s copy \
        -c:v libx265 \
        -preset slow \
        -crf 20 \
        -x265-params log-level=error \
        output.mkv

As with other encoders -preset performs codec feature selection (i.e. trade-off between file size and en-/decoding speed) and -crf sets the visual quality target (again in constrast to file size).

I like to keep slow for the former and pick a value between 18 and 25 for the latter.

GPU based (VA-API)

The following works fine on my RX 6800XT. I'm not sure about differing options for other cards.

$ ffmpeg -vaapi_device /dev/dri/renderD128 \
        -i input.mkv \
        -vf 'format=nv12,hwupload' \
        -c:a copy \
        -c:s copy \
        -c:v hevc_vaapi \
        -profile:v main \
        -rc_mode CQP \
        -qp 23 \
        output.mkv

In CQP (Constant Quantization Parameter) mode -qp is the equivalent of -crf (with different scale and meaning). A good range should be 21-25.

AV1

$ ffmpeg -i input.mkv \
    -c:a copy \
    -c:s copy \
    -c:v libsvtav1 \
    -crf 18 \
    -preset 6 \
    -g 240 \
    -pix_fmt yuv420p10le \
    -svtav1-params tune=0:film-grain=8 \
    output.mkv

Tuning knobs

option range trade-off recommendation
-crf 1-63 file size vs. visual quality good range should be 10-23
-preset 0-13 file size vs. en-/decoding speed 4-6 for 'home enthusiasts'
-g file size vs. skippability and error resilience 240 for 24 Hz input
-pix_fmt yuv420p or yuv420p10le 8-bit vs. 10-bit encoding go with 10-bit
tune 0-1 psycho-visual quality (0) vs. PSNR (1) go with 0
film-grain 0-50 file size vs. sharpness and encoding speed use 8 for grainy material

General recommendations

  • Regarding CRF one can go as low as output file size allows.
  • Movies with dark scenes benefit greatly from 10-bit encoding (reduced colour artifacts).
  • AV1 is very sensitive to film grain, resulting in larger output files. For movies shot with analogue grain, using film-grain=8 (or higher) can compensate for that, potentially allowing a higher CRF.

VMAF Rating

In addition to visual inspection there is an objective quality metric developed by Netflix: VMAF.

$ ffmpeg -i encoded.mp4 -i original.mp4 \
    -filter_complex libvmaf="n_threads=$(nproc)" -f null -

Notes

  • VMAF rating requires frame accuracy between both files which may not be fulfilled when using ffmpegs -ss option for taking samples.

Automatic CRF Determination for a Given VMAF Score

Using the incredibly handy tool ab-av1 one can easily find the minimal CRF value that reaches a certain VMAF score.

I like to use the following for AV1 encoding:

$ ab-av1 crf-search \
    --min-vmaf 96 \
    --preset 6 \
    --svt tune=0 \
    --keyint 240 \
    --input input.mkv

There are plenty other options for other purposes and it can even be used as a nice ffmpeg wrapper for encoding (providing a progress bar and ETA).