diff --git a/README.md b/README.md index b2c559d..d28fb37 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -A collection of scripts for building ffmpeg and encoding content with the built ffmpeg +# ffmpeg-builder +A collection of scripts for building `ffmpeg` and encoding content with the built `ffmpeg`. Tested on: - linux x86_64/aarch64 on: @@ -9,39 +10,140 @@ Tested on: - archlinux - darwin aarch64 -```bash -~~~ Usable Commands ~~~ +With these scripts you can: +1. install required dependencies using `./scripts/install_deps.sh` +2. build ffmpeg with the desired configuration using `./scripts/build.sh` +3. encode a file using libsvtav1_psy and libopus using `./scripts/encode.sh` +4. estimate the film grain of a given file using `./scripts/efg.sh` -print_cmds: - print usable commands -do_build: - build a specific project -build: - build ffmpeg with desired configuration -docker_build_image: - build docker image with required dependencies pre-installed -docker_save_image: - save docker image into tar.zst -docker_load_image: - load docker image from tar.zst -docker_run_image: - run docker image with given flags -build_with_docker: - run docker image with given flags -docker_build_multiarch_image: - build multiarch docker image -efg: - estimate the film grain of a given file -encode: - encode a file using libsvtav1_psy and libopus -print_pkg_mgr: - print out evaluated package manager commands and required packages -install_deps: - install required dependencies -package: - package ffmpeg build -gen_readme: - generate project README.md +# Building +This project supports multiple ways to build. + +## Configuration +Configuration is done through environment variables. +By default, this project will build a static `ffmpeg` binary in `./gitignore/sysroot/bin/ffmpeg`. +The default enabled libraries included in the `ffmpeg` build are: +- libsvtav1_psy +- libopus +- libdav1d +- libaom +- librav1e +- libvmaf +- libx264 +- libx265 +- libwebp +- libmp3lame + +The user-overridable compile options are: +- `CLEAN`: clean build directories before building +- `LTO`: enable link time optimization +- `OPT`: optimization level (0-3) +- `STATIC`: static or shared build +- `ARCH`: architecture type (x86-64-v{1,2,3,4}, armv8-a, etc) +- `PREFIX`: prefix to install to, default is local install in ./gitignore/sysroot +- `ENABLE`: configure what ffmpeg enables + +Examples: +- only build libsvtav1_psy and libopus: `ENABLE='libsvtav1_psy libopus' ./scripts/build.sh` +- build shared libraries in custom path: `PREFIX=/usr/local STATIC=OFF ./scripts/build.sh` +- build without LTO at O1: `LTO=OFF OPT=1 ./scripts/build.sh` + +### Native build +Make sure to install required dependencies using `./scripts/install_deps.sh`. Then build ffmpeg with the desired configuration using `./scripts/build.sh`. + +### Docker build +1. choose a given distro from: ubuntu fedora debian archlinux. +2. build a docker image with the required dependencies pre-installed using `./scripts/docker_build_image.sh` ``. +3. run a docker image with the given arguments using `./scripts/docker_run_image.sh` `` `./scripts/build.sh` + +Docker builds support the same configuration options as native builds. For example: +`ENABLE='libdav1d' ./scripts/docker_run_image.sh ubuntu ./scripts/build.sh` to build `ffmpeg` on ubuntu with only `libdav1d` enabled. + +# Encoding scripts +The encoding scripts are designed to be installed to system paths for re-use via symbolic links back to this repo. + +## Encoding with svtav1-psy and opus +```bash +encode -i input [options] output + [-P NUM] set preset (default: 3) + [-C NUM] set CRF (default: 25) + [-g NUM] set film grain for encode + [-p] print the command instead of executing it (default: false) + [-c] use cropdetect (default: false) + [-d] enable dolby vision (default: false) + [-v] print relevant version info + [-s] use same container as input, default is convert to mkv + + [output] if unset, defaults to ${HOME}/av1-input-file-name.mkv + + [-u] update script (git pull ffmpeg-builder) + [-I] system install at /usr/local/bin/encode + [-U] uninstall from /usr/local/bin/encode +``` +- Uses svtav1-psy for the video encoder +- Uses libopus for the audio encoder +- Skips re-encoding av1/opus content +- Only maps audio streams that match the video stream language if the video stream has a defined language. +- Only maps english subtitle streams. +- Adds track statistics to the output mkv file and embeds the encoder versions to the output metadata. For example: +``` +ENCODE : aa4d7e6 +FFMPEG : 8.0 +LIBOPUS : 1.5.2 +LIBSVTAV1_PSY : 3.0.2-B +SVTAV1_PARAMS : film-grain=8:film-grain-denoise=1:tune=0:... +VIDEO_PARAMS : -crf 25 -preset 3 -g 240 ``` +Example usage: +- standard usage: `encode -i input.mkv output.mkv` +- print out what will be executed without starting encoding: `encode -i input.mkv -p` +- encode with film-grain synthesis value of 20 with denoising: `encode -g 20 -i input.mkv` + +## Estimate film-grain +```bash +efg -i input [options] + [-l NUM] low value (default: 0) + [-s NUM] step value (default: 1) + [-h NUM] high value (default: 30) + [-p] plot bitrates using gnuplot + + [-I] system install at /usr/local/bin/efg + [-U] uninstall from /usr/local/bin/efg +``` +- Provides a way to estimate the ideal film-grain amount by encoding from low-high given values and the step amount. +- Observing the point of diminishing returns, one can make an informed decision for a given film-grain value to choose. + +Example usage: +- `efg -i input.mkv -p` +``` + 1 +------------------------------------------------------------------------------------------------------+ + | *****G***** + + + | + | *****G** '/tmp/plot.dat' ***G*** | + 0.95 |-+ ***** +-| + | **G* | + | *** | + | **** | + 0.9 |-+ *G* +-| + | **** | + | **** | + 0.85 |-+ *G* +-| + | *** | + | **** | + 0.8 |-+ *G* +-| + | *** | + | **** | + | *G* | + 0.75 |-+ *** +-| + | **** | + | *G* | + 0.7 |-+ *** +-| + | ** | + | *** | + | *G* | + 0.65 |-+ *** +-| + | **** | + | + + + *| + 0.6 +------------------------------------------------------------------------------------------------------+ +``` diff --git a/lib/build.sh b/lib/build.sh index 092aee6..f952ac9 100644 --- a/lib/build.sh +++ b/lib/build.sh @@ -454,7 +454,7 @@ do_build() { FB_FUNC_NAMES+=('build') # shellcheck disable=SC2034 -FB_FUNC_DESCS['build']='build ffmpeg with desired configuration' +FB_FUNC_DESCS['build']='build ffmpeg with the desired configuration' build() { set_compile_opts || return 1 diff --git a/lib/compile_opts.sh b/lib/compile_opts.sh index 4e270b0..3eb0580 100644 --- a/lib/compile_opts.sh +++ b/lib/compile_opts.sh @@ -3,21 +3,30 @@ # variables used externally # shellcheck disable=SC2034 -# default compile options +# compile option descriptions +unset FB_COMP_OPTS_DESC +declare -Ag FB_COMP_OPTS_DESC -# clean build directories before building +# default compile options +FB_COMP_OPTS_DESC['CLEAN']='clean build directories before building' DEFAULT_CLEAN=true -# enable link time optimization + +FB_COMP_OPTS_DESC['LTO']='enable link time optimization' DEFAULT_LTO=ON -# optimization level (0-3) + +FB_COMP_OPTS_DESC['OPT']='optimization level (0-3)' DEFAULT_OPT=3 -# static or shared build + +FB_COMP_OPTS_DESC['STATIC']='static or shared build' DEFAULT_STATIC=ON -# architecture type (x86-64-v{1,2,3,4}, armv8-a, etc) + +FB_COMP_OPTS_DESC['ARCH']='architecture type (x86-64-v{1,2,3,4}, armv8-a, etc)' DEFAULT_ARCH=native -# prefix to install to, default is local install + +FB_COMP_OPTS_DESC['PREFIX']='prefix to install to, default is local install in ./gitignore/sysroot' DEFAULT_PREFIX='local' -# configure what ffmpeg enables + +FB_COMP_OPTS_DESC['ENABLE']='configure what ffmpeg enables' DEFAULT_ENABLE="\ libsvtav1_psy \ libopus \ diff --git a/lib/docker.sh b/lib/docker.sh index ac87bbf..25faef1 100644 --- a/lib/docker.sh +++ b/lib/docker.sh @@ -100,7 +100,7 @@ docker_login() { } FB_FUNC_NAMES+=('docker_build_image') -FB_FUNC_DESCS['docker_build_image']='build docker image with required dependencies pre-installed' +FB_FUNC_DESCS['docker_build_image']='build a docker image with the required dependencies pre-installed' FB_FUNC_COMPLETION['docker_build_image']="${VALID_DOCKER_IMAGES[*]}" docker_build_image() { local image="$1" @@ -243,7 +243,7 @@ docker_load_image() { } FB_FUNC_NAMES+=('docker_run_image') -FB_FUNC_DESCS['docker_run_image']='run docker image with given flags' +FB_FUNC_DESCS['docker_run_image']='run a docker image with the given arguments' FB_FUNC_COMPLETION['docker_run_image']="${VALID_DOCKER_IMAGES[*]}" docker_run_image() { local image="$1" diff --git a/lib/efg.sh b/lib/efg.sh index 6ce5a56..6a0c3c2 100644 --- a/lib/efg.sh +++ b/lib/efg.sh @@ -25,8 +25,8 @@ set_efg_opts() { local minOpt=1 # using all local maxOpt=${numOpts} - test $# -lt ${minOpt} && echo_fail "not enough arguments" && efg_usage && return 1 - test $# -gt ${maxOpt} && echo_fail "too many arguments" && efg_usage && return 1 + test $# -lt ${minOpt} && efg_usage && return 1 + test $# -gt ${maxOpt} && efg_usage && return 1 local OPTARG OPTIND while getopts "${opts}" flag; do case "${flag}" in diff --git a/lib/encode.sh b/lib/encode.sh index 8ac9f22..dd0d724 100644 --- a/lib/encode.sh +++ b/lib/encode.sh @@ -152,10 +152,10 @@ encode_usage() { echo -e "\t[-p] print the command instead of executing it (default: ${PRINT_OUT})" echo -e "\t[-c] use cropdetect (default: ${CROP})" echo -e "\t[-d] enable dolby vision (default: ${DV_TOGGLE})" - echo -e "\t[-v] Print relevant version info" + echo -e "\t[-v] print relevant version info" echo -e "\t[-s] use same container as input, default is convert to mkv" - echo -e "\n\t[output] if unset, defaults to ${HOME}/" - echo -e "\n\t[-u] update script (git pull at ${REPO_DIR})" + echo -e "\n\t[output] if unset, defaults to \${HOME}/av1-input-file-name.mkv" + echo -e "\n\t[-u] update script (git pull ffmpeg-builder)" echo -e "\t[-I] system install at ${ENCODE_INSTALL_PATH}" echo -e "\t[-U] uninstall from ${ENCODE_INSTALL_PATH}" return 0 @@ -181,8 +181,8 @@ set_encode_opts() { local minOpt=1 # using all + output name local maxOpt=$((numOpts + 1)) - test $# -lt ${minOpt} && echo_fail "not enough arguments" && encode_usage && return 1 - test $# -gt ${maxOpt} && echo_fail "too many arguments" && encode_usage && return 1 + test $# -lt ${minOpt} && encode_usage && return 1 + test $# -gt ${maxOpt} && encode_usage && return 1 local optsUsed=0 local OPTARG OPTIND while getopts "${opts}" flag; do diff --git a/lib/readme.sh b/lib/readme.sh index 865a047..28fffff 100644 --- a/lib/readme.sh +++ b/lib/readme.sh @@ -1,22 +1,136 @@ #!/usr/bin/env bash +make_bullet_points() { + for arg in "$@"; do + echo "- ${arg}" + done +} + +gen_function_info() { + local funcName="$1" + echo "${FB_FUNC_DESCS[${funcName}]} using \`./scripts/${funcName}.sh\`" +} + +gen_compile_opts_info() { + for opt in "${FB_COMP_OPTS[@]}"; do + echo "- \`${opt}\`: ${FB_COMP_OPTS_DESC[${opt}]}" + done +} + FB_FUNC_NAMES+=('gen_readme') FB_FUNC_DESCS['gen_readme']='generate project README.md' gen_readme() { local readme="${REPO_DIR}/README.md" echo " -A collection of scripts for building ffmpeg and encoding content with the built ffmpeg +# ffmpeg-builder +A collection of scripts for building \`ffmpeg\` and encoding content with the built \`ffmpeg\`. Tested on: - linux x86_64/aarch64 on: $(printf ' - %s\n' "${VALID_DOCKER_IMAGES[@]}") - darwin aarch64 +With these scripts you can: +1. $(gen_function_info install_deps) +2. $(gen_function_info build) +3. $(gen_function_info encode) +4. $(gen_function_info efg) + +# Building +This project supports multiple ways to build. + +## Configuration +Configuration is done through environment variables. +By default, this project will build a static \`ffmpeg\` binary in \`./gitignore/sysroot/bin/ffmpeg\`. +The default enabled libraries included in the \`ffmpeg\` build are: +$(make_bullet_points ${DEFAULT_ENABLE}) + +The user-overridable compile options are: +$(gen_compile_opts_info) + +Examples: +- only build libsvtav1_psy and libopus: \`ENABLE='libsvtav1_psy libopus' ./scripts/build.sh\` +- build shared libraries in custom path: \`PREFIX=/usr/local STATIC=OFF ./scripts/build.sh\` +- build without LTO at O1: \`LTO=OFF OPT=1 ./scripts/build.sh\` + +### Native build +Make sure to $(gen_function_info install_deps). Then $(gen_function_info build). + +### Docker build +1. choose a given distro from: ${VALID_DOCKER_IMAGES[*]}. +2. $(gen_function_info docker_build_image) \`\`. +3. $(gen_function_info docker_run_image) \`\` \`./scripts/build.sh\` + +Docker builds support the same configuration options as native builds. For example: +\`ENABLE='libdav1d' ./scripts/docker_run_image.sh ubuntu ./scripts/build.sh\` to build \`ffmpeg\` on ubuntu with only \`libdav1d\` enabled. + +# Encoding scripts +The encoding scripts are designed to be installed to system paths for re-use via symbolic links back to this repo. + +## Encoding with svtav1-psy and opus \`\`\`bash -$(COLOR=OFF print_cmds) +$(encode) +\`\`\` +- Uses svtav1-psy for the video encoder +- Uses libopus for the audio encoder +- Skips re-encoding av1/opus content +- Only maps audio streams that match the video stream language if the video stream has a defined language. +- Only maps english subtitle streams. +- Adds track statistics to the output mkv file and embeds the encoder versions to the output metadata. For example: +\`\`\` +ENCODE : aa4d7e6 +FFMPEG : 8.0 +LIBOPUS : 1.5.2 +LIBSVTAV1_PSY : 3.0.2-B +SVTAV1_PARAMS : film-grain=8:film-grain-denoise=1:tune=0:... +VIDEO_PARAMS : -crf 25 -preset 3 -g 240 \`\`\` +Example usage: +- standard usage: \`encode -i input.mkv output.mkv\` +- print out what will be executed without starting encoding: \`encode -i input.mkv -p\` +- encode with film-grain synthesis value of 20 with denoising: \`encode -g 20 -i input.mkv\` + +## Estimate film-grain +\`\`\`bash +$(efg) +\`\`\` +- Provides a way to estimate the ideal film-grain amount by encoding from low-high given values and the step amount. +- Observing the point of diminishing returns, one can make an informed decision for a given film-grain value to choose. + +Example usage: +- \`efg -i input.mkv -p\` +\`\`\` + 1 +------------------------------------------------------------------------------------------------------+ + | *****G***** + + + | + | *****G** '/tmp/plot.dat' ***G*** | + 0.95 |-+ ***** +-| + | **G* | + | *** | + | **** | + 0.9 |-+ *G* +-| + | **** | + | **** | + 0.85 |-+ *G* +-| + | *** | + | **** | + 0.8 |-+ *G* +-| + | *** | + | **** | + | *G* | + 0.75 |-+ *** +-| + | **** | + | *G* | + 0.7 |-+ *** +-| + | ** | + | *** | + | *G* | + 0.65 |-+ *** +-| + | **** | + | + + + *| + 0.6 +------------------------------------------------------------------------------------------------------+ +\`\`\` " >"${readme}" } diff --git a/main.sh b/main.sh index d1ac92a..d02fdd1 100755 --- a/main.sh +++ b/main.sh @@ -69,18 +69,18 @@ FB_FUNC_NAMES+=('print_cmds') FB_FUNC_DESCS['print_cmds']='print usable commands' print_cmds() { echo -e "~~~ Usable Commands ~~~\n" - for funcname in "${FB_FUNC_NAMES[@]}"; do - color="${CYAN}" word="${funcname}:" echo_wrapper "\n\t${FB_FUNC_DESCS[${funcname}]}" + for funcName in "${FB_FUNC_NAMES[@]}"; do + color="${CYAN}" word="${funcName}:" echo_wrapper "\n\t${FB_FUNC_DESCS[${funcName}]}" if [[ $FB_RUNNING_AS_SCRIPT -eq 0 ]]; then - (cd "$SCRIPT_DIR" && ln -sf entry.sh "${funcname}.sh") + (cd "$SCRIPT_DIR" && ln -sf entry.sh "${funcName}.sh") fi done echo -e "\n" } set_completions() { - for funcname in "${FB_FUNC_NAMES[@]}"; do - complete -W "${FB_FUNC_COMPLETION[${funcname}]}" "${funcname}" + for funcName in "${FB_FUNC_NAMES[@]}"; do + complete -W "${FB_FUNC_COMPLETION[${funcName}]}" "${funcName}" done } @@ -88,7 +88,7 @@ src_scripts || return 1 determine_pkg_mgr || return 1 check_compile_opts_override || return 1 -if [[ $FB_RUNNING_AS_SCRIPT -eq 0 ]]; then +if [[ ${FB_RUNNING_AS_SCRIPT} -eq 0 ]]; then print_cmds || return 1 fi set_completions || return 1