Skip to content

Audio: Opus direct play, stereo in Auto (part of #918) - #992

Merged
GLinnik21 merged 6 commits into
GLinnik21:mainfrom
Dr-Amp:contrib/opus-direct-play
Oct 25, 2026
Merged

GLinnik21 merged 6 commits into
GLinnik21:mainfrom
Dr-Amp:contrib/opus-direct-play

Conversation

@Dr-Amp

@Dr-Amp Dr-Amp commented Oct 24, 2026

Copy link
Copy Markdown

Part of #918 ("Opus direct play up to stereo").

LG's buffer feed decodes Opus (OPUS + contents.opusInfo, as moonlight-tv's ss4s feeds it), but DP_AUDIO_CODECS never offered it, so every Opus track went to the server and Force Direct Play refused it.

Change

  • devcaps: opus is a pipeline-only audio codec: admitted only on a device table that was actually read, never on the assumed fallback, in Auto and in Force Direct Play (forced_feeds_audio) alike.
  • Auto takes stereo only (OPUS_AUTO_MAX_CHANNELS = 2), whatever a table row claims, following the C1 measurements in FLAC: decode to PCM in the app, up to stereo #917 (5.1 stalls once packets pass ~1500 bytes, with nothing in the log). The cap rides audio_channels, so the profile's audio.channels limitation hands surround Opus to the server and the planner refuses it locally. Force Direct Play keeps no ceiling, as for every codec.
  • engine: an OPUS Load carries opusInfo {channels, sampleRate: 48, streamHeader: base64(OpusHead)}: mapping family 0 for 1–2 channels, Vorbis family 1 for 3–8 (libopus's surround layout; the 5.1 header matches a real track's CodecPrivate byte for byte). A width it cannot describe refuses the Load instead of guessing.
  • transcoder: opus joins the copy-only target lanes, last, so it is never the encode head.
  • ci/list-caps.ini classes the new constant (not-a-list).

Verification

Host: CachyOS x86_64, pinned toolchain 1.99.1, on 3b7c6e28 (main e843b99f):

  • check-cargo-lint: pass. check-nodef: pass. check-python-harness: Ran 542 tests … OK (skipped=1).
  • check-cargo-unit-default / check-cargo-unit-hostsim: every crate passes except plx_media's two curlio::keymode_tests (1709 passed; 2 failed / 1870 passed; 2 failed). The same two fail on unmodified main e843b99f on this host (6 passed; 2 failed, filtered run): libcurl 8.22 here, not this change.
  • check-python-rest: one failure, ci/test_disk_preflight.py ("only 2.0 GiB free for ~/main/rust-modules/target"), identical on unmodified main: the host's disk.
  • Not run here: the ARM cross-build (CI's job) and check-replay (no Mac; no screen shape changes).

Device: LG C5, webOS 10.3: an AV1 + Opus 5.1 MKV direct-played through the family-1 header, with seeks and next-episode autoplay. That run predates the stereo cap, so it was Auto; it is now Force Direct Play's path. The stereo (family 0) header is covered by unit tests but was not played on the panel.

Known gap: a native switch between two Opus tracks of different widths keeps the first track's channel count (route::stream_achannels).

Signed-off-by: Dr-Amp dr_amp@users.noreply.github.com

Dr-Amp and others added 2 commits October 24, 2026 13:43
The device table already lists OPUS, but DP_AUDIO_CODECS never offered it, so
every Opus track went to the server for conversion and Force direct play failed.

- devcaps: opus is a pipeline-only audio codec, appended only when the device
  table was actually read; the assumed fallback still omits it, and Force
  direct play applies the same table gate (forced_feeds_audio).
- engine: the Load payload carries codec.audio "OPUS" plus contents.opusInfo
  {channels, sampleRate 48, streamHeader = base64 OpusHead} (family 0 for 1-2
  channels, Vorbis mapping family 1 for 3-8). An unknown width refuses the Load.
- transcoder: opus joins the copy-only lanes, last, so it is never the encode head.

Verified on an LG C5 (webOS 10.3): AV1 + Opus 5.1 MKV direct play, seeks and
next-episode autoplay. Known gap: a native switch between two Opus tracks of
different widths keeps the first track's channel count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 26bf886)
Signed-off-by: Dr-Amp <dr_amp@users.noreply.github.com>
…he server

A C1 (webOS 6) stalls on 5.1 Opus once packets pass ~1500 bytes, with nothing in the log
(GLinnik21#917, GLinnik21#918). Auto now caps Opus at two channels whatever a table row claims, so the profile's
audio.channels limitation hands surround Opus to the server and the planner refuses it locally.
Force Direct Play keeps no ceiling, as for every codec; a 5.1 Opus film played through on a C5
(webOS 10.3), which is what a later per-model allowance would rest on.

Signed-off-by: Dr-Amp <dr_amp@users.noreply.github.com>
Dr-Amp added 2 commits October 24, 2026 22:19
Signed-off-by: Dr-Amp <dr_amp@users.noreply.github.com>
Brings upstream main (e87ca65: FLAC decoded in the app and fed as PCM, GLinnik21#950) into
the Opus branch. Conflicts, each resolved by keeping both sides:

- Load audio code: upstream's FLAC PCM already takes 5, so Opus moves to 6
  (audio_payload_codec, load_a_str, and the Opus Load test).
- devcaps: forced_feeds_audio admits pipeline-only Opus and app-decoded FLAC on a
  read table. The audio list is DP_AUDIO_CODECS's subset, then Opus, then flac.
  OPUS_AUTO_MAX_CHANNELS and PCM_MAX_CHANNELS are both classified in ci/list-caps.ini.
- engine: the opusInfo helpers and upstream's pcmInfo helpers are both kept.

Signed-off-by: Dr-Amp <dr_amp@users.noreply.github.com>
GLinnik21 and others added 2 commits October 26, 2026 02:04
…caps merge

Behaviour is unchanged.

- engine.rs: drop the local `base64` helper; `opusInfo.streamHeader` is encoded with the
  existing `plx_base::b64::encode` (the keymanager and SPKI pin code already share it). The
  RFC 4648 vector test moves to `base/src/b64.rs`, next to the code it covers.
- engine.rs: remove a stray blank line the PR added before an unrelated test.
- devcaps.rs: the Opus Auto ceiling uses `BTreeMap::entry` instead of a get/insert pair, and the
  `table_gated` closure (used once negated, once as a filter) becomes a direct
  `PIPELINE_ONLY_AUDIO.contains` test on each side.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@GLinnik21
GLinnik21 merged commit f70d42b into GLinnik21:main Oct 25, 2026
13 checks passed
@GLinnik21

Copy link
Copy Markdown
Owner

Thanks @Dr-Amp, Opus direct play is a nice one to tick off #918, and the byte-for-byte check of the 5.1 header against a real CodecPrivate is great. Merged.

Small cleanups I pushed before merging, behaviour the same:

  • with_opus_info uses the shared plx_base::b64::encode instead of a local base64 helper. Your RFC 4648 vector test moved next to it in base/src/b64.rs.
  • in devcaps parse(), the Opus Auto ceiling is one entry(..).or_default() + min_nz, and the table_gated closure became direct PIPELINE_ONLY_AUDIO.contains checks.
  • main merged in clean.

tiper's #1034 builds on this, so it goes in next.

GLinnik21 added a commit to tiper/fin-native that referenced this pull request Oct 25, 2026
Resolves the Preferences descriptor imports and count: GLinnik21#992's
InstantDpadSeeking row (17 Setting rows) next to GLinnik21#1034's
NativeAudioRefusal list row (5 list rows).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GLinnik21 added a commit to tiper/fin-native that referenced this pull request Oct 25, 2026
…1034

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GLinnik21 added a commit to tiper/fin-native that referenced this pull request Oct 25, 2026
…his tree

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dr-Amp pushed a commit to Dr-Amp/plx-native that referenced this pull request Oct 25, 2026
…b/opus-live-tv

Signed-off-by: Dr-Amp <dr_amp@users.noreply.github.com>

# Conflicts:
#	rust-modules/media/src/player/engine.rs
#	rust-modules/platform/src/devcaps.rs
#	rust-modules/screens/src/registry.rs
Dr-Amp pushed a commit to Dr-Amp/plx-native that referenced this pull request Oct 25, 2026
…us-live-tv

Signed-off-by: Dr-Amp <dr_amp@users.noreply.github.com>
Dr-Amp pushed a commit to Dr-Amp/plx-native that referenced this pull request Oct 25, 2026
…tree

Signed-off-by: Dr-Amp <dr_amp@users.noreply.github.com>
tiper added a commit to tiper/fin-native that referenced this pull request Oct 26, 2026
…f alone

Upstream's GLinnik21#992 and GLinnik21#1034 now feed stereo Opus to the television's own decoder, with the
per-firmware exclusions and the learned refusals, and GLinnik21#950 feeds FLAC as PCM. That replaces
FinNative's own native FLAC, Opus and Vorbis feed, which this commit used to carry: on the C1
its native FLAC was stereo only, where the PCM route already plays it, and Vorbis was refused at
every channel count.

What remains is the one rule the conversion needs beside them: with Convert audio on the TV, a
track the TV's table decodes at its channel count is fed as it is, not converted to AC3.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

2 participants