Some HEVC files carry a frame Apple's hardware decoder refuses to decode
(kVTVideoDecoderBadDataErr on an otherwise conformant P-frame). mpv fell
back to software decoding for the rest of the file after the first error,
and 4K HEVC software decode drops frames every few minutes on Apple TV.
MPVKit b8b922ec carries an mpv vd_lavc patch that keeps the configured
fast fallback while probing but lets a hardware decoder that has already
delivered frames ride out a mid-stream error burst: the burst one
undecodable frame causes ends at the next keyframe by construction, so
fallback now requires errors to survive two keyframe intervals. Verified
against the reporter's sample: playback logs "Hardware decoding
recovered after 90 errored frames" and stays on VideoToolbox instead of
switching to software.
close#2167
A device with no hardware HEVC decoder could still be handed an HEVC
transcode: the device profile advertised a fixed codec list that assumed
every device decodes everything. Prepending AV1 to reach the AV1 encoders
issue #2131 asks for would have made that worse - an Apple TV 4K and every
iPhone before the A17 Pro have no AV1 decoder at all.
Probe the platform instead. Android enumerates MediaCodecList for a
hardware decoder and iOS/tvOS ask VideoToolbox, both feeding one latched
VideoDecodeCapabilities that the device profile reads when it builds its
codec lists. Desktop deliberately answers nothing: a pre-Kaby-Lake Mac has
no hardware HEVC decoder and an M1 no AV1 one, yet both software-decode in
real time, so narrowing there would force transcodes for nothing. An
unanswered or failed probe advertises everything, so the list never
narrows on missing data.
The transcode target becomes av1,hevc,h264 filtered by the probe. Leading
with AV1 is safe because the server rotates codecs its admin has not
enabled ("Allow encoding in HEVC/AV1 format", both off by default) to the
back before picking one, so it costs nothing on a server that will not
emit AV1.
Audio now accepts everything the path can carry. The direct-play profile
drops its AudioCodec list entirely - an omitted list means "any codec" to
Jellyfin - so an audio stream can no longer be what blocks direct play.
The transcode target lists every codec Jellyfin can put in an fMP4
segment, so a video-only transcode copies DTS or TrueHD instead of
re-encoding it. Two limits bound that string: the server validates it
against ^[a-zA-Z0-9\-\._,|]{0,40}$ when it echoes the list into the
transcode URL, so alac does not fit and * is not a wildcard; and omitting
the key is not "accept everything" here the way it is for direct play,
because the server substitutes the source codec and then ships no audio at
all for a source fMP4 cannot carry.
close#2131
MPVKit publishes binaries for every commit on its main branch now, so a
version tag is no longer the only resolvable thing. Pinning the revision
means a fork fix is consumable as soon as its binaries land, with no release
ceremony there and no version bump here.
Pinned at 0097fea015fe, which carries the PiP subtitle fix: the iPadOS
Picture-in-Picture window no longer resizes when SRT subtitles appear and
disappear. Nine pin sites move together -- three project.pbxproj
requirements plus the six SwiftPM locks that check_apple_spm_locks.py keeps
in agreement.
tvos/scripts/wire_mpv.rb no longer hardcodes the version it writes; it reads
the revision out of the tvOS project's committed lock, so re-wiring the
tvOS target cannot silently revert the pin. Its test asserts the shape and
that all nine sites name one commit, rather than a literal sha, so moving
the pin stays one command:
scripts/set_mpvkit_revision.sh <40-char sha>
close#2078
With Audio Passthrough on, (E-)AC-3 Direct Play on Apple TV degraded
into a repeating buzz after roughly half an hour and never recovered
(a 31.25 Hz comb: one stale 32 ms frame looping). During preroll the
system's Dolby pipeline probes the fake endless resource at a fixed
~183 MiB offset; the AO satisfied it with fabricated copies of the
latest access unit, and CoreMedia cached that range by stream offset
and planned its sequential reads around it. Once playback reached the
cached bytes (33 min at 768 kb/s, 40 min at 640 kb/s) it played the
fabricated frames, and its next read landed past the write head, where
the loader kept fabricating frames forever. Seeks rebuild the item at
offset zero, which is why exit/resume bought another 30-40 minutes.
MPVKit 1.0.26 refuses loader reads past the write head instead of
fabricating data; the pipeline falls back to a plain sequential read
from the write head and prerolls normally with nothing poisonous
cached.
Verified on an Apple TV 4K gen 3 with an HDMI capture rig: the 1.0.25
sink audibly and loggably degrades at stream byte 0x0B770000 (8.3 min
into a 3072 kb/s accelerant stream, 40 min at 640 kb/s), while the
1.0.26 sink crosses the same byte with continuous audio on both the
accelerated stream and a full 40-minute 640 kb/s Plex Direct Play soak
through Plezy itself.
close#1776
With Audio Passthrough on, every (E-)AC-3 track on Apple TV dropped
100-220ms of audio every 1.79s. The system pipeline behind the
compressed AVPlayer path reads unboundedly far ahead of the playhead
and consumes its buffer in fixed ~1.8s quanta; the AO's 2s feed lead
meant each refill overran the write head and the renderer skipped the
missing audio to stay on clock. Bitrate- and route-independent, and
invisible to the AO: item status, time control, and the feed margin
all stayed clean while the sink output gapped (#1300, #1776).
MPVKit 1.0.25 feeds the elementary stream 8s ahead of the observed
playhead -- 4x the pipeline's refill quantum -- sizes the ES window
from the configured lead, exposes it as
--ao-avfoundation-compressed-lead, and keeps waiting during preroll
while a slow source (a realtime-pinned server transcode) is still
making priming progress instead of abandoning Atmos for PCM.
Verified on an Apple TV 4K gen 3 with an HDMI audio capture rig
against EAC3 768k and 640k streams: 21 dropouts/min on 2.15.0, zero
across 10.7 minutes with the new lead; a 997Hz sine encoded as EAC3
through the full passthrough path is statistically identical to
first-party AVKit playback of the same tone; a rate-limited server at
0.95x realtime now prerolls late on the compressed path instead of
downgrading, and once the lead is filled mpv's cache pauses absorb
the deficit with the margin intact.
Direct Play of any (E-)AC-3 track on Apple TV developed persistent
micro-stutter: mpv's audio clock was derived from es_pts on the
compressed path, which is credited a whole 32 ms burst the moment a
payload lands, sawtoothing the clock on a ~0.8 s cycle that video
retires as visible skip/hold pairs. AAC content was unaffected, which
is why the symptom tracked Dolby audio and survived every VO-side fix.
MPVKit 1.0.23 measures the clock from IEC samples actually handed to
ao_read_data instead. Verified with an HDMI capture rig against the
CI-built binary: EAC3 display cadence is now statistically identical
to AAC (was 47.6 skips/min plus 453 multi-frame holds over 38 min).
close#1776
Direct Play on Apple TV develops recurring micro-stutter that worsens
the longer playback runs and clears temporarily on pause/play
(issue #1776). Since 2.4 the avfoundation VO has presented frames
against a media-time CMTimebase anchored and rate-servoed to follow
mpv's audio-slaved schedule; clock skew between the two domains can
only be retired by rate slewing or a re-anchor snap that retimes every
queued frame at once. The media timebase exists for PiP, which tvOS
does not have.
Bump MPVKit to 1.0.22 and opt tvOS into its new
avfoundation-presentation=host mode: samples carry mpv's scheduled
display time against a free-running host-clock timebase, so drift is
absorbed per frame inside mpv and no timing state accumulates in the
VO. The queue lead returns to 128ms; 500ms only dampened media-mode
snaps and quadrupled their blast radius. 1.0.22 also paces the
compressed-passthrough feed off the most-advanced playhead reading,
fixing the periodic audio dropouts passthrough users reported on 2.14.
AC3/EAC3 playback could stutter after AVFoundation began handling compressed streams. Decline compressed formats in macOS AVFoundation so mpv falls through to CoreAudio, while retaining AVFoundation for PCM and the tvOS Dolby path.
close#1940
Picks up two vo_avfoundation fixes: keep the displayed frame across
seek resets, and refuse Core Image geometry rendering for Dolby
Vision passthrough frames (defense in depth - the app already keeps
mpv video-zoom at 0 on iOS/tvOS and zooms the display layer instead).
Nonzero mpv video-zoom flips vo_avfoundation into a per-frame Core
Image re-render that destroys HDR/DV passthrough - DV frames render
near-black on tvOS (verified on Apple TV 4K, DV P7->8.1 content:
panel luma mean 0.0 zoomed vs 87-103 unzoomed at locked exposure).
Zoom now scales the AVSampleBufferDisplayLayer itself (a
sublayerTransform on the container is ignored by the video plane)
via the existing Player.setVideoZoom seam, and VideoFilterManager
pins the mpv property to 0 on backends with native zoom. The layer
tree at 100% stays identical to before: clipping engages only while
zoomed, and updateFrame sizes the layer via bounds/position, which
frame= decomposes to anyway.
macOS keeps the property path (gpu-next zooms losslessly in-shader);
Android is untouched.
RawEc3Loader and its ProbeURLProtocol harness were deleted with the Atmos
output diagnostics, but the iOS RunnerTests kept exercising them, so the
Apple native reliability job no longer compiled.
Detection now runs off the UI isolate and covers every platform where
the answer can be trusted.
Availability was a plain platform check, so Linux always listed VLC, mpv
and Celluloid, macOS always listed VLC and IINA, and Windows always
listed VLC and PotPlayer whether or not any of them existed. Each player
now has a detector that asks exactly the question its launcher asks:
`sh -c 'command -v'` for PATH launches so the kernel performs the
executable check, NSWorkspace/Launch Services for `open -a`, `where.exe`
plus the concrete install paths for Windows VLC, and the registered URL
handler for PotPlayer and the iOS players.
Detection is asynchronous and memoised behind KnownPlayers.probe rather
than a Process.runSync in a static initialiser, which forked three
shells on the UI isolate during ExternalPlayerScreen.build. It is
prewarmed from startup, fails open when a probe throws, and keeps the
selected player listed when a detector misses it so a false negative
cannot leave the list with nothing selected.
iOS and tvOS gained LSApplicationQueriesSchemes entries for vlc and
infuse. Without them canOpenURL returns false for both schemes, so
_launchUrlScheme was already refusing to hand off to either player.
Android keeps the platform check: package visibility needs native
declarations, and a wrong answer there hides a working player.
Brings in the AVFoundation audio output's bounded PCM lookahead, which
is compiled in on macOS only. AVSampleBufferAudioRenderer holds roughly
1.7s of audio there, and mpv multiplies --volume into the samples as it
hands them over, so a volume change stayed inaudible until that backlog
drained. The bound cuts the queue to about 450ms, measured; tvOS and
iOS preprocess to the source they had before the patch, so the deep
buffering their AirPlay path relies on is untouched.
Also carries the Dolby-conformant compressed EAC3 sink from 1.0.15.
Groundwork for #1300. Establishes the session, buffering and route
handling Dolby's application guide prescribes, and adds the diagnostic
arm needed to find out whether Apple's sample-buffer renderer can carry
Atmos objects at all.
Audio session, per the guide's sequence:
- Adopt the long-form playback profile in one atomic call at app launch
and activate the session there. The SDK only accepts that policy with
category Playback, a Default/MoviePlayback/SpokenAudio mode and no
options, so it cannot be assembled from separate calls.
- Report the resolved rendering mode in the player, hidden unless the
system resolves it. Apple only resolves it for CarPlay and AirPlay, so
an unresolved value means unknown, never "not Dolby".
Diagnostics (Apple TV only, Settings > Video Playback > Atmos Output Test):
- Add a sample-buffer arm. It reads the asset with AVAssetReader at
outputSettings nil and hands the untouched compressed buffers and the
untouched format description straight to the renderer, with a variant
that rebuilds the description the way playback builds it. Every
existing mode went through AVPlayer, so nothing exercised the path
playback actually uses; this is what tells us whether the renderer or
our construction is at fault.
- Add an AirPlay route picker. AirPlay is the only route where the system
resolves the rendering mode and the supported channel layouts, so it is
what makes those observations reachable at all, and the AVPlayer arms
now allow external playback so every arm can be compared on the same
destination.
- Add a session-mode toggle for the one profile difference between the
guide and previous playback behaviour.
- Report the session profile, supported layouts, both format
descriptions, the magic cookie and the renderer status, and release the
session on stop so a failed run cannot contaminate the next one.
Also bumps MPVKit to 1.0.14, which carries the matching audio output
work: the channel layout AVFoundation itself uses for Dolby content, a
renderer-failure observer so the fallback to PCM can actually run, the
prescribed feed ordering and preroll, flush recovery that re-supplies the
discarded audio instead of shifting later audio into its place, and
capability-driven fallback on route and capability changes.
This does not yet fix#1300. Whether the sample-buffer renderer can carry
JOC is still unknown; it removes every difference from the documented
setup that could explain the failure, and gives us the arm to answer it
on real hardware.
When the display/system sleeps with a video paused, the window server
stops completing CAMetalLayer presents and mpv's MoltenVK swapchain wedges,
pinning one core at 100% until the app exits. occlusionState does not
reliably change on display sleep, so observe NSWorkspace screensDidSleep/
Wake to hide the metal layer (and drop the playback activity) while dark,
restoring on wake with a forced redraw when paused to avoid a stale frame.
Pairs with MPVKit v1.0.10, which gates libmpv presentation on the layer's
hidden state (check_visible) so presents actually stop while hidden.
The non-mixing playback session was activated in didFinishLaunching,
so merely opening the app stopped other apps' audio. Keep the category
configuration; playback start activates the session.
Audio-only mpv core on every platform (dedicated
com.plezy/mpv_audio_player channels): parameterized android/windows/
linux mpv plugins and a new apple MpvAudioPlayerCore, all skipping
video/window paths (vid=no, audio-display=no, gapless-audio=weak).
MusicPlaybackService drives an in-memory queue with shuffle/repeat,
file-loaded-event gapless arming (property edges coalesce and the
android bridge drops them), per-track progress reporting, OS media
controls, audio focus, sleep timer, and error auto-skip.
PlaybackCoordinator enforces one live native player: starting video
disposes the audio core first.
Re-enables Audio Passthrough on Apple TV against the MPVKit EAC3-JOC
AVPlayer sink (pin bump to v1.0.8 follows), adds an AVPlayer test
harness + diagnostics screen for #1300, and makes loudnorm yield while
passthrough is active (fixes a latent spdif conflict on desktop too).
Routes all single-layer DV profile 8 (incl. compat 0) through the Apple
DV decode path, so VideoToolbox decodes it in hardware as true Dolby
Vision instead of failing over to software HDR10. Refs #1262.