media icon indicating copy to clipboard operation
media copied to clipboard

ExoPlayer crashes with ForwardingSimpleBasePlayer

Open nift4 opened this issue 2 months ago • 2 comments

Version

Media3 main branch

More version details

No response

Devices that reproduce the issue

all of them

Devices that do not reproduce the issue

N/A

Reproducible in the demo app?

Yes

Reproduction steps

apply this patch:

diff --git a/demos/session/src/main/java/androidx/media3/demo/session/PlayerActivity.kt b/demos/session/src/main/java/androidx/media3/demo/session/PlayerActivity.kt
index 6556926fa4..dd7e73a5d9 100644
--- a/demos/session/src/main/java/androidx/media3/demo/session/PlayerActivity.kt
+++ b/demos/session/src/main/java/androidx/media3/demo/session/PlayerActivity.kt
@@ -194,7 +194,6 @@ class PlayerActivity : AppCompatActivity() {
         returnConvertView
           .findViewById<TextView>(R.id.media_item)
           .setTextColor(ContextCompat.getColor(context, R.color.white))
-        deleteButton.visibility = View.GONE
       } else {
         // Styles for any other media item list item.
         returnConvertView.setBackgroundColor(
@@ -203,11 +202,11 @@ class PlayerActivity : AppCompatActivity() {
         returnConvertView
           .findViewById<TextView>(R.id.media_item)
           .setTextColor(ContextCompat.getColor(context, R.color.white))
-        deleteButton.visibility = View.VISIBLE
-        deleteButton.setOnClickListener {
+      }
+      deleteButton.visibility = View.VISIBLE
+      deleteButton.setOnClickListener {
           controller.removeMediaItem(position)
           updateCurrentPlaylistUI()
-        }
       }
 
       return returnConvertView
diff --git a/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt b/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt
index c9ff44d381..fe0bd0d64a 100644
--- a/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt
+++ b/demos/session_service/src/main/java/androidx/media3/demo/session/DemoPlaybackService.kt
@@ -31,6 +31,7 @@ import androidx.datastore.core.Serializer
 import androidx.datastore.dataStore
 import androidx.media3.cast.CastPlayer
 import androidx.media3.common.AudioAttributes
+import androidx.media3.common.ForwardingSimpleBasePlayer
 import androidx.media3.common.Player
 import androidx.media3.common.listenTo
 import androidx.media3.common.util.UnstableApi
@@ -153,10 +154,10 @@ open class DemoPlaybackService : MediaLibraryService() {
   @OptIn(UnstableApi::class)
   protected open fun buildPlayer(): Player {
     val exoPlayer =
-      ExoPlayer.Builder(this)
+        ForwardingSimpleBasePlayer(ExoPlayer.Builder(this)
         .setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true)
-        .build()
-    exoPlayer.addAnalyticsListener(EventLogger())
+        .build())
+    //exoPlayer.addAnalyticsListener(EventLogger())
     return CastPlayer.Builder(/* context= */ this).setLocalPlayer(exoPlayer).build()
   }

then build session-demo, play something, enable repeat-one and remove the currently playing song from playlist

Expected result

it works

Actual result

2025-10-31 12:24:26.977 24725-24725 AndroidRuntime          androidx.media3.demo.session    E  FATAL EXCEPTION: main
                                                                                               Process: androidx.media3.demo.session, PID: 24725
                                                                                               java.lang.IllegalArgumentException: isLoading only allowed when not in STATE_IDLE or STATE_ENDED
                                                                                               	at com.google.common.base.Preconditions.checkArgument(Preconditions.java:143)
                                                                                               	at androidx.media3.common.SimpleBasePlayer$State.<init>(SimpleBasePlayer.java:1071)
                                                                                               	at androidx.media3.common.SimpleBasePlayer$State.<init>(SimpleBasePlayer.java:99)
                                                                                               	at androidx.media3.common.SimpleBasePlayer$State$Builder.build(SimpleBasePlayer.java:842)
                                                                                               	at androidx.media3.common.ForwardingSimpleBasePlayer.getState(ForwardingSimpleBasePlayer.java:187)
                                                                                               	at androidx.media3.common.SimpleBasePlayer.invalidateState(SimpleBasePlayer.java:3176)
                                                                                               	at androidx.media3.common.ForwardingSimpleBasePlayer$PlayerListener.onEvents(ForwardingSimpleBasePlayer.java:531)
                                                                                               	at androidx.media3.exoplayer.ExoPlayerImpl.lambda$new$0$androidx-media3-exoplayer-ExoPlayerImpl(ExoPlayerImpl.java:308)
                                                                                               	at androidx.media3.exoplayer.ExoPlayerImpl$$ExternalSyntheticLambda18.invoke(D8$$SyntheticClass:0)
                                                                                               	at androidx.media3.common.util.ListenerSet$ListenerHolder.iterationFinished(ListenerSet.java:430)
                                                                                               	at androidx.media3.common.util.ListenerSet.handleMessage(ListenerSet.java:374)
                                                                                               	at androidx.media3.common.util.ListenerSet.$r8$lambda$rFcF5Pkb99AL585p5-2u78YfNkY(Unknown Source:0)
                                                                                               	at androidx.media3.common.util.ListenerSet$$ExternalSyntheticLambda0.handleMessage(D8$$SyntheticClass:0)
                                                                                               	at android.os.Handler.dispatchMessage(Handler.java:105)
                                                                                               	at android.os.Looper.loopOnce(Looper.java:232)
                                                                                               	at android.os.Looper.loop(Looper.java:317)
                                                                                               	at android.app.ActivityThread.main(ActivityThread.java:8934)
                                                                                               	at java.lang.reflect.Method.invoke(Native Method)
                                                                                               	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:591)
                                                                                               	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:911)

Media

doesn't matter

Bug Report

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

nift4 avatar Oct 31 '25 11:10 nift4

This crash occurs without your second patch, because CastPlayer uses ForwardingSimpleBasePlayer internally:

FATAL EXCEPTION: main (Explain with AI)
Process: androidx.media3.demo.session, PID: 19667
java.lang.IllegalArgumentException: isLoading only allowed when not in STATE_IDLE or STATE_ENDED
	at com.google.common.base.Preconditions.checkArgument(Preconditions.java:143)
	at androidx.media3.common.SimpleBasePlayer$State.<init>(SimpleBasePlayer.java:1071)
	at androidx.media3.common.SimpleBasePlayer$State.<init>(SimpleBasePlayer.java:99)
	at androidx.media3.common.SimpleBasePlayer$State$Builder.build(SimpleBasePlayer.java:842)
	at androidx.media3.common.ForwardingSimpleBasePlayer.getState(ForwardingSimpleBasePlayer.java:195)
	at androidx.media3.cast.CastPlayerImpl.getState(CastPlayerImpl.java:70)
	at androidx.media3.common.SimpleBasePlayer.invalidateState(SimpleBasePlayer.java:3176)
	at androidx.media3.common.ForwardingSimpleBasePlayer$PlayerListener.onEvents(ForwardingSimpleBasePlayer.java:539)
	at androidx.media3.exoplayer.ExoPlayerImpl.lambda$new$0$androidx-media3-exoplayer-ExoPlayerImpl(ExoPlayerImpl.java:312)
	at androidx.media3.exoplayer.ExoPlayerImpl$$ExternalSyntheticLambda18.invoke(D8$$SyntheticClass:0)
	at androidx.media3.common.util.ListenerSet$ListenerHolder.iterationFinished(ListenerSet.java:469)
	at androidx.media3.common.util.ListenerSet.handleMessage(ListenerSet.java:413)
	at androidx.media3.common.util.ListenerSet.$r8$lambda$rFcF5Pkb99AL585p5-2u78YfNkY(Unknown Source:0)
	at androidx.media3.common.util.ListenerSet$$ExternalSyntheticLambda0.handleMessage(D8$$SyntheticClass:0)
	at android.os.Handler.dispatchMessage(Handler.java:103)
	at android.os.Looper.loopOnce(Looper.java:232)
	at android.os.Looper.loop(Looper.java:317)
	at android.app.ActivityThread.main(ActivityThread.java:8592)
    at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)

Removing CastPlayer from buildPlayer and returning exoPlayer directly results in no crash. Instead ExoPlayer enters state ENDED and the playlist UI updates to show the previous item as the current item, with position at 0, not playing.

It does indeed seem to be loading in state ENDED:

state [eventTime=10.42, mediaPos=0.00, window=0, ENDED]
isPlaying [eventTime=10.43, mediaPos=0.00, window=0, false]
loading [eventTime=10.43, mediaPos=0.00, window=0, period=0, true]

icbaker avatar Nov 05 '25 09:11 icbaker

Please correct me if I'm wrong, but isn't this ExoPlayer entering a state deemed illegal by SimpleBasePlayer. I.e. ended_state + isLoading == true? If so, am I the right assignee for this?

EDIT: For convenience, you can use internal ref cl/842255962 to repro this.

AquilesCanta avatar Dec 09 '25 16:12 AquilesCanta

Thanks, I got confused by the analysis above suggesting it's CastPlayer related. It is indeed a bug in ExoPlayer where it start loading a new item despite being in the ENDED state. We'll provide a fix for that

tonihei avatar Dec 17 '25 18:12 tonihei