2022-12-10 14:01:39 +01:00
import argparse
2022-12-29 02:05:01 +01:00
import os
import pathlib
2023-05-22 21:40:39 +02:00
import shlex
2022-12-10 14:01:39 +01:00
import subprocess
import yaml
# Hannes' AV1 wrapper script
2023-05-22 21:40:39 +02:00
# Version 2.0.1
2022-12-10 14:01:39 +01:00
2022-12-29 02:05:01 +01:00
# This is a good guide for optimizing AV1 parameters with the AOM reference encoder:
# https://www.reddit.com/r/AV1/comments/t59j32/encoder_tuning_part_4_a_2nd_generation_guide_to/
2022-12-10 14:01:39 +01:00
parser = argparse . ArgumentParser ( prog = " hav1w " )
parser . add_argument ( " input " )
parser . add_argument ( " output " )
parser . add_argument ( " preset " )
parser . add_argument ( " --crf " )
parser . add_argument ( " --ba " )
parser . add_argument ( " --alayout " )
parser . add_argument ( " --cpu " )
parser . add_argument ( " --mf " )
2022-12-29 02:05:01 +01:00
parser . add_argument ( " --dnl " )
parser . add_argument ( " --arnr-strength " )
2022-12-21 19:28:46 +01:00
parser . add_argument ( " --crop " )
2022-12-29 02:05:01 +01:00
parser . add_argument ( " --no10bit " , action = " store_true " )
2022-12-21 19:20:19 +01:00
parser . add_argument ( " --dry " , action = " store_true " )
2022-12-10 14:01:39 +01:00
args = parser . parse_args ( )
2022-12-29 02:05:01 +01:00
preset_path = os . path . join ( pathlib . Path ( __file__ ) . parent . resolve ( ) , " av1_presets.yml " )
with open ( preset_path , " r " ) as stream :
2022-12-10 14:01:39 +01:00
presets = yaml . safe_load ( stream )
preset = presets [ args . preset ]
if args . crf is not None :
preset [ " crf " ] = args . crf
if args . ba is not None :
preset [ " ba " ] = args . ba
if args . alayout is not None :
preset [ " alayout " ] = args . alayout
if args . cpu is not None :
preset [ " cpu " ] = args . cpu
if args . mf is not None :
preset [ " mf " ] = args . mf
2022-12-29 02:05:01 +01:00
if args . dnl is not None :
preset [ " dnl " ] = args . dnl
if args . arnr_strength is not None :
preset [ " arnr_strength " ] = args . arnr_strength
cmd = [ " ffmpeg " , " -i " , args . input , " -c:v " , " libaom-av1 " , " -c:a " , " libopus " , " -c:s " , " copy " , " -map " , " 0 " ]
# AV1 Settings
# Constant rate factor, determines target quality
cmd + = [ " -crf " , preset [ " crf " ] , " -b:v " , " 0 " ]
# Set minimum keyframe interval to 12 to prevent placing too many keyframes... just in case
cmd + = [ " -keyint_min " , " 12 " ]
# Set lookahead to 48 frames
# Reddit post says it's worth it...
cmd + = [ " -lag-in-frames " , " 48 " ]
# Temporal filtering parameters
# Set arnr_strength to 0 for animation and low variance stuff, 1 for content with more variance and fast motion scenes
cmd + = [ " -arnr-strength " , preset [ " arnr_strength " ] , " -arnr-max-frames " , " 3 " ]
# Disable deltaq-mode for higher fidelity
aom_params = [ " deltaq-mode=0 " ]
# Enable quantization matrices
# The Reddit post told me to do so, no matter what :P
aom_params + = [ " enable-qm=1 " ]
# Grain synthesis
# Set to -1 for disabling grain synthesis: makes sense for animation content (animes, ...) as
# a) this is not a strength of the aom encoder
# b) animation content usually does not have a lot of noise
#
# If you need to do grain synthesis on animation content, maybe SVT-AV1 is a better choice?
# And read this: https://www.reddit.com/r/AV1/comments/n4si96/encoder_tuning_part_3_av1_grain_synthesis_how_it/
2023-01-01 17:02:34 +01:00
# TEMPORARILY DISABLED DUE TO https://bugs.chromium.org/p/aomedia/issues/detail?id=2768
preset [ " dnl " ] = " -1 "
2022-12-29 02:05:01 +01:00
if preset [ " dnl " ] != " -1 " :
aom_params + = [ " enable-dnl-denoising=0 " , " denoise-noise-level= " + preset [ " dnl " ] ]
cmd + = [ " -aom-params " , " : " . join ( aom_params ) ]
if not args . no10bit : # Encode to 10 bit per default
cmd + = [ " -pix_fmt " , " yuv420p10le " ]
2022-12-10 14:01:39 +01:00
2022-12-21 19:28:46 +01:00
if args . crop is not None :
cmd + = [ " -vf " , " crop= " + args . crop ]
2022-12-29 02:05:01 +01:00
# Encoding efficiency settings (tiles also affect decoding performance)
cmd + = [ " -cpu-used " , preset [ " cpu " ] , " -row-mt " , " 1 " , " -tiles " , " 2x2 " ]
# Audio/Opus settings
cmd + = [ " -b:a " , preset [ " ba " ] , " -af " , f " aformat=channel_layouts= { preset [ ' alayout ' ] } " , " -mapping_family " , preset [ " mf " ] ]
cmd + = [ args . output ]
2022-12-10 14:01:39 +01:00
2022-12-21 19:20:19 +01:00
if args . dry :
2023-05-22 21:40:39 +02:00
print ( " " . join ( list ( map ( lambda x : shlex . quote ( x ) , cmd ) ) ) )
2022-12-21 19:20:19 +01:00
exit ( 0 )
2022-12-10 14:01:39 +01:00
p = subprocess . run ( cmd )
exit ( p . returncode )