Skip to content

feat(audio): hand TrueHD and DTS-HD passthrough to the host as spatial audio - #3

Open
kristofferR wants to merge 2 commits into
streamyfin:avfoundation-ao-tvosfrom
kristofferR:upstream/spatial-audio-output
Open

kristofferR wants to merge 2 commits into
streamyfin:avfoundation-ao-tvosfrom
kristofferR:upstream/spatial-audio-output

Conversation

@kristofferR

@kristofferR kristofferR commented Oct 12, 2026 •

Copy link
Copy Markdown

AI Assisted

AI used: Claude Opus 5.5, extra high reasoning.

Description

mpv decodes TrueHD and DTS-HD to PCM, so the Atmos objects and DTS:X never reach the receiver. Players that want them as Apple spatial audio, through SpatialAudioBridge, need the compressed stream, and libmpv has no API for it.

Patch 0007 adds a spatial audio output for that:

  • With --audio-spdif=truehd,dts-hd and --ao=spatial,<pcm output>, it hands mpv's passthrough bursts, unchanged, to a host sink described in the new public header mpv/spatial_audio.h and passed as --ao-spatial-sink.
  • It owns an AVSampleBufferRenderSynchronizer, gives it to the host to render into, and reports mpv's delay from its clock. The clock waits at the timeline's start until the host has enough queued, so video waits with it. Reset begins a new timeline and pause stops the clock. mpv queues at most 3 s ahead.
  • mpv's software volume and mute go to the host through set_volume, since a bitstream cannot be scaled. ao_set_gain() calls a new optional set_gain driver hook for that, so a change arrives at once, paused or not.
  • A host that fails on a stream can report it through has_failed. The output then latches that format and reloads, the reopen refuses it, and mpv decodes the track. The next PCM output clears the latch.
  • Every other format, and any player that doesn't set the sink, is unaffected: the output refuses and is never autoprobed.
  • Known limitation: it needs loadfile replace between files. Gapless handoff to a queued next file is not supported.

Patches 0008 and 0009 fix mpv's audio output reload, which left the frame held for the old output in the ao filter and never woke the output chain, so the next output never opened. The latch fallback needs them.

FFmpeg now builds its spdif muxer, which mpv's passthrough needs. The release workflow runs the output's unit test and checks that the muxer is in the build. The podspec version and the release notes are left for the release.

Existing players are unaffected: Streamyfin's current develop needs no change for this release, because nothing selects the new output unless the app sets the sink. Streamyfin's spatial audio PR, streamyfin/streamyfin#2268, on the other hand, needs a release with this patch: its sink uses mpv/spatial_audio.h's types, so it does not compile against 0.41.0-av5.

The same sink interface (version 2) is in Plezy's mpv build, byte for byte, so one host sink works with both.

Testing

  • scripts/test-spatial-audio-output.sh builds the output against the pinned mpv with every patch applied and drives it like mpv's push thread, against a fake sink and a real synchronizer: the clock hold, pause, underruns, seeks, the end of a stream, volume (including before open and while paused) and the latch. The release workflow runs it.
  • On Apple TV 4K (tvOS 27.2 beta) and iPhone 16 Pro with AirPods Pro, through Streamyfin with a build of these commits: TrueHD Atmos and DTS:X play as spatial audio, and the soundbar shows Atmos; seeks, pause, track switches, mute and volume work; AC-3 opens through spatial,audiounit as PCM; DTS-HD MA without DTS:X falls back to PCM.
  • Not tested: macOS (not built), gapless playback, two players in one process (the latch is process-wide, like avfoundation's), and Android, which this repository does not build for.

The CI workflow's Demo-macOS build fails here, as it already does on release/0.41.0-av5: Package.swift points at an iOS and tvOS-only xcframework.

mpv's reload left the frame it had handed to the old output in the ao
filter, which then never asked the chain for more, and never woke the
output chain after resetting it, so the next output never opened. Both
patches come from kristofferR/mpv-build (pool 0034 and 0102).
…l audio

mpv decodes TrueHD and DTS-HD to PCM, so the Atmos objects and DTS:X
never reach the receiver. Patch 0007 adds a "spatial" audio output that
hands the passthrough bursts to a host sink (mpv/spatial_audio.h, sink
version 2) and keeps mpv's clock on the AVSampleBufferRenderSynchronizer
the host renders into. mpv's volume and mute reach the host through
set_volume, and a stream the host fails on falls back to PCM.

FFmpeg now builds its spdif muxer, which passthrough needs. The release
workflow runs the output's unit test and checks the muxer is in the
build. Every other format, and any player that doesn't set the sink, is
unaffected.
@coderabbitai

coderabbitai Bot commented Oct 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 3ca0e7a0-fb84-46d1-a1c3-9f1fc1c60732

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant