Architecture Notes
Design decisions worth understanding before touching the render or audio pipeline.
No Metal render backend
libmpv's public render API (include/mpv/render.h) only defines two
backends: MPV_RENDER_API_TYPE_OPENGL and a software renderer — there is
no Metal render-API type, verified directly against libmpv's own
headers rather than assumed.
mpv's own Metal usage on macOS goes through Vulkan+MoltenVK inside an
AppKit-only window path (video/out/vulkan/context_mac.m), which
requires NSApplication and doesn't run on iOS.
This project instead uses OpenGL ES via EAGL — the path mpv upstream
itself ships for iOS (video/out/hwdec/hwdec_ios_gl.m, gated by meson's
ios-gl feature), with VideoToolbox hardware frames imported zero-copy
via CVOpenGLESTextureCache. See MPVGLView.swift in MPVKit for the
implementation — CAEAGLLayer + EAGLContext + libmpv's OpenGL render
API, with a dedicated render queue and a C callback trampoline bridging
mpv's "new frame ready" notification into Swift.
:::tip See also
A Vulkan-via-MoltenVK path has been investigated as a possible second
render backend, confirmed technically feasible via VK_EXT_metal_surface
but not yet attempted — see the Roadmap and
Research Log entry 14 for the full breakdown.
:::
No fontconfig
iOS provides CoreText; libass and harfbuzz are built with their CoreText backends instead of fontconfig + libxml2-based font matching, matching how mpv itself is typically built for Apple platforms.
Static linking throughout
Everything is merged into one static libmpv-combined.a per platform via
libtool -static, then wrapped in an XCFramework — App Store apps can't
casually ship a collection of loose .dylibs the way Android apps ship
.so files per-ABI.
mpv source is patched for iOS before building
A small number of mpv's audio-output files (ao_avfoundation.m and the
CoreAudio utility files it shares code with) call macOS-only APIs from
otherwise iOS-compatible code paths. Rather than disabling those features
entirely, buildscripts/patches/mpv/ contains small, targeted patches
that narrow the actual problem down to the specific unavailable
call/type, applied automatically by download.sh.
This is what lets the build re-enable avfoundation (mpv's more modern
audio output, with spatial audio support) instead of falling back to only
the more limited audiounit. See buildscripts/patches/mpv/README.md
and Research Log entries 10–12 for the complete
story of what's patched and why.
Dependency versioning strategy
- Tarball-pinned dependencies (
lua,freetype,harfbuzz,fribidi,mbedtls,libxml2,unibreak) have exact versions pinned inbuildscripts/include/depinfo.sh, checked weekly by an automateddependency-check.ymlworkflow that opens a PR per available update (Dependabot doesn't cover this — there's no ecosystem for versions embedded in a shell script). - Git-cloned dependencies (
mpv,ffmpeg,dav1d,libass,libplacebo) are pinned in CI to a tag/commit set viav_ci_*variables in the same file, following the same pattern mpv-android itself uses forffmpeg(v_ci_ffmpeg) — local/manual builds still clone latest HEAD for convenience, but CI needs a reproducible, intentionally chosen version so an unrelated upstream commit landing between two pushes can't silently break CI on code nobody here touched.
CI cache invalidation
The dependency-prefix cache (see buildscripts/include/ci.sh) is keyed
partly on a BUILD_LOGIC_REV marker, not just on dependency versions —
this exists because the cached prefix/<platform>/ directory contains
the compiled output of this project's own build scripts, not just
"whatever version of each dependency was requested." A change to any
build script's logic (not just a version bump) needs this marker bumped,
or a stale cache can mask the fix. See Research Log entries 6 and 13 for
two real incidents this caused.