media icon indicating copy to clipboard operation
media copied to clipboard

CompositionPlayer.release() ANR

Open AradiPatrik opened this issue 7 months ago • 3 comments

Version

Media3 pre-release (alpha, beta or RC not in this list)

More version details

1.8.0-alpha01

Devices that reproduce the issue

Galaxy S24 Galaxy S24 ultra Galaxy S23 V40

Devices that do not reproduce the issue

Reproducible in the demo app?

Not tested

Reproduction steps

I use the code below for my composable showing a composition inside a composition player. From time-to-time player.release will block indefinitely and this will cause ANR. This happens when the composition changes and I need to create a new player and dispose the previous player.

@SuppressLint("RestrictedApi")
@OptIn(UnstableApi::class)
@Composable
fun CompositionPlayerView(
    state: CompositionPlayerViewState,
    modifier: Modifier,
) {
    val context = LocalContext.current

    val player = remember(state.composition) {
        CompositionPlayer.Builder(context)
            .build()
            .apply {
                setComposition(state.composition)
                prepare()
                playWhenReady = true
            }
    }

    val lifecycleOwner = context as LifecycleOwner
    ComposableLifecycle(lifecycleOwner) { _, event ->
        when (event) {
            Lifecycle.Event.ON_PAUSE -> {
                player.pause()
            }

            else -> Unit
        }
    }

    PlayerSurface(
        player = player,
        modifier = modifier.clickable {
            if (player.isPlaying) {
                player.pause()
            } else {
                player.play()
            }
        },
    )

    DisposableEffect(player) {
        onDispose {
            player.pause()
            player.stop()
            player.release()
        }
    }
}

I traced the issue and found that the ANR happens because conditionVariable.block() blocks indefinitely

  public void release() {
    checkState(!released);
    // Set released to true now to silence any pending listener callback.
    released = true;
    ConditionVariable conditionVariable = new ConditionVariable();
    handler.obtainMessage(MSG_RELEASE, conditionVariable).sendToTarget();
    clock.onThreadBlocked();
    try {
      conditionVariable.block(); // this is the exact point where we get blocked indefinetely
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new IllegalStateException(e);
    }
  }

Expected result

compositionPlayer.release() shouldn't cause ANR

Actual result

compositionPlayer.release() causes ANR

Media

doesn't matter

Bug Report

  • [ ] You will email the zip file produced by adb bugreport to [email protected] after filing this issue.

AradiPatrik avatar Jun 18 '25 05:06 AradiPatrik

Do you use custom effects in the Composition? Can you check if it's DefaultVideoFrameProcessor.release() blocked indefinitely? (Sorry I cannot reproduce it locally)

claincly avatar Jun 18 '25 14:06 claincly

Do you use custom effects in the Composition? Can you check if it's DefaultVideoFrameProcessor.release() blocked indefinitely? (Sorry I cannot reproduce it locally)

Sorry I forgot to link the stack trace from the ANR you can find it below. I do use effects: Presentation.createForWIdthAndHight and my own MatrixTransformation for scaling and translation. I also use clipping configuration on the videos and images.

          main (waiting):tid=1 systid=11866 
       at java.lang.Object.wait(Native method)
       at java.lang.Object.wait(Object.java:405)
       at java.lang.Object.wait(Object.java:543)
       at androidx.media3.common.util.ConditionVariable.block(ConditionVariable.java:86)
       at androidx.media3.transformer.CompositionPlayerInternal.release(CompositionPlayerInternal.java:149)
       at androidx.media3.transformer.CompositionPlayer.handleRelease(CompositionPlayer.java:599)
       at androidx.media3.common.SimpleBasePlayer.release(SimpleBasePlayer.java:2608)
       at com.cardinalblue.onbeat.ui.screens.maineditor.player.MultiMediaItemExoPlayerKt$CompositionPlayerView$lambda$13$lambda$12$$inlined$onDispose$1.dispose(Effects.kt:71)
       at androidx.compose.runtime.DisposableEffectImpl.onForgotten(Effects.kt:87)
       at androidx.compose.runtime.internal.RememberEventDispatcher.dispatchRememberObservers(RememberEventDispatcher.kt:158)
       at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:1044)
       at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:1067)
       at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:684)
       at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:591)
       at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:39)
       at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.java:108)
       at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.java:41)
       at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1749)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1760)
       at android.view.Choreographer.doCallbacks(Choreographer.java:1216)
       at android.view.Choreographer.doFrame(Choreographer.java:1132)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1707)
       at android.os.Handler.handleCallback(Handler.java:959)
       at android.os.Handler.dispatchMessage(Handler.java:100)
       at android.os.Looper.loopOnce(Looper.java:257)
       at android.os.Looper.loop(Looper.java:342)
       at android.app.ActivityThread.main(ActivityThread.java:9638)
       at java.lang.reflect.Method.invoke(Native method)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:619)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:929)

It seems like it's not DefaultVideoFrameProcessor.release() but CompositionPlayerInternal.release()

AradiPatrik avatar Jun 19 '25 02:06 AradiPatrik

Hi @AradiPatrik , Are you using the new Compose PlayerSurface together with CompositionPlayer? If this is the case, unfortunately, we have not integrated and tested CompositionPlayer fully with Compose yet. Please use playerView if you want to use CompositionPlayer.

droid-girl avatar Jun 20 '25 09:06 droid-girl

Hi @droid-girl! Thanks for the support. Yes I'm using PlayerSurface. I will change the implementation to use PlayerView and report back whether the ANRs disappeared from crashlytics as a result or not.

AradiPatrik avatar Jun 23 '25 01:06 AradiPatrik

@droid-girl Transitioning to PlayerView had this error: https://github.com/androidx/media/issues/2554

I will keep this issue open for checking if the ANR issue is solved after using PlayerView with the workaround mentioned in the issue above.

AradiPatrik avatar Jun 23 '25 03:06 AradiPatrik

@droid-girl I can still reproduce the ANR changing PlayerSurface to PlayerView. I was able to recover additional stack trace (this is the error before the ANR)

playerFailed [eventTime=8.57, mediaPos=11.54, window=0, period=7, errorCode=ERROR_CODE_TIMEOUT
androidx.media3.exoplayer.ExoPlaybackException: Unexpected runtime error
    at androidx.media3.exoplayer.ExoPlayerImpl.lambda$release$6(Unknown Source:8)
    at androidx.media3.exoplayer.ExoPlayerImpl.C(Unknown Source:0)
    at androidx.media3.exoplayer.x.invoke(Unknown Source:2)
    at androidx.media3.common.util.ListenerSet$ListenerHolder.invoke(Unknown Source:17)
    at androidx.media3.common.util.ListenerSet.lambda$queueEvent$0(Unknown Source:16)
    at androidx.media3.common.util.ListenerSet.a(Unknown Source:0)
    at Z3.e.run(Unknown Source:129)
    at androidx.media3.common.util.ListenerSet.flushEvents(Unknown Source:67)
    at androidx.media3.common.util.ListenerSet.sendEvent(Unknown Source:3)
    at androidx.media3.exoplayer.ExoPlayerImpl.release(Unknown Source:104)
    at androidx.media3.transformer.CompositionPlayer.handleRelease(Unknown Source:39)
    at androidx.media3.common.SimpleBasePlayer.release(Unknown Source:14)
    at A.Q.a(Unknown Source:131)
    at S.F.b(Unknown Source:4)
    at a0.h.d(Unknown Source:49)
    at S.w.e(Unknown Source:60)
    at S.w.d(Unknown Source:5)
    at S.w0.invoke(Unknown Source:190)
    at E0.e0.doFrame(Unknown Source:6)
    at E0.c0.doFrame(Unknown Source:47)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1687)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1698)
    at android.view.Choreographer.doCallbacks(Choreographer.java:1153)
    at android.view.Choreographer.doFrame(Choreographer.java:1069)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1646)
    at android.os.Handler.handleCallback(Handler.java:958)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:230)
    at android.os.Looper.loop(Looper.java:319)
    at android.app.ActivityThread.main(ActivityThread.java:8919)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
Caused by: androidx.media3.exoplayer.ExoTimeoutException: Player release timed out.
    at androidx.media3.exoplayer.ExoPlayerImpl.lambda$release$6(Unknown Source:3)
    at androidx.media3.exoplayer.ExoPlayerImpl.C(Unknown Source:0) 
    at androidx.media3.exoplayer.x.invoke(Unknown Source:2) 
    at androidx.media3.common.util.ListenerSet$ListenerHolder.invoke(Unknown Source:17) 
    at androidx.media3.common.util.ListenerSet.lambda$queueEvent$0(Unknown Source:16) 
    at androidx.media3.common.util.ListenerSet.a(Unknown Source:0) 
    at Z3.e.run(Unknown Source:129) 
    at androidx.media3.common.util.ListenerSet.flushEvents(Unknown Source:67) 
    at androidx.media3.common.util.ListenerSet.sendEvent(Unknown Source:3) 
    at androidx.media3.exoplayer.ExoPlayerImpl.release(Unknown Source:104) 
    at androidx.media3.transformer.CompositionPlayer.handleRelease(Unknown Source:39) 
    at androidx.media3.common.SimpleBasePlayer.release(Unknown Source:14) 
    at A.Q.a(Unknown Source:131) 
    at S.F.b(Unknown Source:4) 
    at a0.h.d(Unknown Source:49) 
    at S.w.e(Unknown Source:60) 
    at S.w.d(Unknown Source:5) 
    at S.w0.invoke(Unknown Source:190) 
    at E0.e0.doFrame(Unknown Source:6) 
    at E0.c0.doFrame(Unknown Source:47) 
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1687) 
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1698) 
    at android.view.Choreographer.doCallbacks(Choreographer.java:1153) 
    at android.view.Choreographer.doFrame(Choreographer.java:1069) 
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1646) 
    at android.os.Handler.handleCallback(Handler.java:958) 
    at android.os.Handler.dispatchMessage(Handler.java:99) 
    at android.os.Looper.loopOnce(Looper.java:230) 
    at android.os.Looper.loop(Looper.java:319) 
    at android.app.ActivityThread.main(ActivityThread.java:8919) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103) 

AradiPatrik avatar Jun 23 '25 04:06 AradiPatrik

Can you verify if this Player release timed out error still reproduces on the main branch?

The error you're seeing may be fixed by some of our recent changes

ychaparov avatar Jun 23 '25 10:06 ychaparov

@ychaparov This only shows up in production (≈2% ANRs) and is hard to reproduce locally, so I’ll test the next release and report back.

AradiPatrik avatar Jun 30 '25 05:06 AradiPatrik

After updating to 1.8.0-beta01 can no longer reproduce (or see it in crashlytics). Thanks 🙏

AradiPatrik avatar Jul 07 '25 02:07 AradiPatrik