From b440b326d05fbe8bbef7f2524dbc629ab5548d87 Mon Sep 17 00:00:00 2001 From: flan Date: Sat, 18 Jul 2026 18:33:07 +0000 Subject: [PATCH 1/2] Route multichannel FLAC to the FFmpeg decoder The AOSP software FLAC decoder (c2.android.flac / OMX.google.flac) advertises multichannel support but fails at runtime with "no streaminfo metadata block", so 5.1 FLAC can't Direct Play and stalls or falls back to transcoding. Report multichannel (>2 channel) FLAC as unsupported in a MediaCodecAudioRenderer subclass so the track selector routes it to the bundled FFmpeg extension decoder, but only when the AOSP software FLAC decoder is the one that would be used, so devices with a vendor FLAC decoder, stereo FLAC, and every other format keep using the platform decoder, unchanged. --- .../wholphin/services/PlayerFactory.kt | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt b/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt index 24736015d..1bb0abecc 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt @@ -8,6 +8,8 @@ import android.os.Handler import androidx.annotation.OptIn import androidx.media3.common.AudioAttributes import androidx.media3.common.C +import androidx.media3.common.Format +import androidx.media3.common.MimeTypes import androidx.media3.common.Player import androidx.media3.common.TrackSelectionParameters.AudioOffloadPreferences import androidx.media3.common.util.ExperimentalApi @@ -17,7 +19,11 @@ import androidx.media3.datasource.okhttp.OkHttpDataSource import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.Renderer +import androidx.media3.exoplayer.RendererCapabilities import androidx.media3.exoplayer.RenderersFactory +import androidx.media3.exoplayer.audio.AudioRendererEventListener +import androidx.media3.exoplayer.audio.AudioSink +import androidx.media3.exoplayer.audio.MediaCodecAudioRenderer import androidx.media3.exoplayer.mediacodec.MediaCodecSelector import androidx.media3.exoplayer.source.DefaultMediaSourceFactory import androidx.media3.exoplayer.trackselection.DefaultTrackSelector @@ -309,4 +315,61 @@ class WholphinRenderersFactory( } } } + + override fun buildAudioRenderers( + context: Context, + extensionRendererMode: Int, + mediaCodecSelector: MediaCodecSelector, + enableDecoderFallback: Boolean, + audioSink: AudioSink, + eventHandler: Handler, + eventListener: AudioRendererEventListener, + out: ArrayList, + ) { + super.buildAudioRenderers( + context, + extensionRendererMode, + mediaCodecSelector, + enableDecoderFallback, + audioSink, + eventHandler, + eventListener, + out, + ) + // Replace the platform audio renderer with one that reports multichannel FLAC as + // unsupported, so the track selector routes it to the FFmpeg decoder instead. The AOSP + // software FLAC decoder (OMX.google.flac / c2.android.flac) claims to support it but fails + // at runtime with "no streaminfo metadata block"; stereo FLAC and vendor decoders are left + // alone. Via supportsFormat -- that, not getDecoderInfos, is what track selection consults. + val platformIndex = out.indexOfFirst { it is MediaCodecAudioRenderer } + if (platformIndex < 0) return + out[platformIndex] = + object : MediaCodecAudioRenderer( + context, + codecAdapterFactory, + mediaCodecSelector, + enableDecoderFallback, + eventHandler, + eventListener, + audioSink, + ) { + override fun supportsFormat( + mediaCodecSelector: MediaCodecSelector, + format: Format, + ): Int { + if (MimeTypes.AUDIO_FLAC == format.sampleMimeType && format.channelCount > 2) { + val decoders = super.getDecoderInfos(mediaCodecSelector, format, false) + val aospSoftwareOnly = + decoders.isNotEmpty() && + decoders.all { + it.name.startsWith("OMX.google.") || it.name.startsWith("c2.android.") + } + if (aospSoftwareOnly) { + return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE) + } + } + return super.supportsFormat(mediaCodecSelector, format) + } + } + } } From 4456bdda045ad2006fbe6de7cb7f596ed2555a8a Mon Sep 17 00:00:00 2001 From: flan Date: Thu, 23 Jul 2026 17:23:11 +0000 Subject: [PATCH 2/2] Scope the multichannel FLAC workaround to the NVIDIA SHIELD 2019 Gate the reroute on KnownDefects.multichannelFlacBug, keyed on Build.DEVICE "sif" (the 2019 non-Pro SHIELD), instead of applying it to every device whose FLAC decoder is the AOSP software codec. Other devices decode multichannel FLAC natively and keep the platform decoder, unchanged. --- .../wholphin/services/PlayerFactory.kt | 25 ++++++++----------- .../wholphin/util/profile/KnownDefects.kt | 12 +++++++++ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt b/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt index 1bb0abecc..efd292194 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/PlayerFactory.kt @@ -39,6 +39,7 @@ import com.github.damontecres.wholphin.preferences.PlayerBackend import com.github.damontecres.wholphin.preferences.get import com.github.damontecres.wholphin.services.hilt.AuthOkHttpClient import com.github.damontecres.wholphin.util.WholphinDispatchers +import com.github.damontecres.wholphin.util.profile.KnownDefects import dagger.hilt.android.qualifiers.ApplicationContext import io.github.peerless2012.ass.media.AssHandler import io.github.peerless2012.ass.media.factory.AssRenderersFactory @@ -336,11 +337,13 @@ class WholphinRenderersFactory( eventListener, out, ) - // Replace the platform audio renderer with one that reports multichannel FLAC as - // unsupported, so the track selector routes it to the FFmpeg decoder instead. The AOSP - // software FLAC decoder (OMX.google.flac / c2.android.flac) claims to support it but fails - // at runtime with "no streaminfo metadata block"; stereo FLAC and vendor decoders are left - // alone. Via supportsFormat -- that, not getDecoderInfos, is what track selection consults. + // Devices in KnownDefects.multichannelFlacBug can't decode multichannel FLAC with the + // platform decoder. Replace the platform audio renderer with one that reports >2-channel + // FLAC as unsupported, so the track selector routes it to the bundled FFmpeg decoder; + // stereo FLAC and every other format keep the platform decoder, unchanged. Replaced in + // place (not appended) to preserve renderer order, and done via supportsFormat -- that, + // not getDecoderInfos, is what track selection consults. + if (!KnownDefects.multichannelFlacBug) return val platformIndex = out.indexOfFirst { it is MediaCodecAudioRenderer } if (platformIndex < 0) return out[platformIndex] = @@ -357,16 +360,8 @@ class WholphinRenderersFactory( mediaCodecSelector: MediaCodecSelector, format: Format, ): Int { - if (MimeTypes.AUDIO_FLAC == format.sampleMimeType && format.channelCount > 2) { - val decoders = super.getDecoderInfos(mediaCodecSelector, format, false) - val aospSoftwareOnly = - decoders.isNotEmpty() && - decoders.all { - it.name.startsWith("OMX.google.") || it.name.startsWith("c2.android.") - } - if (aospSoftwareOnly) { - return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE) - } + if (format.sampleMimeType == MimeTypes.AUDIO_FLAC && format.channelCount > 2) { + return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE) } return super.supportsFormat(mediaCodecSelector, format) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/profile/KnownDefects.kt b/app/src/main/java/com/github/damontecres/wholphin/util/profile/KnownDefects.kt index d2a3c0428..995cd1f4e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/profile/KnownDefects.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/profile/KnownDefects.kt @@ -22,6 +22,17 @@ private val modelsWithEac3TsPassthroughBug = "AFTKRT", // Amazon Fire TV 4K Max (2nd Gen) ) +/** + * List of device codenames whose platform FLAC decoder advertises multichannel support but fails + * at runtime on multichannel (>2 channel) FLAC with "no streaminfo metadata block". Keyed on + * Build.DEVICE, not Build.MODEL: every SHIELD TV reports the same model ("SHIELD Android TV"), so + * the codename is what isolates the affected 2019 (non-Pro) unit from the Pro and older models. + */ +private val devicesWithMultichannelFlacBug = + listOf( + "sif", // NVIDIA SHIELD TV 2019 (non-Pro, 2GB "Tube") + ) + /** * List of device models that support H264 Hi10P 5.2, but don't advertise it * @@ -39,5 +50,6 @@ private val modelsWithHi10P52Support = object KnownDefects { val hevcDoviHdr10PlusBug = Build.MODEL in modelsWithDoViHdr10PlusBug val eac3HlsPassthroughBug = Build.MODEL in modelsWithEac3TsPassthroughBug + val multichannelFlacBug = Build.DEVICE in devicesWithMultichannelFlacBug val supportsHi10P52 = Build.MODEL in modelsWithHi10P52Support }