Repository navigation
Description
Version
Media3 1.5.1
More version details
No response
Devices that reproduce the issue
Samsung Galaxy Note20 Ultra running Android 13
Samsung S23 Ultra running Android 14
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
Not tested
Reproduction steps
- Integrate the library to play video streams using an ExoPlayer instance (which works correctly on its own).
- Replace the direct ExoPlayer usage with a ForwardingSimpleBasePlayer without any overrides.
- Start playback of a online streaming media item.
- Call the stop() method on the player.
- Observe the state transitions logged by ExoPlayer via Player.Listener.onEvents(), which report the following sequence:
ExoPlayer State changed. playbackState=STATE_IDLE, isLoading=true
ExoPlayer State changed. playbackState=STATE_IDLE, isLoading=true
ExoPlayer State changed. playbackState=STATE_IDLE, isLoading=false
- During these transitions, ForwardingSimpleBasePlayer.getState() passes the playback state and the isLoading flag directly to a SimpleBasePlayer.StateBuilder. When StateBuilder.build() is called, the constructor from SimpleBasePlayer.State() runs a check:
if (builder.playbackState == Player.STATE_IDLE || builder.playbackState == Player.STATE_ENDED) {
checkArgument(!builder.isLoading, "isLoading only allowed when not in STATE_IDLE or STATE_ENDED");
}
- Because the builder receives isLoading=true while the playback state is STATE_IDLE, the check fails and an exception is thrown.
Expected result
The player should update its state without throwing an exception. In particular, when transitioning (e.g., after a stop() call on a stream), the state reported by ForwardingSimpleBasePlayer should correctly handle the isLoading flag—either by ensuring it is false when in STATE_IDLE/STATE_ENDED or by adjusting the check logic—so that no exception occurs.
Actual result
An exception is thrown with the message:
"isLoading only allowed when not in STATE_IDLE or STATE_ENDED"
This occurs because ForwardingSimpleBasePlayer.getState() transfers the playback state and isLoading flag directly to the StateBuilder, and when the underlying ExoPlayer reports STATE_IDLE with isLoading=true during a stop transition, the check in StateBuilder’s constructor fails. Consequently, the ForwardingSimpleBasePlayer becomes unusable in this scenario.
Stacktrace after Player.stop() call:
java.lang.IllegalArgumentException: isLoading only allowed when not in STATE_IDLE or STATE_ENDED
at androidx.media3.common.util.Assertions.checkArgument(Assertions.java:55)
at androidx.media3.common.SimpleBasePlayer$State.(SimpleBasePlayer.java:1027)
at androidx.media3.common.SimpleBasePlayer$State.(SimpleBasePlayer.java:101)
at androidx.media3.common.SimpleBasePlayer$State$Builder.build(SimpleBasePlayer.java:809)
at xxx.xxx.xxx.xxx.xxx.core.player.media3.video.ForwardingSimpleBasePlayer.getState(ForwardingSimpleBasePlayer.java:172)
at androidx.media3.common.SimpleBasePlayer.invalidateState(SimpleBasePlayer.java:3036)
at xxx.xxx.xxx.xxx.xxx.core.player.media3.video.ForwardingSimpleBasePlayer.access$000(ForwardingSimpleBasePlayer.java:27)
at xxx.xxx.xxx.xxx.xxx.core.player.media3.video.ForwardingSimpleBasePlayer$1.onEvents(ForwardingSimpleBasePlayer.java:89)
at androidx.media3.exoplayer.ExoPlayerImpl.lambda$new$0$androidx-media3-exoplayer-ExoPlayerImpl(ExoPlayerImpl.java:296)
at androidx.media3.exoplayer.ExoPlayerImpl$$ExternalSyntheticLambda15.invoke(D8$$SyntheticClass:0)
at androidx.media3.common.util.ListenerSet$ListenerHolder.iterationFinished(ListenerSet.java:353)
at androidx.media3.common.util.ListenerSet.handleMessage(ListenerSet.java:297)
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:102)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8762)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
Media
Not applicable
Bug Report
- You will email the zip file produced by
adb bugreportto android-media-github@google.com after filing this issue.
Activity
tianyif commentedon Feb 12, 2025
Hi @samaudi,
Thanks for reporting! When the
stop()is called,ExoPlayerwill immediately update the state toIDLEon the application thread and propagate it to theForwardingSimpleBasePlayer:media/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
Lines 1906 to 1919 in 76088cd
However, the
isLoadingis updated asynchronously on the playback thread, and it is possible that the update comes late.We will be providing a solution for this!
samaudi commentedon Feb 12, 2025
Hi @tianyif,
I have now implemented it in ForwardingSimpleBasePlayer.geState() as follows, and it seems to work so far:
However, I can't quite see what side effects this solution has.
Mask ExoPlayer.isLoading when it transitions to IDLE or ENDED states
Mask ExoPlayer.isLoading when it transitions to IDLE or ENDED states
tianyif commentedon Mar 21, 2025
Fix is available in the latest 1.6.0 prerelease, and will be 1.6.0 stable soon.