#!/bin/bash
# FFmpeg for the Windows app (spec 2026-10-14-own-ffmpeg-build-design.md section 6): win64, static,
# only the components the app's command lines use. Runs as root inside debian:forky (mingw-w64 14:
# 12 has windows.graphics.capture.interop.h but no IGraphicsCaptureSession3, and gfxcapture needs
# both), as the CI does:
#   docker run --rm -v "$PWD:/repo" -w /repo debian:forky bash app/ffmpeg/build.sh
# Sources come from our GPL mirror (sources.txt, sha256-checked), so the build can only use what is
# published. Output: app/ffmpeg/out/{ffmpeg.exe,LICENSE.txt,ffmpeg.exe.sha256,buildconf.txt}
# (LICENSE.txt: FFmpeg's GPL v3, then the linked libraries' notices).
# FFBUILD_SOURCES: where downloads are kept between runs (the CI fills and caches it); FFBUILD_WORK: scratch.
set -euo pipefail
here=$(cd "$(dirname "$0")" && pwd)
work=${FFBUILD_WORK:-/tmp/ffbuild}
src=${FFBUILD_SOURCES:-$work/sources}
prefix=$work/prefix
out=$here/out
host=x86_64-w64-mingw32
jobs=$(nproc)

export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq --no-install-recommends \
  gcc-mingw-w64-x86-64-posix g++-mingw-w64-x86-64-posix mingw-w64-tools binutils-mingw-w64-x86-64 \
  nasm cmake ninja-build make pkg-config autoconf automake libtool patch git xz-utils curl ca-certificates >/dev/null
# The posix thread model: gfxcapture runs its capture on a std::thread.
update-alternatives --set $host-gcc /usr/bin/$host-gcc-posix
update-alternatives --set $host-g++ /usr/bin/$host-g++-posix

rm -rf "$work/build" "$prefix" "$out"
mkdir -p "$src" "$work/build" "$prefix" "$out"

# Download (once) and check every source.
while read -r url sha; do
  case $url in ''|'#'*) continue ;; esac
  f=$src/${url##*/}
  if [ ! -f "$f" ]; then
    rm -f "$f.part"
    curl -fsSL -o "$f.part" "$url"
    mv "$f.part" "$f"
  fi
  echo "$sha  $f" | sha256sum -c --quiet -
done < "$here/sources.txt"
tarball() { ls "$src"/"$1"*.tar.?z; }

# A dependency packed by BtbN's download.sh: the tree at the archive's root.
unpack() {
  mkdir -p "$work/build/$1"
  tar -xJf "$(tarball "$2")" -C "$work/build/$1"
}

cat > "$work/toolchain.cmake" <<EOF
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_C_COMPILER $host-gcc)
set(CMAKE_CXX_COMPILER $host-g++)
set(CMAKE_RC_COMPILER $host-windres)
set(CMAKE_FIND_ROOT_PATH /usr/$host $prefix)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
EOF

# x264, as BtbN's 50-x264.sh.
unpack x264 50-x264_
(
  cd "$work/build/x264"
  ./configure --host=$host --cross-prefix=$host- --prefix="$prefix" \
    --disable-cli --enable-static --enable-pic --disable-lavf --disable-swscale
  make -j"$jobs"
  make install
)

# opus, as the Mac's build.sh: autoreconf first (the packed tree lacks the autotools auxiliary files,
# and its m4/ holds symlinks into BtbN's container).
unpack opus 50-libopus_
(
  cd "$work/build/opus"
  rm -rf aclocal.m4 autom4te.cache
  find m4 -type l -delete
  autoreconf -isf
  ./configure --host=$host --prefix="$prefix" --disable-shared --enable-static --disable-extra-programs --disable-doc
  make -j"$jobs"
  make install
)

# nv-codec-headers: the tree BtbN uses for FFmpeg 9 (ffnvcodec/, master eddcea9e).
unpack ffnvcodec 50-ffnvcodec_
make -C "$work/build/ffnvcodec/ffnvcodec" PREFIX="$prefix" install

# AMF: headers only, under include/AMF as FFmpeg expects.
unpack amf 50-amf_
mkdir -p "$prefix/include"
cp -r "$work/build/amf/amf/public/include" "$prefix/include/AMF"

# libvpl (QSV), as BtbN's 50-onevpl.sh: its _MSC_VER fix, the static dispatcher, and -lstdc++ for
# whoever links it.
unpack libvpl 50-onevpl_
(
  cd "$work/build/libvpl"
  sed -i 's/^#if _MSC_VER < 1400$/#if defined(_MSC_VER) \&\& _MSC_VER < 1400/' libvpl/src/windows/mfx_dispatcher_defs.h
  cmake -S . -B build -GNinja -DCMAKE_TOOLCHAIN_FILE="$work/toolchain.cmake" -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX="$prefix" -DCMAKE_INSTALL_BINDIR="$prefix/bin" -DCMAKE_INSTALL_LIBDIR="$prefix/lib" \
    -DBUILD_DISPATCHER=ON -DBUILD_DEV=ON -DBUILD_PREVIEW=OFF -DBUILD_TOOLS=OFF \
    -DBUILD_TOOLS_ONEVPL_EXPERIMENTAL=OFF -DINSTALL_EXAMPLE_CODE=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTS=OFF
  ninja -C build -j"$jobs"
  ninja -C build install
  echo "Libs.private: -lstdc++" >> "$prefix/lib/pkgconfig/vpl.pc"
)

# FFmpeg. --disable-everything --disable-autodetect, then by name what the app's command lines use
# (encoders.rs, transmitter/args.rs, thumbnail.rs), what fftools inserts on its own (scale, aformat,
# aresample, and trim/atrim for an output -t, which the proofs and tests use; the buffer sources and
# sinks are always built) and nothing else. Proof F3 is the authority on this list. FFmpeg's own
# threads are w32threads (mingw's default; BtbN uses pthreads).
mkdir -p "$work/build/ffmpeg"
tar -xzf "$(tarball FFmpeg-)" -C "$work/build/ffmpeg" --strip-components=1
(
  cd "$work/build/ffmpeg"
  # Without it, version.sh runs `git describe` on the repo it sits in.
  echo n9.0.2-22-g46d8f462ee > VERSION
  # gfxcapture's dup_frames (spec section 4): a still source keeps sending frames.
  patch -p1 < "$here/gfxcapture-dup-frames.patch"
  PKG_CONFIG_LIBDIR=$prefix/lib/pkgconfig ./configure --prefix="$work/ffout" \
    --target-os=mingw32 --arch=x86_64 --cross-prefix=$host- --enable-cross-compile \
    --pkg-config=pkg-config --pkg-config-flags=--static \
    --extra-cflags="-I$prefix/include" --extra-ldflags="-L$prefix/lib -static" \
    --disable-everything --disable-autodetect \
    --enable-gpl --enable-version3 --enable-static --disable-shared \
    --disable-ffplay --disable-ffprobe --disable-doc --disable-debug \
    --enable-w32threads --enable-d3d11va \
    --enable-libx264 --enable-libopus --enable-ffnvcodec --enable-nvenc --enable-amf --enable-libvpl \
    --enable-filter=gfxcapture,fps,select,setpts,asetpts,hwdownload,format,scale,aformat,aresample,anull,null,trim,atrim \
    --enable-encoder=h264_nvenc,hevc_nvenc,av1_nvenc,h264_amf,hevc_amf,av1_amf,h264_qsv,hevc_qsv,av1_qsv,libx264,libopus,mjpeg \
    --enable-decoder=pcm_f32le --enable-demuxer=wav \
    --enable-muxer=rtsp,rtp,null,image2pipe --enable-protocol=pipe,file,tcp,udp,rtp \
    --extra-version=screen-share | tee "$work/configure.txt"
  grep -q '^License: GPL version 3 or later' "$work/configure.txt" || { echo "FAIL: license is not GPL v3 or later"; exit 1; }
  grep -q '^#define CONFIG_GFXCAPTURE_FILTER 1' config_components.h || {
    # configure only lists the dependencies, not the one that failed: show its own checks.
    echo "== why gfxcapture is off (ffbuild/config.log)"
    ls -l /usr/$host/include/windows.graphics.capture.interop.h || true
    grep -n -E '^(check_|test_cxx|C\+\+|.*cxx17)' ffbuild/config.log | grep -iE 'cxx17|std=c\+\+17|thread|d3d11|GraphicsCapture' | head -40 || true
    grep -n -i -B3 -A30 -E 'check_(cxx|type|cpp|struct).*(CIGraphicsCaptureSession3|IGraphicsCaptureItemInterop)|IGraphicsCaptureItemInterop|CIGraphicsCaptureSession3' ffbuild/config.log | head -150 || true
    grep -n -i -A12 -E 'check_cxxflags|test_cxx.*17|cxx17' ffbuild/config.log | head -60 || true
    echo "FAIL: gfxcapture is not enabled (toolchain headers?)"; exit 1
  }
  grep -q dup_frames libavfilter/vsrc_gfxcapture.c || { echo "FAIL: the dup_frames patch is not in"; exit 1; }
  make -j"$jobs"
  make install
  cp COPYING.GPLv3 "$out/LICENSE.txt"
  grep FFMPEG_CONFIGURATION config.h > "$out/buildconf.txt"
)
# LICENSE.txt: FFmpeg's GPL v3, which covers the whole exe (x264 is GPL v2 or later), then the notices
# of what is linked in with it, which BSD and MIT ask for next to the binary. GCC's libstdc++ and libgcc
# carry the GCC Runtime Library Exception, which asks for nothing.
notice() { printf '\n\n==== %s ====\n\n' "$1"; shift; cat "$@"; }
# winpthreads (gfxcapture's std::thread) and the CRT come from mingw-w64-x86-64-dev: its Debian copyright
# file holds the notices of every part of that runtime.
dpkg -S libwinpthread.a | grep -q '^mingw-w64-x86-64-dev:' || { echo "FAIL: libwinpthread.a is not from mingw-w64-x86-64-dev"; exit 1; }
{
  printf '\n\n==== Libraries linked into ffmpeg.exe with FFmpeg ====\n\n'
  printf 'x264 (GPL v2 or later, used under the GPL v3 above), opus (BSD 3-clause), libvpl (MIT),\n'
  printf 'AMF headers (MIT), nv-codec-headers (MIT), the mingw-w64 runtime and winpthreads (ZPL 2.1, MIT\n'
  printf "and others), GCC's libstdc++ and libgcc (GPL v3 with the GCC Runtime Library Exception).\n"
  printf 'Their notices follow.\n'
  notice 'opus' "$work/build/opus/COPYING"
  notice 'libvpl (Intel VPL dispatcher)' "$work/build/libvpl/LICENSE"
  notice 'AMF headers' "$work/build/amf/LICENSE.txt"
  # nv-codec-headers has no license file: each header opens with its notice (two different holders).
  notice 'nv-codec-headers' /dev/null
  for h in nvEncodeAPI.h dynlink_loader.h; do sed -n '1,/\*\//p' "$work/build/ffnvcodec/ffnvcodec/include/ffnvcodec/$h"; done
  notice "mingw-w64 runtime and winpthreads (Debian's copyright file of mingw-w64-x86-64-dev)" \
    /usr/share/doc/mingw-w64-x86-64-dev/copyright
} >> "$out/LICENSE.txt"
# apt-get takes forky's current packages (a testing suite): record the toolchain this build used.
dpkg-query -W 'gcc-mingw-w64*' 'g++-mingw-w64*' mingw-w64-x86-64-dev binutils-mingw-w64-x86-64 nasm cmake >> "$out/buildconf.txt"

$host-strip -o "$out/ffmpeg.exe" "$work/ffout/bin/ffmpeg.exe"
echo "== DLLs (Windows system DLLs only)"
dlls=$($host-objdump -p "$out/ffmpeg.exe" | sed -n 's/.*DLL Name: //p' | sort -u)
echo "$dlls"
if echo "$dlls" | grep -iE '^lib'; then echo "FAIL: a mingw runtime DLL is linked"; exit 1; fi
(cd "$out" && sha256sum ffmpeg.exe > ffmpeg.exe.sha256)
ls -l "$out"
cat "$out/buildconf.txt" "$out/ffmpeg.exe.sha256"
