feat: publish av-tools 0.2.0
This commit is contained in:
420
scripts/build-reviewed-ffmpeg-core.sh
Normal file
420
scripts/build-reviewed-ffmpeg-core.sh
Normal file
@@ -0,0 +1,420 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
scripts/build-reviewed-ffmpeg-core.sh st|mt SOURCE_ARCHIVE EMSDK_ROOT [JOBS]
|
||||
|
||||
Builds one private, staged FFmpeg WebAssembly core from the exact
|
||||
0.12.10 Corresponding Source archive. The only upstream source change is
|
||||
ffmpeg.wasm commit b409e36475bc21f0451b5b1e1d126fa82871439a:
|
||||
`-sSTACK_SIZE=5MB`, which fixes upstream issues #591 and #775.
|
||||
|
||||
Output remains below .core-build/reviewed-stack5m and is never copied into the
|
||||
application automatically. Re-running resumes at verified component stamps.
|
||||
EOF
|
||||
}
|
||||
|
||||
if [[ $# -lt 3 || $# -gt 4 ]]; then
|
||||
usage >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
MODE=$1
|
||||
SOURCE_ARCHIVE=$2
|
||||
EMSDK_ROOT=$3
|
||||
JOBS=${4:-2}
|
||||
|
||||
if [[ "$MODE" != "st" && "$MODE" != "mt" ]]; then
|
||||
echo "Mode must be st or mt." >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ ! "$JOBS" =~ ^[1-8]$ ]]; then
|
||||
echo "JOBS must be an integer from 1 to 8." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
SCRIPT_DIRECTORY=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
|
||||
REPOSITORY_ROOT=$(cd -- "$SCRIPT_DIRECTORY/.." && pwd -P)
|
||||
SOURCE_ARCHIVE=$(realpath -- "$SOURCE_ARCHIVE")
|
||||
EMSDK_ROOT=$(realpath -- "$EMSDK_ROOT")
|
||||
|
||||
EXPECTED_ARCHIVE_SHA256=2ab505f72be883d1257bc71cbb93880d1ade5bc029792621cb87b4ecc068399a
|
||||
EXPECTED_EMCC_SHA256=b0c9551f9155fa9c028efa24591acdfd30f02f32fb247cf9266f5f0f2ede7589
|
||||
EXPECTED_CMAKE_SHA256=ade9326f83f4996b17e2cb1bbfe4848df30a237ef5769cd3c00239b9f0d3ec58
|
||||
EXPECTED_SOURCE_ROOT=ffmpeg-core-0.12.10-reviewed-stack5m.1-corresponding-source
|
||||
SOURCE_DATE_EPOCH=1736208000
|
||||
|
||||
if [[ ! -f "$SOURCE_ARCHIVE" || -L "$SOURCE_ARCHIVE" ]]; then
|
||||
echo "SOURCE_ARCHIVE must be a regular, non-symlink file." >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ ! -d "$EMSDK_ROOT" || -L "$EMSDK_ROOT" ]]; then
|
||||
echo "EMSDK_ROOT must be a real directory." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
actual_archive_sha256=$(sha256sum -- "$SOURCE_ARCHIVE" | cut -d' ' -f1)
|
||||
if [[ "$actual_archive_sha256" != "$EXPECTED_ARCHIVE_SHA256" ]]; then
|
||||
echo "Corresponding Source archive SHA-256 mismatch." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "$EMSDK_ROOT/emsdk_env.sh" >/dev/null
|
||||
export PATH="$REPOSITORY_ROOT/scripts/build-tools:$PATH"
|
||||
if [[ -z "${AV_REVIEWED_CMAKE_BIN:-}" ]]; then
|
||||
echo "AV_REVIEWED_CMAKE_BIN must identify the exact CMake 3.27.9 binary directory." >&2
|
||||
exit 2
|
||||
fi
|
||||
AV_REVIEWED_CMAKE_BIN=$(realpath -- "$AV_REVIEWED_CMAKE_BIN")
|
||||
if [[ ! -d "$AV_REVIEWED_CMAKE_BIN" || -L "$AV_REVIEWED_CMAKE_BIN" ]]; then
|
||||
echo "AV_REVIEWED_CMAKE_BIN must be a real directory." >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ $(sha256sum -- "$AV_REVIEWED_CMAKE_BIN/cmake" | cut -d' ' -f1) != "$EXPECTED_CMAKE_SHA256" ]]; then
|
||||
echo "CMake binary SHA-256 mismatch; exact PyPI CMake 3.27.9 is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
export PATH="$AV_REVIEWED_CMAKE_BIN:$PATH"
|
||||
EMCC=$(realpath -- "$EMSDK_ROOT/upstream/emscripten/emcc")
|
||||
actual_emcc_sha256=$(sha256sum -- "$EMCC" | cut -d' ' -f1)
|
||||
if [[ "$actual_emcc_sha256" != "$EXPECTED_EMCC_SHA256" ]]; then
|
||||
echo "Emscripten compiler SHA-256 mismatch; activate exact version 3.1.40." >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! "$EMCC" --version | head -1 | grep -Fq \
|
||||
"3.1.40 (5c27e79dd0a9c4e27ef2326841698cdd4f6b5784)"; then
|
||||
echo "Emscripten identity mismatch; exact version 3.1.40 is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ $(cmake --version | head -1) != "cmake version 3.27.9" ]]; then
|
||||
echo "CMake identity mismatch; exact version 3.27.9 is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUILD_PARENT="$REPOSITORY_ROOT/.core-build/reviewed-stack5m"
|
||||
MODE_ROOT="$BUILD_PARENT/$MODE"
|
||||
EXTRACT_ROOT="$MODE_ROOT/source"
|
||||
SOURCE_ROOT="$EXTRACT_ROOT/$EXPECTED_SOURCE_ROOT/sources"
|
||||
INSTALL_DIR="$MODE_ROOT/install"
|
||||
OUTPUT_DIR="$MODE_ROOT/output"
|
||||
STAMP_DIR="$MODE_ROOT/stamps"
|
||||
|
||||
case "$MODE_ROOT" in
|
||||
"$REPOSITORY_ROOT"/.core-build/reviewed-stack5m/st | \
|
||||
"$REPOSITORY_ROOT"/.core-build/reviewed-stack5m/mt) ;;
|
||||
*)
|
||||
echo "Refusing an unexpected build directory: $MODE_ROOT" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p -- "$MODE_ROOT" "$STAMP_DIR"
|
||||
if [[ ! -f "$STAMP_DIR/source-prepared" ]]; then
|
||||
if [[ -e "$EXTRACT_ROOT" ]]; then
|
||||
echo "Incomplete source staging already exists at $EXTRACT_ROOT." >&2
|
||||
echo "Remove only that private mode directory to restart it." >&2
|
||||
exit 1
|
||||
fi
|
||||
mkdir -p -- "$EXTRACT_ROOT"
|
||||
tar -xf "$SOURCE_ARCHIVE" -C "$EXTRACT_ROOT"
|
||||
|
||||
lock="$EXTRACT_ROOT/$EXPECTED_SOURCE_ROOT/SOURCE_LOCK.json"
|
||||
node -e '
|
||||
const fs = require("node:fs");
|
||||
const lock = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
||||
const expected = new Map([
|
||||
["ffmpeg.wasm", "71aa99d37c02a7b4c435275ca9ef50e612f6efa1"],
|
||||
["FFmpeg", "4729204c17f756e186d622060088371d10b34f7e"],
|
||||
["Opus", "e85ed7726db5d677c9c0677298ea0cb9c65bdd23"],
|
||||
]);
|
||||
for (const [name, revision] of expected) {
|
||||
const entry = lock.sources.find((candidate) => candidate.name === name);
|
||||
if (!entry || entry.revision !== revision) {
|
||||
throw new Error(`Unexpected ${name} source identity.`);
|
||||
}
|
||||
}
|
||||
' "$lock"
|
||||
|
||||
wasm_build_script="$SOURCE_ROOT/ffmpeg.wasm/build/ffmpeg-wasm.sh"
|
||||
if grep -Fq -- "-sSTACK_SIZE=" "$wasm_build_script"; then
|
||||
echo "Base archive unexpectedly already selects a stack size." >&2
|
||||
exit 1
|
||||
fi
|
||||
sed -i \
|
||||
'/-sUSE_SDL=2/a\ -sSTACK_SIZE=5MB # upstream b409e364: support libopus' \
|
||||
"$wasm_build_script"
|
||||
if [[ $(grep -Fc -- "-sSTACK_SIZE=5MB" "$wasm_build_script") -ne 1 ]]; then
|
||||
echo "Could not apply the reviewed one-line stack patch exactly once." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
touch "$STAMP_DIR/source-prepared"
|
||||
fi
|
||||
|
||||
# The upstream container uses unbounded `make -j`. Keep local builds bounded
|
||||
# without changing compiled source, and allow a resumed build to select a new
|
||||
# bounded job count.
|
||||
while IFS= read -r -d '' build_script; do
|
||||
sed -E -i \
|
||||
-e "s/ -j([1-8])?$/ -j$JOBS/" \
|
||||
-e "s/ -j([1-8])? / -j$JOBS /g" \
|
||||
"$build_script"
|
||||
done < <(find "$SOURCE_ROOT/ffmpeg.wasm/build" -type f -name '*.sh' -print0)
|
||||
|
||||
# The upstream container does not provide native NASM. Some development hosts
|
||||
# do; allowing CMake to discover it would produce native x86 objects that
|
||||
# cannot be linked into WebAssembly. Make the container's effective choice
|
||||
# explicit and deterministic.
|
||||
X265_BUILD_SCRIPT="$SOURCE_ROOT/ffmpeg.wasm/build/x265.sh"
|
||||
if ! grep -Fq -- "-DENABLE_ASSEMBLY=OFF" "$X265_BUILD_SCRIPT"; then
|
||||
sed -i \
|
||||
'/-DENABLE_LIBNUMA=OFF/a\ -DENABLE_ASSEMBLY=OFF' \
|
||||
"$X265_BUILD_SCRIPT"
|
||||
fi
|
||||
if [[ $(grep -Fc -- "-DENABLE_ASSEMBLY=OFF" "$X265_BUILD_SCRIPT") -ne 1 ]]; then
|
||||
echo "Could not disable native x265 assembly exactly once." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Upstream builds each dependency in an isolated container stage. A cumulative
|
||||
# local prefix would otherwise let FreeType discover the already-built zlib and
|
||||
# let HarfBuzz discover FreeType. Their resulting private dependencies are not
|
||||
# present in FFmpeg's pkg-config probes and, on a developer workstation, optional
|
||||
# libraries such as libpng may even resolve to host-native /usr objects. Make the
|
||||
# isolated-stage dependency choices explicit.
|
||||
FREETYPE_BUILD_SCRIPT="$SOURCE_ROOT/ffmpeg.wasm/build/freetype2.sh"
|
||||
if ! grep -Fq -- "--with-zlib=no" "$FREETYPE_BUILD_SCRIPT"; then
|
||||
sed -i \
|
||||
'/--without-harfbuzz/a\ --with-zlib=no\n --with-bzip2=no\n --with-png=no\n --with-brotli=no' \
|
||||
"$FREETYPE_BUILD_SCRIPT"
|
||||
fi
|
||||
for freetype_flag in \
|
||||
"--without-harfbuzz" \
|
||||
"--with-zlib=no" \
|
||||
"--with-bzip2=no" \
|
||||
"--with-png=no" \
|
||||
"--with-brotli=no"; do
|
||||
if [[ $(grep -Fc -- "$freetype_flag" "$FREETYPE_BUILD_SCRIPT") -ne 1 ]]; then
|
||||
echo "Could not select isolated FreeType dependency flags exactly once." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
HARFBUZZ_BUILD_SCRIPT="$SOURCE_ROOT/ffmpeg.wasm/build/harfbuzz.sh"
|
||||
for harfbuzz_flag in \
|
||||
"--with-freetype=no" \
|
||||
"--with-glib=no" \
|
||||
"--with-gobject=no" \
|
||||
"--with-cairo=no" \
|
||||
"--with-icu=no" \
|
||||
"--with-graphite2=no" \
|
||||
"--with-uniscribe=no" \
|
||||
"--with-directwrite=no" \
|
||||
"--with-coretext=no"; do
|
||||
if ! grep -Fq -- "$harfbuzz_flag" "$HARFBUZZ_BUILD_SCRIPT"; then
|
||||
sed -i \
|
||||
"/--enable-static/a\\ $harfbuzz_flag" \
|
||||
"$HARFBUZZ_BUILD_SCRIPT"
|
||||
fi
|
||||
if [[ $(grep -Fc -- "$harfbuzz_flag" "$HARFBUZZ_BUILD_SCRIPT") -ne 1 ]]; then
|
||||
echo "Could not select isolated HarfBuzz dependency flags exactly once." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p -- "$INSTALL_DIR" "$OUTPUT_DIR"
|
||||
|
||||
export INSTALL_DIR
|
||||
export EM_PKG_CONFIG_PATH="$EMSDK/upstream/emscripten/system/lib/pkgconfig:$INSTALL_DIR/lib/pkgconfig"
|
||||
export EM_TOOLCHAIN_FILE="$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake"
|
||||
export PKG_CONFIG_PATH="$INSTALL_DIR/lib/pkgconfig:$EM_PKG_CONFIG_PATH"
|
||||
export LANG=C
|
||||
export LC_ALL=C
|
||||
export TZ=UTC
|
||||
export SOURCE_DATE_EPOCH
|
||||
export ZERO_AR_DATE=1
|
||||
|
||||
MODE_FLAGS="-O3 -msimd128"
|
||||
if [[ "$MODE" == "st" ]]; then
|
||||
export FFMPEG_ST=yes
|
||||
unset FFMPEG_MT || true
|
||||
else
|
||||
MODE_FLAGS="$MODE_FLAGS -sUSE_PTHREADS -pthread"
|
||||
# HarfBuzz's pinned upstream build script reads this value directly under
|
||||
# `set -u`, while the other scripts use its presence to disable threading.
|
||||
# An exported empty value keeps both behaviours correct for the MT build.
|
||||
export FFMPEG_ST=
|
||||
export FFMPEG_MT=yes
|
||||
fi
|
||||
export CFLAGS="-I$INSTALL_DIR/include $MODE_FLAGS"
|
||||
export CXXFLAGS="$CFLAGS"
|
||||
export LDFLAGS="-L$INSTALL_DIR/lib $CFLAGS"
|
||||
|
||||
FFMPEG_WASM="$SOURCE_ROOT/ffmpeg.wasm"
|
||||
|
||||
build_component() {
|
||||
local stamp=$1
|
||||
local directory=$2
|
||||
local script=$3
|
||||
if [[ -f "$STAMP_DIR/$stamp" ]]; then
|
||||
echo "[reviewed-core:$MODE] reuse $stamp"
|
||||
return
|
||||
fi
|
||||
echo "[reviewed-core:$MODE] build $stamp"
|
||||
(
|
||||
cd -- "$SOURCE_ROOT/$directory"
|
||||
if [[ "$stamp" == "libvpx" ]]; then
|
||||
# libvpx embeds its configure command in the linked library. A relative
|
||||
# prefix keeps that shipped identity independent of the checkout path
|
||||
# while still installing into this mode's private prefix.
|
||||
INSTALL_DIR=../../../../install \
|
||||
CFLAGS="-I../../../../install/include $MODE_FLAGS" \
|
||||
CXXFLAGS="-I../../../../install/include $MODE_FLAGS" \
|
||||
LDFLAGS="-L../../../../install/lib -I../../../../install/include $MODE_FLAGS" \
|
||||
bash "$FFMPEG_WASM/build/$script"
|
||||
else
|
||||
bash "$FFMPEG_WASM/build/$script"
|
||||
fi
|
||||
)
|
||||
touch "$STAMP_DIR/$stamp"
|
||||
}
|
||||
|
||||
if [[ ! -f "$STAMP_DIR/sdl2" ]]; then
|
||||
if [[ "$MODE" == "st" ]]; then
|
||||
embuilder build sdl2
|
||||
else
|
||||
embuilder build sdl2-mt
|
||||
fi
|
||||
touch "$STAMP_DIR/sdl2"
|
||||
fi
|
||||
|
||||
build_component x264 x264 x264.sh
|
||||
build_component x265 x265 x265.sh
|
||||
build_component libvpx libvpx libvpx.sh
|
||||
build_component lame lame lame.sh
|
||||
build_component ogg ogg ogg.sh
|
||||
build_component theora theora theora.sh
|
||||
build_component opus opus opus.sh
|
||||
build_component vorbis vorbis vorbis.sh
|
||||
build_component zlib zlib zlib.sh
|
||||
build_component libwebp libwebp libwebp.sh
|
||||
build_component freetype2 freetype2 freetype2.sh
|
||||
build_component fribidi fribidi fribidi.sh
|
||||
build_component harfbuzz harfbuzz harfbuzz.sh
|
||||
build_component libass libass libass.sh
|
||||
build_component zimg zimg zimg.sh
|
||||
|
||||
# Configuration probes from Autotools projects must never smuggle host-native
|
||||
# include/library paths into the reviewed WebAssembly link.
|
||||
if grep -R -E -n -- \
|
||||
'/usr/(lib|include)|/lib/(x86_64|i[3-6]86)-linux-gnu' \
|
||||
"$INSTALL_DIR/lib/pkgconfig" "$INSTALL_DIR/lib"/*.la; then
|
||||
echo "A staged dependency recorded a host-native build path." >&2
|
||||
exit 1
|
||||
fi
|
||||
if emnm "$INSTALL_DIR/lib/libfreetype.a" |
|
||||
grep -F -- " U png_" >/dev/null; then
|
||||
echo "FreeType unexpectedly retained an unbundled libpng dependency." >&2
|
||||
exit 1
|
||||
fi
|
||||
if emnm "$INSTALL_DIR/lib/libfreetype.a" |
|
||||
grep -E -- ' U (inflate|BZ2_|Brotli)' >/dev/null; then
|
||||
echo "FreeType unexpectedly retained a cross-stage compression dependency." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FFMPEG_SOURCE="$SOURCE_ROOT/FFmpeg"
|
||||
# FFmpeg records --extra-cflags in `ffmpeg -buildconf`. Use a path relative to
|
||||
# its source root so the shipped runtime identity is independent of the
|
||||
# maintainer's checkout location. Dependency builds above retain their
|
||||
# absolute private staging prefix; only the final FFmpeg configuration is
|
||||
# embedded in the distributed core.
|
||||
FFMPEG_INSTALL_RELATIVE=../../../../install
|
||||
export CFLAGS="-I$FFMPEG_INSTALL_RELATIVE/include $MODE_FLAGS"
|
||||
export CXXFLAGS="$CFLAGS"
|
||||
export LDFLAGS="-L$INSTALL_DIR/lib $CFLAGS"
|
||||
if [[ ! -f "$STAMP_DIR/ffmpeg" ]]; then
|
||||
echo "[reviewed-core:$MODE] build FFmpeg"
|
||||
(
|
||||
cd -- "$FFMPEG_SOURCE"
|
||||
bash "$FFMPEG_WASM/build/ffmpeg.sh" \
|
||||
--enable-gpl \
|
||||
--enable-libx264 \
|
||||
--enable-libx265 \
|
||||
--enable-libvpx \
|
||||
--enable-libmp3lame \
|
||||
--enable-libtheora \
|
||||
--enable-libvorbis \
|
||||
--enable-libopus \
|
||||
--enable-zlib \
|
||||
--enable-libwebp \
|
||||
--enable-libfreetype \
|
||||
--enable-libfribidi \
|
||||
--enable-libass \
|
||||
--enable-libzimg
|
||||
)
|
||||
touch "$STAMP_DIR/ffmpeg"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$STAMP_DIR/core" ]]; then
|
||||
echo "[reviewed-core:$MODE] link ffmpeg-core with 5 MiB stack"
|
||||
mkdir -p -- "$FFMPEG_SOURCE/src"
|
||||
cp -a -- "$FFMPEG_WASM/src/bind" "$FFMPEG_SOURCE/src/"
|
||||
cp -a -- "$FFMPEG_WASM/src/fftools" "$FFMPEG_SOURCE/src/"
|
||||
cp -- "$FFMPEG_WASM/build/ffmpeg-wasm.sh" "$FFMPEG_SOURCE/build-reviewed-core.sh"
|
||||
(
|
||||
cd -- "$FFMPEG_SOURCE"
|
||||
bash ./build-reviewed-core.sh \
|
||||
-lx264 \
|
||||
-lx265 \
|
||||
-lvpx \
|
||||
-lmp3lame \
|
||||
-logg \
|
||||
-ltheora \
|
||||
-lvorbis \
|
||||
-lvorbisenc \
|
||||
-lvorbisfile \
|
||||
-lopus \
|
||||
-lz \
|
||||
-lwebpmux \
|
||||
-lwebp \
|
||||
-lsharpyuv \
|
||||
-lfreetype \
|
||||
-lfribidi \
|
||||
-lharfbuzz \
|
||||
-lass \
|
||||
-lzimg \
|
||||
-sEXPORT_ES6 \
|
||||
-o "$OUTPUT_DIR/ffmpeg-core.js"
|
||||
)
|
||||
touch "$STAMP_DIR/core"
|
||||
fi
|
||||
|
||||
expected_files=(ffmpeg-core.js ffmpeg-core.wasm)
|
||||
if [[ "$MODE" == "mt" ]]; then
|
||||
expected_files+=(ffmpeg-core.worker.js)
|
||||
fi
|
||||
for expected_file in "${expected_files[@]}"; do
|
||||
if [[ ! -s "$OUTPUT_DIR/$expected_file" || -L "$OUTPUT_DIR/$expected_file" ]]; then
|
||||
echo "Missing staged output: $OUTPUT_DIR/$expected_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -aFq -- "$REPOSITORY_ROOT" "$OUTPUT_DIR/$expected_file" ||
|
||||
grep -aFq -- "$EMSDK_ROOT" "$OUTPUT_DIR/$expected_file"; then
|
||||
echo "Staged output leaks a maintainer-local build path: $expected_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
(
|
||||
cd -- "$OUTPUT_DIR"
|
||||
sha256sum -- "${expected_files[@]}" >SHA256SUMS
|
||||
)
|
||||
|
||||
echo "[reviewed-core:$MODE] staged outputs"
|
||||
cat "$OUTPUT_DIR/SHA256SUMS"
|
||||
Reference in New Issue
Block a user