Plezy consumed mpv through four unrelated supply chains: an MPVKit fork
via SwiftPM for the Apple platforms, a libmpv-android fork's AAR for
Android, an in-CI from-source build for Linux, and an unpinned
sourceforge mpv-dev 7z for Windows. All four now consume the same
per-commit, content-addressed binaries from
https://github.com/edde746/mpv-build, pinned to one commit and built
from one set of pinned sources (mpv v0.41.0 on Apple/Android/Windows,
ffmpeg n8.0.1, our libass fork).
- Apple: the SwiftPM package moves from edde746/MPVKit to
edde746/mpv-build across the ios/macos/tvos projects;
scripts/set_mpvkit_revision.sh becomes set_native_revision.sh, writes
every pin site plus the new repo-root mpv-build.lock.json, and
tvos/scripts/wire_mpv.rb derives the package repo from the locks.
- Android: the mpv Kotlin API and JNI glue move in-app under
android/libmpv (repackaged com.edde746.plezy.libmpv, exports renamed,
shrinker rules covered), and the module downloads per-ABI native
tarballs (lib/*.so incl. libc++_shared.so + include/) driven entirely
by mpv-build.lock.json, with a PLEZY_LOCAL_MPV_DIR escape hatch for
locally built artifacts. The fork AAR and its Maven coordinates are
gone.
- Linux: CI downloads the prebuilt self-relocating libmpv prefix
(lock-driven, sha256-verified) instead of compiling mpv/ffmpeg/dav1d/
libplacebo/shaderc from source; linux/packaging/build-libmpv.sh and
its test are deleted and native-inputs.json shrinks to the simdutf
entry the CMake builds still fetch.
- Windows: both arches FetchContent the mpv-build dev zips with
URL_HASH enforcement, replacing the checksum-less sourceforge
download and its ARM64 7-Zip special case; guard scripts updated.
The lock plus the Apple pin sites all point at mpv-build commit
d7c3d559, whose manifest was verified asset-by-asset against the
published release digests (17/17 match). Verified locally: wire and
pin-script suites green, runtime-input checks green, all four Android
ABI tarballs downloaded/verified/extracted through the real Gradle
tasks, the Windows FetchContent block exercised end to end through
cmake, the Linux asset hash and layout checked against the workflow
contract, and xcodebuild resolved the flipped SwiftPM graph with
SwiftPM validating every binary checksum.
The in-app FFmpeg container demuxer classified every DTS variant as
plain DTS (profile-blind MIME mapping at the demux boundary), so
DTS-HD MA lost its lossless identity downstream, and it ignored
container display-aspect-ratio overrides. media3's extractors get both
right.
Delete the FFmpeg demuxer (JNI, extractor, and its setting); ExoPlayer
direct play demuxes with media3's DefaultExtractorsFactory again, and
mpv - the Android default - reads container display dimensions itself.
close#2124close#2115
Direct play on Android kept accumulating container patches: AVI with
XviD packed-bitstream timestamps and missing VOL csd played broken,
ASF/WMV and MPEG-PS/VOB had no media3 extractor at all, and MKV needed
a custom extractor stack for zlib-compressed subtitles, LOAS/LATM
audio, cueless seeking, and font attachments.
Demux progressive containers with libavformat behind media3's
extractor API. An AVIO bridge serves libavformat from the
ExtractorInput: every position divergence defers into a RESULT_SEEK
round trip, header reads replay from a block cache while
avformat_open_input restarts, and avformat_seek_file executes seeks
with bounded loader round trips. Packets feed media3's TrackOutputs,
so decoders, passthrough carriers, Dolby Vision RPU/EL conversion
(dvh1 codecs string surfaced from DOVI side data), and the subtitle
pipeline are untouched: embedded fonts feed AssHandler over JNI, ASS
reaches libass as per-sample dialogue, SRT/VTT render as cues, VobSub
maps through media3's VobsubParser, and LOAS/LATM AAC unwraps via
LatmTrackOutput.
Under the default Auto preference FFmpeg demuxes AVI, ASF/WMV,
MPEG-PS, and Matroska/WebM and sits behind media3's list as an
any-container fallback; MP4/TS keep media3's extractors. A playback
setting exposes auto / FFmpeg first / media3 only, and the JNI
load-failure path falls back to stock media3. The custom Matroska
extractor stack and its reflection keeps are retired.
Verified on Pixel 7 (API 36), Box R 4K Plus TV (API 34), and SHIELD
TV (API 30): instrumentation playback suites, the minified R8
reachability gate, and an on-device container matrix with forward and
backward seek landings plus visual subtitle checks.
close#2052
Turning subtitles off (or hiding them) while an ASS/SSA cue was on screen left
that cue painted until its natural end time on the ExoPlayer backend: AssHandler
nulls the libass track, after which every render returns null, no payload ever
reaches the GL thread again, and the overlay keeps its last swapped atlas. The
existing invalidateSubtitles() call from the #1387 fix could never repaint it.
The atlas pipeline now detects a trackless render request and hands the GL
thread one zero-quad payload, which clears and swaps a transparent frame. The
clear is keyed on the renderer state generation so one dropped as stale is
retried, and it self-heals from per-video-frame requests while playing; the
existing invalidate on the disable transition covers the paused case.
close#1884
MKV files whose Tracks element follows media clusters could direct-play with audio but no video. Upgrade Media3 to 1.11.0 and cover the extractor regression.
close#1947
Signs built from hundreds of overlapping paint-stroke drawings (masked
smartphone screens and similar typesetting) sum to far more bitmap area
than the paged ALPHA_8 atlas can hold: the issue sample needs 5 pages of
16M px at 1080p and 19 at 4K against the 4-page cap, so the packer
dropped the painter-order tail - the sign's text and late mask strokes.
Move the packer out of the JNI file into AssPack.c (pure C, compilable
against a desktop libass for verification) and add a composite fallback:
when a frame can never fit MAX_ATLAS_PAGES pages or the vertex budget,
blend the image list CPU-side into one premultiplied RGBA rect over the
union bounding box - O(frame area) instead of O(sum of image areas) -
and draw it as a single quad through a new MODE_COMPOSITE path in the
GL renderer. Oversized composites reuse the existing grow-and-re-render
contract; the atlas fast path is byte-identical for every frame that fits.
Verified with a desktop harness compiling the shipped AssPack.c against
fork libass 0.18.3 and the issue sample: all atlas-mode frames byte-match
the previous packer, the sign's frames composite with zero truncation and
byte-match a reference full-frame blend at 1080p and 4K, and the
multi-page composite grow path round-trips.
close#1868