Skip to content

Crash when player.removeMediaItems is called during postroll (advert at C.TIME_END_OF_SOURCE) #2746

@amorenkov

Description

@amorenkov

Version

Media3 1.7.1 (same as 1.6.1)

More version details

No response

Devices that reproduce the issue

Pixel 6 running Android 15
Samsung S23 Ultra running Android 14
Pixel 9 emulator running Android 15
Techno Spark 20 Pro running Android 13

Devices that do not reproduce the issue

No

Reproducible in the demo app?

Not tested

Reproduction steps

We have a video-based application with custom ads logic (not IMA). Steps to reproduce describe the simplified logic from our application

Steps to reproduce:

  1. Start any video playing by ExoPlayer class:
val videoA = MediaItem.fromUri(<any mp4 video url>)
player.addMediaItem(videoA)
player.prepare()
player.play()
  1. Add some media items to player next to current video (playlist):
val videoB = MediaItem.fromUri(<any mp4 video url>)
val videoC = MediaItem.fromUri(<any mp4 video url>)
player.addMediaItem(videoB)
player.addMediaItem(videoC)
  1. Apply postroll url to AdPlaybackState and send it to player for current video (in our app we implemented AdsLoader with correct connecting it to mediaSourceFactory etc, but here is only snippet of simplified core logic):
val postrollMediaItem = MediaItem.Builder().setUri(<any mp4 video url>).build()
val adPlaybackState = AdPlaybackState(adsId, C.TIME_END_OF_SOURCE)
.withAdCount(0, 1)
.withAvailableAdMediaItem(0,0, postrollMediaItem)

AdsLoader.EventListener.onAdPlaybackState(adPlaybackState )
  1. Wait until almost the very end of the video (for example if video duration is 60.000ms, the position we are interested is ~59.000-59.900ms)

  2. At this time moment, clear playlist, apply new video to playlist and switch to next media item (at this moment player.isPlayingAd == false):

// Clears videoB and videoC from player's playlist
player.removeMediaItems(player.currentMediaItemIndex + 1, player.mediaItemCount)
// Setup new playlist as VideoX
val videoX = MediaItem.fromUri(<any mp4 video url>)
player.addMediaItem(videoX)
// Switch to next media item (i.e VideoX)
player.seekToNextMediaItem()

Expected result

Player successfully seek to new next media item (i.e VideoX)

Actual result

Not always, but quite often we get a crash:

java.lang.IllegalStateException
                 	at androidx.media3.common.util.Assertions.checkState(Assertions.java:85)
                 	at androidx.media3.exoplayer.ExoPlayerImpl.maskTimelineAndPosition(ExoPlayerImpl.java:2480)
                 	at androidx.media3.exoplayer.ExoPlayerImpl.removeMediaItemsInternal(ExoPlayerImpl.java:2410)
                 	at androidx.media3.exoplayer.ExoPlayerImpl.removeMediaItems(ExoPlayerImpl.java:674)
                 	at <Our app player wrapper class>.removeMediaItems(just call ExoPlayer's method without specific logic)
                 	at androidx.media3.common.ForwardingPlayer.removeMediaItems(ForwardingPlayer.java:189)
                 	at androidx.media3.session.PlayerWrapper.removeMediaItems(PlayerWrapper.java:594)
                 	at androidx.media3.session.MediaSessionStub.lambda$removeMediaItems$43$androidx-media3-session-MediaSessionStub(MediaSessionStub.java:1298)
                 	at androidx.media3.session.MediaSessionStub$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
                 	at androidx.media3.session.MediaSessionStub.lambda$sendSessionResultSuccess$1(MediaSessionStub.java:174)
                 	at androidx.media3.session.MediaSessionStub$$ExternalSyntheticLambda84.run(D8$$SyntheticClass:0)
                 	at androidx.media3.session.MediaSessionStub.lambda$queueSessionTaskWithPlayerCommandForControllerInfo$13(MediaSessionStub.java:344)
                 	at androidx.media3.session.MediaSessionStub$$ExternalSyntheticLambda40.run(D8$$SyntheticClass:0)
                 	at androidx.media3.session.ConnectedControllersManager.lambda$flushCommandQueue$3$androidx-media3-session-ConnectedControllersManager(ConnectedControllersManager.java:295)
                 	at androidx.media3.session.ConnectedControllersManager$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
                 	at androidx.media3.session.MediaSessionImpl.lambda$callWithControllerForCurrentRequestSet$3$androidx-media3-session-MediaSessionImpl(MediaSessionImpl.java:355)
                 	at androidx.media3.session.MediaSessionImpl$$ExternalSyntheticLambda21.run(D8$$SyntheticClass:0)
                 	at androidx.media3.common.util.Util.postOrRun(Util.java:800)
                 	at androidx.media3.session.ConnectedControllersManager.flushCommandQueue(ConnectedControllersManager.java:289)
                 	at androidx.media3.session.ConnectedControllersManager.flushCommandQueue(ConnectedControllersManager.java:270)
                 	at androidx.media3.session.MediaSessionStub.lambda$flushCommandQueue$64$androidx-media3-session-MediaSessionStub(MediaSessionStub.java:1692)
                 	at androidx.media3.session.MediaSessionStub$$ExternalSyntheticLambda37.run(D8$$SyntheticClass:0)
                 	at androidx.media3.common.util.Util.postOrRun(Util.java:800)
                 	at androidx.media3.session.MediaSessionStub.flushCommandQueue(MediaSessionStub.java:1690)
                 	at androidx.media3.session.MediaControllerImplBase$FlushCommandQueueHandler.flushCommandQueue(MediaControllerImplBase.java:3646)
                 	at androidx.media3.session.MediaControllerImplBase$FlushCommandQueueHandler.handleMessage(MediaControllerImplBase.java:3639)
                 	at androidx.media3.session.MediaControllerImplBase$FlushCommandQueueHandler.$r8$lambda$TVKiXTCmsW2hn-6HNXqbaigkfJc(Unknown Source:0)
                 	at androidx.media3.session.MediaControllerImplBase$FlushCommandQueueHandler$$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:8705)
                 	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:886)

androidx.media3.common.util.Assertions.checkState(Assertions.java:85) fails with check:
checkState(!newPeriodId.isAd());

Additional notes

this crash only happens for postroll (C.TIME_END_OF_SOURCE) advert. Preroll and midroll adverts are works fine without crashes

Media

Any mp4 video url, doesn't depend on specific video

Bug Report

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions