Skip to content

feat(player): optional local decoding so volume keys work on passthrough audio - #416

Open
Pierroons wants to merge 5 commits into
oxyroid:masterfrom
Pierroons:feat/disable-audio-passthrough-pr
Open

feat(player): optional local decoding so volume keys work on passthrough audio#416
Pierroons wants to merge 5 commits into
oxyroid:masterfrom
Pierroons:feat/disable-audio-passthrough-pr

Conversation

@Pierroons

Copy link
Copy Markdown
Contributor

On a TV box that feeds the speakers itself, the volume keys do nothing on
5.1 content while working normally on stereo channels.

Why

ExoPlayer asks the audio output what it can handle, and when the sink claims
AC3/EAC3 support it forwards the stream untouched. That is right with an A/V
receiver, but where the box decodes for itself the stream never reaches the
mixer — so Android volume has no hold on it. A stereo channel goes through
type 0 (MIXER) as PCM and responds; a 5.1 film goes out as
type 1 (DIRECT) and ignores the remote entirely.

An opt-in setting, off by default, declares stereo-only capabilities so
ExoPlayer decodes instead and audio flows back through the mixer.

Three traps on the way, one commit each

DefaultAudioSink.Builder(context) silently discards
setAudioCapabilities.
With a Context it probes the real output and drops
what was declared. The builder has to be constructed without one.

The hardware AC3 decoder produces nothing when asked for PCM.
EXTENSION_RENDERER_MODE_ON makes ExoPlayer prefer
OMX.amlogic.audio.decoder.ac3, built to feed a passthrough path. Asked for
PCM it emits no output and reports no error — the stream plays, the mixer
runs, and nothing is audible.

Raising the extension mode on the factory applies it to video too.
setExtensionRendererMode covers every renderer, so preferring FFmpeg
globally also hands video to the software decoders, turning smooth playback
into a slideshow on a modest box. The mode is overridden inside
buildAudioRenderers instead.

Verification

Measured on device (Amlogic box, Android 9) on an AC3 5.1 film:

video  OMX.amlogic.avc.decoder.awesome   hardware, smooth
audio  MIXER / PCM_16_BIT                decoded, volume keys working

Confirmed by ear and by eye, not only from dumpsys: each of the three
earlier attempts looked correct in the audio routing while producing silence
or stutter.

On Amlogic TV boxes, AC3/EAC3 streams are handed to the HDMI sink untouched.
Playback works, but the stream never reaches the mixer, so volume keys have no
effect on it: a stereo channel responds normally while a 5.1 movie ignores the
remote entirely. Users read this as a broken remote, and there is no in-app way
to change it.

Adds a DISABLE_AUDIO_PASSTHROUGH preference, off by default — passthrough is the
right behaviour when an A/V receiver does the decoding, and forcing it off would
cost those users their surround track.

NOT WORKING YET. The override targets DefaultRenderersFactory.buildAudioSink,
which NextRenderersFactory appears to bypass when building its FFmpeg audio
renderers: measured after install, the DIRECT/AC3 output thread is still created.
The preference, its propagation and the UI entry are correct and verified; only
the interception point is wrong.

Next step: inspect how NextRenderersFactory assembles audio renderers and hook
buildAudioRenderers instead.

Verified so far: builds clean, installs alongside the release build via a debug
applicationId suffix, preference persists, and the setting appears in Optional
settings.
…apply

Follow-up to the previous commit, which added the preference but had no effect.

DefaultAudioSink.Builder only honours setAudioCapabilities when built WITHOUT a
Context. Given one, it probes the real output instead and discards whatever was
declared — silently, with no warning at build or run time. The override was
therefore running exactly as written, and being ignored.

Confirmed in the 1.9.1 bytecode: build() reads the context field and branches on
it being null before applying the capabilities.

Measured on an Amlogic X96 Max+ (Android 9), same 5.1 AC3 movie:

  before  AudioOut_1FD  type DIRECT  AUDIO_FORMAT_AC3  6ch   volume keys dead
  after   AudioOut_D    type MIXER   PCM_16_BIT        2ch   12 through 15, back to 13

Playback is uninterrupted; the FFmpeg renderers already bundled handle the
decoding.

Trade-off unchanged and stated in the setting description: audio is downmixed to
stereo while enabled, so anyone relying on an A/V receiver for surround should
leave it off — which is the default.
Second half of the fix. Routing audio through the mixer was not enough: the
stream played, the mixer ran, and nothing came out of the speakers.

EXTENSION_RENDERER_MODE_ON keeps platform decoders first and only falls back to
the bundled ones. ExoPlayer therefore picked OMX.amlogic.audio.decoder.ac3 —
a hardware decoder built to feed a passthrough path. Asked for PCM instead, it
produced no output at all, and reported no error: the failure is silent in both
senses.

Switching to EXTENSION_RENDERER_MODE_PREFER when the preference is on hands
decoding to the FFmpeg renderers already shipped with the app. Only affects
users who enabled the setting; default behaviour is untouched.

Verified on an Amlogic X96 Max+ (Android 9) with a 5.1 AC3 movie: audio present
and volume keys working across the full range, 13 down to 11 and back to 13.
…oding

The previous commit raised the extension renderer mode on the factory itself.
That mode applies to every renderer, not just audio: video went to the software
decoders too, and playback on a modest TV box became a slideshow. Sound was
fixed, picture was ruined.

The mode is now overridden inside buildAudioRenderers, which receives it as a
parameter, leaving the factory-wide setting untouched. Audio decoding goes to
the bundled FFmpeg renderers; video keeps whatever the platform provides.

Verified on an Amlogic X96 Max+ (Android 9) with a 5.1 AC3 movie:

  video  OMX.amlogic.avc.decoder.awesome   hardware, smooth playback
  audio  MIXER / PCM_16_BIT                decoded, volume keys working

Confirmed by ear and eye, not only by dumpsys: the three earlier attempts each
looked correct in the audio routing while producing silence or stutter.
private val renderersFactory: RenderersFactory by lazy {
Codecs.createRenderersFactory(context)
}
// La fabrique n'est plus mise en cache : le réglage de décodage audio peut

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0ac32ec — it was left in French, which nobody else here reads. Sorry about that. I checked the other branches: it was the only one.

It had been left in French, which no one else on this project reads.

Co-Authored-By: Claude Opus 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