#!/usr/bin/env bash # build-libmpv-linux.sh # # Builds a yauiclient-optimised libmpv SDK for Linux x86_64 / aarch64. # # Output per tag: # libmpv-sdk-vX.X.X-linux-ARCH.tar.gz headers + .so + pkgconfig # libmpv-runtime-vX.X.X-linux-ARCH.tar.gz libmpv.so + FFmpeg .so files # # Feature profile: # OpenGL render API, VAAPI hwdec (x86_64) / V4L2M2M hwdec (aarch64), # LuaJIT, Blu-ray, teletext (libzvbi), network + local playback. # No Vulkan, no encoders, no capture devices. # # Prerequisites - Ubuntu/Debian: # sudo apt install -y \ # build-essential nasm yasm pkg-config meson ninja-build cmake git \ # autoconf automake libtool gettext autopoint \ # libass-dev libfreetype6-dev libfribidi-dev libharfbuzz-dev \ # libbluray-dev liblcms2-dev libluajit-5.1-dev \ # libfontconfig-dev libjpeg-dev zlib1g-dev \ # libplacebo-dev libva-dev libvdpau-dev \ # libpng-dev python3-pip patchelf # # Prerequisites - Arch: # sudo pacman -S --needed \ # base-devel nasm yasm pkgconf meson ninja cmake git \ # autoconf automake libtool gettext autopoint \ # libass freetype2 fribidi harfbuzz \ # libbluray lcms2 luajit \ # fontconfig libjpeg-turbo zlib libpng \ # libplacebo libva libvdpau python patchelf # # Usage: # ./build-libmpv-linux.sh v0.41.0 [v0.40.0 ...] # # Environment overrides (all optional): # FFMPEG6_VERSION FFmpeg 6.x version (default: 6.1.1) - mpv < v0.40.0 # FFMPEG7_VERSION FFmpeg 7.x version (default: 7.1.1) - mpv >= v0.40.0 # ZVBI_VERSION libzvbi version (default: 0.2.44) # CUSTOM_PATCH_DIR directory with ffmpeg/ and/or mpv/ patch subdirs # WORK_DIR scratch space (default: $PWD/work) # OUT_DIR output tarballs (default: $PWD/out) # INSTALL_PREFIX install prefix (default: $HOME/.local) # JOBS parallel jobs (default: nproc) set -euo pipefail # --------------------------------------------------------------------------- # Argument validation # --------------------------------------------------------------------------- if [[ $# -eq 0 ]]; then echo "Usage: $0 v0.41.0 [v0.40.0 ...]" >&2 exit 2 fi for tag in "$@"; do if ! [[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "ERROR: tag '$tag' must be in v0.0.0 format" >&2 exit 2 fi done MPV_TAGS=("$@") # --------------------------------------------------------------------------- # Configuration # --------------------------------------------------------------------------- FFMPEG6_VERSION="${FFMPEG6_VERSION:-6.1.1}" FFMPEG7_VERSION="${FFMPEG7_VERSION:-7.1.1}" ZVBI_VERSION="${ZVBI_VERSION:-0.2.44}" LIBPLACEBO_VERSION="${LIBPLACEBO_VERSION:-7.351.0}" CUSTOM_PATCH_DIR="${CUSTOM_PATCH_DIR:-}" WORK_DIR="${WORK_DIR:-$PWD/work}" SRC_DIR="$WORK_DIR/src" BUILD_DIR="$WORK_DIR/build" OUT_DIR="${OUT_DIR:-$PWD/out}" INSTALL_PREFIX="${INSTALL_PREFIX:-$HOME/.local}" JOBS="${JOBS:-$(nproc)}" ARCH="$(uname -m)" # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- log() { printf "\n==> %s\n" "$*"; } die() { echo "ERROR: $*" >&2; exit 1; } require_tool() { for t in "$@"; do command -v "$t" &>/dev/null || die "'$t' not found - install it first"; done; } # When running as root (e.g. in a CI container) sudo is not available/needed maybe_sudo() { if [[ $EUID -eq 0 ]]; then "$@"; else sudo "$@"; fi; } ffmpeg_version_for_tag() { local minor minor="$(echo "$1" | sed 's/^v[0-9]*\.\([0-9]*\)\..*/\1/')" [[ "$minor" -ge 40 ]] && echo "$FFMPEG7_VERSION" || echo "$FFMPEG6_VERSION" } ffmpeg_deps_dir() { local major="${1%%.*}" echo "$WORK_DIR/deps-ffmpeg${major}" } # --------------------------------------------------------------------------- # Pre-flight # --------------------------------------------------------------------------- require_tool git meson ninja pkg-config tar if ! pkg-config --exists luajit 2>/dev/null; then log "WARNING: luajit not found - mpv will build without Lua support" fi mkdir -p "$SRC_DIR" "$BUILD_DIR" "$OUT_DIR" # --------------------------------------------------------------------------- # apply_patches # --------------------------------------------------------------------------- # --------------------------------------------------------------------------- # version_ge - true (0) if version a >= version b. # Strips a leading 'v' from either argument. Uses sort -V (version sort) # rather than string/numeric comparison, since naive comparison gets # multi-digit version parts wrong (e.g. "0.9.0" vs "0.10.0" - lexically # '9' > '1', which is the wrong answer for a version comparison). # --------------------------------------------------------------------------- version_ge() { local a="${1#v}" b="${2#v}" [[ "$a" == "$b" ]] && return 0 local highest highest="$(printf '%s\n%s\n' "$a" "$b" | sort -V | tail -n1)" [[ "$highest" == "$a" ]] } apply_patches() { local repo="$1" subdir="$2" current_version="${3:-}" [[ -z "$CUSTOM_PATCH_DIR" ]] && return 0 local patch_dir="$CUSTOM_PATCH_DIR/$subdir" if [[ ! -d "$patch_dir" ]]; then # CUSTOM_PATCH_DIR is set but this specific subdir (ffmpeg/ or mpv/) # doesn't exist under it - almost always means the patch file was # dropped directly in CUSTOM_PATCH_DIR instead of CUSTOM_PATCH_DIR/$subdir. # This used to fail silently (no log line at all, patches just never # applied); flag it loudly instead since a silently-unpatched build # is much worse than a noisy warning. if [[ -n "$(find "$CUSTOM_PATCH_DIR" -maxdepth 1 -name '*.patch' 2>/dev/null)" ]]; then log "WARNING: found *.patch file(s) directly in $CUSTOM_PATCH_DIR but" log " expected them under $patch_dir - these will NOT be applied." log " Move them into: $patch_dir" fi return 0 fi log "Applying $subdir patches from $patch_dir" shopt -s nullglob local any_applied=0 local any_failed=0 for p in "$patch_dir"/*.patch; do # Optional gating: a patch file may declare a minimum version it # applies to via a header comment on one of its first few lines: # # min-version: 0.40.0 # Patches written against a specific FFmpeg/mpv source structure can # fail to apply - or in the worst case apply "successfully" against # coincidentally-similar-looking but structurally different older # code - if the same CUSTOM_PATCH_DIR is reused across builds # targeting different versions (e.g. testing against v0.41.0 but # also building an older v0.37.0 for production, which may not even # have the source files/functions a newer-version-specific patch # expects). This lets a patch opt in to being skipped outright, # loudly, rather than either failing to apply (aborting the whole # build, per the die below) or - worse - silently applying somewhere # it was never verified against. if [[ -n "$current_version" ]]; then local min_ver min_ver="$(grep -m1 -oE '^# *min-version: *[0-9][0-9.]*' "$p" 2>/dev/null | grep -oE '[0-9][0-9.]*$' || true)" if [[ -n "$min_ver" ]] && ! version_ge "$current_version" "$min_ver"; then log " skipping $(basename "$p") - requires >= $min_ver, building $current_version" continue fi fi echo " patch: $(basename "$p")" if ( cd "$repo" && patch -p1 < "$p" ); then any_applied=1 else any_failed=1 log "ERROR: patch failed to apply: $(basename "$p")" fi done shopt -u nullglob if [[ $any_failed -eq 1 ]]; then die "One or more $subdir patches failed to apply (see .rej files in $repo). Aborting build rather than silently compiling unpatched source." fi if [[ $any_applied -eq 0 ]]; then log "WARNING: $patch_dir exists but contained no *.patch files" fi } # --------------------------------------------------------------------------- # build_libzvbi_if_needed # --------------------------------------------------------------------------- build_libzvbi_if_needed() { local deps_dir="$WORK_DIR/deps-zvbi" local stamp="$deps_dir/.zvbi-built-$ZVBI_VERSION" if [[ -f "$stamp" ]] && [[ -f "$deps_dir/lib/libzvbi.a" ]]; then log "libzvbi $ZVBI_VERSION already built - skipping" return 0 fi # Ensure autopoint (gettext) is available if ! command -v autopoint &>/dev/null || ! command -v libtool &>/dev/null; then if command -v apt-get &>/dev/null; then maybe_sudo apt-get install -y autoconf automake libtool autopoint gettext elif command -v pacman &>/dev/null; then maybe_sudo pacman -S --needed --noconfirm autoconf automake libtool gettext else die "autoconf/automake/libtool not found - install them for your distro" fi fi require_tool autoconf automake # Debian installs libtoolize, other distros may have libtool command -v libtool &>/dev/null || command -v libtoolize &>/dev/null \ || die "'libtool' not found - install libtool for your distro" mkdir -p "$deps_dir" local src="$SRC_DIR/zvbi" log "Fetching libzvbi $ZVBI_VERSION" if [[ ! -d "$src/.git" ]]; then git clone https://github.com/zapping-vbi/zvbi.git "$src" fi ( cd "$src" git fetch --tags --prune || true git reset --hard HEAD git clean -fd git checkout "v${ZVBI_VERSION}" ) log "Bootstrapping libzvbi $ZVBI_VERSION" # zvbi 0.2.44+ requires autoconf 2.71 - check and upgrade if needed local ac_ver ac_ver=$(autoconf --version 2>/dev/null | head -1 | grep -oP '[0-9]+\.[0-9]+' | head -1) local ac_major ac_minor ac_major=$(echo "$ac_ver" | cut -d. -f1) ac_minor=$(echo "$ac_ver" | cut -d. -f2) if [[ "$ac_major" -lt 2 ]] || [[ "$ac_major" -eq 2 && "$ac_minor" -lt 71 ]]; then log "autoconf $ac_ver too old for zvbi (need 2.71+) - installing newer version" local ac_src="$WORK_DIR/src/autoconf-2.71" if [[ ! -d "$ac_src" ]]; then wget -q -P "$WORK_DIR/src" https://ftp.gnu.org/gnu/autoconf/autoconf-2.71.tar.gz tar xzf "$WORK_DIR/src/autoconf-2.71.tar.gz" -C "$WORK_DIR/src" fi ( cd "$ac_src" && ./configure --prefix=/usr/local && make -j"$JOBS" && maybe_sudo make install ) log "autoconf upgraded: $(autoconf --version | head -1)" fi # Create missing files that automake requires but aren't in the git repo ( cd "$src" [[ -f README ]] || touch README [[ -f AUTHORS ]] || touch AUTHORS [[ -f NEWS ]] || touch NEWS [[ -f ChangeLog ]] || touch ChangeLog [[ -f README ]] || touch README ./autogen.sh ) log "Configuring libzvbi $ZVBI_VERSION" ( cd "$src" ./configure \ --prefix="$deps_dir" \ --enable-static \ --disable-shared \ --disable-dvb \ --disable-v4l \ --disable-bktr \ --disable-proxy \ --disable-nls \ --without-doxygen \ CFLAGS="-O2 -fPIC" ) log "Building libzvbi $ZVBI_VERSION" make -C "$src" -j"$JOBS" make -C "$src" install # Remove any shared artefacts so FFmpeg picks up static .a only rm -f "$deps_dir/lib/libzvbi.so"* rm -f "$deps_dir/lib/libzvbi.la" [[ -f "$deps_dir/lib/libzvbi.a" ]] || die "libzvbi.a not found after build" log "libzvbi built: $deps_dir/lib/libzvbi.a" echo "$ZVBI_VERSION" > "$stamp" } # --------------------------------------------------------------------------- # build_libplacebo_if_needed # # Builds libplacebo from source with Vulkan disabled so libmpv.so does not # pull in vulkan, libavdevice and their dependency chains. # Output: $WORK_DIR/deps-placebo/lib/libplacebo.so # --------------------------------------------------------------------------- build_libplacebo_if_needed() { local deps_dir="$WORK_DIR/deps-placebo" local stamp="$deps_dir/.placebo-built-$LIBPLACEBO_VERSION" if [[ -f "$stamp" ]] && find "$deps_dir/lib" -name "libplacebo.so*" 2>/dev/null | grep -q .; then log "libplacebo $LIBPLACEBO_VERSION already built - skipping" return 0 fi mkdir -p "$deps_dir" local src="$SRC_DIR/libplacebo" log "Fetching libplacebo $LIBPLACEBO_VERSION" if [[ ! -d "$src/.git" ]]; then git clone https://code.videolan.org/videolan/libplacebo.git "$src" fi ( cd "$src" git fetch --tags --prune || true git reset --hard HEAD git clean -fd git checkout "v${LIBPLACEBO_VERSION}" git submodule update --init --recursive ) log "Configuring libplacebo $LIBPLACEBO_VERSION (no Vulkan)" local build_dir="$BUILD_DIR/placebo-$LIBPLACEBO_VERSION" rm -rf "$build_dir" ( cd "$src" meson setup "$build_dir" --prefix="$deps_dir" --buildtype=release --default-library=shared --wrap-mode=nodownload -Dvulkan=disabled -Dd3d11=disabled -Dopengl=enabled -Dglslang=disabled -Dshaderc=disabled -Dlcms=disabled -Ddovi=disabled -Dunwind=disabled -Dtests=false -Dbench=false -Dfuzz=false ) log "Building libplacebo $LIBPLACEBO_VERSION" ninja -C "$build_dir" -j"$JOBS" ninja -C "$build_dir" install restore_system_libs trap - EXIT local so so="$(find "$deps_dir/lib" -name "libplacebo.so*" | head -1)" [[ -n "$so" ]] || die "libplacebo.so not found after build" log "libplacebo built: $(basename "$so")" log "Dependencies:" ldd "$so" | grep -v "linux-vdso\|ld-linux\|libc\.\|libm\.\|libstdc\+\+\|libgcc" || true echo "$LIBPLACEBO_VERSION" > "$stamp" } # --------------------------------------------------------------------------- # build_ffmpeg_if_needed # --------------------------------------------------------------------------- build_ffmpeg_if_needed() { local ver="$1" local deps_dir deps_dir="$(ffmpeg_deps_dir "$ver")" local stamp="$deps_dir/.ffmpeg-built-$ver" if [[ -f "$stamp" ]]; then log "FFmpeg $ver already built - skipping" return 0 fi mkdir -p "$deps_dir" local ff_src="$SRC_DIR/ffmpeg" log "Fetching FFmpeg $ver" if [[ ! -d "$ff_src/.git" ]]; then git clone https://github.com/FFmpeg/FFmpeg.git "$ff_src" fi ( cd "$ff_src" git fetch --tags --prune git reset --hard HEAD git clean -fd git checkout "n${ver}" ) apply_patches "$ff_src" "ffmpeg" "$ver" ( cd "$ff_src" && make distclean >/dev/null 2>&1 || true ) local hwdec_flags="--enable-vaapi --enable-vdpau" [[ "$ARCH" == "aarch64" ]] && hwdec_flags="--enable-vaapi --enable-v4l2_m2m" local zvbi_deps="$WORK_DIR/deps-zvbi" export PKG_CONFIG_PATH="${zvbi_deps}/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" log "Configuring FFmpeg $ver ($ARCH)" ( cd "$ff_src" ./configure \ --prefix="$deps_dir" \ --enable-shared \ --disable-static \ --disable-programs \ --disable-doc \ --disable-debug \ --cpu=generic \ --enable-runtime-cpudetect \ --disable-encoders \ --disable-decoder=png \ --disable-decoder=apng \ --disable-decoder=magicyuv \ --disable-encoder=png \ --disable-encoder=apng \ --disable-parser=png \ --disable-demuxer=apng \ --disable-demuxer=image_png_pipe \ --disable-muxer=apng \ --disable-muxer=image2 \ --disable-avdevice \ --enable-pic \ --disable-lzma \ --disable-bzlib \ --enable-libzvbi \ --extra-cflags="-I${zvbi_deps}/include" \ --extra-ldflags="-L${zvbi_deps}/lib -Wl,-Bstatic -lzvbi -lpng -Wl,-Bdynamic" \ --enable-swresample \ --enable-swscale \ $hwdec_flags \ || die "FFmpeg $ver configure failed" ) log "Building FFmpeg $ver" ( cd "$ff_src" && make -j"$JOBS" && make install ) || die "FFmpeg $ver build failed" find "$deps_dir/lib" -name "libavcodec.so*" | grep -q . \ || die "FFmpeg $ver build produced no libavcodec.so" # Bake RUNPATH=$ORIGIN into each FFmpeg .so so that, e.g., libavcodec # finds its own siblings (or transitive deps shipped alongside) in the # same directory. Threading the rpath through configure --extra-ldflags # is unreliable across shell/Make/libtool expansion of the literal # '$ORIGIN' string; using patchelf post-install is deterministic. command -v patchelf >/dev/null || die "patchelf not installed - apt install patchelf" for so in "$deps_dir/lib"/lib*.so.*; do [[ -f "$so" && ! -L "$so" ]] || continue patchelf --set-rpath '$ORIGIN' "$so" \ || die "patchelf failed on $so" done log "FFmpeg $ver: patched RUNPATH=\$ORIGIN on $(find "$deps_dir/lib" -maxdepth 1 -name 'lib*.so.*' -not -type l | wc -l) library files" log "FFmpeg $ver built successfully" echo "$ver" > "$stamp" } # --------------------------------------------------------------------------- # hide_system_libs / restore_system_libs # # Temporarily rename system .so symlinks that we don't want mpv to pick up. # mpv's meson finds these by scanning /usr/lib directly, bypassing PKG_CONFIG_PATH. # --------------------------------------------------------------------------- HIDDEN_SYSTEM_LIBS=() hide_system_libs() { local libs=( /usr/lib/x86_64-linux-gnu/libavdevice.so /usr/lib/x86_64-linux-gnu/libcdio.so /usr/lib/x86_64-linux-gnu/libcdio_paranoia.so /usr/lib/x86_64-linux-gnu/libcdio_cdda.so ) HIDDEN_SYSTEM_LIBS=() for lib in "${libs[@]}"; do if [[ -f "$lib" ]]; then maybe_sudo mv "$lib" "${lib}.mpvbak" HIDDEN_SYSTEM_LIBS+=("$lib") log "Hidden: $lib" fi done } restore_system_libs() { for lib in "${HIDDEN_SYSTEM_LIBS[@]}"; do [[ -f "${lib}.mpvbak" ]] && maybe_sudo mv "${lib}.mpvbak" "$lib" done HIDDEN_SYSTEM_LIBS=() } # --------------------------------------------------------------------------- # build_libmpv_tag # --------------------------------------------------------------------------- build_libmpv_tag() { local tag="$1" local ff_ver ff_ver="$(ffmpeg_version_for_tag "$tag")" local deps_dir deps_dir="$(ffmpeg_deps_dir "$ff_ver")" local mpv_src="$SRC_DIR/mpv" local build_dir="$BUILD_DIR/mpv-$tag" local stage_dir="$BUILD_DIR/stage-$tag" local sdk_dir="$OUT_DIR/sdk-$tag" local runtime_dir="$OUT_DIR/runtime-$tag" log "Building libmpv $tag (FFmpeg $ff_ver, $ARCH)" if [[ ! -d "$mpv_src/.git" ]]; then git clone https://github.com/mpv-player/mpv.git "$mpv_src" fi ( cd "$mpv_src" git fetch --tags --prune git reset --hard HEAD git clean -fd git checkout "$tag" git submodule update --init --recursive ) apply_patches "$mpv_src" "mpv" "$tag" rm -rf "$build_dir" "$stage_dir" mkdir -p "$build_dir" "$stage_dir" local placebo_deps="$WORK_DIR/deps-placebo" export PKG_CONFIG_PATH="$deps_dir/lib/pkgconfig:$placebo_deps/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" export LD_LIBRARY_PATH="$deps_dir/lib:$placebo_deps/lib:$placebo_deps/lib/x86_64-linux-gnu${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" # Override system pkg-config so our FFmpeg and placebo take priority # Include multiarch path for Debian/Ubuntu where meson installs to lib/x86_64-linux-gnu local placebo_pc_dir if [[ -f "$placebo_deps/lib/x86_64-linux-gnu/pkgconfig/libplacebo.pc" ]]; then placebo_pc_dir="$placebo_deps/lib/x86_64-linux-gnu/pkgconfig" else placebo_pc_dir="$placebo_deps/lib/pkgconfig" fi export PKG_CONFIG_LIBDIR="$deps_dir/lib/pkgconfig:$placebo_pc_dir:/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig" local vaapi_flag="-Dvaapi=auto" local v4l2_flag="" if [[ "$ARCH" == "aarch64" ]]; then vaapi_flag="-Dvaapi=auto" v4l2_flag="" fi local version_suffix_flag=() grep -q "version-suffix" "$mpv_src/meson.build" 2>/dev/null \ && version_suffix_flag=("-Dversion-suffix=-yauiclient") hide_system_libs trap "restore_system_libs" EXIT ( cd "$mpv_src" meson setup "$build_dir" \ --prefix="$stage_dir" \ --buildtype=release \ --default-library=shared \ --wrap-mode=nodownload \ -Dlibmpv=true \ -Dcplayer=false \ -Dgl=enabled \ -Dlua=luajit \ -Dlcms2=enabled \ -Dlibbluray=enabled \ $vaapi_flag \ $v4l2_flag \ -Dvulkan=disabled \ -Dd3d11=disabled \ -Djavascript=disabled \ -Dcaca=disabled \ -Drubberband=disabled \ -Dzimg=disabled \ -Dsdl2-audio=disabled \ -Dsdl2-video=disabled \ -Dvapoursynth=disabled \ -Dsndio=disabled \ -Dopenal=disabled \ -Djack=disabled \ -Dsixel=disabled \ -Dc_link_args='-Wl,-rpath,$ORIGIN' \ "${version_suffix_flag[@]}" ) log "Compiling libmpv" ninja -C "$build_dir" -j"$JOBS" ninja -C "$build_dir" install local so so="$(find "$build_dir" -maxdepth 2 -name "libmpv.so*" | head -1)" [[ -n "$so" ]] || die "libmpv.so not found after build of $tag" log "Shared library dependencies:" ldd "$so" || true # SDK assembly rm -rf "$sdk_dir" mkdir -p "$sdk_dir/lib/pkgconfig" "$sdk_dir/include" find "$stage_dir/lib" -name "libmpv.so*" -exec cp -Pf {} "$sdk_dir/lib/" \; cp -R "$stage_dir/include/mpv" "$sdk_dir/include/" cat > "$sdk_dir/lib/pkgconfig/libmpv.pc" </dev/null || true fi # Tarballs ( cd "$runtime_dir" && tar czf "$OUT_DIR/libmpv-runtime-$tag-linux-$ARCH.tar.gz" . ) ( cd "$sdk_dir" && tar czf "$OUT_DIR/libmpv-sdk-$tag-linux-$ARCH.tar.gz" . ) sha256sum "$OUT_DIR/libmpv-runtime-$tag-linux-$ARCH.tar.gz" | awk '{print $1}' > "$OUT_DIR/libmpv-runtime-$tag-linux-$ARCH.sha256" sha256sum "$OUT_DIR/libmpv-sdk-$tag-linux-$ARCH.tar.gz" | awk '{print $1}' > "$OUT_DIR/libmpv-sdk-$tag-linux-$ARCH.sha256" log "Done: $tag" log " SDK: $OUT_DIR/libmpv-sdk-$tag-linux-$ARCH.tar.gz" log " SHA256: $OUT_DIR/libmpv-sdk-$tag-linux-$ARCH.sha256" log " Runtime: $OUT_DIR/libmpv-runtime-$tag-linux-$ARCH.tar.gz" log " SHA256: $OUT_DIR/libmpv-runtime-$tag-linux-$ARCH.sha256" } # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- log "FFmpeg 6 version : $FFMPEG6_VERSION (mpv < v0.40.0)" log "FFmpeg 7 version : $FFMPEG7_VERSION (mpv >= v0.40.0)" log "libzvbi version : $ZVBI_VERSION" log "libplacebo ver : $LIBPLACEBO_VERSION" log "MPV tags : ${MPV_TAGS[*]}" log "Architecture : $ARCH" declare -A ff_versions_needed for tag in "${MPV_TAGS[@]}"; do ff_versions_needed["$(ffmpeg_version_for_tag "$tag")"]=1 done build_libzvbi_if_needed build_libplacebo_if_needed for ver in "${!ff_versions_needed[@]}"; do build_ffmpeg_if_needed "$ver" done for tag in "${MPV_TAGS[@]}"; do build_libmpv_tag "$tag" done log "All builds complete. Output in: $OUT_DIR"