androidx / media

Jetpack Media3 support libraries for media use cases, including ExoPlayer, an extensible media player for Android
Apache License 2.0
1.35k stars 320 forks source link

Crash on playback in onAudioCapabilitiesChanged #1191

Open nielsvanvelzen opened 3 months ago

nielsvanvelzen commented 3 months ago

Version

Media3 1.3.0

More version details

No response

Devices that reproduce the issue

Devices that do not reproduce the issue

Reproducible in the demo app?

Not tested

Reproduction steps

Steps To Reproduce:

  1. Install and launch the app
  2. Go through the app content and use the functionality as outlined in description
  3. Select Music Category
  4. Click on play button Actual Result: Observe app force closes to launch page Expected Result: App must not experience force close, crash, or soft/hard lock while in use, even when dealing with conflicting inputs.

(Via Amazon tester)

Expected result

The androidx.media3 library should not cause a crash due to invalid state.

Actual result

The check in onAudioCapabilitiesChanged() fails because it is not called from the main thread. This appears to be a regression introduced in 432cde60511db47a739973cff7c2da287380fa15, to be specific the newly introduced "OnRoutingChangedListenerApi24".

03-17 02:58:54.887: E/ACRA(10455): ACRA caught a IllegalStateException for org.jellyfin.androidtv
03-17 02:58:54.887: E/ACRA(10455): java.lang.IllegalStateException
03-17 02:58:54.887: E/ACRA(10455):  at androidx.media3.common.util.Assertions.checkState(Assertions.java:85)
03-17 02:58:54.887: E/ACRA(10455):  at androidx.media3.exoplayer.audio.DefaultAudioSink.onAudioCapabilitiesChanged(DefaultAudioSink.java:1527)
03-17 02:58:54.887: E/ACRA(10455):  at androidx.media3.exoplayer.audio.DefaultAudioSink$$ExternalSyntheticLambda16.onAudioCapabilitiesChanged(D8$$SyntheticClass)
03-17 02:58:54.887: E/ACRA(10455):  at androidx.media3.exoplayer.audio.AudioCapabilitiesReceiver.onNewAudioCapabilities(AudioCapabilitiesReceiver.java:205)
03-17 02:58:54.887: E/ACRA(10455):  at androidx.media3.exoplayer.audio.AudioCapabilitiesReceiver.setRoutedDevice(AudioCapabilitiesReceiver.java:142)
03-17 02:58:54.887: E/ACRA(10455):  at androidx.media3.exoplayer.audio.DefaultAudioSink$OnRoutingChangedListenerApi24.onRoutingChanged(DefaultAudioSink.java:1939)
03-17 02:58:54.887: E/ACRA(10455):  at androidx.media3.exoplayer.audio.DefaultAudioSink$OnRoutingChangedListenerApi24.$r8$lambda$HRb3r58UkCrShJGoyBtN21Qgd6c(DefaultAudioSink.java)
03-17 02:58:54.887: E/ACRA(10455):  at androidx.media3.exoplayer.audio.DefaultAudioSink$OnRoutingChangedListenerApi24$$ExternalSyntheticLambda4.onRoutingChanged(D8$$SyntheticClass)
03-17 02:58:54.887: E/ACRA(10455):  at android.media.AudioTrack$NativeRoutingEventHandlerDelegate$1.handleMessage(AudioTrack.java:2764)
03-17 02:58:54.887: E/ACRA(10455):  at android.os.Handler.dispatchMessage(Handler.java:102)
03-17 02:58:54.887: E/ACRA(10455):  at android.os.Looper.loop(Looper.java:154)
03-17 02:58:54.887: E/ACRA(10455):  at android.os.HandlerThread.run(HandlerThread.java:61)

Media

The Amazon tester used an MP3 file.

Bug Report

nielsvanvelzen commented 3 months ago

The issue template didn't have any field for additional information so I'll post it in a comment; we recently pushed an update to our app to the Amazon Appstore and it was rejected during testing with the above exception. Unfortunately I can't reproduce it myself on my Fire TV Stick 4K Max nor in the emulator or my Nvidia Shield.

Amazon lists the following devices as crashing with this specific error, although the Fire TV Stick 4K is the only one with adb logs.

image

The logs they provided: logcat.log

tonihei commented 3 months ago

The check in onAudioCapabilitiesChanged() fails because it is not called from the main thread.

That's interesting, because all these callbacks should happen on the internal ExoPlayer playback thread. When we set up the routing change listener, we pass in a Handler for the current thread (which is the playback thread). See here: https://github.com/androidx/media/blob/d13a0f4ec62ca092b79746a5725b62a3244cc5b4/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DefaultAudioSink.java#L1921

You are not using or setting up AudioCapabilitiesReceiver yourself, this is really just the internal handling in ExoPlayer?

And second question: Is this definitely only reproducing on the Amazon devices or this is because the evaluation is coming from the Amazon Appstore?

Unfortunately I can't reproduce it myself on my Fire TV Stick 4K Max

Have you tried changing the audio routing mid-playback (e.g. (dis-)connecting Bluetooth)? This is needed to trigger the callback in the first place.

nielsvanvelzen commented 3 months ago

You are not using or setting up AudioCapabilitiesReceiver yourself, this is really just the internal handling in ExoPlayer?

Correct, we don't have any services/receivers or code calling the AudioCapabilitiesReceiver in our code.

And second question: Is this definitely only reproducing on the Amazon devices or this is because the evaluation is coming from the Amazon Appstore?

We rolled out the app update to Google Play to thousands of users without any report about this issue, and I can't find anything in the Play Console related to this issue.

tonihei commented 3 months ago

Correct, we don't have any services/receivers or code calling the AudioCapabilitiesReceiver in our code.

Do you have any other custom code related to the audio renderer, checking format support outside of the player or similar things? I'm trying to exclude any issues that may be caused by using the class in a way we didn't expect it be used.

Unfortunately I can't reproduce it myself on my Fire TV Stick 4K Max

I'm considering a fallback workaround that detects this case and posts another message to the right thread. Can you easily verify it fixes your problem by submitting the code again? And are you able to build from source to test a fix directly?

nielsvanvelzen commented 2 months ago

Do you have any other custom code related to the audio renderer, checking format support outside of the player or similar things? I'm trying to exclude any issues that may be caused by using the class in a way we didn't expect it be used.

Nope, none at all, we initialize ExoPlayer like so in case it is helpful:

ExoPlayer.Builder(context)
            .setRenderersFactory(DefaultRenderersFactory(context).apply {
                setEnableDecoderFallback(true)
                setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
            })
            .setTrackSelector(DefaultTrackSelector(context).apply {
                setParameters(buildUponParameters().apply {
                    setTunnelingEnabled(true)
                    setAudioOffloadPreferences(TrackSelectionParameters.AudioOffloadPreferences.DEFAULT.buildUpon().apply {
                        setAudioOffloadMode(TrackSelectionParameters.AudioOffloadPreferences.AUDIO_OFFLOAD_MODE_ENABLED)
                    }.build())
                })
            })
            .setMediaSourceFactory(DefaultMediaSourceFactory(
                context,
                DefaultExtractorsFactory().apply {
                    val isLowRamDevice = context.getSystemService<ActivityManager>()?.isLowRamDevice == true
                    setTsExtractorTimestampSearchBytes(when (isLowRamDevice) {
                        true -> TS_SEARCH_BYTES_LM
                        false -> TS_SEARCH_BYTES_HM
                    })
                }
            ))
            .setPauseAtEndOfMediaItems(true)
            .build()
            .also { player -> player.addListener(PlayerListener()) }

Can you easily verify it fixes your problem by submitting the code again?

I resubmitted the exact same app release to Amazon and this time they approved it, so unfortunately it will be hard to reproduce or confirm if a fix is working.

tonihei commented 2 months ago

I resubmitted the exact same app release to Amazon and this time they approved it

If this is not reproducible, I'm inclined to leave it for now until we know more. I'll leave the issue open and if you or anyone else sees something like that again we can look into submitting a workaround.

snapwoodapps commented 1 month ago

My usage is similar to the original posters and I am seeing crash reports for this in the field.

It is not limited to Amazon TVs. I am seeing crashes for the following device + models:

Google TV Chromecast Google TV (HD) Sony TV BRAVIA_VU3 Amazon (many fire tv models) various other tvs too obscure to mention...

Random Android versions from 7 to 12.

While still rare, this is the top crash in my apps since releasing on 1.3.1.

tonihei commented 1 month ago

setTunnelingEnabled(true)

@snapwoodapps Are you also using tunneling for your playbacks? Or are you doing any customizations or calling audio capabilities methods manually at any moment?

snapwoodapps commented 1 month ago

No, I do not call setTunnelingEnabled(true).

The only audio code I have in the app is the following during my init of the player:

AudioAttributes audioAttributes = new AudioAttributes.Builder()
        .setUsage(C.USAGE_MEDIA)
        .setContentType(C.AUDIO_CONTENT_TYPE_MOVIE)
        .build();

if(getIntent().getBooleanExtra("mute", false)) {
    mExoPlayer.setAudioAttributes(audioAttributes, /* handleAudioFocus= */ false);
    mExoPlayer.setVolume(0.0f);
} else {
    mExoPlayer.setAudioAttributes(audioAttributes, /* handleAudioFocus= */ true);
}

I was not able to recreate this issue by connecting / disconnecting a headset to a Google Chromecast during playback.

tonihei commented 1 month ago

Thanks for the information, that's useful to know! We are planning to add additional logging to the exception so that we can figure out which of the two threads that are compared is not the one we expected. This won't solve the issue yet, but it would hopefully provide more hints to where this may be coming from.