mirror of
https://github.com/levogevo/ffmpeg-builder.git
synced 2026-01-15 19:06:17 +00:00
Compare commits
14 Commits
30ddc39ed5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 38c160e2bd | |||
| 47e287ef7f | |||
| 691d0f857a | |||
| 3c3fc6164c | |||
| 1adcaa15a4 | |||
| 108561a002 | |||
| ff1b0c2304 | |||
| ff656d477f | |||
| 7ce42985e2 | |||
| 8feeccc46a | |||
| 87f65e48f4 | |||
| 200d4c4a6c | |||
| 71c7266661 | |||
| 69ada12884 |
17
Jenkinsfile
vendored
17
Jenkinsfile
vendored
@@ -15,7 +15,6 @@ pipeline {
|
||||
agent none
|
||||
environment {
|
||||
DEBUG = "1"
|
||||
HEADLESS = "1"
|
||||
}
|
||||
options { buildDiscarder logRotator(numToKeepStr: '4') }
|
||||
stages {
|
||||
@@ -39,14 +38,16 @@ pipeline {
|
||||
stage('build ffmpeg on darwin') {
|
||||
matrix {
|
||||
axes {
|
||||
axis { name 'OPT_LTO'; values 'OPT=0 LTO=OFF', 'OPT=3 LTO=ON' }
|
||||
axis { name 'STATIC'; values 'ON', 'OFF' }
|
||||
axis {
|
||||
name 'COMP_OPTS';
|
||||
values 'OPT=0 LTO=OFF STATIC=OFF', 'OPT=2 LTO=OFF STATIC=ON', 'OPT=3 LTO=ON STATIC=ON PGO=ON'
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage('build on darwin ') {
|
||||
agent { label "darwin" }
|
||||
steps {
|
||||
sh "${OPT_LTO} ./scripts/build.sh"
|
||||
sh "${COMP_OPTS} ./scripts/build.sh"
|
||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'gitignore/package/*.tar.xz', defaultExcludes: false
|
||||
}
|
||||
}
|
||||
@@ -58,15 +59,17 @@ pipeline {
|
||||
axes {
|
||||
axis { name 'ARCH'; values 'armv8-a', 'x86-64-v3' }
|
||||
axis { name 'DISTRO'; values 'ubuntu', 'fedora', 'debian', 'archlinux' }
|
||||
axis { name 'OPT_LTO'; values 'OPT=0 LTO=OFF', 'OPT=3 LTO=ON' }
|
||||
axis { name 'STATIC'; values 'ON', 'OFF' }
|
||||
axis {
|
||||
name 'COMP_OPTS';
|
||||
values 'OPT=0 LTO=OFF STATIC=OFF', 'OPT=2 LTO=OFF STATIC=ON', 'OPT=3 LTO=ON STATIC=ON PGO=ON'
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage('build ffmpeg on linux using docker') {
|
||||
agent { label "linux && ${ARCH}" }
|
||||
steps {
|
||||
withDockerCreds {
|
||||
sh "${OPT_LTO} ./scripts/build_with_docker.sh ${DISTRO}"
|
||||
sh "${COMP_OPTS} ./scripts/build_with_docker.sh ${DISTRO}"
|
||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'gitignore/package/*.tar.xz', defaultExcludes: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,13 @@ The default enabled libraries included in the `ffmpeg` build are:
|
||||
- libx264
|
||||
- libx265
|
||||
- libwebp
|
||||
- libvpx
|
||||
- libass
|
||||
- libvorbis
|
||||
- libmp3lame
|
||||
|
||||
The user-overridable compile options are:
|
||||
- `ENABLE`: configure what ffmpeg enables (default: libsvtav1_psy libopus libdav1d libaom librav1e libvmaf libx264 libx265 libwebp libmp3lame)
|
||||
- `ENABLE`: configure what ffmpeg enables (default: libsvtav1_psy libopus libdav1d libaom librav1e libvmaf libx264 libx265 libwebp libvpx libass libvorbis libmp3lame)
|
||||
- `PREFIX`: prefix to install to, default is local install in ./gitignore/sysroot (default: local)
|
||||
- `STATIC`: static or shared build (default: ON)
|
||||
- `LTO`: enable link time optimization (default: ON)
|
||||
@@ -78,7 +81,7 @@ encode -i input [options] output
|
||||
[-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
|
||||
[output] if unset, defaults to ${PWD}/av1-input-file-name.mkv
|
||||
|
||||
[-u] update script (git pull ffmpeg-builder)
|
||||
[-I] system install at /usr/local/bin/encode
|
||||
@@ -107,6 +110,7 @@ Example usage:
|
||||
## Estimate film-grain
|
||||
```bash
|
||||
efg -i input [options]
|
||||
[-P NUM] set preset (default: 10)
|
||||
[-l NUM] low value (default: 0)
|
||||
[-s NUM] step value (default: 1)
|
||||
[-h NUM] high value (default: 30)
|
||||
|
||||
419
lib/0-utils.sh
Normal file
419
lib/0-utils.sh
Normal file
@@ -0,0 +1,419 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
# ANSI colors
|
||||
RED='\e[0;31m'
|
||||
CYAN='\e[0;36m'
|
||||
GREEN='\e[0;32m'
|
||||
YELLOW='\e[0;33m'
|
||||
NC='\e[0m'
|
||||
|
||||
# echo wrappers
|
||||
echo_wrapper() {
|
||||
local args
|
||||
if [[ $1 == '-n' ]]; then
|
||||
args=("$1")
|
||||
shift
|
||||
fi
|
||||
# COLOR is override for using ${color}
|
||||
# shellcheck disable=SC2153
|
||||
if [[ ${COLOR} == 'OFF' ]]; then
|
||||
color=''
|
||||
endColor=''
|
||||
else
|
||||
endColor="${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${args[@]}" "${color}${word:-''}${endColor}" "$@"
|
||||
}
|
||||
echo_fail() { color="${RED}" word="FAIL" echo_wrapper "$@"; }
|
||||
echo_info() { color="${CYAN}" word="INFO" echo_wrapper "$@"; }
|
||||
echo_pass() { color="${GREEN}" word="PASS" echo_wrapper "$@"; }
|
||||
echo_warn() { color="${YELLOW}" word="WARN" echo_wrapper "$@"; }
|
||||
echo_exit() {
|
||||
echo_fail "$@"
|
||||
exit 1
|
||||
}
|
||||
void() { echo "$@" >/dev/null; }
|
||||
|
||||
echo_if_fail() {
|
||||
local cmd=("$@")
|
||||
local logName="${LOGNAME:-${RANDOM}}-"
|
||||
local out="${TMP_DIR}/${logName}stdout"
|
||||
local err="${TMP_DIR}/${logName}stderr"
|
||||
|
||||
# set trace to the cmdEvalTrace and open file descriptor
|
||||
local cmdEvalTrace="${TMP_DIR}/${logName}cmdEvalTrace"
|
||||
exec 5>"${cmdEvalTrace}"
|
||||
export BASH_XTRACEFD=5
|
||||
|
||||
set -x
|
||||
"${cmd[@]}" >"${out}" 2>"${err}"
|
||||
local retval=$?
|
||||
|
||||
# unset and close file descriptor
|
||||
set +x
|
||||
exec 5>&-
|
||||
|
||||
# parse out relevant part of the trace
|
||||
local cmdEvalLines=()
|
||||
while IFS= read -r line; do
|
||||
line="${line/${PS4}/}"
|
||||
test "${line}" == 'set +x' && continue
|
||||
test "${line}" == '' && continue
|
||||
cmdEvalLines+=("${line}")
|
||||
done <"${cmdEvalTrace}"
|
||||
|
||||
if ! test ${retval} -eq 0; then
|
||||
echo
|
||||
echo_fail "command failed with ${retval}:"
|
||||
printf "%s\n" "${cmdEvalLines[@]}"
|
||||
echo_warn "command stdout:"
|
||||
tail -n 32 "${out}"
|
||||
echo_warn "command stderr:"
|
||||
tail -n 32 "${err}"
|
||||
echo
|
||||
fi
|
||||
if [[ -z ${LOGNAME} ]]; then
|
||||
rm "${out}" "${err}" "${cmdEvalTrace}"
|
||||
fi
|
||||
return ${retval}
|
||||
}
|
||||
|
||||
is_root_owned() {
|
||||
local path=$1
|
||||
local uid
|
||||
|
||||
if stat --version >/dev/null 2>&1; then
|
||||
# GNU coreutils (Linux)
|
||||
uid=$(stat -c '%u' "$path")
|
||||
else
|
||||
# BSD/macOS
|
||||
uid=$(stat -f '%u' "$path")
|
||||
fi
|
||||
|
||||
test "$uid" -eq 0
|
||||
}
|
||||
|
||||
dump_arr() {
|
||||
local arrayNames=("$@")
|
||||
for arrayName in "${arrayNames[@]}"; do
|
||||
declare -n array="${arrayName}"
|
||||
arrayExpanded=("${array[@]}")
|
||||
|
||||
# skip showing single element arrays by default
|
||||
if [[ ! ${#arrayExpanded[@]} -gt 1 ]]; then
|
||||
if [[ ${SHOW_SINGLE} == true ]]; then
|
||||
echo_info "${arrayName}='${arrayExpanded[*]}'"
|
||||
else
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
# don't care that the variable has "ARR"
|
||||
echo_info "${arrayName//"_ARR"/}"
|
||||
printf "\t%s\n" "${arrayExpanded[@]}"
|
||||
done
|
||||
}
|
||||
|
||||
has_cmd() {
|
||||
local cmds=("$@")
|
||||
local rv=0
|
||||
for cmd in "${cmds[@]}"; do
|
||||
command -v "${cmd}" >/dev/null 2>&1 || rv=1
|
||||
done
|
||||
|
||||
return ${rv}
|
||||
}
|
||||
|
||||
missing_cmd() {
|
||||
local cmds=("$@")
|
||||
local rv=1
|
||||
for cmd in "${cmds[@]}"; do
|
||||
if ! has_cmd "${cmd}"; then
|
||||
echo_warn "missing ${cmd}"
|
||||
rv=0
|
||||
fi
|
||||
done
|
||||
|
||||
return ${rv}
|
||||
}
|
||||
|
||||
bash_dirname() {
|
||||
local tmp=${1:-.}
|
||||
|
||||
[[ $tmp != *[!/]* ]] && {
|
||||
printf '/\n'
|
||||
return
|
||||
}
|
||||
|
||||
tmp=${tmp%%"${tmp##*[!/]}"}
|
||||
|
||||
[[ $tmp != */* ]] && {
|
||||
printf '.\n'
|
||||
return
|
||||
}
|
||||
|
||||
tmp=${tmp%/*}
|
||||
tmp=${tmp%%"${tmp##*[!/]}"}
|
||||
|
||||
printf '%s\n' "${tmp:-/}"
|
||||
}
|
||||
|
||||
bash_basename() {
|
||||
local tmp
|
||||
path="$1"
|
||||
suffix="${2:-''}"
|
||||
|
||||
tmp=${path%"${path##*[!/]}"}
|
||||
tmp=${tmp##*/}
|
||||
tmp=${tmp%"${suffix/"$tmp"/}"}
|
||||
|
||||
printf '%s\n' "${tmp:-/}"
|
||||
}
|
||||
|
||||
bash_realpath() {
|
||||
local file=$1
|
||||
local dir
|
||||
|
||||
# If the file is already absolute
|
||||
[[ $file == /* ]] && {
|
||||
printf '%s\n' "$file"
|
||||
return
|
||||
}
|
||||
|
||||
# Otherwise: split into directory + basename
|
||||
dir="$(bash_dirname "${file}")"
|
||||
file="$(bash_basename "${file}")"
|
||||
|
||||
# If no directory component, use current directory
|
||||
if [[ $dir == "$file" ]]; then
|
||||
dir="$PWD"
|
||||
else
|
||||
# Save current dir, move into target dir, capture $PWD, then return
|
||||
local oldpwd="$PWD"
|
||||
cd "$dir" || return 1
|
||||
dir="$PWD"
|
||||
cd "$oldpwd" || return 1
|
||||
fi
|
||||
|
||||
printf '%s/%s\n' "$dir" "$file"
|
||||
}
|
||||
|
||||
line_contains() {
|
||||
local line="$1"
|
||||
local substr="$2"
|
||||
if [[ $line == *"${substr}"* ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
line_starts_with() {
|
||||
local line="$1"
|
||||
local substr="$2"
|
||||
if [[ $line == "${substr}"* ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
is_linux() {
|
||||
line_contains "${OSTYPE}" 'linux'
|
||||
}
|
||||
|
||||
is_darwin() {
|
||||
line_contains "$(print_os)" darwin
|
||||
}
|
||||
|
||||
is_windows() {
|
||||
line_contains "$(print_os)" windows
|
||||
}
|
||||
|
||||
is_android() {
|
||||
line_contains "$(print_os)" android
|
||||
}
|
||||
|
||||
print_os() {
|
||||
# cached response
|
||||
if [[ -n ${FB_OS} ]]; then
|
||||
echo "${FB_OS}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
unset FB_OS
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
source /etc/os-release
|
||||
FB_OS="${ID}"
|
||||
if [[ ${VERSION_ID} != '' ]]; then
|
||||
FB_OS+="-${VERSION_ID}"
|
||||
fi
|
||||
if line_starts_with "${FB_OS}" 'arch'; then
|
||||
FB_OS='archlinux'
|
||||
fi
|
||||
else
|
||||
FB_OS="$(uname -o)"
|
||||
fi
|
||||
|
||||
# lowercase
|
||||
FB_OS="${FB_OS,,}"
|
||||
|
||||
# special treatment for windows
|
||||
if line_contains "${FB_OS}" 'windows' || line_contains "${FB_OS}" 'msys'; then
|
||||
FB_OS='windows'
|
||||
fi
|
||||
|
||||
echo "${FB_OS}"
|
||||
}
|
||||
|
||||
is_positive_integer() {
|
||||
local input="$1"
|
||||
if [[ ${input} != ?(-)+([[:digit:]]) || ${input} -lt 0 ]]; then
|
||||
echo_fail "${input} is not a positive integer"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
print_line_indent() {
|
||||
local line="$1"
|
||||
if [[ ${line} =~ ^( +) ]]; then
|
||||
echo -n "${BASH_REMATCH[1]}"
|
||||
fi
|
||||
}
|
||||
|
||||
replace_line() {
|
||||
local file="$1"
|
||||
local search="$2"
|
||||
local newLine="$3"
|
||||
local newFile="${TMP_DIR}/$(bash_basename "${file}")"
|
||||
|
||||
test -f "${newFile}" && rm "${newFile}"
|
||||
while IFS= read -r line; do
|
||||
if line_contains "${line}" "${search}"; then
|
||||
print_line_indent "${line}" >>"${newFile}"
|
||||
echo -en "${newLine}" >>"${newFile}"
|
||||
continue
|
||||
fi
|
||||
echo "${line}" >>"${newFile}"
|
||||
done <"${file}"
|
||||
|
||||
cp "${newFile}" "${file}"
|
||||
}
|
||||
|
||||
remove_line() {
|
||||
local file="$1"
|
||||
local search="$2"
|
||||
replace_line "${file}" "${search}" ''
|
||||
}
|
||||
|
||||
bash_sort() {
|
||||
local arr=("$@")
|
||||
local n=${#arr[@]}
|
||||
local i j val1 val2
|
||||
|
||||
# Bubble sort, numeric comparison
|
||||
for ((i = 0; i < n; i++)); do
|
||||
for ((j = 0; j < n - i - 1; j++)); do
|
||||
read -r val1 _ <<<"${arr[j]}"
|
||||
read -r val2 _ <<<"${arr[j + 1]}"
|
||||
if (("${val1}" > "${val2}")); then
|
||||
local tmp=${arr[j]}
|
||||
arr[j]=${arr[j + 1]}
|
||||
arr[j + 1]=$tmp
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
printf '%s\n' "${arr[@]}"
|
||||
}
|
||||
|
||||
_start_spinner() {
|
||||
local spinChars=(
|
||||
"-"
|
||||
'\'
|
||||
"|"
|
||||
"/"
|
||||
)
|
||||
|
||||
sleep 1
|
||||
|
||||
while true; do
|
||||
for ((ind = 0; ind < "${#spinChars[@]}"; ind++)); do
|
||||
echo -ne "${spinChars[${ind}]}" '\b\b'
|
||||
sleep .25
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
spinner() {
|
||||
local action="$1"
|
||||
local spinPidFile="${TMP_DIR}/.spinner-pid"
|
||||
case "${action}" in
|
||||
start)
|
||||
test -f "${spinPidFile}" && rm "${spinPidFile}"
|
||||
|
||||
_start_spinner &
|
||||
echo $! >"${spinPidFile}"
|
||||
;;
|
||||
stop)
|
||||
test -f "${spinPidFile}" && kill "$(<"${spinPidFile}")"
|
||||
echo -ne ' \n'
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_pkgconfig_version() {
|
||||
local pkg="$1"
|
||||
pkg-config --modversion "${pkg}"
|
||||
}
|
||||
|
||||
using_cmake_4() {
|
||||
local cmakeVersion
|
||||
IFS=$' \t' read -r _ _ cmakeVersion <<<"$(command cmake --version)"
|
||||
line_starts_with "${cmakeVersion}" 4
|
||||
}
|
||||
|
||||
recreate_dir() {
|
||||
local dirs=("$@")
|
||||
for dir in "${dirs[@]}"; do
|
||||
test -d "${dir}" && rm -rf "${dir}"
|
||||
mkdir -p "${dir}" || return 1
|
||||
done
|
||||
}
|
||||
|
||||
ensure_dir() {
|
||||
local dirs=("$@")
|
||||
for dir in "${dirs[@]}"; do
|
||||
test -d "${dir}" || mkdir -p "${dir}" || return 1
|
||||
done
|
||||
}
|
||||
|
||||
get_remote_head() {
|
||||
local url="$1"
|
||||
local remoteHEAD=''
|
||||
IFS=$' \t' read -r remoteHEAD _ <<< \
|
||||
"$(git ls-remote "${url}" HEAD)"
|
||||
echo "${remoteHEAD}"
|
||||
}
|
||||
|
||||
fb_max() {
|
||||
local a="$1"
|
||||
local b="$2"
|
||||
test "${a}" -gt "${b}" &&
|
||||
echo "${a}" ||
|
||||
echo "${b}"
|
||||
}
|
||||
|
||||
print_padded() {
|
||||
local str="$1"
|
||||
local padding="$2"
|
||||
echo -n "${str}"
|
||||
for ((i = 0; i < padding - ${#str}; i++)); do
|
||||
echo -n ' '
|
||||
done
|
||||
}
|
||||
1763
lib/build.sh
1763
lib/build.sh
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,9 @@ libvmaf \
|
||||
libx264 \
|
||||
libx265 \
|
||||
libwebp \
|
||||
libvpx \
|
||||
libass \
|
||||
libvorbis \
|
||||
libmp3lame\
|
||||
"
|
||||
|
||||
@@ -48,15 +51,15 @@ FB_COMP_OPTS=("${!FB_COMP_OPTS_DESC[@]}")
|
||||
|
||||
# sets FB_COMP_OPTS to allow for user-overriding
|
||||
check_compile_opts_override() {
|
||||
for opt in "${FB_COMP_OPTS[@]}"; do
|
||||
declare -n defOptVal="DEFAULT_${opt}"
|
||||
declare -n optVal="${opt}"
|
||||
# use given value if not overridden
|
||||
if [[ -v optVal && ${optVal} != "${defOptVal}" ]]; then
|
||||
echo_info "setting given value for ${opt}=${optVal}"
|
||||
declare -g "${opt}=${optVal}"
|
||||
else
|
||||
declare -g "${opt}=${defOptVal}"
|
||||
fi
|
||||
done
|
||||
for opt in "${FB_COMP_OPTS[@]}"; do
|
||||
declare -n defOptVal="DEFAULT_${opt}"
|
||||
declare -n optVal="${opt}"
|
||||
# use given value if not overridden
|
||||
if [[ -v optVal && ${optVal} != "${defOptVal}" ]]; then
|
||||
echo_info "setting given value for ${opt}=${optVal}"
|
||||
declare -g "${opt}=${optVal}"
|
||||
else
|
||||
declare -g "${opt}=${defOptVal}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
475
lib/docker.sh
475
lib/docker.sh
@@ -1,327 +1,326 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
VALID_DOCKER_IMAGES=(
|
||||
'ubuntu'
|
||||
'fedora'
|
||||
'debian'
|
||||
'archlinux'
|
||||
'ubuntu'
|
||||
'fedora'
|
||||
'debian'
|
||||
'archlinux'
|
||||
)
|
||||
DOCKER_WORKDIR='/workdir'
|
||||
|
||||
set_docker_run_flags() {
|
||||
local cargo_git="${IGN_DIR}/cargo/git"
|
||||
local cargo_registry="${IGN_DIR}/cargo/registry"
|
||||
test -d "${cargo_git}" || mkdir -p "${cargo_git}"
|
||||
test -d "${cargo_registry}" || mkdir -p "${cargo_registry}"
|
||||
DOCKER_RUN_FLAGS=(
|
||||
--rm
|
||||
-v "${cargo_git}:/root/.cargo/git"
|
||||
-v "${cargo_registry}:/root/.cargo/registry"
|
||||
-v "${REPO_DIR}:${REPO_DIR}"
|
||||
-w "${REPO_DIR}"
|
||||
-e "DEBUG=${DEBUG}"
|
||||
-e "HEADLESS=${HEADLESS}"
|
||||
)
|
||||
for opt in "${FB_COMP_OPTS[@]}"; do
|
||||
declare -n defOptVal="DEFAULT_${opt}"
|
||||
declare -n optVal="${opt}"
|
||||
if [[ -v optVal && ${optVal} != "${defOptVal}" ]]; then
|
||||
DOCKER_RUN_FLAGS+=("-e" "${opt}=${optVal}")
|
||||
fi
|
||||
done
|
||||
local cargo_git="${IGN_DIR}/cargo/git"
|
||||
local cargo_registry="${IGN_DIR}/cargo/registry"
|
||||
ensure_dir "${cargo_git}" "${cargo_registry}"
|
||||
DOCKER_RUN_FLAGS=(
|
||||
--rm
|
||||
-v "${cargo_git}:/root/.cargo/git"
|
||||
-v "${cargo_registry}:/root/.cargo/registry"
|
||||
-v "${REPO_DIR}:${REPO_DIR}"
|
||||
-w "${REPO_DIR}"
|
||||
-e "DEBUG=${DEBUG}"
|
||||
-t
|
||||
)
|
||||
for opt in "${FB_COMP_OPTS[@]}"; do
|
||||
declare -n defOptVal="DEFAULT_${opt}"
|
||||
declare -n optVal="${opt}"
|
||||
if [[ -v optVal && ${optVal} != "${defOptVal}" ]]; then
|
||||
DOCKER_RUN_FLAGS+=("-e" "${opt}=${optVal}")
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
check_docker() {
|
||||
if missing_cmd docker; then
|
||||
echo_info "install docker"
|
||||
curl https://get.docker.com -sSf | bash
|
||||
fi
|
||||
set_docker_run_flags || return 1
|
||||
if missing_cmd docker; then
|
||||
echo_info "install docker"
|
||||
curl https://get.docker.com -sSf | bash
|
||||
fi
|
||||
set_docker_run_flags || return 1
|
||||
}
|
||||
|
||||
# get full image digest for a given image
|
||||
get_docker_image_tag() {
|
||||
local image="$1"
|
||||
local tag=''
|
||||
case "${image}" in
|
||||
ubuntu) tag='ubuntu:24.04@sha256:c35e29c9450151419d9448b0fd75374fec4fff364a27f176fb458d472dfc9e54' ;;
|
||||
debian) tag='debian:13@sha256:0d01188e8dd0ac63bf155900fad49279131a876a1ea7fac917c62e87ccb2732d' ;;
|
||||
fedora) tag='fedora:42@sha256:b3d16134560afa00d7cc2a9e4967eb5b954512805f3fe27d8e70bbed078e22ea' ;;
|
||||
archlinux) tag='ogarcia/archlinux:latest@sha256:1d70273180e43b1f51b41514bdaa73c61f647891a53a9c301100d5c4807bf628' ;;
|
||||
esac
|
||||
echo "${tag}"
|
||||
local image="$1"
|
||||
local tag=''
|
||||
case "${image}" in
|
||||
ubuntu) tag='ubuntu:24.04@sha256:c35e29c9450151419d9448b0fd75374fec4fff364a27f176fb458d472dfc9e54' ;;
|
||||
debian) tag='debian:13@sha256:0d01188e8dd0ac63bf155900fad49279131a876a1ea7fac917c62e87ccb2732d' ;;
|
||||
fedora) tag='fedora:42@sha256:b3d16134560afa00d7cc2a9e4967eb5b954512805f3fe27d8e70bbed078e22ea' ;;
|
||||
archlinux) tag='ogarcia/archlinux:latest@sha256:1d70273180e43b1f51b41514bdaa73c61f647891a53a9c301100d5c4807bf628' ;;
|
||||
esac
|
||||
echo "${tag}"
|
||||
}
|
||||
|
||||
# change dash to colon for docker and add namespace
|
||||
set_distro_image_tag() {
|
||||
local image_tag="${1}"
|
||||
echo "ffmpeg_builder_${image_tag//-/:}"
|
||||
local image_tag="${1}"
|
||||
echo "ffmpeg_builder_${image_tag//-/:}"
|
||||
}
|
||||
|
||||
# change colon to dash and add extension type
|
||||
docker_image_archive_name() {
|
||||
local image_tag="${1}"
|
||||
echo "${image_tag//:/-}.tar.zst"
|
||||
local image_tag="${1}"
|
||||
echo "${image_tag//:/-}.tar.zst"
|
||||
}
|
||||
|
||||
echo_platform() {
|
||||
local platKernel platCpu
|
||||
platKernel="$(uname)"
|
||||
platKernel="${platKernel,,}"
|
||||
if [[ ${HOSTTYPE} == 'x86_64' ]]; then
|
||||
platCpu='amd64'
|
||||
else
|
||||
platCpu='arm64'
|
||||
fi
|
||||
local platKernel platCpu
|
||||
platKernel="$(uname)"
|
||||
platKernel="${platKernel,,}"
|
||||
if [[ ${HOSTTYPE} == 'x86_64' ]]; then
|
||||
platCpu='amd64'
|
||||
else
|
||||
platCpu='arm64'
|
||||
fi
|
||||
|
||||
echo "${platKernel}/${platCpu}"
|
||||
echo "${platKernel}/${platCpu}"
|
||||
}
|
||||
|
||||
validate_selected_image() {
|
||||
local selectedImage="$1"
|
||||
local valid=1
|
||||
for image in "${VALID_DOCKER_IMAGES[@]}"; do
|
||||
if [[ ${selectedImage} == "${image}" ]]; then
|
||||
valid=0
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ valid -eq 1 ]]; then
|
||||
echo_fail "${selectedImage} is not valid"
|
||||
echo_info "valid images:" "${VALID_DOCKER_IMAGES[@]}"
|
||||
return 1
|
||||
fi
|
||||
local selectedImage="$1"
|
||||
local valid=1
|
||||
for image in "${VALID_DOCKER_IMAGES[@]}"; do
|
||||
if [[ ${selectedImage} == "${image}" ]]; then
|
||||
valid=0
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ valid -eq 1 ]]; then
|
||||
echo_fail "${selectedImage} is not valid"
|
||||
echo_info "valid images:" "${VALID_DOCKER_IMAGES[@]}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
docker_login() {
|
||||
echo_if_fail docker login \
|
||||
-u "${DOCKER_REGISTRY_USER}" \
|
||||
-p "${DOCKER_REGISTRY_PASS}" \
|
||||
"${DOCKER_REGISTRY}"
|
||||
echo_if_fail docker login \
|
||||
-u "${DOCKER_REGISTRY_USER}" \
|
||||
-p "${DOCKER_REGISTRY_PASS}" \
|
||||
"${DOCKER_REGISTRY}"
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('docker_build_image')
|
||||
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"
|
||||
validate_selected_image "${image}" || return 1
|
||||
check_docker || return 1
|
||||
PLATFORM="${PLATFORM:-$(echo_platform)}"
|
||||
local image="$1"
|
||||
validate_selected_image "${image}" || return 1
|
||||
check_docker || return 1
|
||||
PLATFORM="${PLATFORM:-$(echo_platform)}"
|
||||
|
||||
echo_info "sourcing package manager for ${image}"
|
||||
local dockerDistro="$(get_docker_image_tag "${image}")"
|
||||
# specific file for evaluated package manager info
|
||||
local distroPkgMgr="${DOCKER_DIR}/$(bash_basename "${image}")-pkg_mgr"
|
||||
# get package manager info
|
||||
docker run \
|
||||
"${DOCKER_RUN_FLAGS[@]}" \
|
||||
"${dockerDistro}" \
|
||||
bash -c "./scripts/print_pkg_mgr.sh" | tr -d '\r' >"${distroPkgMgr}"
|
||||
# shellcheck disable=SC1090
|
||||
cat "${distroPkgMgr}"
|
||||
# shellcheck disable=SC1090
|
||||
source "${distroPkgMgr}"
|
||||
echo_info "sourcing package manager for ${image}"
|
||||
local dockerDistro="$(get_docker_image_tag "${image}")"
|
||||
# specific file for evaluated package manager info
|
||||
local distroPkgMgr="${DOCKER_DIR}/$(bash_basename "${image}")-pkg_mgr"
|
||||
# get package manager info
|
||||
docker run \
|
||||
"${DOCKER_RUN_FLAGS[@]}" \
|
||||
"${dockerDistro}" \
|
||||
bash -c "./scripts/print_pkg_mgr.sh" | tr -d '\r' >"${distroPkgMgr}"
|
||||
# shellcheck disable=SC1090
|
||||
cat "${distroPkgMgr}"
|
||||
# shellcheck disable=SC1090
|
||||
source "${distroPkgMgr}"
|
||||
|
||||
local dockerfile="${DOCKER_DIR}/Dockerfile_$(bash_basename "${image}")"
|
||||
local embedPath='/Dockerfile'
|
||||
{
|
||||
echo "FROM ${dockerDistro}"
|
||||
echo 'SHELL ["/bin/bash", "-c"]'
|
||||
echo 'RUN ln -sf /bin/bash /bin/sh'
|
||||
echo 'ENV DEBIAN_FRONTEND=noninteractive'
|
||||
echo "RUN ${pkg_mgr_update} && ${pkg_mgr_upgrade} && ${pkg_install} ${req_pkgs[*]}"
|
||||
local dockerfile="${DOCKER_DIR}/Dockerfile_$(bash_basename "${image}")"
|
||||
local embedPath='/Dockerfile'
|
||||
{
|
||||
echo "FROM ${dockerDistro}"
|
||||
echo 'SHELL ["/bin/bash", "-c"]'
|
||||
echo 'RUN ln -sf /bin/bash /bin/sh'
|
||||
echo 'ENV DEBIAN_FRONTEND=noninteractive'
|
||||
echo "RUN ${pkg_mgr_update} && ${pkg_mgr_upgrade} && ${pkg_install} ${req_pkgs[*]}"
|
||||
|
||||
# ENV for pipx/rust
|
||||
echo 'ENV PIPX_HOME=/root/.local'
|
||||
echo 'ENV PIPX_BIN_DIR=/root/.local/bin'
|
||||
echo 'ENV PATH="/root/.local/bin:$PATH"'
|
||||
echo 'ENV CARGO_HOME="/root/.cargo"'
|
||||
echo 'ENV RUSTUP_HOME="/root/.rustup"'
|
||||
echo 'ENV PATH="/root/.cargo/bin:$PATH"'
|
||||
# add to profile
|
||||
echo 'RUN export PIPX_HOME=${PIPX_HOME} >> /etc/profile'
|
||||
echo 'RUN export PIPX_BIN_DIR=${PIPX_BIN_DIR} >> /etc/profile'
|
||||
echo 'RUN export CARGO_HOME=${CARGO_HOME} >> /etc/profile'
|
||||
echo 'RUN export RUSTUP_HOME=${RUSTUP_HOME} >> /etc/profile'
|
||||
echo 'RUN export PATH=${PATH} >> /etc/profile'
|
||||
# ENV for pipx/rust
|
||||
echo 'ENV PIPX_HOME=/root/.local'
|
||||
echo 'ENV PIPX_BIN_DIR=/root/.local/bin'
|
||||
echo 'ENV PATH="/root/.local/bin:$PATH"'
|
||||
echo 'ENV CARGO_HOME="/root/.cargo"'
|
||||
echo 'ENV RUSTUP_HOME="/root/.rustup"'
|
||||
echo 'ENV PATH="/root/.cargo/bin:$PATH"'
|
||||
# add to profile
|
||||
echo 'RUN export PIPX_HOME=${PIPX_HOME} >> /etc/profile'
|
||||
echo 'RUN export PIPX_BIN_DIR=${PIPX_BIN_DIR} >> /etc/profile'
|
||||
echo 'RUN export CARGO_HOME=${CARGO_HOME} >> /etc/profile'
|
||||
echo 'RUN export RUSTUP_HOME=${RUSTUP_HOME} >> /etc/profile'
|
||||
echo 'RUN export PATH=${PATH} >> /etc/profile'
|
||||
|
||||
# make nobody:nogroup usable
|
||||
echo 'RUN sed -i '/nobody/d' /etc/passwd || true'
|
||||
echo 'RUN echo "nobody:x:65534:65534:nobody:/root:/bin/bash" >> /etc/passwd'
|
||||
echo 'RUN sed -i '/nogroup/d' /etc/group || true'
|
||||
echo 'RUN echo "nogroup:x:65534:" >> /etc/group'
|
||||
# open up permissions before switching user
|
||||
echo 'RUN chmod 777 -R /root/'
|
||||
# run as nobody:nogroup for rest of install
|
||||
echo 'USER 65534:65534'
|
||||
# pipx
|
||||
echo "RUN pipx install virtualenv"
|
||||
# rust
|
||||
local rustupVersion='1.28.2'
|
||||
local rustcVersion='1.90.0'
|
||||
local rustupTarball="rustup-${rustupVersion}.tar.gz"
|
||||
local rustupTarballPath="${DOCKER_DIR}/${rustupTarball}"
|
||||
if [[ ! -f ${rustupTarballPath} ]]; then
|
||||
wget https://github.com/rust-lang/rustup/archive/refs/tags/${rustupVersion}.tar.gz -O "${rustupTarballPath}"
|
||||
fi
|
||||
# make nobody:nogroup usable
|
||||
echo 'RUN sed -i '/nobody/d' /etc/passwd || true'
|
||||
echo 'RUN echo "nobody:x:65534:65534:nobody:/root:/bin/bash" >> /etc/passwd'
|
||||
echo 'RUN sed -i '/nogroup/d' /etc/group || true'
|
||||
echo 'RUN echo "nogroup:x:65534:" >> /etc/group'
|
||||
# open up permissions before switching user
|
||||
echo 'RUN chmod 777 -R /root/'
|
||||
# run as nobody:nogroup for rest of install
|
||||
echo 'USER 65534:65534'
|
||||
# pipx
|
||||
echo "RUN pipx install virtualenv"
|
||||
# rust
|
||||
local rustupVersion='1.28.2'
|
||||
local rustcVersion='1.90.0'
|
||||
local rustupTarball="rustup-${rustupVersion}.tar.gz"
|
||||
local rustupTarballPath="${DOCKER_DIR}/${rustupTarball}"
|
||||
if [[ ! -f ${rustupTarballPath} ]]; then
|
||||
wget https://github.com/rust-lang/rustup/archive/refs/tags/${rustupVersion}.tar.gz -O "${rustupTarballPath}"
|
||||
fi
|
||||
|
||||
echo "ADD ${rustupTarball} /tmp/"
|
||||
echo "RUN cd /tmp/rustup-${rustupVersion} && bash rustup-init.sh -y --default-toolchain=${rustcVersion}"
|
||||
# install cargo-binstall
|
||||
echo "RUN curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash"
|
||||
# install cargo-c
|
||||
echo "RUN cargo-binstall -y cargo-c"
|
||||
echo "ADD ${rustupTarball} /tmp/"
|
||||
echo "RUN cd /tmp/rustup-${rustupVersion} && bash rustup-init.sh -y --default-toolchain=${rustcVersion}"
|
||||
# install cargo-binstall
|
||||
echo "RUN curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash"
|
||||
# install cargo-c
|
||||
echo "RUN cargo-binstall -y cargo-c"
|
||||
|
||||
# final mods for PS1
|
||||
echo
|
||||
echo 'USER root'
|
||||
echo "RUN echo \"PS1='id=\\\$(id -u)@${image}:\w\\$ '\" >> /etc/bash.bashrc"
|
||||
echo 'USER 65534:65534'
|
||||
echo
|
||||
# final mods for PS1
|
||||
echo
|
||||
echo 'USER root'
|
||||
echo "RUN echo \"PS1='id=\\\$(id -u)@${image}:\w\\$ '\" >> /etc/bash.bashrc"
|
||||
echo 'USER 65534:65534'
|
||||
echo
|
||||
|
||||
# embed dockerfile into docker image itself
|
||||
# shellcheck disable=SC2094
|
||||
echo "COPY $(bash_basename "${dockerfile}") ${embedPath}"
|
||||
# embed dockerfile into docker image itself
|
||||
# shellcheck disable=SC2094
|
||||
echo "COPY $(bash_basename "${dockerfile}") ${embedPath}"
|
||||
|
||||
echo "WORKDIR ${DOCKER_WORKDIR}"
|
||||
echo "WORKDIR ${DOCKER_WORKDIR}"
|
||||
|
||||
} >"${dockerfile}"
|
||||
} >"${dockerfile}"
|
||||
|
||||
# docker buildx is too aggressive with invalidating
|
||||
# build layer caches. Instead of relying on docker
|
||||
# to check for when to rebuild, compare the to-build
|
||||
# dockerfile with the embedded dockerfile
|
||||
local oldDockerfile="${dockerfile}.old"
|
||||
docker_run_image "${image}" cp "${embedPath}" "${oldDockerfile}"
|
||||
if diff "${dockerfile}" "${oldDockerfile}"; then
|
||||
echo_pass "no dockerfile changes detected, skipping rebuild"
|
||||
return 0
|
||||
else
|
||||
echo_warn "dockerfile changes detected, proceeding with build"
|
||||
fi
|
||||
# docker buildx is too aggressive with invalidating
|
||||
# build layer caches. Instead of relying on docker
|
||||
# to check for when to rebuild, compare the to-build
|
||||
# dockerfile with the embedded dockerfile
|
||||
local oldDockerfile="${dockerfile}.old"
|
||||
docker_run_image "${image}" cp "${embedPath}" "${oldDockerfile}"
|
||||
if diff "${dockerfile}" "${oldDockerfile}"; then
|
||||
echo_pass "no dockerfile changes detected, skipping rebuild"
|
||||
return 0
|
||||
else
|
||||
echo_warn "dockerfile changes detected, proceeding with build"
|
||||
fi
|
||||
|
||||
image_tag="$(set_distro_image_tag "${image}")"
|
||||
docker buildx build \
|
||||
--platform "${PLATFORM}" \
|
||||
-t "${image_tag}" \
|
||||
-f "${dockerfile}" \
|
||||
"${DOCKER_DIR}" || return 1
|
||||
image_tag="$(set_distro_image_tag "${image}")"
|
||||
docker buildx build \
|
||||
--platform "${PLATFORM}" \
|
||||
-t "${image_tag}" \
|
||||
-f "${dockerfile}" \
|
||||
"${DOCKER_DIR}" || return 1
|
||||
|
||||
# if a docker registry is defined, push to it
|
||||
if [[ ${DOCKER_REGISTRY} != '' ]]; then
|
||||
docker_login || return 1
|
||||
docker buildx build \
|
||||
--push \
|
||||
--platform "${PLATFORM}" \
|
||||
-t "${DOCKER_REGISTRY}/${image_tag}" \
|
||||
-f "${dockerfile}" \
|
||||
"${DOCKER_DIR}" || return 1
|
||||
fi
|
||||
# if a docker registry is defined, push to it
|
||||
if [[ ${DOCKER_REGISTRY} != '' ]]; then
|
||||
docker_login || return 1
|
||||
docker buildx build \
|
||||
--push \
|
||||
--platform "${PLATFORM}" \
|
||||
-t "${DOCKER_REGISTRY}/${image_tag}" \
|
||||
-f "${dockerfile}" \
|
||||
"${DOCKER_DIR}" || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('docker_save_image')
|
||||
FB_FUNC_DESCS['docker_save_image']='save docker image into tar.zst'
|
||||
FB_FUNC_COMPLETION['docker_save_image']="${VALID_DOCKER_IMAGES[*]}"
|
||||
docker_save_image() {
|
||||
local image="$1"
|
||||
validate_selected_image "${image}" || return 1
|
||||
check_docker || return 1
|
||||
image_tag="$(set_distro_image_tag "${image}")"
|
||||
echo_info "saving docker image for ${image_tag}"
|
||||
docker save "${image_tag}" |
|
||||
zstd -T0 >"${DOCKER_DIR}/$(docker_image_archive_name "${image_tag}")" ||
|
||||
return 1
|
||||
local image="$1"
|
||||
validate_selected_image "${image}" || return 1
|
||||
check_docker || return 1
|
||||
image_tag="$(set_distro_image_tag "${image}")"
|
||||
echo_info "saving docker image for ${image_tag}"
|
||||
docker save "${image_tag}" |
|
||||
zstd -T0 >"${DOCKER_DIR}/$(docker_image_archive_name "${image_tag}")" ||
|
||||
return 1
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('docker_load_image')
|
||||
FB_FUNC_DESCS['docker_load_image']='load docker image from tar.zst'
|
||||
FB_FUNC_COMPLETION['docker_load_image']="${VALID_DOCKER_IMAGES[*]}"
|
||||
docker_load_image() {
|
||||
local image="$1"
|
||||
validate_selected_image "${image}" || return 1
|
||||
check_docker || return 1
|
||||
image_tag="$(set_distro_image_tag "${image}")"
|
||||
echo_info "loading docker image for ${image_tag}"
|
||||
local archive="${DOCKER_DIR}/$(docker_image_archive_name "${image_tag}")"
|
||||
test -f "$archive" || return 1
|
||||
zstdcat -T0 "$archive" | docker load || return 1
|
||||
docker system prune -f
|
||||
local image="$1"
|
||||
validate_selected_image "${image}" || return 1
|
||||
check_docker || return 1
|
||||
image_tag="$(set_distro_image_tag "${image}")"
|
||||
echo_info "loading docker image for ${image_tag}"
|
||||
local archive="${DOCKER_DIR}/$(docker_image_archive_name "${image_tag}")"
|
||||
test -f "$archive" || return 1
|
||||
zstdcat -T0 "$archive" | docker load || return 1
|
||||
docker system prune -f
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('docker_run_image')
|
||||
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"
|
||||
shift
|
||||
validate_selected_image "${image}" || return 1
|
||||
check_docker || return 1
|
||||
local image="$1"
|
||||
shift
|
||||
validate_selected_image "${image}" || return 1
|
||||
check_docker || return 1
|
||||
|
||||
local cmd=("$@")
|
||||
local runCmd=()
|
||||
if [[ ${cmd[*]} == '' ]]; then
|
||||
DOCKER_RUN_FLAGS+=("-it")
|
||||
else
|
||||
runCmd+=("${cmd[@]}")
|
||||
fi
|
||||
local cmd=("$@")
|
||||
local runCmd=()
|
||||
if [[ ${cmd[*]} == '' ]]; then
|
||||
DOCKER_RUN_FLAGS+=("-it")
|
||||
else
|
||||
runCmd+=("${cmd[@]}")
|
||||
fi
|
||||
|
||||
local image_tag="$(set_distro_image_tag "${image}")"
|
||||
local image_tag="$(set_distro_image_tag "${image}")"
|
||||
|
||||
# if a docker registry is defined, pull from it
|
||||
if [[ ${DOCKER_REGISTRY} != '' ]]; then
|
||||
docker_login || return 1
|
||||
docker pull \
|
||||
"${DOCKER_REGISTRY}/${image_tag}" || return 1
|
||||
docker tag "${DOCKER_REGISTRY}/${image_tag}" "${image_tag}"
|
||||
fi
|
||||
# if a docker registry is defined, pull from it
|
||||
if [[ ${DOCKER_REGISTRY} != '' ]]; then
|
||||
docker_login || return 1
|
||||
docker pull \
|
||||
"${DOCKER_REGISTRY}/${image_tag}" || return 1
|
||||
docker tag "${DOCKER_REGISTRY}/${image_tag}" "${image_tag}"
|
||||
fi
|
||||
|
||||
echo_info "running docker image ${image_tag}"
|
||||
docker run \
|
||||
"${DOCKER_RUN_FLAGS[@]}" \
|
||||
-u "$(id -u):$(id -g)" \
|
||||
"${image_tag}" \
|
||||
"${runCmd[@]}"
|
||||
echo_info "running docker image ${image_tag}"
|
||||
docker run \
|
||||
"${DOCKER_RUN_FLAGS[@]}" \
|
||||
-u "$(id -u):$(id -g)" \
|
||||
"${image_tag}" \
|
||||
"${runCmd[@]}"
|
||||
|
||||
local rv=$?
|
||||
docker image prune -f
|
||||
return ${rv}
|
||||
local rv=$?
|
||||
docker image prune -f
|
||||
return ${rv}
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('build_with_docker')
|
||||
FB_FUNC_DESCS['build_with_docker']='run docker image with given flags'
|
||||
FB_FUNC_COMPLETION['build_with_docker']="${VALID_DOCKER_IMAGES[*]}"
|
||||
build_with_docker() {
|
||||
local image="$1"
|
||||
docker_run_image "${image}" ./scripts/build.sh || return 1
|
||||
local image="$1"
|
||||
docker_run_image "${image}" ./scripts/build.sh || return 1
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('docker_build_multiarch_image')
|
||||
FB_FUNC_DESCS['docker_build_multiarch_image']='build multiarch docker image'
|
||||
FB_FUNC_COMPLETION['docker_build_multiarch_image']="${VALID_DOCKER_IMAGES[*]}"
|
||||
docker_build_multiarch_image() {
|
||||
local image="$1"
|
||||
validate_selected_image "${image}" || return 1
|
||||
check_docker || return 1
|
||||
PLATFORM='linux/amd64,linux/arm64'
|
||||
local image="$1"
|
||||
validate_selected_image "${image}" || return 1
|
||||
check_docker || return 1
|
||||
PLATFORM='linux/amd64,linux/arm64'
|
||||
|
||||
# check if we need to create multiplatform builder
|
||||
local buildxPlats="$(docker buildx inspect | grep Platforms)"
|
||||
IFS=','
|
||||
local createBuilder=0
|
||||
for plat in $PLATFORM; do
|
||||
grep -q "${plat}" <<<"${buildxPlats}" || createBuilder=1
|
||||
done
|
||||
unset IFS
|
||||
# check if we need to create multiplatform builder
|
||||
local buildxPlats="$(docker buildx inspect | grep Platforms)"
|
||||
IFS=','
|
||||
local createBuilder=0
|
||||
for plat in $PLATFORM; do
|
||||
grep -q "${plat}" <<<"${buildxPlats}" || createBuilder=1
|
||||
done
|
||||
unset IFS
|
||||
|
||||
if [[ ${createBuilder} == 1 ]]; then
|
||||
echo_info "creating multiplatform (${PLATFORM}) docker builder"
|
||||
docker buildx create \
|
||||
--use \
|
||||
--platform="${PLATFORM}" \
|
||||
--name my-multiplatform-builder \
|
||||
--driver=docker-container
|
||||
fi
|
||||
if [[ ${createBuilder} == 1 ]]; then
|
||||
echo_info "creating multiplatform (${PLATFORM}) docker builder"
|
||||
docker buildx create \
|
||||
--use \
|
||||
--platform="${PLATFORM}" \
|
||||
--name my-multiplatform-builder \
|
||||
--driver=docker-container
|
||||
fi
|
||||
|
||||
docker_build_image "$@"
|
||||
docker_build_image "$@"
|
||||
}
|
||||
|
||||
448
lib/efg.sh
448
lib/efg.sh
@@ -1,232 +1,246 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
efg_usage() {
|
||||
echo "efg -i input [options]"
|
||||
echo -e "\t[-l NUM] low value (default: ${LOW})"
|
||||
echo -e "\t[-s NUM] step value (default: ${STEP})"
|
||||
echo -e "\t[-h NUM] high value (default: ${HIGH})"
|
||||
echo -e "\t[-p] plot bitrates using gnuplot"
|
||||
echo -e "\n\t[-I] system install at ${EFG_INSTALL_PATH}"
|
||||
echo -e "\t[-U] uninstall from ${EFG_INSTALL_PATH}"
|
||||
return 0
|
||||
echo "efg -i input [options]"
|
||||
echo -e "\t[-P NUM] set preset (default: ${PRESET})"
|
||||
echo -e "\t[-l NUM] low value (default: ${LOW})"
|
||||
echo -e "\t[-s NUM] step value (default: ${STEP})"
|
||||
echo -e "\t[-h NUM] high value (default: ${HIGH})"
|
||||
echo -e "\t[-p] plot bitrates using gnuplot"
|
||||
echo -e "\n\t[-I] system install at ${EFG_INSTALL_PATH}"
|
||||
echo -e "\t[-U] uninstall from ${EFG_INSTALL_PATH}"
|
||||
return 0
|
||||
}
|
||||
|
||||
set_efg_opts() {
|
||||
local opts='pl:s:h:i:IU'
|
||||
local numOpts=${#opts}
|
||||
# default values
|
||||
unset INPUT
|
||||
LOW=0
|
||||
STEP=1
|
||||
HIGH=30
|
||||
PLOT=false
|
||||
EFG_INSTALL_PATH='/usr/local/bin/efg'
|
||||
# only using -I or -U
|
||||
local minOpt=1
|
||||
# using all
|
||||
local maxOpt=${numOpts}
|
||||
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
|
||||
I)
|
||||
echo_warn "attempting install"
|
||||
sudo ln -sf "${SCRIPT_DIR}/efg.sh" \
|
||||
"${EFG_INSTALL_PATH}" || return 1
|
||||
echo_pass "succesfull install"
|
||||
exit 0
|
||||
;;
|
||||
U)
|
||||
echo_warn "attempting uninstall"
|
||||
sudo rm "${EFG_INSTALL_PATH}" || return 1
|
||||
echo_pass "succesfull uninstall"
|
||||
exit 0
|
||||
;;
|
||||
i)
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo_fail "wrong arguments given"
|
||||
efg_usage
|
||||
return 1
|
||||
fi
|
||||
INPUT="${OPTARG}"
|
||||
;;
|
||||
p)
|
||||
missing_cmd gnuplot && return 1
|
||||
PLOT=true
|
||||
;;
|
||||
l)
|
||||
if ! is_positive_integer "${OPTARG}"; then
|
||||
efg_usage
|
||||
return 1
|
||||
fi
|
||||
LOW="${OPTARG}"
|
||||
;;
|
||||
s)
|
||||
if ! is_positive_integer "${OPTARG}"; then
|
||||
efg_usage
|
||||
return 1
|
||||
fi
|
||||
STEP="${OPTARG}"
|
||||
;;
|
||||
h)
|
||||
if ! is_positive_integer "${OPTARG}"; then
|
||||
efg_usage
|
||||
return 1
|
||||
fi
|
||||
HIGH="${OPTARG}"
|
||||
;;
|
||||
*)
|
||||
echo "wrong flags given"
|
||||
efg_usage
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
local opts='P:pl:s:h:i:IU'
|
||||
local numOpts=${#opts}
|
||||
# default values
|
||||
unset INPUT
|
||||
PRESET=10
|
||||
LOW=0
|
||||
STEP=1
|
||||
HIGH=30
|
||||
PLOT=false
|
||||
EFG_INSTALL_PATH='/usr/local/bin/efg'
|
||||
# only using -I or -U
|
||||
local minOpt=1
|
||||
# using all
|
||||
local maxOpt=${numOpts}
|
||||
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
|
||||
P)
|
||||
if ! is_positive_integer "${OPTARG}"; then
|
||||
efg_usage
|
||||
return 1
|
||||
fi
|
||||
PRESET="${OPTARG}"
|
||||
;;
|
||||
I)
|
||||
echo_warn "attempting install"
|
||||
sudo ln -sf "${SCRIPT_DIR}/efg.sh" \
|
||||
"${EFG_INSTALL_PATH}" || return 1
|
||||
echo_pass "succesfull install"
|
||||
return ${FUNC_EXIT_SUCCESS}
|
||||
;;
|
||||
U)
|
||||
echo_warn "attempting uninstall"
|
||||
sudo rm "${EFG_INSTALL_PATH}" || return 1
|
||||
echo_pass "succesfull uninstall"
|
||||
return ${FUNC_EXIT_SUCCESS}
|
||||
;;
|
||||
i)
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo_fail "wrong arguments given"
|
||||
efg_usage
|
||||
return 1
|
||||
fi
|
||||
INPUT="${OPTARG}"
|
||||
;;
|
||||
p)
|
||||
missing_cmd gnuplot && return 1
|
||||
PLOT=true
|
||||
;;
|
||||
l)
|
||||
if ! is_positive_integer "${OPTARG}"; then
|
||||
efg_usage
|
||||
return 1
|
||||
fi
|
||||
LOW="${OPTARG}"
|
||||
;;
|
||||
s)
|
||||
if ! is_positive_integer "${OPTARG}"; then
|
||||
efg_usage
|
||||
return 1
|
||||
fi
|
||||
STEP="${OPTARG}"
|
||||
;;
|
||||
h)
|
||||
if ! is_positive_integer "${OPTARG}"; then
|
||||
efg_usage
|
||||
return 1
|
||||
fi
|
||||
HIGH="${OPTARG}"
|
||||
;;
|
||||
*)
|
||||
echo "wrong flags given"
|
||||
efg_usage
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! -f ${INPUT} ]]; then
|
||||
echo "${INPUT} does not exist"
|
||||
efg_usage
|
||||
return 1
|
||||
fi
|
||||
if [[ ! -f ${INPUT} ]]; then
|
||||
echo "${INPUT} does not exist"
|
||||
efg_usage
|
||||
return 1
|
||||
fi
|
||||
|
||||
# set custom EFG_DIR based off of sanitized inputfile
|
||||
local sanitizedInput="$(bash_basename "${INPUT}")"
|
||||
local sanitizeChars=(' ' '@' ':')
|
||||
for char in "${sanitizeChars[@]}"; do
|
||||
sanitizedInput="${sanitizedInput//${char}/}"
|
||||
done
|
||||
EFG_DIR+="-${sanitizedInput}"
|
||||
# set custom EFG_DIR based off of sanitized inputfile
|
||||
local sanitizedInput="$(bash_basename "${INPUT}")"
|
||||
local sanitizeChars=(' ' '@' ':')
|
||||
for char in "${sanitizeChars[@]}"; do
|
||||
sanitizedInput="${sanitizedInput//${char}/}"
|
||||
done
|
||||
EFG_DIR+="-${sanitizedInput}"
|
||||
|
||||
echo_info "estimating film grain for ${INPUT}"
|
||||
echo_info "range: $LOW-$HIGH with $STEP step increments"
|
||||
echo_info "estimating film grain for ${INPUT}"
|
||||
echo_info "range: $LOW-$HIGH with $STEP step increments"
|
||||
}
|
||||
|
||||
efg_segment() {
|
||||
# number of segments to split video
|
||||
local segments=30
|
||||
# duration of each segment
|
||||
local segmentTime=3
|
||||
# number of segments to split video
|
||||
local segments=30
|
||||
# duration of each segment
|
||||
local segmentTime=3
|
||||
|
||||
# get times to split the input based
|
||||
# off of number of segments
|
||||
local duration
|
||||
duration="$(get_duration "${INPUT}")" || return 1
|
||||
# trim decimal points if any
|
||||
IFS=. read -r duration _ <<<"${duration}"
|
||||
# number of seconds that equal 1 percent of the video
|
||||
local percentTime=$((duration / 100))
|
||||
# percent that each segment takes
|
||||
local percentSegment=$((100 / segments))
|
||||
# number of seconds to increment between segments
|
||||
local timeBetweenSegments=$((percentTime * percentSegment))
|
||||
if [[ ${timeBetweenSegments} -lt ${segmentTime} ]]; then
|
||||
timeBetweenSegments=${segmentTime}
|
||||
fi
|
||||
local segmentBitrates=()
|
||||
# get times to split the input based
|
||||
# off of number of segments
|
||||
local duration
|
||||
duration="$(get_duration "${INPUT}")" || return 1
|
||||
# trim decimal points if any
|
||||
IFS=. read -r duration _ <<<"${duration}"
|
||||
# number of seconds that equal 1 percent of the video
|
||||
local percentTime=$((duration / 100))
|
||||
# percent that each segment takes
|
||||
local percentSegment=$((100 / segments))
|
||||
# number of seconds to increment between segments
|
||||
local timeBetweenSegments=$((percentTime * percentSegment))
|
||||
if [[ ${timeBetweenSegments} -lt ${segmentTime} ]]; then
|
||||
timeBetweenSegments=${segmentTime}
|
||||
fi
|
||||
local segmentBitrates=()
|
||||
|
||||
# clean workspace
|
||||
test -d "${EFG_DIR}" && rm -rf "${EFG_DIR}"
|
||||
mkdir -p "${EFG_DIR}"
|
||||
# clean workspace
|
||||
recreate_dir "${EFG_DIR}" || return 1
|
||||
|
||||
# split up video into segments based on start times
|
||||
for ((time = 0; time < duration; time += timeBetweenSegments)); do
|
||||
local outSegment="${EFG_DIR}/segment-${#segmentBitrates[@]}.mkv"
|
||||
split_video "${INPUT}" "${time}" "${segmentTime}" "${outSegment}" || return 1
|
||||
local segmentBitrate
|
||||
segmentBitrate="$(get_avg_bitrate "${outSegment}")" || return 1
|
||||
segmentBitrates+=("${segmentBitrate}:${outSegment}")
|
||||
done
|
||||
local numSegments="${#segmentBitrates[@]}"
|
||||
# split up video into segments based on start times
|
||||
for ((time = 0; time < duration; time += timeBetweenSegments)); do
|
||||
local outSegment="${EFG_DIR}/segment-${#segmentBitrates[@]}.mkv"
|
||||
split_video "${INPUT}" "${time}" "${segmentTime}" "${outSegment}" || return 1
|
||||
local segmentBitrate
|
||||
segmentBitrate="$(get_avg_bitrate "${outSegment}")" || return 1
|
||||
segmentBitrates+=("${segmentBitrate}:${outSegment}")
|
||||
done
|
||||
local numSegments="${#segmentBitrates[@]}"
|
||||
|
||||
local removeSegments
|
||||
if [[ ${numSegments} -lt ${ENCODE_SEGMENTS} ]]; then
|
||||
removeSegments=0
|
||||
else
|
||||
removeSegments=$((numSegments - ENCODE_SEGMENTS))
|
||||
fi
|
||||
local removeSegments
|
||||
if [[ ${numSegments} -lt ${ENCODE_SEGMENTS} ]]; then
|
||||
removeSegments=0
|
||||
else
|
||||
removeSegments=$((numSegments - ENCODE_SEGMENTS))
|
||||
fi
|
||||
|
||||
# sort the segments
|
||||
mapfile -t sortedSegments < <(IFS=: bash_sort "${segmentBitrates[@]}")
|
||||
# make sure bitrate for each file is actually increasing
|
||||
local prevBitrate=0
|
||||
# remove all but the highest bitrate segments
|
||||
for segment in "${sortedSegments[@]}"; do
|
||||
test ${removeSegments} -eq 0 && break
|
||||
local file currBitrate
|
||||
IFS=: read -r _ file <<<"${segment}"
|
||||
currBitrate="$(get_avg_bitrate "${file}")" || return 1
|
||||
# sort the segments
|
||||
mapfile -t sortedSegments < <(IFS=: bash_sort "${segmentBitrates[@]}")
|
||||
# make sure bitrate for each file is actually increasing
|
||||
local prevBitrate=0
|
||||
# remove all but the highest bitrate segments
|
||||
for segment in "${sortedSegments[@]}"; do
|
||||
test ${removeSegments} -eq 0 && break
|
||||
local file currBitrate
|
||||
IFS=: read -r _ file <<<"${segment}"
|
||||
currBitrate="$(get_avg_bitrate "${file}")" || return 1
|
||||
|
||||
if [[ ${currBitrate} -lt ${prevBitrate} ]]; then
|
||||
echo_fail "${file} is not a higher bitrate than previous"
|
||||
return 1
|
||||
fi
|
||||
prevBitrate=${currBitrate}
|
||||
if [[ ${currBitrate} -lt ${prevBitrate} ]]; then
|
||||
echo_fail "${file} is not a higher bitrate than previous"
|
||||
return 1
|
||||
fi
|
||||
prevBitrate=${currBitrate}
|
||||
|
||||
rm "${file}" || return 1
|
||||
removeSegments=$((removeSegments - 1))
|
||||
done
|
||||
rm "${file}" || return 1
|
||||
removeSegments=$((removeSegments - 1))
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
efg_encode() {
|
||||
echo -n >"${GRAIN_LOG}"
|
||||
for vid in "${EFG_DIR}/"*.mkv; do
|
||||
echo "file: ${vid}" >>"${GRAIN_LOG}"
|
||||
for ((grain = LOW; grain <= HIGH; grain += STEP)); do
|
||||
local file="$(bash_basename "${vid}")"
|
||||
local out="${EFG_DIR}/grain-${grain}-${file}"
|
||||
echo_info "encoding ${file} with grain ${grain}"
|
||||
echo_if_fail encode -P 10 -g ${grain} -i "${vid}" "${out}"
|
||||
echo -e "\tgrain: ${grain}, bitrate: $(get_avg_bitrate "${out}")" >>"${GRAIN_LOG}"
|
||||
rm "${out}"
|
||||
done
|
||||
done
|
||||
local grainLogWIP="${GRAIN_LOG}.wip"
|
||||
echo -n >"${grainLogWIP}"
|
||||
for vid in "${EFG_DIR}/"*.mkv; do
|
||||
echo "file: ${vid}" >>"${grainLogWIP}"
|
||||
for ((grain = LOW; grain <= HIGH; grain += STEP)); do
|
||||
local file="$(bash_basename "${vid}")"
|
||||
local out="${EFG_DIR}/grain-${grain}-${file}"
|
||||
echo_info "encoding ${file} with grain ${grain}"
|
||||
echo_if_fail encode -P "${PRESET}" -g ${grain} -i "${vid}" "${out}" || return 1
|
||||
echo -e "\tgrain: ${grain}, bitrate: $(get_avg_bitrate "${out}")" >>"${grainLogWIP}"
|
||||
rm "${out}" || return 1
|
||||
done
|
||||
# remove segment once complete
|
||||
rm "${vid}" || return 1
|
||||
done
|
||||
|
||||
less "${GRAIN_LOG}"
|
||||
# atomic move of grain log
|
||||
mv "${grainLogWIP}" "${GRAIN_LOG}" || return 1
|
||||
|
||||
echo "$(<"${GRAIN_LOG}")"
|
||||
}
|
||||
|
||||
efg_plot() {
|
||||
declare -A normalizedBitrateSums=()
|
||||
local referenceBitrate=''
|
||||
local setNewReference=''
|
||||
declare -A normalizedBitrateSums=()
|
||||
local referenceBitrate=''
|
||||
local setNewReference=''
|
||||
|
||||
while read -r line; do
|
||||
local noWhite="${line// /}"
|
||||
# new file, reset logic
|
||||
if line_starts_with "${noWhite}" 'file:'; then
|
||||
setNewReference=true
|
||||
continue
|
||||
fi
|
||||
while read -r line; do
|
||||
local noWhite="${line// /}"
|
||||
# new file, reset logic
|
||||
if line_starts_with "${noWhite}" 'file:'; then
|
||||
setNewReference=true
|
||||
continue
|
||||
fi
|
||||
|
||||
IFS=',' read -r grainText bitrateText <<<"${noWhite}"
|
||||
IFS=':' read -r _ grain <<<"${grainText}"
|
||||
IFS=':' read -r _ bitrate <<<"${bitrateText}"
|
||||
if [[ ${setNewReference} == true ]]; then
|
||||
referenceBitrate="${bitrate}"
|
||||
setNewReference=false
|
||||
fi
|
||||
# bash doesn't support floats, so scale up by 10000
|
||||
local normBitrate=$((bitrate * 10000 / referenceBitrate))
|
||||
local currSumBitrate=${normalizedBitrateSums[${grain}]}
|
||||
normalizedBitrateSums[${grain}]=$((normBitrate + currSumBitrate))
|
||||
setNewReference=false
|
||||
done <"${GRAIN_LOG}"
|
||||
IFS=',' read -r grainText bitrateText <<<"${noWhite}"
|
||||
IFS=':' read -r _ grain <<<"${grainText}"
|
||||
IFS=':' read -r _ bitrate <<<"${bitrateText}"
|
||||
if [[ ${setNewReference} == true ]]; then
|
||||
referenceBitrate="${bitrate}"
|
||||
setNewReference=false
|
||||
fi
|
||||
# bash doesn't support floats, so scale up by 10000
|
||||
local normBitrate=$((bitrate * 10000 / referenceBitrate))
|
||||
local currSumBitrate=${normalizedBitrateSums[${grain}]}
|
||||
normalizedBitrateSums[${grain}]=$((normBitrate + currSumBitrate))
|
||||
setNewReference=false
|
||||
done <"${GRAIN_LOG}"
|
||||
|
||||
# create grain:average plot file
|
||||
local plotFile="${EFG_DIR}/plot.dat"
|
||||
echo -n >"${plotFile}"
|
||||
for ((grain = LOW; grain <= HIGH; grain += STEP)); do
|
||||
local sum=${normalizedBitrateSums[${grain}]}
|
||||
local avg=$((sum / ENCODE_SEGMENTS))
|
||||
echo -e "${grain}\t${avg}" >>"${plotFile}"
|
||||
done
|
||||
# create grain:average plot file
|
||||
local plotFile="${EFG_DIR}/plot.dat"
|
||||
echo -n >"${plotFile}"
|
||||
for ((grain = LOW; grain <= HIGH; grain += STEP)); do
|
||||
local sum=${normalizedBitrateSums[${grain}]}
|
||||
local avg=$((sum / ENCODE_SEGMENTS))
|
||||
echo -e "${grain}\t${avg}" >>"${plotFile}"
|
||||
done
|
||||
|
||||
# plot data
|
||||
# run subprocess for bash COLUMNS/LINES
|
||||
shopt -s checkwinsize
|
||||
(true)
|
||||
gnuplot -p -e "\
|
||||
# plot data
|
||||
# run subprocess for bash COLUMNS/LINES
|
||||
shopt -s checkwinsize
|
||||
(true)
|
||||
gnuplot -p -e "\
|
||||
set terminal dumb size ${COLUMNS}, ${LINES}; \
|
||||
set autoscale; \
|
||||
set style line 1 \
|
||||
@@ -234,31 +248,37 @@ linecolor rgb '#0060ad' \
|
||||
linetype 1 linewidth 2 \
|
||||
pointtype 7 pointsize 1.5; \
|
||||
plot \"${plotFile}\" with linespoints linestyle 1" | less
|
||||
echo_info "grain log: ${GRAIN_LOG}"
|
||||
echo_info "grain log: ${GRAIN_LOG}"
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('efg')
|
||||
# shellcheck disable=SC2034
|
||||
FB_FUNC_DESCS['efg']='estimate the film grain of a given file'
|
||||
efg() {
|
||||
EFG_DIR="${TMP_DIR}/efg"
|
||||
# encode N highest-bitrate segments
|
||||
ENCODE_SEGMENTS=5
|
||||
EFG_DIR="${TMP_DIR}/efg"
|
||||
# encode N highest-bitrate segments
|
||||
ENCODE_SEGMENTS=5
|
||||
|
||||
set_efg_opts "$@" || return 1
|
||||
test -d "${EFG_DIR}" || mkdir "${EFG_DIR}"
|
||||
set_efg_opts "$@"
|
||||
local ret=$?
|
||||
if [[ ${ret} -eq ${FUNC_EXIT_SUCCESS} ]]; then
|
||||
return 0
|
||||
elif [[ ${ret} -ne 0 ]]; then
|
||||
return ${ret}
|
||||
fi
|
||||
ensure_dir "${EFG_DIR}"
|
||||
|
||||
GRAIN_LOG="${EFG_DIR}/${LOW}-${STEP}-${HIGH}-grains.txt"
|
||||
GRAIN_LOG="${EFG_DIR}/${LOW}-${STEP}-${HIGH}-grains.txt"
|
||||
|
||||
if [[ ${PLOT} == true && -f ${GRAIN_LOG} ]]; then
|
||||
efg_plot
|
||||
return $?
|
||||
fi
|
||||
if [[ ${PLOT} == true && -f ${GRAIN_LOG} ]]; then
|
||||
efg_plot
|
||||
return $?
|
||||
fi
|
||||
|
||||
efg_segment || return 1
|
||||
efg_encode || return 1
|
||||
efg_segment || return 1
|
||||
efg_encode || return 1
|
||||
|
||||
if [[ ${PLOT} == true && -f ${GRAIN_LOG} ]]; then
|
||||
efg_plot || return 1
|
||||
fi
|
||||
if [[ ${PLOT} == true && -f ${GRAIN_LOG} ]]; then
|
||||
efg_plot || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
826
lib/encode.sh
826
lib/encode.sh
@@ -2,475 +2,483 @@
|
||||
|
||||
# sets unmapStreams
|
||||
set_unmap_streams() {
|
||||
local file="$1"
|
||||
local unmapFilter='bin_data|jpeg|png'
|
||||
local streamsStr
|
||||
unmapStreams=()
|
||||
streamsStr="$(get_num_streams "${file}")" || return 1
|
||||
mapfile -t streams <<<"${streamsStr}" || return 1
|
||||
for stream in "${streams[@]}"; do
|
||||
if [[ "$(get_stream_codec "${file}" "${stream}")" =~ ${unmapFilter} ]]; then
|
||||
unmapStreams+=("-map" "-0:${stream}")
|
||||
fi
|
||||
done
|
||||
local file="$1"
|
||||
local unmapFilter='bin_data|jpeg|png'
|
||||
local streamsStr
|
||||
unmapStreams=()
|
||||
streamsStr="$(get_num_streams "${file}")" || return 1
|
||||
mapfile -t streams <<<"${streamsStr}" || return 1
|
||||
for stream in "${streams[@]}"; do
|
||||
if [[ "$(get_stream_codec "${file}" "${stream}")" =~ ${unmapFilter} ]]; then
|
||||
unmapStreams+=("-map" "-0:${stream}")
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# sets audioParams
|
||||
set_audio_params() {
|
||||
local file="$1"
|
||||
local videoLang
|
||||
audioParams=()
|
||||
videoLang="$(get_stream_lang "${file}" 'v:0')" || return 1
|
||||
for stream in $(get_num_streams "${file}" 'a'); do
|
||||
local numChannels codec lang
|
||||
numChannels="$(get_num_audio_channels "${file}" "${stream}")" || return 1
|
||||
if [[ ${numChannels} == '' ]]; then
|
||||
echo_fail "could not obtain channel count for stream ${stream}"
|
||||
return 1
|
||||
fi
|
||||
local channelBitrate=$((numChannels * 64))
|
||||
codec="$(get_stream_codec "${file}" "${stream}")" || return 1
|
||||
lang="$(get_stream_lang "${file}" "${stream}")" || return 1
|
||||
if [[ ${videoLang} != '' && ${videoLang} != "${lang}" ]]; then
|
||||
audioParams+=(
|
||||
'-map'
|
||||
"-0:${stream}"
|
||||
)
|
||||
elif [[ ${codec} == 'opus' ]]; then
|
||||
audioParams+=(
|
||||
"-c:${OUTPUT_INDEX}"
|
||||
"copy"
|
||||
)
|
||||
OUTPUT_INDEX=$((OUTPUT_INDEX + 1))
|
||||
else
|
||||
audioParams+=(
|
||||
"-filter:${OUTPUT_INDEX}"
|
||||
"aformat=channel_layouts=7.1|5.1|stereo|mono"
|
||||
"-c:${OUTPUT_INDEX}"
|
||||
"libopus"
|
||||
"-b:${OUTPUT_INDEX}"
|
||||
"${channelBitrate}k"
|
||||
)
|
||||
OUTPUT_INDEX=$((OUTPUT_INDEX + 1))
|
||||
fi
|
||||
done
|
||||
local file="$1"
|
||||
local videoLang
|
||||
audioParams=()
|
||||
videoLang="$(get_stream_lang "${file}" 'v:0')" || return 1
|
||||
for stream in $(get_num_streams "${file}" 'a'); do
|
||||
local numChannels codec lang
|
||||
numChannels="$(get_num_audio_channels "${file}" "${stream}")" || return 1
|
||||
if [[ ${numChannels} == '' ]]; then
|
||||
echo_fail "could not obtain channel count for stream ${stream}"
|
||||
return 1
|
||||
fi
|
||||
local channelBitrate=$((numChannels * 64))
|
||||
codec="$(get_stream_codec "${file}" "${stream}")" || return 1
|
||||
lang="$(get_stream_lang "${file}" "${stream}")" || return 1
|
||||
if [[ ${videoLang} != '' && ${videoLang} != "${lang}" ]]; then
|
||||
audioParams+=(
|
||||
'-map'
|
||||
"-0:${stream}"
|
||||
)
|
||||
elif [[ ${codec} == 'opus' ]]; then
|
||||
audioParams+=(
|
||||
"-c:${OUTPUT_INDEX}"
|
||||
"copy"
|
||||
)
|
||||
OUTPUT_INDEX=$((OUTPUT_INDEX + 1))
|
||||
else
|
||||
audioParams+=(
|
||||
"-filter:${OUTPUT_INDEX}"
|
||||
"aformat=channel_layouts=7.1|5.1|stereo|mono"
|
||||
"-c:${OUTPUT_INDEX}"
|
||||
"libopus"
|
||||
"-b:${OUTPUT_INDEX}"
|
||||
"${channelBitrate}k"
|
||||
)
|
||||
OUTPUT_INDEX=$((OUTPUT_INDEX + 1))
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# sets subtitleParams
|
||||
set_subtitle_params() {
|
||||
local file="$1"
|
||||
local convertCodec='eia_608'
|
||||
local keepLang='eng'
|
||||
local file="$1"
|
||||
local convertCodec='eia_608'
|
||||
local keepLang='eng'
|
||||
|
||||
local defaultTextCodec
|
||||
if [[ ${SAME_CONTAINER} == false && ${FILE_EXT} == 'mkv' ]]; then
|
||||
defaultTextCodec='srt'
|
||||
convertCodec+='|mov_text'
|
||||
else
|
||||
defaultTextCodec='mov_text'
|
||||
convertCodec+='|srt'
|
||||
fi
|
||||
local defaultTextCodec
|
||||
if [[ ${SAME_CONTAINER} == false && ${FILE_EXT} == 'mkv' ]]; then
|
||||
defaultTextCodec='srt'
|
||||
convertCodec+='|mov_text'
|
||||
else
|
||||
defaultTextCodec='mov_text'
|
||||
convertCodec+='|srt'
|
||||
fi
|
||||
|
||||
subtitleParams=()
|
||||
for stream in $(get_num_streams "${file}" 's'); do
|
||||
local codec lang
|
||||
codec="$(get_stream_codec "${file}" "${stream}")" || return 1
|
||||
lang="$(get_stream_lang "${file}" "${stream}")" || return 1
|
||||
if [[ ${lang} != '' && ${keepLang} != "${lang}" ]]; then
|
||||
subtitleParams+=(
|
||||
'-map'
|
||||
"-0:${stream}"
|
||||
)
|
||||
elif [[ ${codec} =~ ${convertCodec} ]]; then
|
||||
subtitleParams+=("-c:${OUTPUT_INDEX}" "${defaultTextCodec}")
|
||||
OUTPUT_INDEX=$((OUTPUT_INDEX + 1))
|
||||
fi
|
||||
done
|
||||
subtitleParams=()
|
||||
for stream in $(get_num_streams "${file}" 's'); do
|
||||
local codec lang
|
||||
codec="$(get_stream_codec "${file}" "${stream}")" || return 1
|
||||
lang="$(get_stream_lang "${file}" "${stream}")" || return 1
|
||||
if [[ ${lang} != '' && ${keepLang} != "${lang}" ]]; then
|
||||
subtitleParams+=(
|
||||
'-map'
|
||||
"-0:${stream}"
|
||||
)
|
||||
elif [[ ${codec} =~ ${convertCodec} ]]; then
|
||||
subtitleParams+=("-c:${OUTPUT_INDEX}" "${defaultTextCodec}")
|
||||
OUTPUT_INDEX=$((OUTPUT_INDEX + 1))
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
get_encode_versions() {
|
||||
action="${1:-}"
|
||||
action="${1:-}"
|
||||
|
||||
encodeVersion="encode=$(git -C "${REPO_DIR}" rev-parse --short HEAD)"
|
||||
ffmpegVersion=''
|
||||
videoEncVersion=''
|
||||
audioEncVersion=''
|
||||
encodeVersion="encode=$(git -C "${REPO_DIR}" rev-parse --short HEAD)"
|
||||
ffmpegVersion=''
|
||||
videoEncVersion=''
|
||||
audioEncVersion=''
|
||||
|
||||
# shellcheck disable=SC2155
|
||||
local output="$(ffmpeg -version 2>&1)"
|
||||
while read -r line; do
|
||||
if line_contains "${line}" 'ffmpeg='; then
|
||||
ffmpegVersion="${line}"
|
||||
elif line_contains "${line}" 'libsvtav1_psy=' || line_contains "${line}" 'libsvtav1='; then
|
||||
videoEncVersion="${line}"
|
||||
elif line_contains "${line}" 'libopus='; then
|
||||
audioEncVersion="${line}"
|
||||
fi
|
||||
done <<<"${output}"
|
||||
# shellcheck disable=SC2155
|
||||
local output="$(ffmpeg -version 2>&1)"
|
||||
while read -r line; do
|
||||
if line_starts_with "${line}" 'ffmpeg='; then
|
||||
ffmpegVersion="${line}"
|
||||
elif line_starts_with "${line}" 'libsvtav1'; then
|
||||
videoEncVersion="${line}"
|
||||
elif line_starts_with "${line}" 'libopus='; then
|
||||
audioEncVersion="${line}"
|
||||
fi
|
||||
done <<<"${output}"
|
||||
|
||||
local version
|
||||
if [[ ${ffmpegVersion} == '' ]]; then
|
||||
while read -r line; do
|
||||
if line_starts_with "${line}" 'ffmpeg version '; then
|
||||
read -r _ _ version _ <<<"${line}"
|
||||
ffmpegVersion="ffmpeg=${version}"
|
||||
break
|
||||
fi
|
||||
done <<<"${output}"
|
||||
fi
|
||||
local version
|
||||
if [[ ${ffmpegVersion} == '' ]]; then
|
||||
while read -r line; do
|
||||
if line_starts_with "${line}" 'ffmpeg version '; then
|
||||
read -r _ _ version _ <<<"${line}"
|
||||
ffmpegVersion="ffmpeg=${version}"
|
||||
break
|
||||
fi
|
||||
done <<<"${output}"
|
||||
fi
|
||||
|
||||
if [[ ${videoEncVersion} == '' ]]; then
|
||||
version="$(get_pkgconfig_version SvtAv1Enc)"
|
||||
test "${version}" == '' && return 1
|
||||
videoEncVersion="libsvtav1=${version}"
|
||||
fi
|
||||
if [[ ${videoEncVersion} == '' ]]; then
|
||||
version="$(get_pkgconfig_version SvtAv1Enc)"
|
||||
test "${version}" == '' && return 1
|
||||
videoEncVersion="libsvtav1=${version}"
|
||||
fi
|
||||
|
||||
if [[ ${audioEncVersion} == '' ]]; then
|
||||
version="$(get_pkgconfig_version opus)"
|
||||
test "${version}" == '' && return 1
|
||||
audioEncVersion="libopus=${version}"
|
||||
fi
|
||||
if [[ ${audioEncVersion} == '' ]]; then
|
||||
version="$(get_pkgconfig_version opus)"
|
||||
test "${version}" == '' && return 1
|
||||
audioEncVersion="libopus=${version}"
|
||||
fi
|
||||
|
||||
test "${ffmpegVersion}" == '' && return 1
|
||||
test "${videoEncVersion}" == '' && return 1
|
||||
test "${audioEncVersion}" == '' && return 1
|
||||
test "${ffmpegVersion}" == '' && return 1
|
||||
test "${videoEncVersion}" == '' && return 1
|
||||
test "${audioEncVersion}" == '' && return 1
|
||||
|
||||
if [[ ${action} == 'print' ]]; then
|
||||
echo "${encodeVersion}"
|
||||
echo "${ffmpegVersion}"
|
||||
echo "${videoEncVersion}"
|
||||
echo "${audioEncVersion}"
|
||||
fi
|
||||
return 0
|
||||
if [[ ${action} == 'print' ]]; then
|
||||
echo "${encodeVersion}"
|
||||
echo "${ffmpegVersion}"
|
||||
echo "${videoEncVersion}"
|
||||
echo "${audioEncVersion}"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
encode_usage() {
|
||||
echo "encode -i input [options] output"
|
||||
echo -e "\t[-P NUM] set preset (default: ${PRESET})"
|
||||
echo -e "\t[-C NUM] set CRF (default: ${CRF})"
|
||||
echo -e "\t[-g NUM] set film grain for encode"
|
||||
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[-s] use same container as input, default is convert to mkv"
|
||||
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
|
||||
echo "encode -i input [options] output"
|
||||
echo -e "\t[-P NUM] set preset (default: ${PRESET})"
|
||||
echo -e "\t[-C NUM] set CRF (default: ${CRF})"
|
||||
echo -e "\t[-g NUM] set film grain for encode"
|
||||
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[-s] use same container as input, default is convert to mkv"
|
||||
echo -e "\n\t[output] if unset, defaults to \${PWD}/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
|
||||
}
|
||||
|
||||
encode_update() {
|
||||
git -C "${REPO_DIR}" pull
|
||||
git -C "${REPO_DIR}" pull
|
||||
}
|
||||
|
||||
set_encode_opts() {
|
||||
local opts='vi:pcsdg:P:C:uIU'
|
||||
local numOpts=${#opts}
|
||||
# default values
|
||||
PRESET=3
|
||||
CRF=25
|
||||
GRAIN=""
|
||||
CROP=false
|
||||
PRINT_OUT=false
|
||||
DV_TOGGLE=false
|
||||
ENCODE_INSTALL_PATH='/usr/local/bin/encode'
|
||||
SAME_CONTAINER="false"
|
||||
# only using -I/U
|
||||
local minOpt=1
|
||||
# using all + output name
|
||||
local maxOpt=$((numOpts + 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
|
||||
case "${flag}" in
|
||||
u)
|
||||
encode_update
|
||||
exit $?
|
||||
;;
|
||||
I)
|
||||
echo_warn "attempting install"
|
||||
sudo ln -sf "${SCRIPT_DIR}/encode.sh" \
|
||||
"${ENCODE_INSTALL_PATH}" || return 1
|
||||
echo_pass "succesfull install"
|
||||
exit 0
|
||||
;;
|
||||
U)
|
||||
echo_warn "attempting uninstall"
|
||||
sudo rm "${ENCODE_INSTALL_PATH}" || return 1
|
||||
echo_pass "succesfull uninstall"
|
||||
exit 0
|
||||
;;
|
||||
v)
|
||||
get_encode_versions print
|
||||
exit $?
|
||||
;;
|
||||
i)
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo_fail "wrong arguments given"
|
||||
encode_usage
|
||||
return 1
|
||||
fi
|
||||
INPUT="${OPTARG}"
|
||||
optsUsed=$((optsUsed + 2))
|
||||
;;
|
||||
p)
|
||||
PRINT_OUT=true
|
||||
optsUsed=$((optsUsed + 1))
|
||||
;;
|
||||
c)
|
||||
CROP=true
|
||||
optsUsed=$((optsUsed + 1))
|
||||
;;
|
||||
d)
|
||||
DV_TOGGLE=true
|
||||
optsUsed=$((optsUsed + 1))
|
||||
;;
|
||||
s)
|
||||
SAME_CONTAINER=true
|
||||
optsUsed=$((optsUsed + 1))
|
||||
;;
|
||||
g)
|
||||
if ! is_positive_integer "${OPTARG}"; then
|
||||
encode_usage
|
||||
return 1
|
||||
fi
|
||||
GRAIN="film-grain=${OPTARG}:film-grain-denoise=1:"
|
||||
optsUsed=$((optsUsed + 2))
|
||||
;;
|
||||
P)
|
||||
if ! is_positive_integer "${OPTARG}"; then
|
||||
encode_usage
|
||||
return 1
|
||||
fi
|
||||
PRESET="${OPTARG}"
|
||||
optsUsed=$((optsUsed + 2))
|
||||
;;
|
||||
C)
|
||||
if ! is_positive_integer "${OPTARG}" || test ${OPTARG} -gt 63; then
|
||||
echo_fail "${OPTARG} is not a valid CRF value (0-63)"
|
||||
encode_usage
|
||||
exit 1
|
||||
fi
|
||||
CRF="${OPTARG}"
|
||||
OPTS_USED=$((OPTS_USED + 2))
|
||||
;;
|
||||
*)
|
||||
echo_fail "wrong flags given"
|
||||
encode_usage
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
local opts='vi:pcsdg:P:C:uIU'
|
||||
local numOpts=${#opts}
|
||||
# default values
|
||||
PRESET=3
|
||||
CRF=25
|
||||
GRAIN=""
|
||||
CROP=false
|
||||
PRINT_OUT=false
|
||||
DV_TOGGLE=false
|
||||
ENCODE_INSTALL_PATH='/usr/local/bin/encode'
|
||||
SAME_CONTAINER="false"
|
||||
# only using -I/U
|
||||
local minOpt=1
|
||||
# using all + output name
|
||||
local maxOpt=$((numOpts + 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
|
||||
case "${flag}" in
|
||||
u)
|
||||
encode_update || return 1
|
||||
return ${FUNC_EXIT_SUCCESS}
|
||||
;;
|
||||
I)
|
||||
echo_warn "attempting install"
|
||||
sudo ln -sf "${SCRIPT_DIR}/encode.sh" \
|
||||
"${ENCODE_INSTALL_PATH}" || return 1
|
||||
echo_pass "succesfull install"
|
||||
return ${FUNC_EXIT_SUCCESS}
|
||||
;;
|
||||
U)
|
||||
echo_warn "attempting uninstall"
|
||||
sudo rm "${ENCODE_INSTALL_PATH}" || return 1
|
||||
echo_pass "succesfull uninstall"
|
||||
return ${FUNC_EXIT_SUCCESS}
|
||||
;;
|
||||
v)
|
||||
get_encode_versions print || return 1
|
||||
return ${FUNC_EXIT_SUCCESS}
|
||||
;;
|
||||
i)
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo_fail "wrong arguments given"
|
||||
encode_usage
|
||||
return 1
|
||||
fi
|
||||
INPUT="${OPTARG}"
|
||||
optsUsed=$((optsUsed + 2))
|
||||
;;
|
||||
p)
|
||||
PRINT_OUT=true
|
||||
optsUsed=$((optsUsed + 1))
|
||||
;;
|
||||
c)
|
||||
CROP=true
|
||||
optsUsed=$((optsUsed + 1))
|
||||
;;
|
||||
d)
|
||||
DV_TOGGLE=true
|
||||
optsUsed=$((optsUsed + 1))
|
||||
;;
|
||||
s)
|
||||
SAME_CONTAINER=true
|
||||
optsUsed=$((optsUsed + 1))
|
||||
;;
|
||||
g)
|
||||
if ! is_positive_integer "${OPTARG}"; then
|
||||
encode_usage
|
||||
return 1
|
||||
fi
|
||||
GRAIN="film-grain=${OPTARG}:film-grain-denoise=1:"
|
||||
optsUsed=$((optsUsed + 2))
|
||||
;;
|
||||
P)
|
||||
if ! is_positive_integer "${OPTARG}"; then
|
||||
encode_usage
|
||||
return 1
|
||||
fi
|
||||
PRESET="${OPTARG}"
|
||||
optsUsed=$((optsUsed + 2))
|
||||
;;
|
||||
C)
|
||||
if ! is_positive_integer "${OPTARG}" || test ${OPTARG} -gt 63; then
|
||||
echo_fail "${OPTARG} is not a valid CRF value (0-63)"
|
||||
encode_usage
|
||||
return 1
|
||||
fi
|
||||
CRF="${OPTARG}"
|
||||
OPTS_USED=$((OPTS_USED + 2))
|
||||
;;
|
||||
*)
|
||||
echo_fail "wrong flags given"
|
||||
encode_usage
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# allow optional output filename
|
||||
if [[ $(($# - optsUsed)) == 1 ]]; then
|
||||
OUTPUT="${*: -1}"
|
||||
else
|
||||
local basename="$(bash_basename "${INPUT}")"
|
||||
OUTPUT="${HOME}/av1-${basename}"
|
||||
fi
|
||||
# allow optional output filename
|
||||
if [[ $(($# - optsUsed)) == 1 ]]; then
|
||||
OUTPUT="${*: -1}"
|
||||
else
|
||||
local basename="$(bash_basename "${INPUT}")"
|
||||
OUTPUT="${PWD}/av1-${basename}"
|
||||
fi
|
||||
|
||||
# use same container for output
|
||||
if [[ $SAME_CONTAINER == "true" ]]; then
|
||||
local fileFormat
|
||||
fileFormat="$(get_file_format "${INPUT}")" || return 1
|
||||
FILE_EXT=''
|
||||
if [[ ${fileFormat} == 'MPEG-4' ]]; then
|
||||
FILE_EXT='mp4'
|
||||
elif [[ ${fileFormat} == 'Matroska' ]]; then
|
||||
FILE_EXT='mkv'
|
||||
else
|
||||
echo "unrecognized input format"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
FILE_EXT="mkv"
|
||||
fi
|
||||
OUTPUT="${OUTPUT%.*}"
|
||||
OUTPUT+=".${FILE_EXT}"
|
||||
# use same container for output
|
||||
if [[ $SAME_CONTAINER == "true" ]]; then
|
||||
local fileFormat
|
||||
fileFormat="$(get_file_format "${INPUT}")" || return 1
|
||||
FILE_EXT=''
|
||||
if [[ ${fileFormat} == 'MPEG-4' ]]; then
|
||||
FILE_EXT='mp4'
|
||||
elif [[ ${fileFormat} == 'Matroska' ]]; then
|
||||
FILE_EXT='mkv'
|
||||
else
|
||||
echo "unrecognized input format"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
FILE_EXT="mkv"
|
||||
fi
|
||||
OUTPUT="${OUTPUT%.*}"
|
||||
OUTPUT+=".${FILE_EXT}"
|
||||
|
||||
if [[ ! -f ${INPUT} ]]; then
|
||||
echo "${INPUT} does not exist"
|
||||
encode_usage
|
||||
return 1
|
||||
fi
|
||||
if [[ ! -f ${INPUT} ]]; then
|
||||
echo "${INPUT} does not exist"
|
||||
encode_usage
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo_info "INPUT: ${INPUT}"
|
||||
echo_info "GRAIN: ${GRAIN}"
|
||||
echo_info "OUTPUT: ${OUTPUT}"
|
||||
echo
|
||||
echo
|
||||
echo_info "INPUT: ${INPUT}"
|
||||
echo_info "GRAIN: ${GRAIN}"
|
||||
echo_info "OUTPUT: ${OUTPUT}"
|
||||
echo
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
# shellcheck disable=SC2155
|
||||
# shellcheck disable=SC2016
|
||||
gen_encode_script() {
|
||||
local genScript="${TMP_DIR}/$(bash_basename "${OUTPUT}").sh"
|
||||
local genScript="${TMP_DIR}/$(bash_basename "${OUTPUT}").sh"
|
||||
|
||||
# global output index number to increment
|
||||
OUTPUT_INDEX=0
|
||||
# global output index number to increment
|
||||
OUTPUT_INDEX=0
|
||||
|
||||
# single string params
|
||||
local params=(
|
||||
INPUT
|
||||
OUTPUT
|
||||
PRESET
|
||||
CRF
|
||||
crop
|
||||
encodeVersion
|
||||
ffmpegVersion
|
||||
videoEncVersion
|
||||
audioEncVersion
|
||||
svtAv1Params
|
||||
)
|
||||
local crop=''
|
||||
if [[ $CROP == "true" ]]; then
|
||||
crop="$(get_crop "${INPUT}")" || return 1
|
||||
fi
|
||||
# single string params
|
||||
local params=(
|
||||
INPUT
|
||||
OUTPUT
|
||||
PRESET
|
||||
CRF
|
||||
crop
|
||||
encodeVersion
|
||||
ffmpegVersion
|
||||
videoEncVersion
|
||||
audioEncVersion
|
||||
svtAv1Params
|
||||
)
|
||||
local crop=''
|
||||
if [[ $CROP == "true" ]]; then
|
||||
crop="$(get_crop "${INPUT}")" || return 1
|
||||
fi
|
||||
|
||||
svtAv1ParamsArr=(
|
||||
"tune=0"
|
||||
"complex-hvs=1"
|
||||
"spy-rd=1"
|
||||
"psy-rd=1"
|
||||
"sharpness=3"
|
||||
"enable-overlays=1"
|
||||
"scd=1"
|
||||
"fast-decode=1"
|
||||
"enable-variance-boost=1"
|
||||
"enable-qm=1"
|
||||
"qm-min=4"
|
||||
"qm-max=15"
|
||||
)
|
||||
IFS=':'
|
||||
local svtAv1Params="${GRAIN}${svtAv1ParamsArr[*]}"
|
||||
unset IFS
|
||||
svtAv1ParamsArr=(
|
||||
"tune=0"
|
||||
"complex-hvs=1"
|
||||
"spy-rd=1"
|
||||
"psy-rd=1"
|
||||
"sharpness=3"
|
||||
"enable-overlays=0"
|
||||
"hbd-mds=1"
|
||||
"scd=1"
|
||||
"fast-decode=1"
|
||||
"enable-variance-boost=1"
|
||||
"enable-qm=1"
|
||||
"chroma-qm-min-10"
|
||||
"qm-min=4"
|
||||
"qm-max=15"
|
||||
)
|
||||
IFS=':'
|
||||
local svtAv1Params="${GRAIN}${svtAv1ParamsArr[*]}"
|
||||
unset IFS
|
||||
|
||||
# arrays
|
||||
local arrays=(
|
||||
unmapStreams
|
||||
audioParams
|
||||
videoParams
|
||||
metadata
|
||||
subtitleParams
|
||||
ffmpegParams
|
||||
)
|
||||
local videoParams=(
|
||||
"-crf" '${CRF}' "-preset" '${PRESET}' "-g" "240"
|
||||
)
|
||||
local ffmpegParams=(
|
||||
'-hide_banner'
|
||||
'-i' '${INPUT}'
|
||||
'-y'
|
||||
'-map' '0'
|
||||
'-c:s' 'copy'
|
||||
)
|
||||
# arrays
|
||||
local arrays=(
|
||||
unmapStreams
|
||||
audioParams
|
||||
videoParams
|
||||
metadata
|
||||
subtitleParams
|
||||
ffmpegParams
|
||||
)
|
||||
local videoParams=(
|
||||
"-crf" '${CRF}' "-preset" '${PRESET}' "-g" "240"
|
||||
)
|
||||
local ffmpegParams=(
|
||||
'-hide_banner'
|
||||
'-i' '${INPUT}'
|
||||
'-y'
|
||||
'-map' '0'
|
||||
'-c:s' 'copy'
|
||||
)
|
||||
|
||||
# set video params
|
||||
local inputVideoCodec="$(get_stream_codec "${INPUT}" 'v:0')"
|
||||
if [[ ${inputVideoCodec} == 'av1' ]]; then
|
||||
ffmpegParams+=(
|
||||
"-c:v:${OUTPUT_INDEX}" 'copy'
|
||||
)
|
||||
else
|
||||
ffmpegParams+=(
|
||||
'-pix_fmt' 'yuv420p10le'
|
||||
"-c:v:${OUTPUT_INDEX}" 'libsvtav1' '${videoParams[@]}'
|
||||
'-svtav1-params' '${svtAv1Params}'
|
||||
)
|
||||
fi
|
||||
OUTPUT_INDEX=$((OUTPUT_INDEX + 1))
|
||||
# set video params
|
||||
local inputVideoCodec="$(get_stream_codec "${INPUT}" 'v:0')"
|
||||
if [[ ${inputVideoCodec} == 'av1' ]]; then
|
||||
ffmpegParams+=(
|
||||
"-c:v:${OUTPUT_INDEX}" 'copy'
|
||||
)
|
||||
else
|
||||
ffmpegParams+=(
|
||||
'-pix_fmt' 'yuv420p10le'
|
||||
"-c:v:${OUTPUT_INDEX}" 'libsvtav1' '${videoParams[@]}'
|
||||
'-svtav1-params' '${svtAv1Params}'
|
||||
)
|
||||
fi
|
||||
OUTPUT_INDEX=$((OUTPUT_INDEX + 1))
|
||||
|
||||
# these values may be empty
|
||||
local unmapStr audioParamsStr subtitleParamsStr
|
||||
set_unmap_streams "${INPUT}" || return 1
|
||||
set_audio_params "${INPUT}" || return 1
|
||||
set_subtitle_params "${INPUT}" || return 1
|
||||
# these values may be empty
|
||||
local unmapStr audioParamsStr subtitleParamsStr
|
||||
set_unmap_streams "${INPUT}" || return 1
|
||||
set_audio_params "${INPUT}" || return 1
|
||||
set_subtitle_params "${INPUT}" || return 1
|
||||
|
||||
if [[ ${unmapStreams[*]} != '' ]]; then
|
||||
ffmpegParams+=('${unmapStreams[@]}')
|
||||
fi
|
||||
if [[ ${unmapStreams[*]} != '' ]]; then
|
||||
ffmpegParams+=('${unmapStreams[@]}')
|
||||
fi
|
||||
|
||||
if [[ ${audioParams[*]} != '' ]]; then
|
||||
ffmpegParams+=('${audioParams[@]}')
|
||||
fi
|
||||
if [[ ${audioParams[*]} != '' ]]; then
|
||||
ffmpegParams+=('${audioParams[@]}')
|
||||
fi
|
||||
|
||||
if [[ ${subtitleParams[*]} != '' ]]; then
|
||||
ffmpegParams+=('${subtitleParams[@]}')
|
||||
fi
|
||||
if [[ ${subtitleParams[*]} != '' ]]; then
|
||||
ffmpegParams+=('${subtitleParams[@]}')
|
||||
fi
|
||||
|
||||
if [[ ${crop} != '' ]]; then
|
||||
ffmpegParams+=('-vf' '${crop}')
|
||||
fi
|
||||
if [[ ${crop} != '' ]]; then
|
||||
ffmpegParams+=('-vf' '${crop}')
|
||||
fi
|
||||
|
||||
get_encode_versions || return 1
|
||||
local metadata=(
|
||||
'-metadata' '${encodeVersion}'
|
||||
'-metadata' '${ffmpegVersion}'
|
||||
'-metadata' '${videoEncVersion}'
|
||||
'-metadata' '${audioEncVersion}'
|
||||
'-metadata' 'svtav1_params=${svtAv1Params}'
|
||||
'-metadata' 'video_params=${videoParams[*]}'
|
||||
)
|
||||
ffmpegParams+=('${metadata[@]}')
|
||||
get_encode_versions || return 1
|
||||
local metadata=(
|
||||
'-metadata' '${encodeVersion}'
|
||||
'-metadata' '${ffmpegVersion}'
|
||||
'-metadata' '${videoEncVersion}'
|
||||
'-metadata' '${audioEncVersion}'
|
||||
'-metadata' 'svtav1_params=${svtAv1Params}'
|
||||
'-metadata' 'video_params=${videoParams[*]}'
|
||||
)
|
||||
ffmpegParams+=('${metadata[@]}')
|
||||
|
||||
{
|
||||
echo '#!/usr/bin/env bash'
|
||||
echo
|
||||
{
|
||||
echo '#!/usr/bin/env bash'
|
||||
echo
|
||||
|
||||
# add normal params
|
||||
for param in "${params[@]}"; do
|
||||
declare -n value="${param}"
|
||||
if [[ ${value} != '' ]]; then
|
||||
echo "${param}=\"${value[*]}\""
|
||||
fi
|
||||
done
|
||||
for arrName in "${arrays[@]}"; do
|
||||
declare -n arr="${arrName}"
|
||||
if [[ -v arr ]]; then
|
||||
echo "${arrName}=("
|
||||
printf '\t"%s"\n' "${arr[@]}"
|
||||
echo ')'
|
||||
fi
|
||||
done
|
||||
# add normal params
|
||||
for param in "${params[@]}"; do
|
||||
declare -n value="${param}"
|
||||
if [[ ${value} != '' ]]; then
|
||||
echo "${param}=\"${value[*]}\""
|
||||
fi
|
||||
done
|
||||
for arrName in "${arrays[@]}"; do
|
||||
declare -n arr="${arrName}"
|
||||
if [[ -v arr ]]; then
|
||||
echo "${arrName}=("
|
||||
printf '\t"%s"\n' "${arr[@]}"
|
||||
echo ')'
|
||||
fi
|
||||
done
|
||||
|
||||
# actually do ffmpeg commmand
|
||||
echo
|
||||
if [[ ${DV_TOGGLE} == true ]]; then
|
||||
echo 'ffmpeg "${ffmpegParams[@]}" -dolbyvision 1 "${OUTPUT}" || \'
|
||||
fi
|
||||
echo 'ffmpeg "${ffmpegParams[@]}" -dolbyvision 0 "${OUTPUT}" || exit 1'
|
||||
# actually do ffmpeg commmand
|
||||
echo
|
||||
if [[ ${DV_TOGGLE} == true ]]; then
|
||||
echo 'ffmpeg "${ffmpegParams[@]}" -dolbyvision 1 "${OUTPUT}" || \'
|
||||
fi
|
||||
echo 'ffmpeg "${ffmpegParams[@]}" -dolbyvision 0 "${OUTPUT}" || exit 1'
|
||||
|
||||
# track-stats and clear title
|
||||
if [[ ${FILE_EXT} == 'mkv' ]]; then
|
||||
{
|
||||
echo
|
||||
echo 'mkvpropedit "${OUTPUT}" --add-track-statistics-tags'
|
||||
echo 'mkvpropedit "${OUTPUT}" --edit info --set "title="'
|
||||
}
|
||||
fi
|
||||
# track-stats and clear title
|
||||
if [[ ${FILE_EXT} == 'mkv' ]] && has_cmd mkvpropedit; then
|
||||
{
|
||||
echo
|
||||
echo 'mkvpropedit "${OUTPUT}" --add-track-statistics-tags'
|
||||
echo 'mkvpropedit "${OUTPUT}" --edit info --set "title="'
|
||||
}
|
||||
fi
|
||||
|
||||
echo
|
||||
} >"${genScript}"
|
||||
echo
|
||||
} >"${genScript}"
|
||||
|
||||
if [[ ${PRINT_OUT} == true ]]; then
|
||||
echo_info "${genScript} contents:"
|
||||
echo "$(<"${genScript}")"
|
||||
else
|
||||
bash -x "${genScript}" || return 1
|
||||
rm "${genScript}"
|
||||
fi
|
||||
if [[ ${PRINT_OUT} == true ]]; then
|
||||
echo_info "${genScript} contents:"
|
||||
echo "$(<"${genScript}")"
|
||||
else
|
||||
bash -x "${genScript}" || return 1
|
||||
rm "${genScript}"
|
||||
fi
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('encode')
|
||||
# shellcheck disable=SC2034
|
||||
FB_FUNC_DESCS['encode']='encode a file using libsvtav1_psy and libopus'
|
||||
encode() {
|
||||
set_encode_opts "$@" || return 1
|
||||
gen_encode_script || return 1
|
||||
set_encode_opts "$@"
|
||||
local ret=$?
|
||||
if [[ ${ret} -eq ${FUNC_EXIT_SUCCESS} ]]; then
|
||||
return 0
|
||||
elif [[ ${ret} -ne 0 ]]; then
|
||||
return ${ret}
|
||||
fi
|
||||
gen_encode_script || return 1
|
||||
}
|
||||
|
||||
237
lib/ffmpeg.sh
237
lib/ffmpeg.sh
@@ -1,128 +1,169 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
get_duration() {
|
||||
local file="$1"
|
||||
ffprobe \
|
||||
-v error \
|
||||
-show_entries format=duration \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
local file="$1"
|
||||
ffprobe \
|
||||
-v error \
|
||||
-show_entries format=duration \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
}
|
||||
|
||||
get_avg_bitrate() {
|
||||
local file="$1"
|
||||
ffprobe \
|
||||
-v error \
|
||||
-select_streams v:0 \
|
||||
-show_entries format=bit_rate \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
local file="$1"
|
||||
ffprobe \
|
||||
-v error \
|
||||
-select_streams v:0 \
|
||||
-show_entries format=bit_rate \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
}
|
||||
|
||||
split_video() {
|
||||
local file="$1"
|
||||
local start="$2"
|
||||
local time="$3"
|
||||
local out="$4"
|
||||
ffmpeg \
|
||||
-ss "${start}" \
|
||||
-i "${file}" \
|
||||
-hide_banner \
|
||||
-loglevel error \
|
||||
-t "${time}" \
|
||||
-map 0:v \
|
||||
-reset_timestamps 1 \
|
||||
-c copy \
|
||||
"${out}"
|
||||
local file="$1"
|
||||
local start="$2"
|
||||
local time="$3"
|
||||
local out="$4"
|
||||
ffmpeg \
|
||||
-ss "${start}" \
|
||||
-i "${file}" \
|
||||
-hide_banner \
|
||||
-loglevel error \
|
||||
-t "${time}" \
|
||||
-map 0:v \
|
||||
-reset_timestamps 1 \
|
||||
-c copy \
|
||||
"${out}"
|
||||
}
|
||||
|
||||
get_crop() {
|
||||
local file="$1"
|
||||
local duration
|
||||
duration="$(get_duration "${file}")" || return 1
|
||||
# don't care about decimal points
|
||||
IFS='.' read -r duration _ <<<"${duration}"
|
||||
# get crop value for first half of input
|
||||
local timeEnc=$((duration / 2))
|
||||
ffmpeg \
|
||||
-y \
|
||||
-hide_banner \
|
||||
-ss 0 \
|
||||
-discard 'nokey' \
|
||||
-i "${file}" \
|
||||
-t "${timeEnc}" \
|
||||
-map '0:v:0' \
|
||||
-filter:v:0 'cropdetect=limit=100:round=16:skip=2:reset_count=0' \
|
||||
-codec:v 'wrapped_avframe' \
|
||||
-f 'null' '/dev/null' 2>&1 |
|
||||
grep -o crop=.* |
|
||||
sort -bh |
|
||||
uniq -c |
|
||||
sort -bh |
|
||||
tail -n1 |
|
||||
grep -o "crop=.*"
|
||||
local file="$1"
|
||||
local duration
|
||||
duration="$(get_duration "${file}")" || return 1
|
||||
# don't care about decimal points
|
||||
IFS='.' read -r duration _ <<<"${duration}"
|
||||
# get crop value for first half of input
|
||||
local timeEnc=$((duration / 2))
|
||||
ffmpeg \
|
||||
-y \
|
||||
-hide_banner \
|
||||
-ss 0 \
|
||||
-discard 'nokey' \
|
||||
-i "${file}" \
|
||||
-t "${timeEnc}" \
|
||||
-map '0:v:0' \
|
||||
-filter:v:0 'cropdetect=limit=100:round=16:skip=2:reset_count=0' \
|
||||
-codec:v 'wrapped_avframe' \
|
||||
-f 'null' '/dev/null' 2>&1 |
|
||||
grep -o crop=.* |
|
||||
sort -bh |
|
||||
uniq -c |
|
||||
sort -bh |
|
||||
tail -n1 |
|
||||
grep -o "crop=.*"
|
||||
}
|
||||
|
||||
get_stream_codec() {
|
||||
local file="$1"
|
||||
local stream="$2"
|
||||
ffprobe \
|
||||
-v error \
|
||||
-select_streams "${stream}" \
|
||||
-show_entries stream=codec_name \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
local file="$1"
|
||||
local stream="$2"
|
||||
ffprobe \
|
||||
-v error \
|
||||
-select_streams "${stream}" \
|
||||
-show_entries stream=codec_name \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
}
|
||||
|
||||
get_file_format() {
|
||||
local file="$1"
|
||||
local probe
|
||||
probe="$(ffprobe \
|
||||
-v error \
|
||||
-show_entries format=format_name \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}")" || return 1
|
||||
if line_contains "${probe}" 'matroska'; then
|
||||
echo mkv
|
||||
else
|
||||
echo mp4
|
||||
fi
|
||||
local file="$1"
|
||||
local probe
|
||||
probe="$(ffprobe \
|
||||
-v error \
|
||||
-show_entries format=format_name \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}")" || return 1
|
||||
if line_contains "${probe}" 'matroska'; then
|
||||
echo mkv
|
||||
else
|
||||
echo mp4
|
||||
fi
|
||||
}
|
||||
|
||||
get_num_streams() {
|
||||
local file="$1"
|
||||
local type="${2:-}"
|
||||
local select=()
|
||||
local file="$1"
|
||||
local type="${2:-}"
|
||||
local select=()
|
||||
|
||||
if [[ ${type} != '' ]]; then
|
||||
select=("-select_streams" "${type}")
|
||||
fi
|
||||
if [[ ${type} != '' ]]; then
|
||||
select=("-select_streams" "${type}")
|
||||
fi
|
||||
|
||||
ffprobe \
|
||||
-v error "${select[@]}" \
|
||||
-show_entries stream=index \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
ffprobe \
|
||||
-v error "${select[@]}" \
|
||||
-show_entries stream=index \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
}
|
||||
|
||||
get_num_audio_channels() {
|
||||
local file="$1"
|
||||
local stream="$2"
|
||||
ffprobe \
|
||||
-v error \
|
||||
-select_streams "${stream}" \
|
||||
-show_entries stream=channels \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
local file="$1"
|
||||
local stream="$2"
|
||||
ffprobe \
|
||||
-v error \
|
||||
-select_streams "${stream}" \
|
||||
-show_entries stream=channels \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
}
|
||||
|
||||
get_stream_lang() {
|
||||
local file="$1"
|
||||
local stream="$2"
|
||||
ffprobe \
|
||||
-v error \
|
||||
-select_streams "${stream}" \
|
||||
-show_entries stream_tags=language \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
local file="$1"
|
||||
local stream="$2"
|
||||
ffprobe \
|
||||
-v error \
|
||||
-select_streams "${stream}" \
|
||||
-show_entries stream_tags=language \
|
||||
-of default=noprint_wrappers=1:nokey=1 \
|
||||
"${file}"
|
||||
}
|
||||
|
||||
gen_video() {
|
||||
local outFile="$1"
|
||||
local addFlags=()
|
||||
shift
|
||||
|
||||
local vf="format=yuv420p10le"
|
||||
for arg in "$@"; do
|
||||
case "${arg}" in
|
||||
'1080p') resolution='1920x1080' ;;
|
||||
'2160p') resolution='3840x2160' ;;
|
||||
'grain=yes') vf+=",noise=alls=15:allf=t+u" ;;
|
||||
'hdr=yes')
|
||||
local colorPrimaries='bt2020'
|
||||
local colorTrc='smpte2084'
|
||||
local colorspace='bt2020nc'
|
||||
vf+=",setparams=color_primaries=${colorPrimaries}:color_trc=${colorTrc}:colorspace=${colorspace}"
|
||||
addFlags+=(
|
||||
-color_primaries "${colorPrimaries}"
|
||||
-color_trc "${colorTrc}"
|
||||
-colorspace "${colorspace}"
|
||||
-metadata:s:v:0 "mastering_display_metadata=G(13250,34500)B(7500,3000)R(34000,16000)WP(15635,16450)L(10000000,1)"
|
||||
-metadata:s:v:0 "content_light_level=1000,400"
|
||||
)
|
||||
;;
|
||||
*) echo_fail "bad arg ${arg}" && return 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo_if_fail ffmpeg -y \
|
||||
-hide_banner \
|
||||
-f lavfi \
|
||||
-i "testsrc2=size=${resolution}:rate=24:duration=5" \
|
||||
-vf "${vf}" \
|
||||
-c:v ffv1 \
|
||||
-level 3 \
|
||||
-g 1 \
|
||||
-color_range tv \
|
||||
"${addFlags[@]}" \
|
||||
"${outFile}"
|
||||
}
|
||||
|
||||
@@ -2,130 +2,133 @@
|
||||
|
||||
# shellcheck disable=SC2120
|
||||
determine_pkg_mgr() {
|
||||
# sudo used externally
|
||||
# shellcheck disable=SC2034
|
||||
if is_windows || test "$(id -u)" -eq 0; then
|
||||
SUDO=''
|
||||
else
|
||||
SUDO='sudo '
|
||||
fi
|
||||
# sudo used externally
|
||||
# shellcheck disable=SC2034
|
||||
if is_windows || test "$(id -u)" -eq 0; then
|
||||
SUDO=''
|
||||
else
|
||||
SUDO='sudo '
|
||||
fi
|
||||
|
||||
# pkg-mgr update-cmd upgrade-cmd install-cmd check-cmd
|
||||
# shellcheck disable=SC2016
|
||||
local PKG_MGR_MAP='
|
||||
# pkg-mgr update-cmd upgrade-cmd install-cmd check-cmd
|
||||
# shellcheck disable=SC2016
|
||||
local PKG_MGR_MAP='
|
||||
pkg:pkg update:pkg upgrade:pkg install -y:dpkg -l ${pkg}
|
||||
brew:brew update:brew upgrade:brew install:brew list --formula ${pkg}
|
||||
apt-get:${SUDO}apt-get update:${SUDO}apt-get upgrade -y:${SUDO}apt-get install -y:dpkg -l ${pkg}
|
||||
pacman:${SUDO}pacman -Syy:${SUDO}pacman -Syu --noconfirm:${SUDO}pacman -S --noconfirm --needed:pacman -Qi ${pkg}
|
||||
dnf:${SUDO}dnf check-update || true:${SUDO}dnf upgrade --refresh -y:${SUDO}dnf install -y:dnf list -q --installed ${pkg}
|
||||
'
|
||||
local supported_pkg_mgr=()
|
||||
unset pkg_mgr pkg_mgr_update pkg_mgr_upgrade pkg_install pkg_check
|
||||
while read -r line; do
|
||||
test "${line}" == '' && continue
|
||||
IFS=':' read -r pkg_mgr pkg_mgr_update pkg_mgr_upgrade pkg_install pkg_check <<<"${line}"
|
||||
supported_pkg_mgr+=("${pkg_mgr}")
|
||||
if ! has_cmd "${pkg_mgr}"; then
|
||||
pkg_mgr=''
|
||||
continue
|
||||
fi
|
||||
# update/install may use SUDO
|
||||
eval "pkg_mgr_update=\"${pkg_mgr_update}\""
|
||||
eval "pkg_mgr_upgrade=\"${pkg_mgr_upgrade}\""
|
||||
eval "pkg_install=\"${pkg_install}\""
|
||||
break
|
||||
done <<<"${PKG_MGR_MAP}"
|
||||
local supported_pkg_mgr=()
|
||||
unset pkg_mgr pkg_mgr_update pkg_mgr_upgrade pkg_install pkg_check
|
||||
while read -r line; do
|
||||
test "${line}" == '' && continue
|
||||
IFS=':' read -r pkg_mgr pkg_mgr_update pkg_mgr_upgrade pkg_install pkg_check <<<"${line}"
|
||||
supported_pkg_mgr+=("${pkg_mgr}")
|
||||
if ! has_cmd "${pkg_mgr}"; then
|
||||
pkg_mgr=''
|
||||
continue
|
||||
fi
|
||||
# update/install may use SUDO
|
||||
eval "pkg_mgr_update=\"${pkg_mgr_update}\""
|
||||
eval "pkg_mgr_upgrade=\"${pkg_mgr_upgrade}\""
|
||||
eval "pkg_install=\"${pkg_install}\""
|
||||
break
|
||||
done <<<"${PKG_MGR_MAP}"
|
||||
|
||||
if [[ ${pkg_mgr} == '' ]]; then
|
||||
echo_fail "system does not use a supported package manager" "${supported_pkg_mgr[@]}"
|
||||
return 1
|
||||
fi
|
||||
if [[ ${pkg_mgr} == '' ]]; then
|
||||
echo_fail "system does not use a supported package manager" "${supported_pkg_mgr[@]}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
return 0
|
||||
}
|
||||
|
||||
print_req_pkgs() {
|
||||
local common_pkgs=(
|
||||
autoconf automake cmake libtool
|
||||
texinfo nasm yasm python3 wget
|
||||
meson doxygen jq ccache gawk
|
||||
git gnuplot bison rsync ragel
|
||||
zip unzip gperf itstool
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local brew_pkgs=(
|
||||
"${common_pkgs[@]}" pkgconf
|
||||
mkvtoolnix pipx uutils-coreutils
|
||||
llvm lld
|
||||
)
|
||||
local common_linux_pkgs=(
|
||||
"${common_pkgs[@]}" clang valgrind
|
||||
curl bc lshw xxd pkgconf sudo llvm
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local apt_get_pkgs=(
|
||||
"${common_linux_pkgs[@]}" pipx
|
||||
build-essential libssl-dev gobjc++
|
||||
mawk libc6-dev mediainfo ninja-build
|
||||
mkvtoolnix libgtest-dev lld
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local pacman_pkgs=(
|
||||
"${common_linux_pkgs[@]}" base-devel
|
||||
python-pipx ninja lld
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local dnf_pkgs=(
|
||||
"${common_linux_pkgs[@]}" openssl-devel
|
||||
pipx ninja-build fontconfig-devel wget2
|
||||
glibc-static glibc-devel patch
|
||||
libstdc++-static libstdc++-devel
|
||||
llvm-cmake-utils llvm-devel
|
||||
llvm-static compiler-rt lld
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local pkg_pkgs=(
|
||||
autoconf automake cmake libtool
|
||||
texinfo nasm yasm python3 wget
|
||||
doxygen jq ccache gawk rust
|
||||
git gnuplot bison rsync ragel
|
||||
zip unzip gperf build-essential
|
||||
binutils ninja ndk-multilib-native-static
|
||||
libandroid-posix-semaphore
|
||||
libandroid-posix-semaphore-static
|
||||
libandroid-shmem
|
||||
libandroid-shmem-static
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local msys_ucrt_pkgs=(
|
||||
mingw-w64-ucrt-x86_64-toolchain
|
||||
mingw-w64-ucrt-x86_64-autotools
|
||||
mingw-w64-ucrt-x86_64-clang
|
||||
mingw-w64-ucrt-x86_64-clang-libs
|
||||
mingw-w64-ucrt-x86_64-cmake
|
||||
mingw-w64-ucrt-x86_64-compiler-rt
|
||||
mingw-w64-ucrt-x86_64-doxygen
|
||||
mingw-w64-ucrt-x86_64-gcc-libs
|
||||
mingw-w64-ucrt-x86_64-gperf
|
||||
mingw-w64-ucrt-x86_64-itstool
|
||||
mingw-w64-ucrt-x86_64-meson
|
||||
mingw-w64-ucrt-x86_64-bc
|
||||
mingw-w64-ucrt-x86_64-nasm
|
||||
mingw-w64-ucrt-x86_64-yasm
|
||||
mingw-w64-ucrt-x86_64-ccache
|
||||
mingw-w64-ucrt-x86_64-rustup
|
||||
mingw-w64-ucrt-x86_64-cargo-c
|
||||
mingw-w64-ucrt-x86_64-perl
|
||||
mingw-w64-ucrt-x86_64-perl-modules
|
||||
)
|
||||
local common_pkgs=(
|
||||
autoconf automake cmake libtool
|
||||
texinfo nasm yasm python3 wget
|
||||
meson doxygen jq ccache gawk
|
||||
git gnuplot bison rsync ragel
|
||||
zip unzip gperf itstool
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local brew_pkgs=(
|
||||
"${common_pkgs[@]}" pkgconf
|
||||
mkvtoolnix pipx uutils-coreutils
|
||||
llvm lld
|
||||
)
|
||||
local common_linux_pkgs=(
|
||||
"${common_pkgs[@]}" clang valgrind
|
||||
curl bc lshw xxd pkgconf sudo llvm
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local apt_get_pkgs=(
|
||||
"${common_linux_pkgs[@]}" pipx
|
||||
build-essential libssl-dev gobjc++
|
||||
mawk libc6-dev mediainfo ninja-build
|
||||
mkvtoolnix libgtest-dev lld
|
||||
libfontconfig-dev libglib2.0-dev
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local pacman_pkgs=(
|
||||
"${common_linux_pkgs[@]}" base-devel
|
||||
python-pipx ninja lld mkvtoolnix-cli
|
||||
glib2-devel
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local dnf_pkgs=(
|
||||
"${common_linux_pkgs[@]}" openssl-devel
|
||||
pipx ninja-build fontconfig-devel wget2
|
||||
glibc-static glibc-devel patch
|
||||
libstdc++-static libstdc++-devel
|
||||
llvm-cmake-utils llvm-devel
|
||||
llvm-static compiler-rt lld
|
||||
mkvtoolnix glib2-static
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local pkg_pkgs=(
|
||||
autoconf automake cmake libtool
|
||||
texinfo nasm yasm python3 wget
|
||||
doxygen jq ccache gawk rust
|
||||
git gnuplot bison rsync ragel
|
||||
zip unzip gperf build-essential
|
||||
binutils ninja ndk-multilib-native-static
|
||||
libandroid-posix-semaphore
|
||||
libandroid-posix-semaphore-static
|
||||
libandroid-shmem
|
||||
libandroid-shmem-static
|
||||
)
|
||||
# shellcheck disable=SC2034
|
||||
local msys_ucrt_pkgs=(
|
||||
mingw-w64-ucrt-x86_64-toolchain
|
||||
mingw-w64-ucrt-x86_64-autotools
|
||||
mingw-w64-ucrt-x86_64-clang
|
||||
mingw-w64-ucrt-x86_64-clang-libs
|
||||
mingw-w64-ucrt-x86_64-cmake
|
||||
mingw-w64-ucrt-x86_64-compiler-rt
|
||||
mingw-w64-ucrt-x86_64-doxygen
|
||||
mingw-w64-ucrt-x86_64-gcc-libs
|
||||
mingw-w64-ucrt-x86_64-gperf
|
||||
mingw-w64-ucrt-x86_64-itstool
|
||||
mingw-w64-ucrt-x86_64-meson
|
||||
mingw-w64-ucrt-x86_64-bc
|
||||
mingw-w64-ucrt-x86_64-nasm
|
||||
mingw-w64-ucrt-x86_64-yasm
|
||||
mingw-w64-ucrt-x86_64-ccache
|
||||
mingw-w64-ucrt-x86_64-rustup
|
||||
mingw-w64-ucrt-x86_64-cargo-c
|
||||
mingw-w64-ucrt-x86_64-perl
|
||||
mingw-w64-ucrt-x86_64-perl-modules
|
||||
)
|
||||
|
||||
if is_windows; then
|
||||
local pkg_mgr='msys_ucrt'
|
||||
fi
|
||||
local req_pkgs_env_name="${pkg_mgr/-/_}_pkgs"
|
||||
declare -n req_pkgs="${req_pkgs_env_name}"
|
||||
local sorted_req_pkgs=($(printf '%s\n' "${req_pkgs[@]}" | sort -u))
|
||||
echo "${sorted_req_pkgs[@]}"
|
||||
if is_windows; then
|
||||
local pkg_mgr='msys_ucrt'
|
||||
fi
|
||||
local req_pkgs_env_name="${pkg_mgr/-/_}_pkgs"
|
||||
declare -n req_pkgs="${req_pkgs_env_name}"
|
||||
local sorted_req_pkgs=($(printf '%s\n' "${req_pkgs[@]}" | sort -u))
|
||||
echo "${sorted_req_pkgs[@]}"
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('print_pkg_mgr')
|
||||
@@ -133,60 +136,60 @@ FB_FUNC_NAMES+=('print_pkg_mgr')
|
||||
# shellcheck disable=SC2034
|
||||
FB_FUNC_DESCS['print_pkg_mgr']='print out evaluated package manager commands and required packages'
|
||||
print_pkg_mgr() {
|
||||
determine_pkg_mgr || return 1
|
||||
echo "export pkg_mgr=\"${pkg_mgr}\""
|
||||
echo "export pkg_mgr_update=\"${pkg_mgr_update}\""
|
||||
echo "export pkg_mgr_upgrade=\"${pkg_mgr_upgrade}\""
|
||||
echo "export pkg_install=\"${pkg_install}\""
|
||||
echo "export pkg_check=\"${pkg_check}\""
|
||||
echo "export req_pkgs=($(print_req_pkgs))"
|
||||
determine_pkg_mgr || return 1
|
||||
echo "export pkg_mgr=\"${pkg_mgr}\""
|
||||
echo "export pkg_mgr_update=\"${pkg_mgr_update}\""
|
||||
echo "export pkg_mgr_upgrade=\"${pkg_mgr_upgrade}\""
|
||||
echo "export pkg_install=\"${pkg_install}\""
|
||||
echo "export pkg_check=\"${pkg_check}\""
|
||||
echo "export req_pkgs=($(print_req_pkgs))"
|
||||
}
|
||||
|
||||
check_for_req_pkgs() {
|
||||
echo_info "checking for required packages"
|
||||
local missing_pkgs=()
|
||||
for pkg in $(print_req_pkgs); do
|
||||
# pkg_check has ${pkg} unexpanded
|
||||
eval "pkg_check=\"${pkg_check}\""
|
||||
${pkg_check} "${pkg}" >/dev/null 2>&1 || missing_pkgs+=("${pkg}")
|
||||
done
|
||||
echo_info "checking for required packages"
|
||||
local missing_pkgs=()
|
||||
for pkg in $(print_req_pkgs); do
|
||||
# pkg_check has ${pkg} unexpanded
|
||||
eval "pkg_check=\"${pkg_check}\""
|
||||
${pkg_check} "${pkg}" >/dev/null 2>&1 || missing_pkgs+=("${pkg}")
|
||||
done
|
||||
|
||||
if [[ ${#missing_pkgs[@]} -gt 0 ]]; then
|
||||
echo_warn "missing packages:" "${missing_pkgs[@]}"
|
||||
# shellcheck disable=SC2086
|
||||
${pkg_mgr_update}
|
||||
# shellcheck disable=SC2086
|
||||
${pkg_install} "${missing_pkgs[@]}" || return 1
|
||||
fi
|
||||
if [[ ${#missing_pkgs[@]} -gt 0 ]]; then
|
||||
echo_warn "missing packages:" "${missing_pkgs[@]}"
|
||||
# shellcheck disable=SC2086
|
||||
${pkg_mgr_update}
|
||||
# shellcheck disable=SC2086
|
||||
${pkg_install} "${missing_pkgs[@]}" || return 1
|
||||
fi
|
||||
|
||||
echo_pass "packages from ${pkg_mgr} installed"
|
||||
has_cmd pipx || echo_if_fail python3 -m pip install --user pipx || return 1
|
||||
has_cmd pipx || echo_if_fail python3 -m pipx ensurepath && source ~/.bashrc || return 1
|
||||
echo_if_fail pipx install virtualenv || return 1
|
||||
echo_if_fail pipx ensurepath || return 1
|
||||
has_cmd meson || echo_if_fail pipx install meson || return 1
|
||||
echo_pass "pipx is installed"
|
||||
echo_pass "packages from ${pkg_mgr} installed"
|
||||
has_cmd pipx || echo_if_fail python3 -m pip install --user pipx || return 1
|
||||
has_cmd pipx || echo_if_fail python3 -m pipx ensurepath && source ~/.bashrc || return 1
|
||||
echo_if_fail pipx install virtualenv || return 1
|
||||
echo_if_fail pipx ensurepath || return 1
|
||||
has_cmd meson || echo_if_fail pipx install meson || return 1
|
||||
echo_pass "pipx is installed"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
test -f "${HOME}/.cargo/env" && source "${HOME}/.cargo/env"
|
||||
# shellcheck disable=SC1091
|
||||
test -f "${HOME}/.cargo/env" && source "${HOME}/.cargo/env"
|
||||
|
||||
if missing_cmd cargo; then
|
||||
if missing_cmd rustup; then
|
||||
echo_warn "installing rustup"
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
# shellcheck disable=SC2016
|
||||
grep -q 'source "${HOME}/.cargo/env"' "${HOME}/.bashrc" ||
|
||||
echo 'source "${HOME}/.cargo/env"' >>"${HOME}/.bashrc"
|
||||
# shellcheck disable=SC1091
|
||||
source "${HOME}/.bashrc"
|
||||
fi
|
||||
fi
|
||||
if missing_cmd cargo; then
|
||||
if missing_cmd rustup; then
|
||||
echo_warn "installing rustup"
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
# shellcheck disable=SC2016
|
||||
grep -q 'source "${HOME}/.cargo/env"' "${HOME}/.bashrc" ||
|
||||
echo 'source "${HOME}/.cargo/env"' >>"${HOME}/.bashrc"
|
||||
# shellcheck disable=SC1091
|
||||
source "${HOME}/.bashrc"
|
||||
fi
|
||||
fi
|
||||
|
||||
has_cmd cargo-cbuild || echo_if_fail cargo install cargo-c || return 1
|
||||
echo_pass "cargo-c is installed"
|
||||
echo_pass "all required packages installed"
|
||||
has_cmd cargo-cbuild || echo_if_fail cargo install cargo-c || return 1
|
||||
echo_pass "cargo-c is installed"
|
||||
echo_pass "all required packages installed"
|
||||
|
||||
return 0
|
||||
return 0
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('install_deps')
|
||||
@@ -194,6 +197,6 @@ FB_FUNC_NAMES+=('install_deps')
|
||||
# shellcheck disable=SC2034
|
||||
FB_FUNC_DESCS['install_deps']='install required dependencies'
|
||||
install_deps() {
|
||||
determine_pkg_mgr || return 1
|
||||
check_for_req_pkgs || return 1
|
||||
determine_pkg_mgr || return 1
|
||||
check_for_req_pkgs || return 1
|
||||
}
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
check_for_package_cfg() {
|
||||
local requiredCfg='ON:ON:3'
|
||||
local currentCfg="${STATIC}:${LTO}:${OPT}"
|
||||
if [[ ${currentCfg} == "${requiredCfg}" ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
local requiredCfg='ON:ON:ON:3'
|
||||
local currentCfg="${STATIC}:${LTO}:${PGO}:${OPT}"
|
||||
if [[ ${currentCfg} == "${requiredCfg}" ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('package')
|
||||
FB_FUNC_DESCS['package']='package ffmpeg build'
|
||||
package() {
|
||||
local pkgDir="${IGN_DIR}/package"
|
||||
test -d "${pkgDir}" && rm -rf "${pkgDir}"
|
||||
check_for_package_cfg || return 0
|
||||
local pkgDir="${IGN_DIR}/package"
|
||||
recreate_dir "${pkgDir}" || return 1
|
||||
check_for_package_cfg || return 0
|
||||
|
||||
echo_info "packaging"
|
||||
mkdir "${pkgDir}" || return 1
|
||||
echo_info "packaging"
|
||||
set_compile_opts || return 1
|
||||
cp "${PREFIX}/bin/ff"* "${pkgDir}/"
|
||||
|
||||
set_compile_opts || return 1
|
||||
cp "${PREFIX}/bin/ff"* "${pkgDir}/"
|
||||
|
||||
cd "${pkgDir}" || return 1
|
||||
local tarball="ffmpeg-build-${HOSTTYPE}-$(print_os).tar"
|
||||
tar -cf "${tarball}" ff* || return 1
|
||||
xz -e -9 "${tarball}" || return 1
|
||||
echo_pass "finished packaging ${tarball}.xz"
|
||||
cd "${pkgDir}" || return 1
|
||||
local tarball="ffmpeg-build-${HOSTTYPE}-$(print_os).tar"
|
||||
tar -cf "${tarball}" ff* || return 1
|
||||
xz -e -9 "${tarball}" || return 1
|
||||
echo_pass "finished packaging ${tarball}.xz"
|
||||
}
|
||||
|
||||
60
lib/pgo.sh
Normal file
60
lib/pgo.sh
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PGO_DIR="${IGN_DIR}/pgo"
|
||||
PGO_PROFDATA="${PGO_DIR}/prof.profdata"
|
||||
gen_profdata() {
|
||||
recreate_dir "${PGO_DIR}" || return 1
|
||||
cd "${PGO_DIR}" || return 1
|
||||
setup_pgo_clips || return 1
|
||||
for vid in *.mkv; do
|
||||
local args=()
|
||||
# add precalculated grain amount based off of filename
|
||||
line_contains "${vid}" 'grain' && args+=(-g 16)
|
||||
# make fhd preset 2
|
||||
line_contains "${vid}" 'fhd' && args+=(-P 2)
|
||||
|
||||
echo_info "encoding pgo vid: ${vid}"
|
||||
LLVM_PROFILE_FILE="${PGO_DIR}/default_%p.profraw" \
|
||||
echo_if_fail encode -i "${vid}" "${args[@]}" "encoded-${vid}" || return 1
|
||||
done
|
||||
|
||||
# merge profraw into profdata
|
||||
local mergeCmd=()
|
||||
# darwin needs special invoke
|
||||
if is_darwin; then
|
||||
mergeCmd+=(xcrun)
|
||||
fi
|
||||
|
||||
mergeCmd+=(
|
||||
llvm-profdata
|
||||
merge
|
||||
"--output=${PGO_PROFDATA}"
|
||||
)
|
||||
"${mergeCmd[@]}" default*.profraw || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
setup_pgo_clips() {
|
||||
local clips=(
|
||||
"fhd-grainy.mkv 1080p,grain=yes"
|
||||
"uhd.mkv 2160p"
|
||||
"uhd-hdr.mkv 2160p,hdr=yes"
|
||||
)
|
||||
for clip in "${clips[@]}"; do
|
||||
local genVid genVidArgs pgoFile genVidArgsArr
|
||||
IFS=' ' read -r genVid genVidArgs <<<"${clip}"
|
||||
# pgo path is separate
|
||||
pgoFile="${PGO_DIR}/${genVid}"
|
||||
genVid="${TMP_DIR}/${genVid}"
|
||||
# create array of args split with ,
|
||||
genVidArgsArr=(${genVidArgs//,/ })
|
||||
# create generated vid without any profiling if needed
|
||||
test -f "${genVid}" ||
|
||||
LLVM_PROFILE_FILE='/dev/null' gen_video "${genVid}" "${genVidArgsArr[@]}" || return 1
|
||||
# and move to the pgo directory
|
||||
test -f "${pgoFile}" ||
|
||||
cp "${genVid}" "${pgoFile}" || return 1
|
||||
|
||||
done
|
||||
}
|
||||
@@ -1,29 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
make_bullet_points() {
|
||||
for arg in "$@"; do
|
||||
echo "- ${arg}"
|
||||
done
|
||||
for arg in "$@"; do
|
||||
echo "- ${arg}"
|
||||
done
|
||||
}
|
||||
|
||||
gen_function_info() {
|
||||
local funcName="$1"
|
||||
echo "${FB_FUNC_DESCS[${funcName}]} using \`./scripts/${funcName}.sh\`"
|
||||
local funcName="$1"
|
||||
echo "${FB_FUNC_DESCS[${funcName}]} using \`./scripts/${funcName}.sh\`"
|
||||
}
|
||||
|
||||
gen_compile_opts_info() {
|
||||
for opt in "${FB_COMP_OPTS[@]}"; do
|
||||
declare -n defOptVal="DEFAULT_${opt}"
|
||||
echo "- \`${opt}\`: ${FB_COMP_OPTS_DESC[${opt}]} (default: ${defOptVal})"
|
||||
done
|
||||
for opt in "${FB_COMP_OPTS[@]}"; do
|
||||
declare -n defOptVal="DEFAULT_${opt}"
|
||||
echo "- \`${opt}\`: ${FB_COMP_OPTS_DESC[${opt}]} (default: ${defOptVal})"
|
||||
done
|
||||
}
|
||||
|
||||
FB_FUNC_NAMES+=('gen_readme')
|
||||
FB_FUNC_DESCS['gen_readme']='generate project README.md'
|
||||
gen_readme() {
|
||||
local readme="${REPO_DIR}/README.md"
|
||||
local readme="${REPO_DIR}/README.md"
|
||||
|
||||
echo "
|
||||
echo "
|
||||
# ffmpeg-builder
|
||||
A collection of scripts for building \`ffmpeg\` and encoding content with the built \`ffmpeg\`.
|
||||
|
||||
|
||||
368
lib/utils.sh
368
lib/utils.sh
@@ -1,368 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
# ANSI colors
|
||||
RED='\e[0;31m'
|
||||
CYAN='\e[0;36m'
|
||||
GREEN='\e[0;32m'
|
||||
YELLOW='\e[0;33m'
|
||||
NC='\e[0m'
|
||||
|
||||
# echo wrappers
|
||||
echo_wrapper() {
|
||||
local args
|
||||
if [[ $1 == '-n' ]]; then
|
||||
args=("$1")
|
||||
shift
|
||||
fi
|
||||
# COLOR is override for using ${color}
|
||||
# shellcheck disable=SC2153
|
||||
if [[ ${COLOR} == 'OFF' ]]; then
|
||||
color=''
|
||||
endColor=''
|
||||
else
|
||||
endColor="${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${args[@]}" "${color}${word:-''}${endColor}" "$@"
|
||||
}
|
||||
echo_fail() { color="${RED}" word="FAIL" echo_wrapper "$@"; }
|
||||
echo_info() { color="${CYAN}" word="INFO" echo_wrapper "$@"; }
|
||||
echo_pass() { color="${GREEN}" word="PASS" echo_wrapper "$@"; }
|
||||
echo_warn() { color="${YELLOW}" word="WARN" echo_wrapper "$@"; }
|
||||
echo_exit() {
|
||||
echo_fail "$@"
|
||||
exit 1
|
||||
}
|
||||
void() { echo "$@" >/dev/null; }
|
||||
|
||||
echo_if_fail() {
|
||||
local cmd=("$@")
|
||||
local logName="${LOGNAME:-${RANDOM}}-"
|
||||
local out="${TMP_DIR}/${logName}stdout"
|
||||
local err="${TMP_DIR}/${logName}stderr"
|
||||
|
||||
# set trace to the cmdEvalTrace and open file descriptor
|
||||
local cmdEvalTrace="${TMP_DIR}/${logName}cmdEvalTrace"
|
||||
exec 5>"${cmdEvalTrace}"
|
||||
export BASH_XTRACEFD=5
|
||||
|
||||
set -x
|
||||
"${cmd[@]}" >"${out}" 2>"${err}"
|
||||
local retval=$?
|
||||
|
||||
# unset and close file descriptor
|
||||
set +x
|
||||
exec 5>&-
|
||||
|
||||
# parse out relevant part of the trace
|
||||
local cmdEvalLines=()
|
||||
while IFS= read -r line; do
|
||||
line="${line/${PS4}/}"
|
||||
test "${line}" == 'set +x' && continue
|
||||
test "${line}" == '' && continue
|
||||
cmdEvalLines+=("${line}")
|
||||
done <"${cmdEvalTrace}"
|
||||
|
||||
if ! test ${retval} -eq 0; then
|
||||
echo
|
||||
echo_fail "command failed:"
|
||||
printf "%s\n" "${cmdEvalLines[@]}"
|
||||
echo_warn "command stdout:"
|
||||
tail -n 32 "${out}"
|
||||
echo_warn "command stderr:"
|
||||
tail -n 32 "${err}"
|
||||
echo
|
||||
fi
|
||||
if [[ -z ${LOGNAME} ]]; then
|
||||
rm "${out}" "${err}" "${cmdEvalTrace}"
|
||||
fi
|
||||
return ${retval}
|
||||
}
|
||||
|
||||
is_root_owned() {
|
||||
local path=$1
|
||||
local uid
|
||||
|
||||
if stat --version >/dev/null 2>&1; then
|
||||
# GNU coreutils (Linux)
|
||||
uid=$(stat -c '%u' "$path")
|
||||
else
|
||||
# BSD/macOS
|
||||
uid=$(stat -f '%u' "$path")
|
||||
fi
|
||||
|
||||
test "$uid" -eq 0
|
||||
}
|
||||
|
||||
dump_arr() {
|
||||
local arrayNames=("$@")
|
||||
for arrayName in "${arrayNames[@]}"; do
|
||||
declare -n array="${arrayName}"
|
||||
arrayExpanded=("${array[@]}")
|
||||
|
||||
# skip showing single element arrays by default
|
||||
if [[ ! ${#arrayExpanded[@]} -gt 1 ]]; then
|
||||
if [[ ${SHOW_SINGLE} == true ]]; then
|
||||
echo_info "${arrayName}='${arrayExpanded[*]}'"
|
||||
else
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
# don't care that the variable has "ARR"
|
||||
echo_info "${arrayName//"_ARR"/}"
|
||||
printf "\t%s\n" "${arrayExpanded[@]}"
|
||||
done
|
||||
}
|
||||
|
||||
has_cmd() {
|
||||
local cmds=("$@")
|
||||
local rv=0
|
||||
for cmd in "${cmds[@]}"; do
|
||||
command -v "${cmd}" >/dev/null 2>&1 || rv=1
|
||||
done
|
||||
|
||||
return ${rv}
|
||||
}
|
||||
|
||||
missing_cmd() {
|
||||
local cmds=("$@")
|
||||
local rv=1
|
||||
for cmd in "${cmds[@]}"; do
|
||||
if ! has_cmd "${cmd}"; then
|
||||
echo_warn "missing ${cmd}"
|
||||
rv=0
|
||||
fi
|
||||
done
|
||||
|
||||
return ${rv}
|
||||
}
|
||||
|
||||
bash_dirname() {
|
||||
local tmp=${1:-.}
|
||||
|
||||
[[ $tmp != *[!/]* ]] && {
|
||||
printf '/\n'
|
||||
return
|
||||
}
|
||||
|
||||
tmp=${tmp%%"${tmp##*[!/]}"}
|
||||
|
||||
[[ $tmp != */* ]] && {
|
||||
printf '.\n'
|
||||
return
|
||||
}
|
||||
|
||||
tmp=${tmp%/*}
|
||||
tmp=${tmp%%"${tmp##*[!/]}"}
|
||||
|
||||
printf '%s\n' "${tmp:-/}"
|
||||
}
|
||||
|
||||
bash_basename() {
|
||||
local tmp
|
||||
path="$1"
|
||||
suffix="${2:-''}"
|
||||
|
||||
tmp=${path%"${path##*[!/]}"}
|
||||
tmp=${tmp##*/}
|
||||
tmp=${tmp%"${suffix/"$tmp"/}"}
|
||||
|
||||
printf '%s\n' "${tmp:-/}"
|
||||
}
|
||||
|
||||
bash_realpath() {
|
||||
local file=$1
|
||||
local dir
|
||||
|
||||
# If the file is already absolute
|
||||
[[ $file == /* ]] && {
|
||||
printf '%s\n' "$file"
|
||||
return
|
||||
}
|
||||
|
||||
# Otherwise: split into directory + basename
|
||||
dir="$(bash_dirname "${file}")"
|
||||
file="$(bash_basename "${file}")"
|
||||
|
||||
# If no directory component, use current directory
|
||||
if [[ $dir == "$file" ]]; then
|
||||
dir="$PWD"
|
||||
else
|
||||
# Save current dir, move into target dir, capture $PWD, then return
|
||||
local oldpwd="$PWD"
|
||||
cd "$dir" || return 1
|
||||
dir="$PWD"
|
||||
cd "$oldpwd" || return 1
|
||||
fi
|
||||
|
||||
printf '%s/%s\n' "$dir" "$file"
|
||||
}
|
||||
|
||||
line_contains() {
|
||||
local line="$1"
|
||||
local substr="$2"
|
||||
if [[ $line == *"${substr}"* ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
line_starts_with() {
|
||||
local line="$1"
|
||||
local substr="$2"
|
||||
if [[ $line == "${substr}"* ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
is_linux() {
|
||||
line_contains "${OSTYPE}" 'linux'
|
||||
}
|
||||
|
||||
is_darwin() {
|
||||
line_contains "$(print_os)" darwin
|
||||
}
|
||||
|
||||
is_windows() {
|
||||
line_contains "$(print_os)" windows
|
||||
}
|
||||
|
||||
is_android() {
|
||||
line_contains "$(print_os)" android
|
||||
}
|
||||
|
||||
print_os() {
|
||||
# cached response
|
||||
if [[ -n ${FB_OS} ]]; then
|
||||
echo "${FB_OS}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
unset FB_OS
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
source /etc/os-release
|
||||
FB_OS="${ID}"
|
||||
if [[ ${VERSION_ID} != '' ]]; then
|
||||
FB_OS+="-${VERSION_ID}"
|
||||
fi
|
||||
if line_starts_with "${FB_OS}" 'arch'; then
|
||||
FB_OS='archlinux'
|
||||
fi
|
||||
else
|
||||
FB_OS="$(uname -o)"
|
||||
fi
|
||||
|
||||
# lowercase
|
||||
FB_OS="${FB_OS,,}"
|
||||
|
||||
# special treatment for windows
|
||||
if line_contains "${FB_OS}" 'windows' || line_contains "${FB_OS}" 'msys'; then
|
||||
FB_OS='windows'
|
||||
fi
|
||||
|
||||
echo "${FB_OS}"
|
||||
}
|
||||
|
||||
is_positive_integer() {
|
||||
local input="$1"
|
||||
if [[ ${input} != ?(-)+([[:digit:]]) || ${input} -lt 0 ]]; then
|
||||
echo_fail "${input} is not a positive integer"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
replace_line() {
|
||||
local file="$1"
|
||||
local search="$2"
|
||||
local newLine="$3"
|
||||
local newFile="${TMP_DIR}/$(bash_basename "${file}")"
|
||||
|
||||
test -f "${newFile}" && rm "${newFile}"
|
||||
while read -r line; do
|
||||
if line_contains "${line}" "${search}"; then
|
||||
echo -en "${newLine}" >>"${newFile}"
|
||||
continue
|
||||
fi
|
||||
echo "${line}" >>"${newFile}"
|
||||
done <"${file}"
|
||||
|
||||
cp "${newFile}" "${file}"
|
||||
}
|
||||
|
||||
remove_line() {
|
||||
local file="$1"
|
||||
local search="$2"
|
||||
replace_line "${file}" "${search}" ''
|
||||
}
|
||||
|
||||
bash_sort() {
|
||||
local arr=("$@")
|
||||
local n=${#arr[@]}
|
||||
local i j val1 val2
|
||||
|
||||
# Bubble sort, numeric comparison
|
||||
for ((i = 0; i < n; i++)); do
|
||||
for ((j = 0; j < n - i - 1; j++)); do
|
||||
read -r val1 _ <<<"${arr[j]}"
|
||||
read -r val2 _ <<<"${arr[j + 1]}"
|
||||
if (("${val1}" > "${val2}")); then
|
||||
local tmp=${arr[j]}
|
||||
arr[j]=${arr[j + 1]}
|
||||
arr[j + 1]=$tmp
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
printf '%s\n' "${arr[@]}"
|
||||
}
|
||||
|
||||
_start_spinner() {
|
||||
local spinChars=(
|
||||
"-"
|
||||
'\'
|
||||
"|"
|
||||
"/"
|
||||
)
|
||||
|
||||
sleep 1
|
||||
|
||||
while true; do
|
||||
for ((ind = 0; ind < "${#spinChars[@]}"; ind++)); do
|
||||
echo -ne "${spinChars[${ind}]}" '\b\b'
|
||||
sleep .25
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
spinner() {
|
||||
local action="$1"
|
||||
local spinPidFile="${TMP_DIR}/.spinner-pid"
|
||||
case "${action}" in
|
||||
start)
|
||||
test -f "${spinPidFile}" && rm "${spinPidFile}"
|
||||
|
||||
# don't want to clutter logs if running headless
|
||||
test "${HEADLESS}" == '1' && return
|
||||
|
||||
_start_spinner &
|
||||
echo $! >"${spinPidFile}"
|
||||
;;
|
||||
stop)
|
||||
test -f "${spinPidFile}" && kill "$(<"${spinPidFile}")"
|
||||
echo -ne ' \n'
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
get_pkgconfig_version() {
|
||||
local pkg="$1"
|
||||
pkg-config --modversion "${pkg}"
|
||||
}
|
||||
67
main.sh
67
main.sh
@@ -2,8 +2,8 @@
|
||||
|
||||
# set top dir
|
||||
if [[ -z ${REPO_DIR} ]]; then
|
||||
thisFile="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
REPO_DIR="$(dirname "${thisFile}")"
|
||||
thisFile="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
REPO_DIR="$(dirname "${thisFile}")"
|
||||
fi
|
||||
|
||||
IGN_DIR="${REPO_DIR}/gitignore"
|
||||
@@ -15,16 +15,21 @@ DOCKER_DIR="${IGN_DIR}/docker"
|
||||
PATCHES_DIR="${REPO_DIR}/patches"
|
||||
export REPO_DIR IGN_DIR TMP_DIR DL_DIR BUILD_DIR CCACHE_DIR DOCKER_DIR PATCHES_DIR
|
||||
|
||||
# some functions need a way to signal early
|
||||
# returns instead of failures, so if a function
|
||||
# returns ${FUNC_EXIT_SUCCESS}, stop processing
|
||||
test -v FUNC_EXIT_SUCCESS || readonly FUNC_EXIT_SUCCESS=9
|
||||
|
||||
# make paths if needed
|
||||
IGN_DIRS=(
|
||||
"${TMP_DIR}"
|
||||
"${DL_DIR}"
|
||||
"${BUILD_DIR}"
|
||||
"${CCACHE_DIR}"
|
||||
"${DOCKER_DIR}"
|
||||
"${TMP_DIR}"
|
||||
"${DL_DIR}"
|
||||
"${BUILD_DIR}"
|
||||
"${CCACHE_DIR}"
|
||||
"${DOCKER_DIR}"
|
||||
)
|
||||
for dir in "${IGN_DIRS[@]}"; do
|
||||
test -d "${dir}" || mkdir -p "${dir}"
|
||||
test -d "${dir}" || mkdir -p "${dir}"
|
||||
done
|
||||
unset IGN_DIRS
|
||||
|
||||
@@ -42,12 +47,12 @@ FB_COMPILE_OPTS_SET=0
|
||||
SCRIPT_DIR="${REPO_DIR}/scripts"
|
||||
ENTRY_SCRIPT="${SCRIPT_DIR}/entry.sh"
|
||||
src_scripts() {
|
||||
local SCRIPT_DIR="${REPO_DIR}/scripts"
|
||||
local SCRIPT_DIR="${REPO_DIR}/scripts"
|
||||
|
||||
if [[ $FB_RUNNING_AS_SCRIPT -eq 0 ]]; then
|
||||
rm "${SCRIPT_DIR}"/*.sh
|
||||
# shellcheck disable=SC2016
|
||||
echo '#!/usr/bin/env bash
|
||||
if [[ $FB_RUNNING_AS_SCRIPT -eq 0 ]]; then
|
||||
rm "${SCRIPT_DIR}"/*.sh
|
||||
# shellcheck disable=SC2016
|
||||
echo '#!/usr/bin/env bash
|
||||
export FB_RUNNING_AS_SCRIPT=1
|
||||
thisFile="$(readlink -f "$0")"
|
||||
export REPO_DIR="$(cd "$(dirname "${thisFile}")/.." && echo "$PWD")"
|
||||
@@ -56,32 +61,32 @@ scr_name="$(bash_basename $0)"
|
||||
cmd="${scr_name//.sh/}"
|
||||
if [[ $DEBUG == 1 ]]; then set -x; fi
|
||||
$cmd "$@"' >"${ENTRY_SCRIPT}"
|
||||
chmod +x "${ENTRY_SCRIPT}"
|
||||
fi
|
||||
chmod +x "${ENTRY_SCRIPT}"
|
||||
fi
|
||||
|
||||
for script in "${REPO_DIR}/lib/"*.sh; do
|
||||
# shellcheck disable=SC1090
|
||||
source "${script}"
|
||||
done
|
||||
for script in "${REPO_DIR}/lib/"*.sh; do
|
||||
# shellcheck disable=SC1090
|
||||
source "${script}"
|
||||
done
|
||||
}
|
||||
|
||||
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}]}"
|
||||
if [[ $FB_RUNNING_AS_SCRIPT -eq 0 ]]; then
|
||||
(cd "$SCRIPT_DIR" && ln -sf entry.sh "${funcName}.sh")
|
||||
fi
|
||||
done
|
||||
echo -e "\n"
|
||||
echo -e "~~~ Usable Commands ~~~\n"
|
||||
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")
|
||||
fi
|
||||
done
|
||||
echo -e "\n"
|
||||
}
|
||||
|
||||
set_completions() {
|
||||
for funcName in "${FB_FUNC_NAMES[@]}"; do
|
||||
complete -W "${FB_FUNC_COMPLETION[${funcName}]}" "${funcName}"
|
||||
done
|
||||
for funcName in "${FB_FUNC_NAMES[@]}"; do
|
||||
complete -W "${FB_FUNC_COMPLETION[${funcName}]}" "${funcName}"
|
||||
done
|
||||
}
|
||||
|
||||
src_scripts || return 1
|
||||
@@ -93,6 +98,6 @@ check_compile_opts_override || return 1
|
||||
LOCAL_PREFIX="${IGN_DIR}/$(print_os)_sysroot"
|
||||
|
||||
if [[ ${FB_RUNNING_AS_SCRIPT} -eq 0 ]]; then
|
||||
print_cmds || return 1
|
||||
print_cmds || return 1
|
||||
fi
|
||||
set_completions || return 1
|
||||
|
||||
24
patches/libvorbis/add-lm-to-pc.patch
Normal file
24
patches/libvorbis/add-lm-to-pc.patch
Normal file
@@ -0,0 +1,24 @@
|
||||
diff --git a/vorbis.pc.in b/vorbis.pc.in
|
||||
index f5ca77d..50cad9e 100644
|
||||
--- a/vorbis.pc.in
|
||||
+++ b/vorbis.pc.in
|
||||
@@ -10,6 +10,6 @@ Description: vorbis is the primary Ogg Vorbis library
|
||||
Version: @VERSION@
|
||||
Requires.private: ogg
|
||||
Conflicts:
|
||||
-Libs: -L${libdir} -lvorbis
|
||||
-Libs.private: @VORBIS_LIBS@
|
||||
+Libs: -L${libdir} -lvorbis
|
||||
+Libs.private: @VORBIS_LIBS@ -lm
|
||||
Cflags: -I${includedir}
|
||||
diff --git a/vorbisenc.pc.in b/vorbisenc.pc.in
|
||||
index a412b7a..4222daf 100644
|
||||
--- a/vorbisenc.pc.in
|
||||
+++ b/vorbisenc.pc.in
|
||||
@@ -10,5 +10,5 @@ Description: vorbisenc is a library that provides a convenient API for setting u
|
||||
Version: @VERSION@
|
||||
Requires.private: vorbis
|
||||
Conflicts:
|
||||
-Libs: -L${libdir} -lvorbisenc
|
||||
+Libs: -L${libdir} -lvorbisenc -lm
|
||||
Cflags: -I${includedir}
|
||||
13
patches/libvpx/ignore-unknown-configure-options.patch
Normal file
13
patches/libvpx/ignore-unknown-configure-options.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
diff --git a/build/make/configure.sh b/build/make/configure.sh
|
||||
index cc5bf6c..c229965 100644
|
||||
--- a/build/make/configure.sh
|
||||
+++ b/build/make/configure.sh
|
||||
@@ -16,8 +16,6 @@
|
||||
die_unknown(){
|
||||
echo "Unknown option \"$1\"."
|
||||
echo "See $0 --help for available options."
|
||||
- clean_temp_files
|
||||
- exit 1
|
||||
}
|
||||
|
||||
die() {
|
||||
@@ -3,13 +3,13 @@
|
||||
base="$(dirname "$(readlink -f "$0")")"
|
||||
|
||||
inotifywait -m -r \
|
||||
-e close_write \
|
||||
-e moved_to \
|
||||
--format '%w%f' \
|
||||
"$base/lib" \
|
||||
"$base/scripts" \
|
||||
"$base/main.sh" | while read -r file; do
|
||||
if [[ -f $file && $file =~ .sh ]]; then
|
||||
shfmt --write --simplify "$file"
|
||||
fi
|
||||
-e close_write \
|
||||
-e moved_to \
|
||||
--format '%w%f' \
|
||||
"${base}/lib" \
|
||||
"${base}/scripts" \
|
||||
"${base}/main.sh" | while read -r file; do
|
||||
if [[ -f $file && $file =~ .sh ]]; then
|
||||
shfmt --indent 4 --write --simplify "${file}"
|
||||
fi
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user