Files
2026-10-13 20:11:17 +02:00

700 lines
32 KiB
Bash
Executable File

#!/usr/bin/env bash
# =============================================================================
# scripts/build-ffmpeg.sh - build FFmpeg for the PS5.
#
# ./scripts/build-ffmpeg.sh --profile baseline reproduce pacbrew's build
# ./scripts/build-ffmpeg.sh --profile minimal production build (default)
# ./scripts/build-ffmpeg.sh --profile full compatibility research
# ./scripts/build-ffmpeg.sh --install install into the SDK sysroot
# ./scripts/build-ffmpeg.sh --version 7.1.1 build a different release
#
# ---------------------------------------------------------------------------
# WHY 7.1.1 IS THE DEFAULT (was 7.0.1 until 2026-09-25)
# 7.0.1's AV1 parser cannot play a stream whose temporal delimiters the
# container has stripped - which is every AV1 track in Matroska, where the
# spec says to strip them. cbs_av1 never clears `seen_frame_header` without a
# temporal delimiter, so the next frame header is rejected as a repeat:
# Invalid repeated frame header OBU / Failed to parse temporal unit
# Measured on hardware against 4K 10-bit AV1: 7.0.1 decoded 352 frames of a
# 1592-frame clip and then died, while the same file decodes clean end to end
# on a current FFmpeg. The raw .obu path failed the same way after 1 frame,
# through av1_frame_merge rather than the parser.
#
# #94 SHOWED THAT DIAGNOSIS WAS WRONG (2026-09-25/26). FFmpeg was never the
# cause. A host build of this exact 7.1.1 - and of 7.0.1 - with the AV1
# parser, the obu demuxer, av1_frame_merge and libdav1d plays the console's
# own files with ZERO cbs_av1 errors (EVO-style probe and seeks, gcc and
# clang -O3, MALLOC_PERTURB_, valgrind). On the console the errors were the
# symptom of two EVO faults, both fixed in #94:
# - flexible memory ran out under 4K 10-bit dav1d (malloc shim map_fail);
# the first refused allocation broke av1_frame_merge / the parser for
# the rest of the file. The shim now falls back to direct memory.
# - every 4K 10-bit software frame overflowed the AGC transient-ring slot,
# so none reached the screen - the "corrupt, flashing picture".
# The parser and the raw demuxers are back ON, unpatched.
#
# The pacbrew-matching rationale below is kept because it is still the reason
# the FIRST pin existed, and it explains the profile - but EVO has long since
# diverged from ProsperoPlayer's codec set, and matching a baseline is worth
# less than playing the files.
#
# WHY 7.0.1 WAS THE DEFAULT
# ProsperoPlayer's Makefile does not pin an FFmpeg version - it links
# prebuilt static libraries out of the SDK sysroot:
# $(PS5_PAYLOAD_SDK)/target/user/homebrew/lib/libav*.a
# Those come from ps5-payload-dev/pacbrew-repo, whose ffmpeg/PKGBUILD says
# pkgver=7.0.1. So 7.0.1 IS the version ProsperoPlayer currently uses, and
# it is what EVO Player must reproduce before changing anything.
#
# THE THREE PROFILES
# baseline Byte-for-byte the configure line from pacbrew's PKGBUILD. Use it
# to prove the toolchain can reproduce the environment that the
# existing player already builds against. It intentionally still
# builds ffmpeg/ffplay/ffprobe, exactly as upstream does.
#
# minimal What ships in the PKG. --disable-everything, then only the
# codecs, demuxers and parsers the player actually needs. Smaller
# binary, faster link, and a much shorter list of things that can
# be wrong.
#
# full Everything FFmpeg can decode. Not for shipping - this is the
# build you use to answer "does this file fail because the codec
# is missing, or because our integration is broken?". Diffing a
# failure between minimal and full localises the problem instantly.
# ---------------------------------------------------------------------------
source "$(dirname "${BASH_SOURCE[0]}")/common.sh"
FFMPEG_VERSION="7.1.1"
# sha256 of ffmpeg-7.1.1.tar.xz, from two independent downloads off
# ffmpeg.org (no .sha256 sidecar is published for this release).
FFMPEG_SHA256="733984395e0dbbe5c046abda2dc49a5544e7e0e1e2366bba849222ae9e3a03b1"
# 7.0.1, the previous default, for reference:
# bce9eeb0f17ef8982390b1f37711a61b4290dc8c2a0c1a37b5857e85bfb0e4ff
PROFILE="minimal"
DO_INSTALL=0
JOBS="$(nproc)"
while (( $# )); do
case "$1" in
--profile) shift; PROFILE="${1:?--profile needs a value}" ;;
--version) shift; FFMPEG_VERSION="${1:?--version needs a value}"
FFMPEG_SHA256="" # unknown for a custom version
;;
--install) DO_INSTALL=1 ;;
--jobs|-j) shift; JOBS="${1:?--jobs needs a value}" ;;
-h|--help) sed -n '2,40p' "$0"; exit 0 ;;
*) die "unknown option: $1 (try --help)" ;;
esac
shift
done
case "${PROFILE}" in
baseline|minimal|full) ;;
*) die "unknown profile '${PROFILE}'. Use: baseline | minimal | full" ;;
esac
if ! in_container; then
FWD=(--profile "${PROFILE}" --version "${FFMPEG_VERSION}")
(( DO_INSTALL )) && FWD+=(--install)
reexec_in_container "build-ffmpeg.sh" "${FWD[@]}"
fi
load_sdk
need_cmd wget tar make nasm yasm pkg-config
SRC_ROOT="${REPO_ROOT}/third_party/ffmpeg"
TARBALL="${SRC_ROOT}/ffmpeg-${FFMPEG_VERSION}.tar.xz"
SRC_DIR="${SRC_ROOT}/ffmpeg-${FFMPEG_VERSION}"
# Object trees live on a Linux volume, not the Windows bind mount - FFmpeg
# creates thousands of small files and this is 5-10x faster.
BUILD_DIR="/build/ffmpeg/${FFMPEG_VERSION}-${PROFILE}"
# Staged install so a broken build never corrupts the working sysroot.
STAGE_DIR="/build/ffmpeg/stage-${FFMPEG_VERSION}-${PROFILE}"
mkdir -p "${SRC_ROOT}" "${BUILD_DIR}" "${STAGE_DIR}"
mkdirs
log "FFmpeg ${FFMPEG_VERSION}, profile '${PROFILE}'"
echo " source : ${SRC_DIR}"
echo " build : ${BUILD_DIR}"
echo " jobs : ${JOBS}"
# -----------------------------------------------------------------------------
begin "fetching source"
if [[ ! -f "${TARBALL}" ]]; then
wget -q --show-progress -O "${TARBALL}.part" \
"https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz" \
|| die "download failed for FFmpeg ${FFMPEG_VERSION}"
mv "${TARBALL}.part" "${TARBALL}"
else
ok "tarball already present"
fi
if [[ -n "${FFMPEG_SHA256}" ]]; then
echo "${FFMPEG_SHA256} ${TARBALL}" | sha256sum -c - >/dev/null \
|| die "checksum mismatch on ${TARBALL}.
Delete it and re-run to re-download."
ok "sha256 verified"
else
warn "no known checksum for version ${FFMPEG_VERSION} - skipping verification"
fi
if [[ ! -d "${SRC_DIR}" ]]; then
begin "extracting"
tar -xf "${TARBALL}" -C "${SRC_ROOT}" || die "extract failed"
fi
need_file "${SRC_DIR}/configure" "FFmpeg source tree looks wrong."
# =============================================================================
# Configure flags.
#
# Cross-compilation settings common to every profile. These mirror what
# pacbrew's PKGBUILD does, because that is the configuration known to produce
# working PS5 libraries:
# --target-os=freebsd the PS5 kernel is a modified FreeBSD
# --arch=x86_64 Zen 2 CPU
# --cross-prefix points configure at the prospero-* wrappers
# --enable-static --disable-shared
# payloads link statically; there is no loader story
# for shared libav* on the console
# =============================================================================
COMMON_FLAGS=(
--prefix="${PREFIX}"
--enable-cross-compile
--cross-prefix="${PS5_PAYLOAD_SDK}/bin/prospero-"
--enable-static --disable-shared
--arch=x86_64
--target-os=freebsd
--cc="${CC}" --cxx="${CXX}" --nm="${NM}" --strip="${STRIP}"
--ar="${AR}" --ranlib="${RANLIB}" --pkg-config="${PKG_CONFIG}"
--disable-debug
--disable-doc
)
# -----------------------------------------------------------------------------
# Audio: every codec FFmpeg 7.0.1 decodes natively.
#
# The old list was assembled from whatever the test corpus happened to hold,
# and every gap in it reached a user the same way - a file that plays with no
# sound, or does not play at all, and no reason offered anywhere. Dolby TrueHD
# was the third time. Curating this list is not a feature; it is a standing
# promise to rediscover the same bug once per format somebody owns.
#
# So: all of them. A decoder is a few KB of .text that allocates nothing until
# a stream uses it, none of these need an external library, and none carry a
# gpl/version3/nonfree dependency - checked against configure, not assumed.
# The one name in allcodecs.c's audio block that is NOT here is `hdr`: the
# Radiance RGBE *image* decoder, sitting alphabetically between hcom and iac.
#
# Video is deliberately left alone. It is gated by hardware decode and by the
# flexible-memory ceiling that makes a 4K software decode fault rather than
# fail, so it is a different decision with different risks. Audio has neither
# constraint: frames are kilobytes.
#
# Regenerate with:
# sed -n '/audio codecs/,/subtitles/p' libavcodec/allcodecs.c | grep -oE 'ff_[a-z0-9_]+_decoder'
# then drop any whose .p.type is not AVMEDIA_TYPE_AUDIO (the pcm/adpcm/dpcm
# families define theirs through a macro, and are all audio).
# -----------------------------------------------------------------------------
AUDIO_DECODERS=(
# -- named codecs: lossy, lossless, speech, game and console formats --
aac_fixed ac3_fixed acelp_kelvin als amrnb amrwb apac ape aptx
aptx_hd atrac1 atrac3 atrac3al atrac3p atrac3pal atrac9
binkaudio_dct binkaudio_rdft bmv_audio bonk cook dfpwm dolby_e
dsd_lsbf dsd_lsbf_planar dsd_msbf dsd_msbf_planar dsicinaudio dss_sp
dst evrc fastaudio ffwavesynth ftr g723_1 g729 gsm gsm_ms hca hcom
iac ilbc imc interplay_acm mace3 mace6 metasound misc4 mp1 mp1float
mp2float mp3adu mp3adufloat mp3float mp3on4 mp3on4float mpc7 mpc8
msnsiren nellymoser on2avc osq paf_audio qcelp qdm2 qdmc qoa ra_144
ra_288 ralf sbc shorten sipr siren smackaud sonic tak truespeech tta
twinvq vmdaudio wavarc wavpack wmalossless wmapro wmav1 wmav2
wmavoice ws_snd1 xma1 xma2
# -- raw PCM: every width, sign, endianness and planar layout ---------
pcm_alaw pcm_bluray pcm_dvd pcm_f16le pcm_f24le pcm_f32be pcm_f64be
pcm_f64le pcm_lxf pcm_mulaw pcm_s16be_planar pcm_s16le_planar
pcm_s24be pcm_s24daud pcm_s24le_planar pcm_s32be pcm_s32le
pcm_s32le_planar pcm_s64be pcm_s64le pcm_s8 pcm_s8_planar pcm_sga
pcm_u16be pcm_u16le pcm_u24be pcm_u24le pcm_u32be pcm_u32le pcm_u8
pcm_vidc
# -- ADPCM: the container-specific variants AVI/WAV/MOV use, plus the
# game formats -----------------------------------------------------
adpcm_4xm adpcm_adx adpcm_afc adpcm_agm adpcm_aica adpcm_argo
adpcm_ct adpcm_dtk adpcm_ea adpcm_ea_maxis_xa adpcm_ea_r1
adpcm_ea_r2 adpcm_ea_r3 adpcm_ea_xas adpcm_g722 adpcm_g726
adpcm_g726le adpcm_ima_acorn adpcm_ima_alp adpcm_ima_amv
adpcm_ima_apc adpcm_ima_apm adpcm_ima_cunning adpcm_ima_dat4
adpcm_ima_dk3 adpcm_ima_dk4 adpcm_ima_ea_eacs adpcm_ima_ea_sead
adpcm_ima_iss adpcm_ima_moflex adpcm_ima_mtf adpcm_ima_oki
adpcm_ima_qt adpcm_ima_rad adpcm_ima_smjpeg adpcm_ima_ssi
adpcm_ima_wav adpcm_ima_ws adpcm_ms adpcm_mtaf adpcm_psx
adpcm_sbpro_2 adpcm_sbpro_3 adpcm_sbpro_4 adpcm_swf adpcm_thp
adpcm_thp_le adpcm_vima adpcm_xa adpcm_xmd adpcm_yamaha adpcm_zork
# -- DPCM --------------------------------------------------------------
cbd2_dpcm derf_dpcm gremlin_dpcm interplay_dpcm roq_dpcm sdx2_dpcm
sol_dpcm wady_dpcm xan_dpcm
)
# Containers and raw streams for the above. Without these an .ape or .wv is
# not "unsupported codec" - it does not open at all. asf is here for .wma;
# .wmv still needs the video half, which this pass does not add.
AUDIO_DEMUXERS=(
adx aiff amr amrnb amrwb apac ape aptx aptx_hd argo_asf asf ast au
bfstm bonk brstm caf codec2 dfpwm dsf dtshd g723_1 g729 gsm hca hcom
iff ilbc ircam mlp mpc mpc8 nistsphere oma osq pcm_alaw pcm_f32be
pcm_f32le pcm_f64be pcm_f64le pcm_mulaw pcm_s16be pcm_s16le
pcm_s24be pcm_s24le pcm_s32be pcm_s32le pcm_s8 pcm_u16be pcm_u16le
pcm_u24be pcm_u24le pcm_u32be pcm_u32le pcm_u8 pcm_vidc pvf qoa rso
rm sbc shorten sln sox spdif tak tta voc vqf w64 wavarc wv wve xa
xwma
)
# Framing. Without a parser, a stream in a container that does not carry frame
# boundaries decodes to noise, or to nothing.
AUDIO_PARSERS=(
adx amr cook dolby_e dvaudio ftr g723_1 g729 gsm misc4 sbc sipr tak
xma
)
# -----------------------------------------------------------------------------
# Video components, approved 2026-09-24. The subtitle decoders and the
# subtitle-file demuxers went in stage 6 (2026-10-13) with the subtitle engine:
# a subtitle stream inside a file is dropped by evo_demux.
#
# Same reasoning as AUDIO_DECODERS: the old list was what the test corpus
# happened to hold. Two of these were worse than missing - EVO's file browser
# (since removed) advertised .wmv and .flv as playable, so those
# files listed, got a poster attempt, and could not even be demuxed because
# neither ASF nor FLV was built. EVO's subtitle engine (since removed)
# likewise already handled AV_CODEC_ID_WEBVTT while no webvtt decoder existed.
#
# All native, no external libraries, no gpl/version3 dependency - each name
# checked against allcodecs.c / allformats.c / parsers.c rather than assumed.
#
# NOT here, deliberately: FFmpeg's own `av1` decoder. It is a hwaccel-only
# wrapper with no software path and dereferences null on a machine with no AV1
# hardware, which is every PS5. AV1 resolves to libdav1d only - see the note by
# --enable-libdav1d above.
# -----------------------------------------------------------------------------
VIDEO_DECODERS=(
# WMV / VC-1 family (.wmv, .asf), MS-MPEG4 (old DivX in .avi)
vc1 wmv1 wmv2 wmv3 msmpeg4v1 msmpeg4v2 msmpeg4v3 mpeg1video
# FLV / Sorenson / VP6, H.263, and the old AVI + QuickTime codecs
flv vp6 vp6a vp6f h263 h263i h263p msvideo1 cinepak indeo3 indeo4
indeo5 huffyuv ffv1 utvideo qtrle rpza smc svq1 svq3 mjpegb theora
# Theora, pro/intermediate formats, Chinese AVS, RealVideo, WebP stills
prores dnxhd dvvideo cavs rv10 rv20 rv30 rv40 webp
)
VIDEO_DEMUXERS=(
asf asf_o flv live_flv rm dv mxf vc1 vc1t m4v ivf mpegvideo
)
VIDEO_PARSERS=(
vc1 h263 webp
)
case "${PROFILE}" in
# -----------------------------------------------------------------------------
baseline)
# Exactly pacbrew-repo/ffmpeg/PKGBUILD. Depends on openssl/freetype/
# fribidi/harfbuzz/libass already being in the sysroot - they are, because
# the image installs the full pacbrew tarball.
CONFIGURE_FLAGS=(
"${COMMON_FLAGS[@]}"
--enable-openssl --enable-version3
--enable-libfreetype --enable-libfribidi --enable-libharfbuzz
--enable-libass
)
;;
# -----------------------------------------------------------------------------
minimal)
# Start from nothing and add back only what the player needs. This is the
# production build.
CONFIGURE_FLAGS=(
"${COMMON_FLAGS[@]}"
--disable-everything
# -- components we do not ship ------------------------------------
# The brief asks for these off explicitly. The CLI tools alone are
# several MB and are useless inside a payload.
--disable-programs # ffmpeg, ffplay, ffprobe
--disable-avdevice # no capture devices on a console
--disable-postproc # legacy libpostproc, unused
--disable-encoders # playback only
--disable-muxers # playback only
--disable-bsfs # re-enabled selectively below
--disable-devices
--disable-filters # re-enabled selectively below
# -- network ------------------------------------------------------
# On since #90. Provider playback (IPTV, Emby, debrid) resolves to an
# http(s) URL and hands it to avformat_open_input, so without this the
# open returns AVERROR_PROTOCOL_NOT_FOUND and avformat_network_init()
# is a no-op - which it was, every boot, for the whole 0.6-0.10 era.
#
# openssl rather than the native TLS: the sysroot's libssl/libcrypto
# are already linked into the app module (projects/evoplayer/Makefile
# -lssl -lcrypto, for addons/src/evo_net.c), the `baseline` profile has
# proven --enable-openssl against this exact sysroot, and gnutls is not
# in the pacbrew tarball at all.
--enable-network
--enable-openssl
--disable-iconv
--disable-xlib
--disable-sdl2 # the player owns SDL, FFmpeg must not
# -- libraries we keep --------------------------------------------
# swresample is NOT optional: AudioOut is hard-wired to 48 kHz stereo,
# so anything decoded at 44.1 kHz or with a different channel layout
# has to be resampled before it can be played.
--enable-swresample
--enable-swscale
# -- audio decoders -------------------------------------------------
# The mainstream set stays spelled out: these are what the player is
# tuned around, and what a silent build break would cost the most.
--enable-decoder=aac
--enable-decoder=aac_latm # AAC inside MPEG-TS
--enable-decoder=ac3
--enable-decoder=eac3 # the "silent E-AC3" bug's home
--enable-decoder=dca # DTS, incl. DTS-HD MA lossless
--enable-decoder=truehd # Dolby TrueHD, and the MLP under it
--enable-decoder=mlp
--enable-decoder=mp3
--enable-decoder=mp2
--enable-decoder=flac
--enable-decoder=opus
--enable-decoder=vorbis
--enable-decoder=alac
--enable-decoder=pcm_s16le
--enable-decoder=pcm_s16be
--enable-decoder=pcm_s24le
--enable-decoder=pcm_f32le
# ...and then everything else FFmpeg decodes natively. See
# AUDIO_DECODERS at the top for why this is not a curated list.
"${AUDIO_DECODERS[@]/#/--enable-decoder=}"
"${VIDEO_DECODERS[@]/#/--enable-decoder=}"
# -- video decoders -------------------------------------------------
--enable-decoder=h264
--enable-decoder=hevc # includes Main10 / 10-bit
--enable-decoder=vp9
--enable-decoder=vp8
--enable-decoder=mpeg2video
--enable-decoder=mpeg4
# AV1 = libdav1d ONLY. FFmpeg's own `av1` decoder (av1dec.c) is a
# hwaccel-only wrapper with no software path: on a machine with no
# AV1 hardware - every PS5 - it logs "Your platform doesn't
# suppport hardware accelerated AV1 decoding" at the first frame and
# then dereferences null inside libavcodec, taking the app with it.
# It was enabled here for months behind the comment "native decoder;
# slow, but present", which it is not. Deliberately NOT re-enabled:
# leaving it out means AV1 can only ever resolve to libdav1d.
--enable-libdav1d
--enable-decoder=libdav1d
# -- still images: cover art and thumbnail scrubbing ---------------
--enable-decoder=mjpeg
--enable-decoder=png
# -- containers (brief section 13) -----------------------------------
--enable-demuxer=matroska # MKV and WebM share this demuxer
--enable-demuxer=mov # MP4, MOV, M4A, 3GP
--enable-demuxer=mpegts
--enable-demuxer=mpegps
--enable-demuxer=avi
--enable-demuxer=flac
--enable-demuxer=mp3
--enable-demuxer=ogg
--enable-demuxer=wav
--enable-demuxer=aac
--enable-demuxer=ac3
--enable-demuxer=eac3
--enable-demuxer=dts
--enable-demuxer=truehd # raw .thd/.ac3 streams
--enable-demuxer=h264
--enable-demuxer=hevc
--enable-demuxer=image2 # the thumbnail/cover-art path
# -- AV1 raw streams ------------------------------------------------
# ivf, plus the `obu` (low-overhead .obu) and `av1` (Annex B) raw
# demuxers. configure has
# obu_demuxer_select="av1_frame_merge_bsf av1_parser"
# so the raw demuxers need av1_parser and av1_frame_merge. They were
# left out from 2ddd018 until #94 because the parser was blamed for
# corrupting AV1 in containers - see the parser block below for why
# that no longer stands. av1_frame_merge is named explicitly because
# --disable-bsfs is in force; select would pull it in anyway.
--enable-demuxer=ivf
--enable-demuxer=obu
--enable-demuxer=av1
--enable-bsf=av1_frame_merge
# -- streaming containers (#90) -------------------------------------
# hls is what an IPTV .m3u8 actually is. It has no external dependency
# and demuxes its segments through mpegts and mov, both already on.
#
# dash needs libxml2, which in turn has unresolved iconv_* symbols;
# both libxml2.a and libiconv.a are in the pacbrew prefix and link,
# so -lxml2 joins the transitive list in build-evoplayer.sh,
# package-app.sh and the Makefile alongside the -liconv already there.
--enable-demuxer=hls
--enable-demuxer=dash
--enable-libxml2
"${AUDIO_DEMUXERS[@]/#/--enable-demuxer=}"
"${VIDEO_DEMUXERS[@]/#/--enable-demuxer=}"
# -- parsers ----------------------------------------------------------
# Without these, raw and MPEG-TS streams will not frame correctly -
# a classic cause of "plays in VLC, fails on console".
--enable-parser=h264
--enable-parser=hevc
--enable-parser=vp9
# av1 parser: ON again since #94, unpatched. The note below is why it
# was off from 2ddd018 - kept because the measurement in it is real,
# but its explanation was not (see the correction after it).
#
# matroskadec sets need_parsing = AVSTREAM_PARSE_HEADERS for every
# codec except AAC, so whenever this parser exists it runs on every AV1
# track - and on 7.0.1 AND 7.1.1 it fails on streams whose temporal
# delimiters the container stripped, which is every AV1-in-Matroska by
# spec. cbs_av1 never clears `seen_frame_header` without a temporal
# delimiter, so the next frame header is rejected as a repeat.
# Measured on hardware, 4K 10-bit AV1 in .mkv: 1106 parser errors in
# 3.5 s, ~3 per published frame - the frames still reach dav1d, which is
# why it reads as a corrupt, flashing picture rather than a clean
# failure. The same file decodes end to end on a current FFmpeg, so the
# fix landed after 7.1.1.
#
# Without the parser, av_parser_init() returns NULL and whole packets go
# straight to dav1d - which is exactly what a container already frames.
#
# Correction (#94): the parser was a victim, not the cause. The exact
# 7.1.1 here runs EVO_TEST_av1_4k.mkv - the very file measured above,
# pulled off the stick - through the parser and libdav1d with zero
# cbs_av1 errors on the host, and 7.1.1 already clears
# `seen_frame_header` after a frame's last tile group
# (cbs_av1_syntax_template.c, tile_group_obu), so the "never cleared
# without a temporal delimiter" reading was wrong. On the console the
# errors followed out-of-memory (map_fail in evo.log's alloc lines) and
# stopped once the malloc shim gained its direct-memory fallback;
# 4K AV1 in .mkv and raw .obu both play clean on hardware (2026-09-26).
# If cbs_av1 errors come back, check map_fail before blaming FFmpeg.
--enable-parser=av1
--enable-parser=aac
--enable-parser=aac_latm
--enable-parser=ac3
--enable-parser=dca
--enable-parser=mlp # TrueHD/MLP framing in MKV and .thd
--enable-parser=flac
--enable-parser=opus
--enable-parser=vorbis
--enable-parser=mpegaudio
--enable-parser=mpegvideo
--enable-parser=mpeg4video
"${AUDIO_PARSERS[@]/#/--enable-parser=}"
"${VIDEO_PARSERS[@]/#/--enable-parser=}"
# -- bitstream filters -------------------------------------------------
# Required to feed MP4/MKV-contained H.264/HEVC to a decoder that
# expects Annex-B - and mandatory for any future hardware decoder.
--enable-bsf=h264_mp4toannexb
--enable-bsf=hevc_mp4toannexb
--enable-bsf=extract_extradata
--enable-bsf=aac_adtstoasc
--enable-bsf=vp9_superframe
# -- filters ------------------------------------------------------------
# Only the graph plumbing plus format conversion; no effects.
--enable-filter=aformat
--enable-filter=aresample
--enable-filter=anull
--enable-filter=format
--enable-filter=scale
--enable-filter=null
# -- protocols ------------------------------------------------------------
# tcp and tls are not optional extras: http is built on tcp, and https
# is http over tls. Enabling only http,https silently produces a build
# where every network open fails at the transport layer.
--enable-protocol=file
--enable-protocol=pipe
--enable-protocol=http
--enable-protocol=https
--enable-protocol=tcp
--enable-protocol=tls
--enable-protocol=ftp
# AES-128 segment encryption is routine on IPTV playlists; without
# the crypto protocol those channels open and then decode to noise.
--enable-protocol=crypto
)
# NOTE: there is no '--enable-image2' option. image2 is a DEMUXER and is
# enabled above via --enable-demuxer=image2; passing it as a bare option
# makes configure abort. Component names are version specific - always
# check `configure --help` / libav*/Makefile rather than guessing.
;;
# -----------------------------------------------------------------------------
full)
# Everything the source tree can decode. Deliberately does NOT use
# --enable-everything (which would also pull in encoders, muxers and
# every external library we have not built). Instead: default component
# set, all decoders/demuxers/parsers on, no encoders or muxers.
CONFIGURE_FLAGS=(
"${COMMON_FLAGS[@]}"
--disable-programs
--disable-avdevice
--disable-encoders
--disable-muxers
--disable-devices
--enable-network
--enable-openssl
--disable-sdl2
--enable-swresample
--enable-swscale
--enable-decoders
--enable-demuxers
--enable-parsers
--enable-bsfs
--enable-filters
--enable-protocols
# Extra external decoders that ARE in the pacbrew sysroot. These give
# better quality/speed than the native decoders and are worth having
# in the research build to compare against.
--enable-libvpx
--enable-libopus
--enable-libvorbis
--enable-libmp3lame
--enable-version3 # required by some GPL/v3 components
)
;;
esac
# -----------------------------------------------------------------------------
begin "configure (${PROFILE})"
# FFmpeg's configure insists on running from the build directory for an
# out-of-tree build.
cd "${BUILD_DIR}" || die "cannot enter build directory ${BUILD_DIR}"
CONFIG_LOG="${LOG_OUT}/ffmpeg-${FFMPEG_VERSION}-${PROFILE}-configure.log"
if ! "${SRC_DIR}/configure" "${CONFIGURE_FLAGS[@]}" > "${CONFIG_LOG}" 2>&1; then
echo ""
echo "--- last 40 lines of configure output ---"
tail -40 "${CONFIG_LOG}" | sed 's/^/ /'
echo "--- and from config.log ---"
tail -30 "${BUILD_DIR}/ffbuild/config.log" 2>/dev/null | sed 's/^/ /'
die "FFmpeg configure failed (profile: ${PROFILE}).
Full log: ${CONFIG_LOG#"${REPO_ROOT}/"}
Common causes:
* a --enable-lib<x> whose library is not in the sysroot
* nasm/yasm missing (they are in the image; check \$PATH)
* an option name that changed between FFmpeg releases - option names
are version specific, check ${SRC_DIR}/configure --help"
fi
ok "configured"
grep -E '^(External libraries|Enabled decoders)' -A2 "${CONFIG_LOG}" 2>/dev/null | head -6 || true
# -----------------------------------------------------------------------------
begin "compiling (-j${JOBS})"
BUILD_LOG="${LOG_OUT}/ffmpeg-${FFMPEG_VERSION}-${PROFILE}-build.log"
if ! make -j"${JOBS}" > "${BUILD_LOG}" 2>&1; then
echo ""
echo "--- last 40 lines ---"
tail -40 "${BUILD_LOG}" | sed 's/^/ /'
die "FFmpeg build failed. Full log: ${BUILD_LOG#"${REPO_ROOT}/"}"
fi
ok "compiled"
# -----------------------------------------------------------------------------
begin "staging install"
make install DESTDIR="${STAGE_DIR}" >> "${BUILD_LOG}" 2>&1 \
|| die "'make install' into the staging directory failed."
STAGED_LIB="${STAGE_DIR}${PREFIX}/lib"
for l in libavcodec.a libavformat.a libavutil.a libswresample.a libswscale.a; do
need_file "${STAGED_LIB}/${l}" "FFmpeg did not produce ${l}."
printf ' %-18s %8s KiB\n' "${l}" "$(( $(stat -c %s "${STAGED_LIB}/${l}") / 1024 ))"
done
# -----------------------------------------------------------------------------
# Record what this build can actually decode. This artifact is the whole point
# of keeping a minimal and a full profile side by side: when a file will not
# play, diff the two lists.
# -----------------------------------------------------------------------------
begin "recording codec inventory"
INVENTORY="${LOG_OUT}/ffmpeg-${FFMPEG_VERSION}-${PROFILE}-codecs.txt"
# FFmpeg 6.0 split the per-component CONFIG_* defines out of config.h into
# config_components.h. Search both so this works across versions, and tolerate
# a component class being entirely absent (grep exits 1 on no match, which
# under `set -o pipefail` would otherwise abort the whole script).
FF_CONFIG_HEADERS=()
for h in "${BUILD_DIR}/config_components.h" "${BUILD_DIR}/config.h"; do
[[ -f "${h}" ]] && FF_CONFIG_HEADERS+=("${h}")
done
list_components() {
local kind="$1"
grep -hoE "^#define CONFIG_[A-Z0-9_]+_${kind} 1" "${FF_CONFIG_HEADERS[@]}" 2>/dev/null \
| sed "s/#define CONFIG_//; s/_${kind} 1//" \
| tr 'A-Z' 'a-z' | sort -u || true
}
if (( ${#FF_CONFIG_HEADERS[@]} == 0 )); then
warn "no config header found in ${BUILD_DIR} - skipping codec inventory"
else
{
echo "# FFmpeg ${FFMPEG_VERSION} - profile ${PROFILE}"
echo "# generated $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "# source: ${FF_CONFIG_HEADERS[*]}"
for kind in DECODER DEMUXER PARSER BSF PROTOCOL; do
echo ""
echo "## $(echo "${kind}" | tr 'A-Z' 'a-z')s"
list_components "${kind}"
done
} > "${INVENTORY}"
ok "$(grep -c '^[a-z0-9]' "${INVENTORY}" || true) components -> ${INVENTORY#"${REPO_ROOT}/"}"
# A quick, readable confirmation that the codecs the brief cares about are
# actually in this build.
echo " key codecs:"
# libdav1d, not av1: a decoder named `av1` exists even when it is the
# useless hwaccel-only stub, so checking that name proved nothing.
for c in aac ac3 eac3 dca mp3 flac opus vorbis alac h264 hevc vp9 mpeg2video libdav1d; do
if grep -qx "${c}" "${INVENTORY}"; then
printf ' [x] %s\n' "${c}"
else
printf ' [ ] %s MISSING\n' "${c}"
fi
done
fi
# -----------------------------------------------------------------------------
if (( DO_INSTALL )); then
begin "installing into the SDK sysroot"
# PS5_CROSS_FIX_ROOT rewrites absolute paths inside .pc and libtool files
# so pkg-config resolves them relative to the sysroot rather than to the
# on-console /user/homebrew prefix. pacbrew's PKGBUILD does the same.
cp -a "${STAGE_DIR}${PREFIX}/." "${PS5_SYSROOT}${PREFIX}/"
if [[ -x "${PS5_CROSS_FIX_ROOT:-}" ]]; then
"${PS5_CROSS_FIX_ROOT}" "${PS5_SYSROOT}${PREFIX}"
fi
ok "installed to ${PS5_SYSROOT}${PREFIX}"
warn "this REPLACED the pacbrew FFmpeg ${PROFILE} build in the sysroot."
warn "to get the pristine one back: docker volume rm ps5twitch_ps5_sdk"
else
echo ""
log "staged, not installed"
echo " libraries: ${STAGED_LIB}"
echo " install into the sysroot with:"
echo " ./scripts/build-ffmpeg.sh --profile ${PROFILE} --install"
fi
echo ""
ok "FFmpeg ${FFMPEG_VERSION} (${PROFILE}) done"