-
Notifications
You must be signed in to change notification settings - Fork 769
ArrayIndexOutOfBoundsException in PlaylistTimeline.getFirstWindowIndexByChildIndex during player release after pre-roll ad completes #3142
Description
When using MediaItem with AdsConfiguration and ImaAdsLoader registered via
DefaultMediaSourceFactory.setLocalAdInsertionComponents() inside a MediaLibraryService,
releasing the player after a pre-roll ad finishes and content begins playing causes a crash:
java.lang.ArrayIndexOutOfBoundsException: length=1; index=-1
at androidx.media3.exoplayer.PlaylistTimeline.getFirstWindowIndexByChildIndex(PlaylistTimeline.java:104)
at androidx.media3.exoplayer.AbstractConcatenatedTimeline.getPeriod(AbstractConcatenatedTimeline.java:230)
at androidx.media3.common.Timeline.getPeriod(Timeline.java:1332)
## Reproduction
The crash is state-dependent:
| Scenario | Result |
|---|---|
| No ad configured | ✅ No crash |
Ad currently playing when release() is called |
✅ No crash |
Ad failed / timed out, then release() called |
❌ Crash |
Ad finished, content playing, then release() called |
❌ Crash |
// ImaAdsLoader is created lazily and setPlayer() called inside the lambda
var imaAdsLoader: ImaAdsLoader? = null
fun getClientSideAdsLoader(): AdsLoader? {
if (imaAdsLoader == null) {
imaAdsLoader = ImaAdsLoader.Builder(context).build()
}
imaAdsLoader?.setPlayer(player)
return imaAdsLoader
}
// Factory wires ImaAdsLoader via setLocalAdInsertionComponents
val mediaSourceFactory = DefaultMediaSourceFactory(context)
.setLocalAdInsertionComponents(
{ _: MediaItem.AdsConfiguration? -> getClientSideAdsLoader() },
playerView
)
// ExoPlayer built with the factory
val player = ExoPlayer.Builder(context)
.setMediaSourceFactory(mediaSourceFactory)
.build()
// MediaItem carries the ad tag via AdsConfiguration
val mediaItem = MediaItem.Builder()
.setUri(contentUri)
.setAdsConfiguration(
MediaItem.AdsConfiguration.Builder(Uri.parse(adTagUrl)).build()
)
.build()
player.setMediaItem(mediaItem)
player.prepare()
player.play()
// After pre-roll finishes and content is playing, press back:
imaAdsLoader?.release()
player.release() // ← crash here Root Cause
During ExoPlayer.release(), the player walks the timeline to finalize state.
AbstractConcatenatedTimeline.getPeriod(periodIndex) calls
getChildIndexByPeriodIndex(periodIndex), which returns -1 when the period index
is unresolved — left over from the ads → content transition in the MediaPeriodQueue.
That -1 is then passed directly into PlaylistTimeline.getFirstWindowIndexByChildIndex(-1),
producing the ArrayIndexOutOfBoundsException.
This happens because after a pre-roll completes and ExoPlayer transitions to the content
period, the internal currentPeriodIndex can be left in an unresolved state. When
release() is called at that moment the timeline walk hits this invalid index.
Relation to #3125
This crash is not fixed by the patch in #3125 (included in 1.10.0-rc03). That commit
added bounds checks in AdsMediaSource.java and ExoPlayerImplInternal.java for
durationsUs and states arrays. The crash here originates in PlaylistTimeline.java
and AbstractConcatenatedTimeline.java, neither of which were modified by #3125.
Conclusion
We have been tracking this crash across multiple Media3 versions and it remains
reproducible on 1.10.0-rc03. We initially assumed #3125 would resolve it since the
stack trace looks similar, but after analyzing the commit diff it is clear the affected
classes are different.
Happy to provide a test additional logs if that would help move this forward.