ExoPlayer crashes with ForwardingSimpleBasePlayer
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 bugreportto [email protected] after filing this issue.
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]
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.
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