Fix ArrayIndexOutOfBoundIndex when re-preparing after exception
When an app tried to re-prepare a live streeam with server side inserted ad after a playback exception, the player tried to find the ad group by its index in the ad playback state of the next timeline when creating the first period. If a source that supports server side ad, has removed the ad playback state when the source has been removed, this causes a crash. For live streams this is a reasonable thing to do given the exception could be caused by an invalid ad playback state. This change removes the ad metadata from the current period for live streams and the timeline. In case the ad playback state is not reset by the source, the first timeline refresh would ad the metadata again. PiperOrigin-RevId: 541959628
This commit is contained in:
parent
63ca2595be
commit
4604f0cde6
@ -1494,11 +1494,21 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
Timeline timeline = playbackInfo.timeline;
|
||||
if (releaseMediaSourceList && timeline instanceof PlaylistTimeline) {
|
||||
// Wrap the current live timeline to make sure the current period is marked as a placeholder
|
||||
// to force resolving the default start position with the next timeline refresh.
|
||||
// Wrap the current timeline to make sure the current period is marked as a placeholder to
|
||||
// force resolving the default start position with the next timeline refresh.
|
||||
timeline =
|
||||
((PlaylistTimeline) playbackInfo.timeline)
|
||||
.copyWithPlaceholderTimeline(mediaSourceList.getShuffleOrder());
|
||||
if (mediaPeriodId.adGroupIndex != C.INDEX_UNSET) {
|
||||
timeline.getPeriodByUid(mediaPeriodId.periodUid, period);
|
||||
if (timeline.getWindow(period.windowIndex, window).isLive()) {
|
||||
// Drop ad metadata to allow live streams to reset the ad playback state. In case the ad
|
||||
// playback state is not reset by the source, the first timeline refresh after
|
||||
// re-preparation will add the ad metadata to the period again.
|
||||
mediaPeriodId =
|
||||
new MediaPeriodId(mediaPeriodId.periodUid, mediaPeriodId.windowSequenceNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
playbackInfo =
|
||||
new PlaybackInfo(
|
||||
|
@ -15,7 +15,9 @@
|
||||
*/
|
||||
package androidx.media3.exoplayer;
|
||||
|
||||
import androidx.media3.common.AdPlaybackState;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.Timeline;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.exoplayer.source.ForwardingTimeline;
|
||||
@ -117,15 +119,43 @@ import java.util.List;
|
||||
return periodCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the timeline and wraps each child timeline with a {@link ForwardingTimeline}
|
||||
* that overrides {@link Timeline#getPeriod(int, Period, boolean)} to set the {@link
|
||||
* Period#isPlaceholder} flag.
|
||||
*
|
||||
* <p>For periods of a live window, the {@link AdPlaybackState} is set to {@link
|
||||
* AdPlaybackState#NONE} to allow a live source with ad support to drop the ad playback state.
|
||||
*
|
||||
* <p>This method should be used when the player is reset (for instance when a playback error
|
||||
* occurs or {@link Player#stop()} is called) to make the player resolve the start position like
|
||||
* when prepared initially. In this state, each source needs to be prepared again at which point
|
||||
* the first timeline delivered by the source will replace the wrapped source to continue
|
||||
* playback.
|
||||
*/
|
||||
public PlaylistTimeline copyWithPlaceholderTimeline(ShuffleOrder shuffleOrder) {
|
||||
Timeline[] newTimelines = new Timeline[timelines.length];
|
||||
for (int i = 0; i < timelines.length; i++) {
|
||||
newTimelines[i] =
|
||||
new ForwardingTimeline(timelines[i]) {
|
||||
private final Window window = new Window();
|
||||
|
||||
@Override
|
||||
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
|
||||
Period superPeriod = super.getPeriod(periodIndex, period, setIds);
|
||||
superPeriod.isPlaceholder = true;
|
||||
if (super.getWindow(superPeriod.windowIndex, window).isLive()) {
|
||||
// Reset the ad playback state for placeholder period of a live streams.
|
||||
superPeriod.set(
|
||||
period.id,
|
||||
period.uid,
|
||||
period.windowIndex,
|
||||
period.durationUs,
|
||||
period.positionInWindowUs,
|
||||
AdPlaybackState.NONE,
|
||||
/* isPlaceholder= */ true);
|
||||
} else {
|
||||
superPeriod.isPlaceholder = true;
|
||||
}
|
||||
return superPeriod;
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user