#!/usr/bin/env python3 """Build Blender-pinned FFmpeg audio libraries for the embedded Audaspace plugin.""" import argparse import hashlib import json import os from pathlib import Path import shutil import subprocess import tarfile import urllib.request ROOT = Path(__file__).resolve().parents[2] VERSION = "8.1" SHA256 = "c07039598df7d64d3c8b42c4e25b1959fc908621c6f6c2946881133f3b27eda2" def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--source", type=Path) parser.add_argument("--jobs", type=int, default=3) args = parser.parse_args() base = ROOT / "build_web_ffmpeg" base.mkdir(exist_ok=True) source = args.source.resolve() if args.source else base / f"ffmpeg-{VERSION}" if not (source / "configure").is_file(): if args.source: raise FileNotFoundError(source) filename = f"ffmpeg-{VERSION}.tar.bz2" caches = [base / filename, Path.home() / "working/packages" / filename, Path.home() / "working/build_oiio_deps/downloads" / filename] archive = next((path for path in caches if path.is_file()), caches[0]) if not archive.is_file(): partial = archive.with_suffix(".partial") with urllib.request.urlopen(f"https://ffmpeg.org/releases/{filename}", timeout=60) as response, partial.open("wb") as out: shutil.copyfileobj(response, out) partial.replace(archive) if hashlib.sha256(archive.read_bytes()).hexdigest() != SHA256: raise RuntimeError("Blender-pinned FFmpeg archive digest mismatch") with tarfile.open(archive) as package: package.extractall(base, filter="data") if (source / "RELEASE").read_text().strip() != VERSION: raise RuntimeError("Expected Blender-pinned FFmpeg " + VERSION) prefix = base / "install" build = base / "build" build.mkdir(exist_ok=True) em = ROOT / ".emsdk/upstream/emscripten" codecs = ROOT / "build_web_sndfile/install" env = dict(os.environ) env["PATH"] = str(em) + os.pathsep + env["PATH"] env["PKG_CONFIG_LIBDIR"] = str(codecs / "lib/pkgconfig") env["PKG_CONFIG_PATH"] = "" command = [str(source / "configure"), f"--prefix={prefix}", "--target-os=none", "--arch=wasm32", "--enable-cross-compile", "--cc=" + str(em / "emcc"), "--cxx=" + str(em / "em++"), "--ar=" + str(em / "emar"), "--ranlib=" + str(em / "emranlib"), "--nm=" + str(em.parent / "bin/llvm-nm"), f"--extra-cflags=-O2 -fPIC -I{codecs / 'include'}", f"--extra-ldflags=-L{codecs / 'lib'}", "--disable-asm", "--disable-x86asm", "--disable-pthreads", "--disable-w32threads", "--disable-os2threads", "--disable-network", "--disable-autodetect", "--disable-everything", "--disable-programs", "--disable-doc", "--disable-debug", "--disable-avdevice", "--disable-avfilter", "--disable-swscale", "--disable-shared", "--enable-static", "--enable-small", "--enable-protocol=file", "--enable-libmp3lame", "--enable-libopus", "--enable-libvorbis", "--pkg-config-flags=--static", "--enable-encoder=aac,ac3,mp2,flac,libmp3lame,libopus,libvorbis,pcm_u8,pcm_s16le,pcm_s24le,pcm_s32le,pcm_f32le,pcm_f64le", "--enable-decoder=aac,ac3,mp2,mp3,flac,opus,vorbis,pcm_u8,pcm_s16le,pcm_s24le,pcm_s32le,pcm_f32le,pcm_f64le", "--enable-parser=aac,ac3,mpegaudio,flac,opus,vorbis", "--enable-muxer=adts,ac3,mp2,mp3,flac,matroska,ogg,wav", "--enable-demuxer=aac,ac3,mp3,flac,matroska,ogg,wav,mov", "--enable-bsf=aac_adtstoasc,extract_extradata"] settings = json.dumps([command, env["PKG_CONFIG_LIBDIR"]]) stamp = build / "web-configure.json" if not stamp.is_file() or stamp.read_text() != settings: subprocess.run(command, cwd=build, env=env, check=True) stamp.write_text(settings) subprocess.run(["make", f"-j{args.jobs}", "install"], cwd=build, env=env, check=True) artifacts = {str(path.relative_to(ROOT)): hashlib.sha256(path.read_bytes()).hexdigest() for path in sorted((prefix / "lib").glob("*.a"))} (base / "build-manifest.json").write_text(json.dumps({ "version": VERSION, "source": str(source), "upstreamArchiveSha256": SHA256, "configure": command, "artifacts": artifacts, "validation": "NOT_RUN", "acceptance": "NOT_CLAIMED"}, indent=2) + "\n") if __name__ == "__main__": main()