#!/usr/bin/env bash # SPDX-License-Identifier: MIT # Copyright (c) 2026 AArts Lab # # Builds the LGPL FFmpeg the Windows build of Bernard Review bundles, cross-compiled from Linux with MinGW-w64, # and packs it in BtbN's layout (bin/*.dll, lib/*.lib, include/, LICENSE.txt), which CMakeLists.txt takes with # -DFFMPEG_DIR= and no code change. FFmpeg 8.1.3 (libavcodec 62, libavformat 62, libavutil # 60, libswscale 9, libswresample 6), the release tools/linux/build_ffmpeg_lgpl.sh builds for Linux, LGPL # version 2.1 or later. It replaces BtbN's LGPL build, whose DLLs carry OpenH264, Kvazaar and 54 other external # libraries: this one carries **no external codec library at all**, only FFmpeg's own code plus the glue of the # hardware encoders and decoders, whose drivers are loaded at run time: # - nv-codec-headers (MIT, header-only): NVENC, NVDEC, CUVID, CUDA (nvcuda.dll / nvEncodeAPI64.dll, from the # NVIDIA driver, 551.76 or newer for the pinned 12.2 API); # - AMD AMF headers (MIT, header-only): h264_amf / hevc_amf (amfrt64.dll, from the AMD driver); # - Intel libvpl, the oneVPL dispatcher (MIT), compiled statically into avcodec/avutil: h264_qsv / hevc_qsv # (libmfx-gen.dll / libmfxhw64.dll, from the Intel driver). Static rather than libvpl.dll: no extra DLL to # ship or keep in step, and no libstdc++ DLL (the dispatcher is C++); # - Windows' own Media Foundation (h264_mf / hevc_mf), D3D11VA / DXVA2 decode and Schannel (TLS); # - zlib (zlib licence), built statically from its release (PNG, MOV compressed headers). # The MinGW-w64 runtime (libgcc, libstdc++, the CRT's static parts) is linked statically too: the DLLs import # Windows system DLLs only, which the last step checks with objdump. # # Usage: tools/windows/build_ffmpeg_lgpl_win64.sh [--apt] [--check] [--companion] [work_dir] # --apt install the build tools with apt-get first (root in a Debian / Ubuntu container) # --check only check the build tools, build nothing (exit 0 or 3) # --companion build instead the static ffmpeg.exe the streaming companion (bernard-stream) runs as a # child process, packed as -companion (see "Companion" below) # where the zip, its unpacked folder and the source archive land # [work_dir] where the tarballs are downloaded (default: /work); kept between runs, checked every time # JOBS= parallel jobs (default: 8) # BERNARD_BUILD_IMAGE=@sha256: the container image, recorded in BUILD.txt (a container # cannot see its own digest) # # Run it in the pinned container, without root on the host (podman), from the repository root: # IMG=docker.io/library/ubuntu@sha256:496754492fb28b4d3049432f2ca787449331e23fb14f0dd3fffea86bf5a93eb4 # podman run --rm -v "$PWD":/src:ro -v ~/ffmpeg-win:/work -e BERNARD_BUILD_IMAGE=$IMG $IMG \ # bash /src/tools/windows/build_ffmpeg_lgpl_win64.sh --apt /work/out /work # (Ubuntu 24.04 "noble", image of 2026-09-11: MinGW-w64 GCC 13 with the win32 thread model, binutils 2.41, # mingw-w64 11.0.1, nasm 2.16.01, LLVM 18 for llvm-dlltool; BUILD.txt records the exact package versions.) # # Sources, pinned with the SHA-256 of each tarball as downloaded on 2026-09-28 (a mismatch stops the script): # ffmpeg.org's release tarball, whose GPG signature is also checked, against the FFmpeg release signing key # FCF9 86EA 15E6 E293 A564 4F10 B432 2F04 D676 58D8 (fetched from ffmpeg.org, its fingerprint checked); the # nv-codec-headers tag (the one the Linux build uses), AMD's AMF-headers release tarball, the libvpl tag and # zlib's release tarball. All the tarballs are packed, unmodified, with this script into the source archive # (/-src.tar), to publish with each release beside the zip. # # Configure: LGPL (no --enable-gpl, --enable-nonfree or --enable-version3: nothing used needs them), shared # libraries only, no programs, no documentation, no libavdevice / libavfilter (the player links neither), # --disable-autodetect, then only what is named above. Win32 threads (FFmpeg's own, w32threads), not pthreads. # --enable-cuda is there with the NVIDIA set: NVDEC decodes into CUDA frames, whose hwcontext it needs (it is # loaded from the driver like the rest, through nv-codec-headers). # Nothing of the build machine goes into the DLLs: FFmpeg records its configure line (avcodec_configuration()) # and source paths (__FILE__), so it is configured with the neutral prefix /tmp/bernard-ffmpeg-win, its # dependencies are installed in /tmp/bernard-ffmpeg-win-deps, everything is unpacked and built under # /tmp/bernard-ffmpeg-win-build, and -ffile-prefix-map strips those three from __FILE__ (given to make, not on # the configure line). The PE timestamps are the date of the FFmpeg release signature (SOURCE_DATE_EPOCH, which # strip honours when it rewrites the DLLs at install), so a rebuild from the same inputs gives the same DLLs. # The MSVC import libraries (lib/*.lib) are made from FFmpeg's .def files with llvm-dlltool. # # The checks at the end: config.h has no GPL, version 3 or nonfree part and no external library but libvpl; # every DLL imports Windows system DLLs only; no DLL holds /home/, /root/, /work, the work folder or a user name. # The run-time check (an MSVC program linked against lib/*.lib: avcodec_license(), the encoders, decoders and # demuxers the player needs) is done on Windows, see THIRD_PARTY_LICENSES.md. # # # Companion (--companion): one static ffmpeg.exe (FFmpeg's libraries and the MinGW-w64 runtime linked in, no # DLL beside it), with the same pinned sources, the same LGPL configure rules, the same checks and the same # fixed dates. --disable-everything, then only the components of the companion's command lines (the start-up # probe: rawvideo on a pipe; the stream: Matroska V_UNCOMPRESSED RGBA on a pipe; both: scale / format / # setparams to BT.709 limited NV12, then an H.264 encoder and h264_metadata, FLV on a pipe): # - protocols pipe, file; demuxers rawvideo, matroska; decoder rawvideo; muxer flv; # - encoders h264_nvenc, h264_qsv, h264_amf, h264_mf (the hardware ones only: no software H.264 encoder); # - filters scale, format, setparams, hwupload (plus what the ffmpeg program selects itself: null, trim, ...); # - bitstream filter h264_metadata; # - and, so that the binary can check its own output (the companion's colour round-trip test decodes the # FLV it wrote with the same ffmpeg): demuxer flv, parser h264, decoder h264, encoder and muxer rawvideo # (uncompressed frames out). # NVENC needs the NVIDIA driver 551.76 or newer (the pinned nv-codec-headers 12.2 API). No libavdevice (no # capture), so no microphone, no ffplay and no Opus: the companion's voice features need another ffmpeg. # # Exit status: 0 built; 1 a check after the build failed; 2 usage or download error; 3 a required tool is # missing (nothing is built; run with --apt in the container, or install the packages named). set -euo pipefail APT=0 CHECK_ONLY=0 COMPANION=0 while [ $# -gt 0 ]; do case "$1" in --apt) APT=1; shift ;; --check) CHECK_ONLY=1; shift ;; --companion) COMPANION=1; shift ;; *) break ;; esac done if [ $# -lt 1 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then sed -n '4,/^set -euo/p' "$0" | sed '$d; s/^# \{0,1\}//' exit 2 fi OUT=$(mkdir -p "$1" && cd "$1" && pwd) WORK=${2:-$OUT/work} mkdir -p "$WORK/tarballs" WORK=$(cd "$WORK" && pwd) JOBS=${JOBS:-8} TRIPLE=x86_64-w64-mingw32 FFMPEG_VERSION=8.1.3 FFMPEG_URL=https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz FFMPEG_SHA256=7138d28c96d9d3e3af4ee3d8cad72741f8ffb40da90c1112235dea3ecd3178a3 FFMPEG_KEY_URL=https://ffmpeg.org/ffmpeg-devel.asc FFMPEG_KEY_FPR=FCF986EA15E6E293A5644F10B4322F04D67658D8 NVCODEC_TAG=n12.2.72.0 NVCODEC_URL=https://github.com/FFmpeg/nv-codec-headers/archive/refs/tags/${NVCODEC_TAG}.tar.gz NVCODEC_SHA256=dbeaec433d93b850714760282f1d0992b1254fc3b5a6cb7d76fc1340a1e47563 AMF_TAG=v1.5.2 AMF_URL=https://github.com/GPUOpen-LibrariesAndSDKs/AMF/releases/download/${AMF_TAG}/AMF-headers-${AMF_TAG}.tar.gz AMF_SHA256=d3c12eb324edf05e214608b6a395a51dd95770ed9d45520185d6c3a206811c99 VPL_TAG=v2.17.0 VPL_URL=https://github.com/intel/libvpl/archive/refs/tags/${VPL_TAG}.tar.gz VPL_SHA256=4de3e2faf1e8307fb282e4a43f443191810f6a6b0a484fffa7995ba1c814c6ec ZLIB_VERSION=1.3.2 ZLIB_URL=https://github.com/madler/zlib/releases/download/v${ZLIB_VERSION}/zlib-${ZLIB_VERSION}.tar.xz ZLIB_SHA256=d7a0654783a4da529d1bb793b7ad9c3318020af77667bcae35f95d0e42a792f3 NAME=ffmpeg-${FFMPEG_VERSION}-bernard-lgpl-win64 [ $COMPANION = 1 ] && NAME+=-companion PREFIX=/tmp/bernard-ffmpeg-win DEPS=/tmp/bernard-ffmpeg-win-deps BUILD_ROOT=/tmp/bernard-ffmpeg-win-build PREFIX_MAP="-ffile-prefix-map=$BUILD_ROOT/= -ffile-prefix-map=$DEPS/=deps/ -ffile-prefix-map=$PREFIX/=" log() { printf '\n=== %s\n' "$*"; } has_cmd() { command -v "$1" >/dev/null 2>&1; } APT_PACKAGES=(gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 binutils-mingw-w64-x86-64 mingw-w64-x86-64-dev gcc libc6-dev nasm pkgconf make cmake curl ca-certificates xz-utils gnupg llvm zip) if [ $APT = 1 ]; then log "apt-get install ${APT_PACKAGES[*]}" export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq --no-install-recommends "${APT_PACKAGES[@]}" >/dev/null fi DLLTOOL="" for c in llvm-dlltool llvm-dlltool-20 llvm-dlltool-19 llvm-dlltool-18 llvm-dlltool-17; do if has_cmd "$c"; then DLLTOOL=$(command -v "$c"); break; fi done MISSING=() for t in $TRIPLE-gcc $TRIPLE-g++ $TRIPLE-objdump $TRIPLE-strip $TRIPLE-ar $TRIPLE-windres \ gcc nasm pkg-config make cmake curl xz gpg zip tar sha256sum $TRIPLE-strings; do has_cmd "$t" || MISSING+=("$t") done [ -n "$DLLTOOL" ] || MISSING+=(llvm-dlltool) if [ ${#MISSING[@]} -gt 0 ]; then echo "FFmpeg (win64): missing tools, nothing built: ${MISSING[*]}" >&2 echo " in a Debian / Ubuntu container: apt-get install ${APT_PACKAGES[*]} (or run with --apt)" >&2 exit 3 fi if [ $CHECK_ONLY = 1 ]; then echo "FFmpeg (win64): build tools present"; exit 0; fi # fetch : downloaded once into /tarballs, checked every time. fetch() { local file="$WORK/tarballs/$1" url=$2 sum=$3 if [ ! -f "$file" ]; then log "download $url" curl -fL --retry 3 -o "$file.part" "$url" mv "$file.part" "$file" fi if [ "$sum" != - ] && ! echo "$sum $file" | sha256sum -c --status; then echo "SHA-256 mismatch for $file: expected $sum, got $(sha256sum "$file" | cut -d' ' -f1)" >&2 exit 2 fi } T_ALL=$SECONDS for d in "$PREFIX" "$DEPS" "$BUILD_ROOT"; do rm -rf "${d:?}"; done mkdir -p "$PREFIX" "$DEPS/include" "$DEPS/lib/pkgconfig" "$BUILD_ROOT" LIC="$BUILD_ROOT/licenses" mkdir -p "$LIC" fetch "ffmpeg-${FFMPEG_VERSION}.tar.xz" "$FFMPEG_URL" "$FFMPEG_SHA256" fetch "ffmpeg-${FFMPEG_VERSION}.tar.xz.asc" "$FFMPEG_URL.asc" - fetch "ffmpeg-devel.asc" "$FFMPEG_KEY_URL" - fetch "nv-codec-headers-${NVCODEC_TAG}.tar.gz" "$NVCODEC_URL" "$NVCODEC_SHA256" fetch "AMF-headers-${AMF_TAG}.tar.gz" "$AMF_URL" "$AMF_SHA256" fetch "libvpl-${VPL_TAG}.tar.gz" "$VPL_URL" "$VPL_SHA256" fetch "zlib-${ZLIB_VERSION}.tar.xz" "$ZLIB_URL" "$ZLIB_SHA256" # The FFmpeg signature: made by the pinned release key (the last field of VALIDSIG is the primary key's fingerprint). log "FFmpeg ${FFMPEG_VERSION}: GPG signature" GNUPGHOME=$(mktemp -d) export GNUPGHOME gpg -q --import "$WORK/tarballs/ffmpeg-devel.asc" 2>/dev/null SIG_STATUS=$(gpg --status-fd 1 --verify "$WORK/tarballs/ffmpeg-${FFMPEG_VERSION}.tar.xz.asc" \ "$WORK/tarballs/ffmpeg-${FFMPEG_VERSION}.tar.xz" 2>/dev/null || true) rm -rf "$GNUPGHOME" unset GNUPGHOME SIG_PRIMARY=$(echo "$SIG_STATUS" | sed -n 's/^\[GNUPG:\] VALIDSIG .* \([0-9A-F]\{40\}\)$/\1/p') if [ "$SIG_PRIMARY" != "$FFMPEG_KEY_FPR" ]; then echo "FFmpeg ${FFMPEG_VERSION}: the GPG signature is not a valid one by $FFMPEG_KEY_FPR:" >&2 echo "$SIG_STATUS" >&2 exit 2 fi SIG_DATE=$(echo "$SIG_STATUS" | sed -n 's/^\[GNUPG:\] VALIDSIG [0-9A-F]* \([0-9-]*\) .*/\1/p') SIG_EPOCH=$(echo "$SIG_STATUS" | sed -n 's/^\[GNUPG:\] VALIDSIG [0-9A-F]* [0-9-]* \([0-9]*\) .*/\1/p') echo "good signature by $FFMPEG_KEY_FPR ($SIG_DATE)" export SOURCE_DATE_EPOCH=$SIG_EPOCH # the PE timestamps (ld, strip) and every date in the zip export PKG_CONFIG_LIBDIR="$DEPS/lib/pkgconfig" # the dependencies' .pc files only, never the host's unset PKG_CONFIG_PATH # zlib: static libz.a and its headers (no zlib1.dll, no import library: FFmpeg must take the static one). log "zlib ${ZLIB_VERSION}: build (static)" tar -xJf "$WORK/tarballs/zlib-${ZLIB_VERSION}.tar.xz" -C "$BUILD_ROOT" make -C "$BUILD_ROOT/zlib-${ZLIB_VERSION}" -f win32/Makefile.gcc -j "$JOBS" PREFIX=$TRIPLE- \ CFLAGS="-O2 -Wall $PREFIX_MAP" libz.a >/dev/null install -m 644 "$BUILD_ROOT/zlib-${ZLIB_VERSION}"/{zlib.h,zconf.h} "$DEPS/include/" install -m 644 "$BUILD_ROOT/zlib-${ZLIB_VERSION}/libz.a" "$DEPS/lib/" cp "$BUILD_ROOT/zlib-${ZLIB_VERSION}/LICENSE" "$LIC/zlib-LICENSE.txt" # nv-codec-headers: headers and ffnvcodec.pc (nothing compiled on their own). log "nv-codec-headers ${NVCODEC_TAG}" tar -xzf "$WORK/tarballs/nv-codec-headers-${NVCODEC_TAG}.tar.gz" -C "$BUILD_ROOT" make -C "$BUILD_ROOT/nv-codec-headers-${NVCODEC_TAG}" PREFIX="$DEPS" LIBDIR=lib install >/dev/null : > "$LIC/nv-codec-headers-LICENSE.txt" for h in "$BUILD_ROOT/nv-codec-headers-${NVCODEC_TAG}"/include/ffnvcodec/*.h; do { echo "== nv-codec-headers ${NVCODEC_TAG}, include/ffnvcodec/$(basename "$h") ==" sed -n '1,/\*\//p' "$h" echo; } >> "$LIC/nv-codec-headers-LICENSE.txt" done # AMF headers: include/AMF (header-only; the MIT notice is at the top of each, the same text in all). log "AMF headers ${AMF_TAG}" tar -xzf "$WORK/tarballs/AMF-headers-${AMF_TAG}.tar.gz" -C "$BUILD_ROOT" cp -r "$BUILD_ROOT/amf-headers-${AMF_TAG}/AMF" "$DEPS/include/" { echo "== AMD AMF headers ${AMF_TAG} (AMF-headers-${AMF_TAG}.tar.gz), the notice at the top of every header, e.g. AMF/core/Version.h ==" sed -n '1,/^$/p' "$BUILD_ROOT/amf-headers-${AMF_TAG}/AMF/core/Version.h" | sed 's#^// \{0,1\}##'; } > "$LIC/AMF-LICENSE.txt" # libvpl: the dispatcher, static, C++ runtime linked statically into the FFmpeg DLLs later. log "libvpl ${VPL_TAG}: build (static dispatcher)" tar -xzf "$WORK/tarballs/libvpl-${VPL_TAG}.tar.gz" -C "$BUILD_ROOT" VPL_SRC="$BUILD_ROOT/libvpl-${VPL_TAG#v}" cmake -S "$VPL_SRC" -B "$BUILD_ROOT/build-vpl" -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SYSTEM_PROCESSOR=AMD64 \ -DCMAKE_C_COMPILER=$TRIPLE-gcc -DCMAKE_CXX_COMPILER=$TRIPLE-g++ -DCMAKE_RC_COMPILER=$TRIPLE-windres \ -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$DEPS" -DCMAKE_INSTALL_LIBDIR=lib \ -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DINSTALL_EXAMPLES=OFF \ -DCMAKE_C_FLAGS="$PREFIX_MAP" -DCMAKE_CXX_FLAGS="$PREFIX_MAP" >/dev/null cmake --build "$BUILD_ROOT/build-vpl" -j "$JOBS" >/dev/null cmake --install "$BUILD_ROOT/build-vpl" >/dev/null rm -rf "${DEPS:?}/share" "${DEPS:?}/etc" "${DEPS:?}/bin" # The dispatcher is C++, and vpl.pc does not say so for a static MinGW build: FFmpeg links it with gcc. sed -i 's/^Libs.private:.*/& -lstdc++/' "$DEPS/lib/pkgconfig/vpl.pc" cp "$VPL_SRC/LICENSE" "$LIC/libvpl-LICENSE.txt" echo "vpl.pc: $(pkg-config --static --libs vpl)" # FFmpeg. log "FFmpeg ${FFMPEG_VERSION}: configure" tar -xJf "$WORK/tarballs/ffmpeg-${FFMPEG_VERSION}.tar.xz" -C "$BUILD_ROOT" SRC="$BUILD_ROOT/ffmpeg-${FFMPEG_VERSION}" BLD="$BUILD_ROOT/build" mkdir -p "$BLD" CONFIGURE_ARGS=( --prefix="$PREFIX" --target-os=mingw32 --arch=x86_64 --cross-prefix=$TRIPLE- --pkg-config=pkg-config --pkg-config-flags=--static --enable-shared --disable-static --disable-programs --disable-doc --disable-debug --disable-avdevice --disable-avfilter --disable-autodetect --enable-ffnvcodec --enable-cuda --enable-nvenc --enable-nvdec --enable-cuvid --enable-amf --enable-libvpl --enable-mediafoundation --enable-d3d11va --enable-dxva2 --enable-schannel --enable-zlib --extra-cflags=-I$DEPS/include --extra-ldflags=-L$DEPS/lib ) WANT=(CONFIG_FFNVCODEC CONFIG_CUDA CONFIG_NVENC CONFIG_NVDEC CONFIG_CUVID CONFIG_AMF CONFIG_LIBVPL CONFIG_MEDIAFOUNDATION CONFIG_D3D11VA CONFIG_DXVA2 CONFIG_SCHANNEL CONFIG_ZLIB HAVE_W32THREADS) if [ $COMPANION = 1 ]; then CONFIGURE_ARGS=( --prefix="$PREFIX" --target-os=mingw32 --arch=x86_64 --cross-prefix=$TRIPLE- --pkg-config=pkg-config --pkg-config-flags=--static --enable-static --disable-shared --disable-programs --enable-ffmpeg --disable-doc --disable-debug --disable-avdevice --disable-autodetect --disable-everything --enable-ffnvcodec --enable-cuda --enable-nvenc --enable-amf --enable-libvpl --enable-mediafoundation --enable-d3d11va --enable-dxva2 --enable-zlib --enable-protocol=pipe,file --enable-demuxer=rawvideo,matroska,flv --enable-muxer=flv,rawvideo --enable-decoder=rawvideo,h264 --enable-parser=h264 --enable-encoder=h264_nvenc,h264_qsv,h264_amf,h264_mf,rawvideo --enable-filter=scale,format,setparams,hwupload --enable-bsf=h264_metadata --extra-cflags=-I$DEPS/include --extra-ldflags=-L$DEPS/lib ) WANT=(CONFIG_FFNVCODEC CONFIG_CUDA CONFIG_NVENC CONFIG_AMF CONFIG_LIBVPL CONFIG_MEDIAFOUNDATION CONFIG_D3D11VA CONFIG_DXVA2 CONFIG_ZLIB HAVE_W32THREADS CONFIG_FFMPEG CONFIG_H264_NVENC_ENCODER CONFIG_H264_QSV_ENCODER CONFIG_H264_AMF_ENCODER CONFIG_H264_MF_ENCODER CONFIG_RAWVIDEO_DEMUXER CONFIG_MATROSKA_DEMUXER CONFIG_RAWVIDEO_DECODER CONFIG_FLV_MUXER CONFIG_PIPE_PROTOCOL CONFIG_FILE_PROTOCOL CONFIG_SCALE_FILTER CONFIG_FORMAT_FILTER CONFIG_SETPARAMS_FILTER CONFIG_HWUPLOAD_FILTER CONFIG_H264_METADATA_BSF) fi echo "configure ${CONFIGURE_ARGS[*]}" (cd "$BLD" && "$SRC/configure" "${CONFIGURE_ARGS[@]}") > "$BUILD_ROOT/configure.log" 2>&1 || { tail -40 "$BUILD_ROOT/configure.log"; tail -60 "$BLD/ffbuild/config.log"; exit 1; } cat "$BUILD_ROOT/configure.log" if grep -qE '^#define CONFIG_(GPL|GPLV3|VERSION3|NONFREE) 1' "$BLD/config.h"; then echo "FFmpeg configured GPL, version 3 or nonfree -- check the configure line above" >&2 exit 1 fi for want in "${WANT[@]}"; do grep -qh "^#define $want 1" "$BLD/config.h" "$BLD/config_components.h" || { echo "FFmpeg configured without $want" >&2; exit 1; } done if [ $COMPANION = 1 ]; then # The encoders are exactly the four hardware H.264 ones and rawvideo (no other came in through a select). ENCODERS=$(grep -oE '^#define CONFIG_[A-Z0-9_]+_ENCODER 1' "$BLD/config_components.h" | awk '{print $2}' | LC_ALL=C sort | tr '\n' ' ') if [ "$ENCODERS" != "CONFIG_H264_AMF_ENCODER CONFIG_H264_MF_ENCODER CONFIG_H264_NVENC_ENCODER CONFIG_H264_QSV_ENCODER CONFIG_RAWVIDEO_ENCODER " ]; then echo "FFmpeg (companion) configured with other encoders: $ENCODERS" >&2 exit 1 fi fi # No external library but libvpl (CONFIG_LIB* are FFmpeg's external libraries: libopenh264, libkvazaar, ...; # libvpl also sets CONFIG_LIBMFX, the name FFmpeg's QSV code depends on). EXTERNAL=$(grep -oE '^#define CONFIG_LIB[A-Z0-9_]+ 1' "$BLD/config.h" | awk '{print $2}' | tr '\n' ' ') if [ "$EXTERNAL" != "CONFIG_LIBMFX CONFIG_LIBVPL " ]; then echo "FFmpeg configured with external libraries other than libvpl: $EXTERNAL" >&2 exit 1 fi # Given to make, not the configure line (which avcodec_configuration() records): the path maps, the static # MinGW runtime (libgcc, libstdc++ for the libvpl dispatcher, the CRT's static parts), no PE timestamp. { echo "CFLAGS += $PREFIX_MAP" echo "LDSOFLAGS += -static -static-libgcc -static-libstdc++ -Wl,--no-insert-timestamp" [ $COMPANION = 0 ] || echo "LDEXEFLAGS += -static -static-libgcc -static-libstdc++ -Wl,--no-insert-timestamp" } >> "$BLD/ffbuild/config.mak" log "FFmpeg ${FFMPEG_VERSION}: build ($JOBS jobs)" make -C "$BLD" -j "$JOBS" >"$BUILD_ROOT/make.log" 2>&1 || { tail -60 "$BUILD_ROOT/make.log"; exit 1; } grep -iE 'warning: .*(deprecated|implicit)' "$BUILD_ROOT/make.log" | head -5 || true make -C "$BLD" install >/dev/null cp "$SRC/COPYING.LGPLv2.1" "$LIC/FFmpeg-COPYING.LGPLv2.1.txt" cp "$SRC/LICENSE.md" "$LIC/FFmpeg-LICENSE.md" cp /usr/share/doc/mingw-w64-x86-64-dev/copyright "$LIC/MinGW-w64-runtime-copyright.txt" for p in gcc-mingw-w64-x86-64-win32 gcc-mingw-w64-base; do [ -f "/usr/share/doc/$p/copyright" ] && { cp "/usr/share/doc/$p/copyright" "$LIC/GCC-runtime-copyright.txt"; break; } done # The package: BtbN's layout. log "package $NAME" PKG="$OUT/$NAME" rm -rf "${PKG:?}" "$OUT/$NAME.zip" if [ $COMPANION = 1 ]; then # The companion's layout: ffmpeg.exe alone (stripped at install), its licences, BUILD.txt. mkdir -p "$PKG/licenses" cp "$PREFIX/bin/ffmpeg.exe" "$PKG/ffmpeg.exe" BINARIES=("$PKG/ffmpeg.exe") SUMMED=(ffmpeg.exe) else mkdir -p "$PKG/bin" "$PKG/lib" "$PKG/include" "$PKG/licenses" cp "$PREFIX"/bin/*.dll "$PKG/bin/" cp -r "$PREFIX"/include/* "$PKG/include/" for def in "$PREFIX"/lib/*.def; do base=$(basename "$def" .def) # avcodec-62 lib=${base%-*} # avcodec cp "$def" "$PKG/lib/$lib.def" "$DLLTOOL" -m i386:x86-64 -d "$def" -D "$base.dll" -l "$PKG/lib/$lib.lib" [ -f "$PREFIX/lib/lib$lib.dll.a" ] && cp "$PREFIX/lib/lib$lib.dll.a" "$PKG/lib/" done BINARIES=("$PKG"/bin/*.dll) SUMMED=(bin lib) fi cp "$SRC/COPYING.LGPLv2.1" "$PKG/LICENSE.txt" cp "$LIC"/* "$PKG/licenses/" cp "$0" "$PKG/licenses/build_ffmpeg_lgpl_win64.sh" # The checks: imports and leaks. log "check" ALLOWED=" advapi32.dll bcrypt.dll crypt32.dll d3d11.dll dxgi.dll gdi32.dll kernel32.dll mfplat.dll mf.dll \ msvcrt.dll ncrypt.dll ole32.dll oleaut32.dll psapi.dll secur32.dll shell32.dll shlwapi.dll user32.dll uuid.dll \ ws2_32.dll dxva2.dll mfuuid.dll propsys.dll strmiids.dll setupapi.dll version.dll " IMPORTS_TXT="" ALL_DLLS=$( [ $COMPANION = 1 ] || (cd "$PKG/bin" && ls *.dll) ) for f in "${BINARIES[@]}"; do imports=$($TRIPLE-objdump -p "$f" | sed -n 's/^\s*DLL Name: \(.*\)$/\1/p' | tr 'A-Z' 'a-z' | sort -u | tr '\n' ' ') line="$(basename "$f"): $imports" echo "$line" IMPORTS_TXT+="$line"$'\n' for i in $imports; do case " $(echo "$ALL_DLLS" | tr 'A-Z' 'a-z' | tr '\n' ' ') " in *" $i "*) continue ;; esac case "$i" in api-ms-win-*) continue ;; esac case "$ALLOWED" in *" $i "*) ;; *) echo "$(basename "$f") imports $i, not a Windows system DLL" >&2; exit 1 ;; esac done user=$(id -un) leaks=$($TRIPLE-strings -a "$f" | grep -F -e /home/ -e /root/ -e /work -e "$WORK" -e "$OUT" -e "$HOME/" || true) if [ "$user" != root ]; then leaks+=$($TRIPLE-strings -a "$f" | grep -F -e "$user" || true); fi if [ -n "${SUDO_USER:-}" ]; then leaks+=$($TRIPLE-strings -a "$f" | grep -F -e "$SUDO_USER" || true); fi if [ -n "$leaks" ]; then echo "$f holds paths of the build machine:" >&2; echo "$leaks" | head -5 >&2 exit 1 fi if $TRIPLE-strings -a "$f" | grep -qE -e '--enable-(gpl|nonfree|version3|libopenh264|libkvazaar|libx26[45])'; then echo "$f: its configuration names a GPL, nonfree or external codec option" >&2; exit 1 fi done for gone in libopenh264 libkvazaar libx264 libx265; do if $TRIPLE-strings -a "${BINARIES[@]}" | grep -qx "$gone"; then echo "$NAME holds an encoder named $gone" >&2; exit 1; fi done # BUILD.txt. CONFIGURATION=$(sed -n 's/^#define FFMPEG_CONFIGURATION "\(.*\)"$/\1/p' "$BLD/config.h") LIBVERS="" for l in avcodec avformat avutil swscale swresample; do v="" for part in MAJOR MINOR MICRO; do v+=$(cat "$PREFIX/include/lib$l"/version*.h | sed -n "s/^#define LIB${l^^}_VERSION_${part} *\([0-9]*\).*/\1/p" | head -1). done LIBVERS+="lib$l ${v%.}, " done PKGVERS=$(dpkg-query -W -f '${Package} ${Version}\n' gcc-mingw-w64-x86-64-win32 g++-mingw-w64-x86-64-win32 \ binutils-mingw-w64-x86-64 mingw-w64-x86-64-dev nasm llvm cmake pkgconf 2>/dev/null || echo "(dpkg-query not available)") { if [ $COMPANION = 1 ]; then echo "FFmpeg ${FFMPEG_VERSION} for Windows x64, LGPL version 2.1 or later: the static ffmpeg.exe of the Bernard" echo "Review streaming companion (--companion: only its components, see the configure line), built" else echo "FFmpeg ${FFMPEG_VERSION} for Windows x64, LGPL version 2.1 or later, built for Bernard Review" fi echo "by tools/windows/build_ffmpeg_lgpl_win64.sh (a copy is in licenses/), cross-compiled with MinGW-w64." echo echo "Libraries: ${LIBVERS%, }" echo "Licence: LICENSE.txt is FFmpeg's COPYING.LGPLv2.1 (no component needs version 3). The notices of the" echo "parts compiled in are in licenses/: FFmpeg's LICENSE.md, nv-codec-headers and AMF headers (MIT), libvpl" echo "(MIT), zlib, and the MinGW-w64 / GCC runtime copyright files of the toolchain packages." echo "No external codec library: no OpenH264, Kvazaar, x264, x265, dav1d, aom, vpx, opus or any other." echo "NVENC: nv-codec-headers ${NVCODEC_TAG} (NVENC API 12.2), NVIDIA driver 551.76 or newer." echo echo "Sources (unmodified; all are in ${NAME}-src.tar, to publish with each release):" echo " FFmpeg ${FFMPEG_VERSION} ${FFMPEG_URL}" echo " SHA-256 ${FFMPEG_SHA256}" echo " GPG: good signature ($SIG_DATE) by the FFmpeg release signing key ${FFMPEG_KEY_FPR}" echo " nv-codec-headers ${NVCODEC_TAG} ${NVCODEC_URL}" echo " SHA-256 ${NVCODEC_SHA256}" echo " AMF headers ${AMF_TAG} ${AMF_URL}" echo " SHA-256 ${AMF_SHA256}" echo " libvpl ${VPL_TAG} ${VPL_URL}" echo " SHA-256 ${VPL_SHA256}" echo " zlib ${ZLIB_VERSION} ${ZLIB_URL}" echo " SHA-256 ${ZLIB_SHA256}" echo echo "Configure line (avcodec_configuration()):" echo " ${CONFIGURATION}" echo "Given to make as well (not recorded by FFmpeg): $PREFIX_MAP" if [ $COMPANION = 1 ]; then echo " -static -static-libgcc -static-libstdc++ -Wl,--no-insert-timestamp (program link)" else echo " -static -static-libgcc -static-libstdc++ -Wl,--no-insert-timestamp (shared-library link)" echo "MSVC import libraries lib/*.lib: $(basename "$DLLTOOL") -m i386:x86-64 from lib/*.def" fi echo echo "Build container: ${BERNARD_BUILD_IMAGE:-(not given: set BERNARD_BUILD_IMAGE)}" echo "Toolchain packages:" echo "$PKGVERS" | sed 's/^/ /' echo echo "DLL imports (x86_64-w64-mingw32-objdump -p):" printf '%s' "$IMPORTS_TXT" | sed 's/^/ /' echo echo "SHA-256 of the files:" (cd "$PKG" && find "${SUMMED[@]}" -type f | LC_ALL=C sort | xargs sha256sum | sed 's/^/ /') } > "$PKG/BUILD.txt" # The zip (fixed dates and order, no extra attributes) and the source archive. # Every file is dated from the FFmpeg release signature, so the same inputs give the same zip. SOURCE_DATE=$(TZ=UTC date -d "@$SIG_EPOCH" +%Y%m%d%H%M.%S) find "$PKG" -exec env TZ=UTC touch -h -t "$SOURCE_DATE" {} + (cd "$OUT" && find "$NAME" | LC_ALL=C sort | zip -X -q -9 "$NAME.zip" -@) SRC_TAR="$OUT/$NAME-src.tar" SRC_DIR="$BUILD_ROOT/$NAME-src" mkdir -p "$SRC_DIR" for t in "ffmpeg-${FFMPEG_VERSION}.tar.xz" "ffmpeg-${FFMPEG_VERSION}.tar.xz.asc" "nv-codec-headers-${NVCODEC_TAG}.tar.gz" \ "AMF-headers-${AMF_TAG}.tar.gz" "libvpl-${VPL_TAG}.tar.gz" "zlib-${ZLIB_VERSION}.tar.xz"; do cp "$WORK/tarballs/$t" "$SRC_DIR/" done cp "$0" "$SRC_DIR/build_ffmpeg_lgpl_win64.sh" cp "$PKG/BUILD.txt" "$SRC_DIR/BUILD.txt" tar --sort=name --mtime="@$SIG_EPOCH" --owner=0 --group=0 --numeric-owner \ -cf "$SRC_TAR" -C "$BUILD_ROOT" "$NAME-src" rm -rf "${BUILD_ROOT:?}" "${DEPS:?}" "${PREFIX:?}" log "done in $((SECONDS - T_ALL)) s" (cd "$OUT" && sha256sum "$NAME.zip" "$NAME-src.tar") exit 0