Replace period index with uid in MediaPeriodId.

The MediaPeriodId with index is only properly defined together with a
timeline containing the index. Changing it to the period uid allows to use
the MediaPeriodId independent of the corresponding timeline.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=209430257
This commit is contained in:
tonihei 2018-08-20 09:24:58 -07:00 committed by Oliver Woodman
parent 2cd7d7102b
commit d51b98dd1f
29 changed files with 649 additions and 580 deletions

View File

@ -327,10 +327,10 @@ import java.util.concurrent.CopyOnWriteArraySet;
} else { } else {
long windowPositionUs = positionMs == C.TIME_UNSET long windowPositionUs = positionMs == C.TIME_UNSET
? timeline.getWindow(windowIndex, window).getDefaultPositionUs() : C.msToUs(positionMs); ? timeline.getWindow(windowIndex, window).getDefaultPositionUs() : C.msToUs(positionMs);
Pair<Integer, Long> periodIndexAndPosition = Pair<Object, Long> periodUidAndPosition =
timeline.getPeriodPosition(window, period, windowIndex, windowPositionUs); timeline.getPeriodPosition(window, period, windowIndex, windowPositionUs);
maskingWindowPositionMs = C.usToMs(windowPositionUs); maskingWindowPositionMs = C.usToMs(windowPositionUs);
maskingPeriodIndex = periodIndexAndPosition.first; maskingPeriodIndex = timeline.getIndexOfPeriod(periodUidAndPosition.first);
} }
internalPlayer.seekTo(timeline, windowIndex, C.msToUs(positionMs)); internalPlayer.seekTo(timeline, windowIndex, C.msToUs(positionMs));
for (Player.EventListener listener : listeners) { for (Player.EventListener listener : listeners) {
@ -464,7 +464,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
if (shouldMaskPosition()) { if (shouldMaskPosition()) {
return maskingPeriodIndex; return maskingPeriodIndex;
} else { } else {
return playbackInfo.periodId.periodIndex; return playbackInfo.timeline.getIndexOfPeriod(playbackInfo.periodId.periodUid);
} }
} }
@ -473,7 +473,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
if (shouldMaskPosition()) { if (shouldMaskPosition()) {
return maskingWindowIndex; return maskingWindowIndex;
} else { } else {
return playbackInfo.timeline.getPeriod(playbackInfo.periodId.periodIndex, period).windowIndex; return playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period)
.windowIndex;
} }
} }
@ -499,7 +500,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
} }
if (isPlayingAd()) { if (isPlayingAd()) {
MediaPeriodId periodId = playbackInfo.periodId; MediaPeriodId periodId = playbackInfo.periodId;
timeline.getPeriod(periodId.periodIndex, period); timeline.getPeriodByUid(periodId.periodUid, period);
long adDurationUs = period.getAdDurationUs(periodId.adGroupIndex, periodId.adIndexInAdGroup); long adDurationUs = period.getAdDurationUs(periodId.adGroupIndex, periodId.adIndexInAdGroup);
return C.usToMs(adDurationUs); return C.usToMs(adDurationUs);
} else { } else {
@ -572,7 +573,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Override @Override
public long getContentPosition() { public long getContentPosition() {
if (isPlayingAd()) { if (isPlayingAd()) {
playbackInfo.timeline.getPeriod(playbackInfo.periodId.periodIndex, period); playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period);
return period.getPositionInWindowMs() + C.usToMs(playbackInfo.contentPositionUs); return period.getPositionInWindowMs() + C.usToMs(playbackInfo.contentPositionUs);
} else { } else {
return getCurrentPosition(); return getCurrentPosition();
@ -591,7 +592,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
long contentBufferedPositionUs = playbackInfo.bufferedPositionUs; long contentBufferedPositionUs = playbackInfo.bufferedPositionUs;
if (playbackInfo.loadingMediaPeriodId.isAd()) { if (playbackInfo.loadingMediaPeriodId.isAd()) {
Timeline.Period loadingPeriod = Timeline.Period loadingPeriod =
playbackInfo.timeline.getPeriod(playbackInfo.loadingMediaPeriodId.periodIndex, period); playbackInfo.timeline.getPeriodByUid(playbackInfo.loadingMediaPeriodId.periodUid, period);
contentBufferedPositionUs = contentBufferedPositionUs =
loadingPeriod.getAdGroupTimeUs(playbackInfo.loadingMediaPeriodId.adGroupIndex); loadingPeriod.getAdGroupTimeUs(playbackInfo.loadingMediaPeriodId.adGroupIndex);
if (contentBufferedPositionUs == C.TIME_END_OF_SOURCE) { if (contentBufferedPositionUs == C.TIME_END_OF_SOURCE) {
@ -761,7 +762,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
private long periodPositionUsToWindowPositionMs(MediaPeriodId periodId, long positionUs) { private long periodPositionUsToWindowPositionMs(MediaPeriodId periodId, long positionUs) {
long positionMs = C.usToMs(positionUs); long positionMs = C.usToMs(positionUs);
playbackInfo.timeline.getPeriod(periodId.periodIndex, period); playbackInfo.timeline.getPeriodByUid(periodId.periodUid, period);
positionMs += period.getPositionInWindowMs(); positionMs += period.getPositionInWindowMs();
return positionMs; return positionMs;
} }

View File

@ -594,7 +594,7 @@ import java.util.Collections;
long periodPositionUs; long periodPositionUs;
long contentPositionUs; long contentPositionUs;
boolean seekPositionAdjusted; boolean seekPositionAdjusted;
Pair<Integer, Long> resolvedSeekPosition = Pair<Object, Long> resolvedSeekPosition =
resolveSeekPosition(seekPosition, /* trySubsequentPeriods= */ true); resolveSeekPosition(seekPosition, /* trySubsequentPeriods= */ true);
if (resolvedSeekPosition == null) { if (resolvedSeekPosition == null) {
// The seek position was valid for the timeline that it was performed into, but the // The seek position was valid for the timeline that it was performed into, but the
@ -605,9 +605,9 @@ import java.util.Collections;
seekPositionAdjusted = true; seekPositionAdjusted = true;
} else { } else {
// Update the resolved seek position to take ads into account. // Update the resolved seek position to take ads into account.
int periodIndex = resolvedSeekPosition.first; Object periodUid = resolvedSeekPosition.first;
contentPositionUs = resolvedSeekPosition.second; contentPositionUs = resolvedSeekPosition.second;
periodId = queue.resolveMediaPeriodIdForAds(periodIndex, contentPositionUs); periodId = queue.resolveMediaPeriodIdForAds(periodUid, contentPositionUs);
if (periodId.isAd()) { if (periodId.isAd()) {
periodPositionUs = 0; periodPositionUs = 0;
seekPositionAdjusted = true; seekPositionAdjusted = true;
@ -760,7 +760,7 @@ import java.util.Collections;
int firstPeriodIndex = int firstPeriodIndex =
timeline.getWindow(timeline.getFirstWindowIndex(shuffleModeEnabled), window) timeline.getWindow(timeline.getFirstWindowIndex(shuffleModeEnabled), window)
.firstPeriodIndex; .firstPeriodIndex;
return new MediaPeriodId(firstPeriodIndex); return new MediaPeriodId(timeline.getUidOfPeriod(firstPeriodIndex));
} }
private void resetInternal( private void resetInternal(
@ -889,7 +889,7 @@ import java.util.Collections;
private boolean resolvePendingMessagePosition(PendingMessageInfo pendingMessageInfo) { private boolean resolvePendingMessagePosition(PendingMessageInfo pendingMessageInfo) {
if (pendingMessageInfo.resolvedPeriodUid == null) { if (pendingMessageInfo.resolvedPeriodUid == null) {
// Position is still unresolved. Try to find window in current timeline. // Position is still unresolved. Try to find window in current timeline.
Pair<Integer, Long> periodPosition = Pair<Object, Long> periodPosition =
resolveSeekPosition( resolveSeekPosition(
new SeekPosition( new SeekPosition(
pendingMessageInfo.message.getTimeline(), pendingMessageInfo.message.getTimeline(),
@ -900,9 +900,9 @@ import java.util.Collections;
return false; return false;
} }
pendingMessageInfo.setResolvedPosition( pendingMessageInfo.setResolvedPosition(
periodPosition.first, playbackInfo.timeline.getIndexOfPeriod(periodPosition.first),
periodPosition.second, periodPosition.second,
playbackInfo.timeline.getUidOfPeriod(periodPosition.first)); periodPosition.first);
} else { } else {
// Position has been resolved for a previous timeline. Try to find the updated period index. // Position has been resolved for a previous timeline. Try to find the updated period index.
int index = playbackInfo.timeline.getIndexOfPeriod(pendingMessageInfo.resolvedPeriodUid); int index = playbackInfo.timeline.getIndexOfPeriod(pendingMessageInfo.resolvedPeriodUid);
@ -925,7 +925,8 @@ import java.util.Collections;
oldPeriodPositionUs--; oldPeriodPositionUs--;
} }
// Correct next index if necessary (e.g. after seeking, timeline changes, or new messages) // Correct next index if necessary (e.g. after seeking, timeline changes, or new messages)
int currentPeriodIndex = playbackInfo.periodId.periodIndex; int currentPeriodIndex =
playbackInfo.timeline.getIndexOfPeriod(playbackInfo.periodId.periodUid);
PendingMessageInfo previousInfo = PendingMessageInfo previousInfo =
nextPendingMessageIndex > 0 ? pendingMessages.get(nextPendingMessageIndex - 1) : null; nextPendingMessageIndex > 0 ? pendingMessages.get(nextPendingMessageIndex - 1) : null;
while (previousInfo != null while (previousInfo != null
@ -1153,7 +1154,7 @@ import java.util.Collections;
playbackInfoUpdate.incrementPendingOperationAcks(pendingPrepareCount); playbackInfoUpdate.incrementPendingOperationAcks(pendingPrepareCount);
pendingPrepareCount = 0; pendingPrepareCount = 0;
if (pendingInitialSeekPosition != null) { if (pendingInitialSeekPosition != null) {
Pair<Integer, Long> periodPosition; Pair<Object, Long> periodPosition;
try { try {
periodPosition = periodPosition =
resolveSeekPosition(pendingInitialSeekPosition, /* trySubsequentPeriods= */ true); resolveSeekPosition(pendingInitialSeekPosition, /* trySubsequentPeriods= */ true);
@ -1168,9 +1169,9 @@ import java.util.Collections;
// timeline has changed and a suitable seek position could not be resolved in the new one. // timeline has changed and a suitable seek position could not be resolved in the new one.
handleSourceInfoRefreshEndedPlayback(); handleSourceInfoRefreshEndedPlayback();
} else { } else {
int periodIndex = periodPosition.first; Object periodUid = periodPosition.first;
long positionUs = periodPosition.second; long positionUs = periodPosition.second;
MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(periodIndex, positionUs); MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(periodUid, positionUs);
playbackInfo = playbackInfo =
playbackInfo.fromNewPosition( playbackInfo.fromNewPosition(
periodId, periodId.isAd() ? 0 : positionUs, /* contentPositionUs= */ positionUs); periodId, periodId.isAd() ? 0 : positionUs, /* contentPositionUs= */ positionUs);
@ -1179,11 +1180,12 @@ import java.util.Collections;
if (timeline.isEmpty()) { if (timeline.isEmpty()) {
handleSourceInfoRefreshEndedPlayback(); handleSourceInfoRefreshEndedPlayback();
} else { } else {
Pair<Integer, Long> defaultPosition = getPeriodPosition(timeline, Pair<Object, Long> defaultPosition =
timeline.getFirstWindowIndex(shuffleModeEnabled), C.TIME_UNSET); getPeriodPosition(
int periodIndex = defaultPosition.first; timeline, timeline.getFirstWindowIndex(shuffleModeEnabled), C.TIME_UNSET);
Object periodUid = defaultPosition.first;
long startPositionUs = defaultPosition.second; long startPositionUs = defaultPosition.second;
MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(periodIndex, startPositionUs); MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(periodUid, startPositionUs);
playbackInfo = playbackInfo =
playbackInfo.fromNewPosition( playbackInfo.fromNewPosition(
periodId, periodId,
@ -1197,12 +1199,12 @@ import java.util.Collections;
if (oldTimeline.isEmpty()) { if (oldTimeline.isEmpty()) {
// If the old timeline is empty, the period queue is also empty. // If the old timeline is empty, the period queue is also empty.
if (!timeline.isEmpty()) { if (!timeline.isEmpty()) {
Pair<Integer, Long> defaultPosition = Pair<Object, Long> defaultPosition =
getPeriodPosition( getPeriodPosition(
timeline, timeline.getFirstWindowIndex(shuffleModeEnabled), C.TIME_UNSET); timeline, timeline.getFirstWindowIndex(shuffleModeEnabled), C.TIME_UNSET);
int periodIndex = defaultPosition.first; Object periodUid = defaultPosition.first;
long startPositionUs = defaultPosition.second; long startPositionUs = defaultPosition.second;
MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(periodIndex, startPositionUs); MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(periodUid, startPositionUs);
playbackInfo = playbackInfo =
playbackInfo.fromNewPosition( playbackInfo.fromNewPosition(
periodId, periodId,
@ -1212,37 +1214,32 @@ import java.util.Collections;
return; return;
} }
MediaPeriodHolder periodHolder = queue.getFrontPeriod(); MediaPeriodHolder periodHolder = queue.getFrontPeriod();
int playingPeriodIndex = playbackInfo.periodId.periodIndex;
long contentPositionUs = playbackInfo.contentPositionUs; long contentPositionUs = playbackInfo.contentPositionUs;
Object playingPeriodUid = Object playingPeriodUid =
periodHolder == null ? oldTimeline.getUidOfPeriod(playingPeriodIndex) : periodHolder.uid; periodHolder == null ? playbackInfo.periodId.periodUid : periodHolder.uid;
int periodIndex = timeline.getIndexOfPeriod(playingPeriodUid); int periodIndex = timeline.getIndexOfPeriod(playingPeriodUid);
if (periodIndex == C.INDEX_UNSET) { if (periodIndex == C.INDEX_UNSET) {
// We didn't find the current period in the new timeline. Attempt to resolve a subsequent // We didn't find the current period in the new timeline. Attempt to resolve a subsequent
// period whose window we can restart from. // period whose window we can restart from.
int newPeriodIndex = resolveSubsequentPeriod(playingPeriodIndex, oldTimeline, timeline); Object newPeriodUid = resolveSubsequentPeriod(playingPeriodUid, oldTimeline, timeline);
if (newPeriodIndex == C.INDEX_UNSET) { if (newPeriodUid == null) {
// We failed to resolve a suitable restart position. // We failed to resolve a suitable restart position.
handleSourceInfoRefreshEndedPlayback(); handleSourceInfoRefreshEndedPlayback();
return; return;
} }
// We resolved a subsequent period. Seek to the default position in the corresponding window. // We resolved a subsequent period. Seek to the default position in the corresponding window.
Pair<Integer, Long> defaultPosition = getPeriodPosition(timeline, Pair<Object, Long> defaultPosition =
timeline.getPeriod(newPeriodIndex, period).windowIndex, C.TIME_UNSET); getPeriodPosition(
newPeriodIndex = defaultPosition.first; timeline, timeline.getPeriodByUid(newPeriodUid, period).windowIndex, C.TIME_UNSET);
newPeriodUid = defaultPosition.first;
contentPositionUs = defaultPosition.second; contentPositionUs = defaultPosition.second;
MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(newPeriodIndex, contentPositionUs); MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(newPeriodUid, contentPositionUs);
if (periodHolder != null) { if (periodHolder != null) {
// Clear the index of each holder that doesn't contain the default position. If a holder // Update the new playing media period info if it already exists.
// contains the default position then update its index so it can be re-used when seeking.
Object newPeriodUid = timeline.getUidOfPeriod(newPeriodIndex);
periodHolder.info = periodHolder.info.copyWithPeriodIndex(C.INDEX_UNSET);
while (periodHolder.next != null) { while (periodHolder.next != null) {
periodHolder = periodHolder.next; periodHolder = periodHolder.next;
if (periodHolder.uid.equals(newPeriodUid)) { if (periodHolder.info.id.equals(periodId)) {
periodHolder.info = queue.getUpdatedMediaPeriodInfo(periodHolder.info, newPeriodIndex); periodHolder.info = queue.getUpdatedMediaPeriodInfo(periodHolder.info);
} else {
periodHolder.info = periodHolder.info.copyWithPeriodIndex(C.INDEX_UNSET);
} }
} }
} }
@ -1252,14 +1249,10 @@ import java.util.Collections;
return; return;
} }
// The current period is in the new timeline. Update the playback info.
if (periodIndex != playingPeriodIndex) {
playbackInfo = playbackInfo.copyWithPeriodIndex(periodIndex);
}
MediaPeriodId playingPeriodId = playbackInfo.periodId; MediaPeriodId playingPeriodId = playbackInfo.periodId;
if (playingPeriodId.isAd()) { if (playingPeriodId.isAd()) {
MediaPeriodId periodId = queue.resolveMediaPeriodIdForAds(periodIndex, contentPositionUs); MediaPeriodId periodId =
queue.resolveMediaPeriodIdForAds(playingPeriodUid, contentPositionUs);
if (!periodId.equals(playingPeriodId)) { if (!periodId.equals(playingPeriodId)) {
// The previously playing ad should no longer be played, so skip it. // The previously playing ad should no longer be played, so skip it.
long seekPositionUs = long seekPositionUs =
@ -1284,16 +1277,17 @@ import java.util.Collections;
/** /**
* Given a period index into an old timeline, finds the first subsequent period that also exists * Given a period index into an old timeline, finds the first subsequent period that also exists
* in a new timeline. The index of this period in the new timeline is returned. * in a new timeline. The uid of this period in the new timeline is returned.
* *
* @param oldPeriodIndex The index of the period in the old timeline. * @param oldPeriodUid The index of the period in the old timeline.
* @param oldTimeline The old timeline. * @param oldTimeline The old timeline.
* @param newTimeline The new timeline. * @param newTimeline The new timeline.
* @return The index in the new timeline of the first subsequent period, or {@link C#INDEX_UNSET} * @return The uid in the new timeline of the first subsequent period, or null if no such period
* if no such period was found. * was found.
*/ */
private int resolveSubsequentPeriod( private @Nullable Object resolveSubsequentPeriod(
int oldPeriodIndex, Timeline oldTimeline, Timeline newTimeline) { Object oldPeriodUid, Timeline oldTimeline, Timeline newTimeline) {
int oldPeriodIndex = oldTimeline.getIndexOfPeriod(oldPeriodUid);
int newPeriodIndex = C.INDEX_UNSET; int newPeriodIndex = C.INDEX_UNSET;
int maxIterations = oldTimeline.getPeriodCount(); int maxIterations = oldTimeline.getPeriodCount();
for (int i = 0; i < maxIterations && newPeriodIndex == C.INDEX_UNSET; i++) { for (int i = 0; i < maxIterations && newPeriodIndex == C.INDEX_UNSET; i++) {
@ -1305,11 +1299,11 @@ import java.util.Collections;
} }
newPeriodIndex = newTimeline.getIndexOfPeriod(oldTimeline.getUidOfPeriod(oldPeriodIndex)); newPeriodIndex = newTimeline.getIndexOfPeriod(oldTimeline.getUidOfPeriod(oldPeriodIndex));
} }
return newPeriodIndex; return newPeriodIndex == C.INDEX_UNSET ? null : newTimeline.getUidOfPeriod(newPeriodIndex);
} }
/** /**
* Converts a {@link SeekPosition} into the corresponding (periodIndex, periodPositionUs) for the * Converts a {@link SeekPosition} into the corresponding (periodUid, periodPositionUs) for the
* internal timeline. * internal timeline.
* *
* @param seekPosition The position to resolve. * @param seekPosition The position to resolve.
@ -1319,7 +1313,7 @@ import java.util.Collections;
* @throws IllegalSeekPositionException If the window index of the seek position is outside the * @throws IllegalSeekPositionException If the window index of the seek position is outside the
* bounds of the timeline. * bounds of the timeline.
*/ */
private Pair<Integer, Long> resolveSeekPosition( private Pair<Object, Long> resolveSeekPosition(
SeekPosition seekPosition, boolean trySubsequentPeriods) { SeekPosition seekPosition, boolean trySubsequentPeriods) {
Timeline timeline = playbackInfo.timeline; Timeline timeline = playbackInfo.timeline;
Timeline seekTimeline = seekPosition.timeline; Timeline seekTimeline = seekPosition.timeline;
@ -1333,7 +1327,7 @@ import java.util.Collections;
seekTimeline = timeline; seekTimeline = timeline;
} }
// Map the SeekPosition to a position in the corresponding timeline. // Map the SeekPosition to a position in the corresponding timeline.
Pair<Integer, Long> periodPosition; Pair<Object, Long> periodPosition;
try { try {
periodPosition = seekTimeline.getPeriodPosition(window, period, seekPosition.windowIndex, periodPosition = seekTimeline.getPeriodPosition(window, period, seekPosition.windowIndex,
seekPosition.windowPositionUs); seekPosition.windowPositionUs);
@ -1347,15 +1341,15 @@ import java.util.Collections;
return periodPosition; return periodPosition;
} }
// Attempt to find the mapped period in the internal timeline. // Attempt to find the mapped period in the internal timeline.
int periodIndex = timeline.getIndexOfPeriod(seekTimeline.getUidOfPeriod(periodPosition.first)); int periodIndex = timeline.getIndexOfPeriod(periodPosition.first);
if (periodIndex != C.INDEX_UNSET) { if (periodIndex != C.INDEX_UNSET) {
// We successfully located the period in the internal timeline. // We successfully located the period in the internal timeline.
return Pair.create(periodIndex, periodPosition.second); return periodPosition;
} }
if (trySubsequentPeriods) { if (trySubsequentPeriods) {
// Try and find a subsequent period from the seek timeline in the internal timeline. // Try and find a subsequent period from the seek timeline in the internal timeline.
periodIndex = resolveSubsequentPeriod(periodPosition.first, seekTimeline, timeline); Object periodUid = resolveSubsequentPeriod(periodPosition.first, seekTimeline, timeline);
if (periodIndex != C.INDEX_UNSET) { if (periodUid != null) {
// We found one. Map the SeekPosition onto the corresponding default position. // We found one. Map the SeekPosition onto the corresponding default position.
return getPeriodPosition( return getPeriodPosition(
timeline, timeline.getPeriod(periodIndex, period).windowIndex, C.TIME_UNSET); timeline, timeline.getPeriod(periodIndex, period).windowIndex, C.TIME_UNSET);
@ -1369,7 +1363,7 @@ import java.util.Collections;
* Calls {@link Timeline#getPeriodPosition(Timeline.Window, Timeline.Period, int, long)} using the * Calls {@link Timeline#getPeriodPosition(Timeline.Window, Timeline.Period, int, long)} using the
* current timeline. * current timeline.
*/ */
private Pair<Integer, Long> getPeriodPosition( private Pair<Object, Long> getPeriodPosition(
Timeline timeline, int windowIndex, long windowPositionUs) { Timeline timeline, int windowIndex, long windowPositionUs) {
return timeline.getPeriodPosition(window, period, windowIndex, windowPositionUs); return timeline.getPeriodPosition(window, period, windowIndex, windowPositionUs);
} }
@ -1506,14 +1500,12 @@ import java.util.Collections;
if (info == null) { if (info == null) {
mediaSource.maybeThrowSourceInfoRefreshError(); mediaSource.maybeThrowSourceInfoRefreshError();
} else { } else {
Object uid = playbackInfo.timeline.getUidOfPeriod(info.id.periodIndex);
MediaPeriod mediaPeriod = MediaPeriod mediaPeriod =
queue.enqueueNextMediaPeriod( queue.enqueueNextMediaPeriod(
rendererCapabilities, rendererCapabilities,
trackSelector, trackSelector,
loadControl.getAllocator(), loadControl.getAllocator(),
mediaSource, mediaSource,
uid,
info); info);
mediaPeriod.prepare(this, info.startPositionUs); mediaPeriod.prepare(this, info.startPositionUs);
setIsLoading(true); setIsLoading(true);

View File

@ -62,7 +62,6 @@ import com.google.android.exoplayer2.util.Assertions;
* @param trackSelector The track selector. * @param trackSelector The track selector.
* @param allocator The allocator. * @param allocator The allocator.
* @param mediaSource The media source that produced the media period. * @param mediaSource The media source that produced the media period.
* @param uid The unique identifier for the containing timeline period.
* @param info Information used to identify this media period in its timeline period. * @param info Information used to identify this media period in its timeline period.
*/ */
public MediaPeriodHolder( public MediaPeriodHolder(
@ -71,13 +70,12 @@ import com.google.android.exoplayer2.util.Assertions;
TrackSelector trackSelector, TrackSelector trackSelector,
Allocator allocator, Allocator allocator,
MediaSource mediaSource, MediaSource mediaSource,
Object uid,
MediaPeriodInfo info) { MediaPeriodInfo info) {
this.rendererCapabilities = rendererCapabilities; this.rendererCapabilities = rendererCapabilities;
this.rendererPositionOffsetUs = rendererPositionOffsetUs - info.startPositionUs; this.rendererPositionOffsetUs = rendererPositionOffsetUs - info.startPositionUs;
this.trackSelector = trackSelector; this.trackSelector = trackSelector;
this.mediaSource = mediaSource; this.mediaSource = mediaSource;
this.uid = Assertions.checkNotNull(uid); this.uid = Assertions.checkNotNull(info.id.periodUid);
this.info = info; this.info = info;
sampleStreams = new SampleStream[rendererCapabilities.length]; sampleStreams = new SampleStream[rendererCapabilities.length];
mayRetainStreamFlags = new boolean[rendererCapabilities.length]; mayRetainStreamFlags = new boolean[rendererCapabilities.length];

View File

@ -62,20 +62,6 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
this.isFinal = isFinal; this.isFinal = isFinal;
} }
/**
* Returns a copy of this instance with the period identifier's period index set to the specified
* value.
*/
public MediaPeriodInfo copyWithPeriodIndex(int periodIndex) {
return new MediaPeriodInfo(
id.copyWithPeriodIndex(periodIndex),
startPositionUs,
contentPositionUs,
durationUs,
isLastInTimelinePeriod,
isFinal);
}
/** Returns a copy of this instance with the start position set to the specified value. */ /** Returns a copy of this instance with the start position set to the specified value. */
public MediaPeriodInfo copyWithStartPositionUs(long startPositionUs) { public MediaPeriodInfo copyWithStartPositionUs(long startPositionUs) {
return new MediaPeriodInfo( return new MediaPeriodInfo(

View File

@ -30,7 +30,6 @@ import com.google.android.exoplayer2.util.Assertions;
* loading media period at the end of the queue, with methods for controlling loading and updating * loading media period at the end of the queue, with methods for controlling loading and updating
* the queue. Also has a reference to the media period currently being read. * the queue. Also has a reference to the media period currently being read.
*/ */
@SuppressWarnings("UngroupedOverloads")
/* package */ final class MediaPeriodQueue { /* package */ final class MediaPeriodQueue {
/** /**
@ -135,7 +134,6 @@ import com.google.android.exoplayer2.util.Assertions;
* @param trackSelector The track selector. * @param trackSelector The track selector.
* @param allocator The allocator. * @param allocator The allocator.
* @param mediaSource The media source that produced the media period. * @param mediaSource The media source that produced the media period.
* @param uid The unique identifier for the containing timeline period.
* @param info Information used to identify this media period in its timeline period. * @param info Information used to identify this media period in its timeline period.
*/ */
public MediaPeriod enqueueNextMediaPeriod( public MediaPeriod enqueueNextMediaPeriod(
@ -143,7 +141,6 @@ import com.google.android.exoplayer2.util.Assertions;
TrackSelector trackSelector, TrackSelector trackSelector,
Allocator allocator, Allocator allocator,
MediaSource mediaSource, MediaSource mediaSource,
Object uid,
MediaPeriodInfo info) { MediaPeriodInfo info) {
long rendererPositionOffsetUs = long rendererPositionOffsetUs =
loading == null loading == null
@ -156,7 +153,6 @@ import com.google.android.exoplayer2.util.Assertions;
trackSelector, trackSelector,
allocator, allocator,
mediaSource, mediaSource,
uid,
info); info);
if (loading != null) { if (loading != null) {
Assertions.checkState(hasPlayingPeriod()); Assertions.checkState(hasPlayingPeriod());
@ -304,14 +300,14 @@ import com.google.android.exoplayer2.util.Assertions;
// TODO: Merge this into setTimeline so that the queue gets updated as soon as the new timeline // TODO: Merge this into setTimeline so that the queue gets updated as soon as the new timeline
// is set, once all cases handled by ExoPlayerImplInternal.handleSourceInfoRefreshed can be // is set, once all cases handled by ExoPlayerImplInternal.handleSourceInfoRefreshed can be
// handled here. // handled here.
int periodIndex = playingPeriodId.periodIndex; int periodIndex = timeline.getIndexOfPeriod(playingPeriodId.periodUid);
// The front period is either playing now, or is being loaded and will become the playing // The front period is either playing now, or is being loaded and will become the playing
// period. // period.
MediaPeriodHolder previousPeriodHolder = null; MediaPeriodHolder previousPeriodHolder = null;
MediaPeriodHolder periodHolder = getFrontPeriod(); MediaPeriodHolder periodHolder = getFrontPeriod();
while (periodHolder != null) { while (periodHolder != null) {
if (previousPeriodHolder == null) { if (previousPeriodHolder == null) {
periodHolder.info = getUpdatedMediaPeriodInfo(periodHolder.info, periodIndex); periodHolder.info = getUpdatedMediaPeriodInfo(periodHolder.info);
} else { } else {
// Check this period holder still follows the previous one, based on the new timeline. // Check this period holder still follows the previous one, based on the new timeline.
if (periodIndex == C.INDEX_UNSET if (periodIndex == C.INDEX_UNSET
@ -325,8 +321,8 @@ import com.google.android.exoplayer2.util.Assertions;
// We've loaded a next media period that is not in the new timeline. // We've loaded a next media period that is not in the new timeline.
return !removeAfter(previousPeriodHolder); return !removeAfter(previousPeriodHolder);
} }
// Update the period index. // Update the period holder.
periodHolder.info = getUpdatedMediaPeriodInfo(periodHolder.info, periodIndex); periodHolder.info = getUpdatedMediaPeriodInfo(periodHolder.info);
// Check the media period information matches the new timeline. // Check the media period information matches the new timeline.
if (!canKeepMediaPeriodHolder(periodHolder, periodInfo)) { if (!canKeepMediaPeriodHolder(periodHolder, periodInfo)) {
return !removeAfter(previousPeriodHolder); return !removeAfter(previousPeriodHolder);
@ -348,16 +344,29 @@ import com.google.android.exoplayer2.util.Assertions;
/** /**
* Returns new media period info based on specified {@code mediaPeriodInfo} but taking into * Returns new media period info based on specified {@code mediaPeriodInfo} but taking into
* account the current timeline, and with the period index updated to {@code newPeriodIndex}. * account the current timeline. This method must only be called if the period is still part of
* the current timeline.
* *
* @param mediaPeriodInfo Media period info for a media period based on an old timeline. * @param info Media period info for a media period based on an old timeline.
* @param newPeriodIndex The new period index in the new timeline for the existing media period.
* @return The updated media period info for the current timeline. * @return The updated media period info for the current timeline.
*/ */
public MediaPeriodInfo getUpdatedMediaPeriodInfo( public MediaPeriodInfo getUpdatedMediaPeriodInfo(MediaPeriodInfo info) {
MediaPeriodInfo mediaPeriodInfo, int newPeriodIndex) { boolean isLastInPeriod = isLastInPeriod(info.id);
return getUpdatedMediaPeriodInfo( boolean isLastInTimeline = isLastInTimeline(info.id, isLastInPeriod);
mediaPeriodInfo, mediaPeriodInfo.id.copyWithPeriodIndex(newPeriodIndex)); timeline.getPeriodByUid(info.id.periodUid, period);
long durationUs =
info.id.isAd()
? period.getAdDurationUs(info.id.adGroupIndex, info.id.adIndexInAdGroup)
: (info.id.endPositionUs == C.TIME_END_OF_SOURCE
? period.getDurationUs()
: info.id.endPositionUs);
return new MediaPeriodInfo(
info.id,
info.startPositionUs,
info.contentPositionUs,
durationUs,
isLastInPeriod,
isLastInTimeline);
} }
/** /**
@ -365,13 +374,13 @@ import com.google.android.exoplayer2.util.Assertions;
* played, returning an identifier for an ad group if one needs to be played before the specified * played, returning an identifier for an ad group if one needs to be played before the specified
* position, or an identifier for a content media period if not. * position, or an identifier for a content media period if not.
* *
* @param periodIndex The index of the timeline period to play. * @param periodUid The uid of the timeline period to play.
* @param positionUs The next content position in the period to play. * @param positionUs The next content position in the period to play.
* @return The identifier for the first media period to play, taking into account unplayed ads. * @return The identifier for the first media period to play, taking into account unplayed ads.
*/ */
public MediaPeriodId resolveMediaPeriodIdForAds(int periodIndex, long positionUs) { public MediaPeriodId resolveMediaPeriodIdForAds(Object periodUid, long positionUs) {
long windowSequenceNumber = resolvePeriodIndexToWindowSequenceNumber(periodIndex); long windowSequenceNumber = resolvePeriodIndexToWindowSequenceNumber(periodUid);
return resolveMediaPeriodIdForAds(periodIndex, positionUs, windowSequenceNumber); return resolveMediaPeriodIdForAds(periodUid, positionUs, windowSequenceNumber);
} }
// Internal methods. // Internal methods.
@ -381,15 +390,15 @@ import com.google.android.exoplayer2.util.Assertions;
* played, returning an identifier for an ad group if one needs to be played before the specified * played, returning an identifier for an ad group if one needs to be played before the specified
* position, or an identifier for a content media period if not. * position, or an identifier for a content media period if not.
* *
* @param periodIndex The index of the timeline period to play. * @param periodUid The uid of the timeline period to play.
* @param positionUs The next content position in the period to play. * @param positionUs The next content position in the period to play.
* @param windowSequenceNumber The sequence number of the window in the buffered sequence of * @param windowSequenceNumber The sequence number of the window in the buffered sequence of
* windows this period is part of. * windows this period is part of.
* @return The identifier for the first media period to play, taking into account unplayed ads. * @return The identifier for the first media period to play, taking into account unplayed ads.
*/ */
private MediaPeriodId resolveMediaPeriodIdForAds( private MediaPeriodId resolveMediaPeriodIdForAds(
int periodIndex, long positionUs, long windowSequenceNumber) { Object periodUid, long positionUs, long windowSequenceNumber) {
timeline.getPeriod(periodIndex, period); timeline.getPeriodByUid(periodUid, period);
int adGroupIndex = period.getAdGroupIndexForPositionUs(positionUs); int adGroupIndex = period.getAdGroupIndexForPositionUs(positionUs);
if (adGroupIndex == C.INDEX_UNSET) { if (adGroupIndex == C.INDEX_UNSET) {
int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(positionUs); int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(positionUs);
@ -397,24 +406,23 @@ import com.google.android.exoplayer2.util.Assertions;
nextAdGroupIndex == C.INDEX_UNSET nextAdGroupIndex == C.INDEX_UNSET
? C.TIME_END_OF_SOURCE ? C.TIME_END_OF_SOURCE
: period.getAdGroupTimeUs(nextAdGroupIndex); : period.getAdGroupTimeUs(nextAdGroupIndex);
return new MediaPeriodId(periodIndex, windowSequenceNumber, endPositionUs); return new MediaPeriodId(periodUid, windowSequenceNumber, endPositionUs);
} else { } else {
int adIndexInAdGroup = period.getFirstAdIndexToPlay(adGroupIndex); int adIndexInAdGroup = period.getFirstAdIndexToPlay(adGroupIndex);
return new MediaPeriodId(periodIndex, adGroupIndex, adIndexInAdGroup, windowSequenceNumber); return new MediaPeriodId(periodUid, adGroupIndex, adIndexInAdGroup, windowSequenceNumber);
} }
} }
/** /**
* Resolves the specified period index to a corresponding window sequence number. Either by * Resolves the specified period uid to a corresponding window sequence number. Either by reusing
* reusing the window sequence number of an existing matching media period or by creating a new * the window sequence number of an existing matching media period or by creating a new window
* window sequence number. * sequence number.
* *
* @param periodIndex The index of the timeline period. * @param periodUid The uid of the timeline period.
* @return A window sequence number for a media period created for this timeline period. * @return A window sequence number for a media period created for this timeline period.
*/ */
private long resolvePeriodIndexToWindowSequenceNumber(int periodIndex) { private long resolvePeriodIndexToWindowSequenceNumber(Object periodUid) {
Object periodUid = timeline.getPeriod(periodIndex, period, /* setIds= */ true).uid; int windowIndex = timeline.getPeriodByUid(periodUid, period).windowIndex;
int windowIndex = period.windowIndex;
if (oldFrontPeriodUid != null) { if (oldFrontPeriodUid != null) {
int oldFrontPeriodIndex = timeline.getIndexOfPeriod(oldFrontPeriodUid); int oldFrontPeriodIndex = timeline.getIndexOfPeriod(oldFrontPeriodUid);
if (oldFrontPeriodIndex != C.INDEX_UNSET) { if (oldFrontPeriodIndex != C.INDEX_UNSET) {
@ -469,32 +477,32 @@ import com.google.android.exoplayer2.util.Assertions;
if (lastValidPeriodHolder == null) { if (lastValidPeriodHolder == null) {
return true; return true;
} }
int currentPeriodIndex = timeline.getIndexOfPeriod(lastValidPeriodHolder.uid);
while (true) { while (true) {
int nextPeriodIndex = int nextPeriodIndex =
timeline.getNextPeriodIndex( timeline.getNextPeriodIndex(
lastValidPeriodHolder.info.id.periodIndex, currentPeriodIndex, period, window, repeatMode, shuffleModeEnabled);
period,
window,
repeatMode,
shuffleModeEnabled);
while (lastValidPeriodHolder.next != null while (lastValidPeriodHolder.next != null
&& !lastValidPeriodHolder.info.isLastInTimelinePeriod) { && !lastValidPeriodHolder.info.isLastInTimelinePeriod) {
lastValidPeriodHolder = lastValidPeriodHolder.next; lastValidPeriodHolder = lastValidPeriodHolder.next;
} }
if (nextPeriodIndex == C.INDEX_UNSET
|| lastValidPeriodHolder.next == null if (nextPeriodIndex == C.INDEX_UNSET || lastValidPeriodHolder.next == null) {
|| lastValidPeriodHolder.next.info.id.periodIndex != nextPeriodIndex) { break;
}
int nextPeriodHolderPeriodIndex = timeline.getIndexOfPeriod(lastValidPeriodHolder.next.uid);
if (nextPeriodHolderPeriodIndex != nextPeriodIndex) {
break; break;
} }
lastValidPeriodHolder = lastValidPeriodHolder.next; lastValidPeriodHolder = lastValidPeriodHolder.next;
currentPeriodIndex = nextPeriodIndex;
} }
// Release any period holders that don't match the new period order. // Release any period holders that don't match the new period order.
boolean readingPeriodRemoved = removeAfter(lastValidPeriodHolder); boolean readingPeriodRemoved = removeAfter(lastValidPeriodHolder);
// Update the period info for the last holder, as it may now be the last period in the timeline. // Update the period info for the last holder, as it may now be the last period in the timeline.
lastValidPeriodHolder.info = lastValidPeriodHolder.info = getUpdatedMediaPeriodInfo(lastValidPeriodHolder.info);
getUpdatedMediaPeriodInfo(lastValidPeriodHolder.info, lastValidPeriodHolder.info.id);
// If renderers may have read from a period that's been removed, it is necessary to restart. // If renderers may have read from a period that's been removed, it is necessary to restart.
return !readingPeriodRemoved || !hasPlayingPeriod(); return !readingPeriodRemoved || !hasPlayingPeriod();
@ -525,9 +533,10 @@ import com.google.android.exoplayer2.util.Assertions;
// timeline is updated, to avoid repeatedly checking the same timeline. // timeline is updated, to avoid repeatedly checking the same timeline.
MediaPeriodInfo mediaPeriodInfo = mediaPeriodHolder.info; MediaPeriodInfo mediaPeriodInfo = mediaPeriodHolder.info;
if (mediaPeriodInfo.isLastInTimelinePeriod) { if (mediaPeriodInfo.isLastInTimelinePeriod) {
int currentPeriodIndex = timeline.getIndexOfPeriod(mediaPeriodInfo.id.periodUid);
int nextPeriodIndex = int nextPeriodIndex =
timeline.getNextPeriodIndex( timeline.getNextPeriodIndex(
mediaPeriodInfo.id.periodIndex, period, window, repeatMode, shuffleModeEnabled); currentPeriodIndex, period, window, repeatMode, shuffleModeEnabled);
if (nextPeriodIndex == C.INDEX_UNSET) { if (nextPeriodIndex == C.INDEX_UNSET) {
// We can't create a next period yet. // We can't create a next period yet.
return null; return null;
@ -546,7 +555,7 @@ import com.google.android.exoplayer2.util.Assertions;
// the buffer, and start buffering from this point. // the buffer, and start buffering from this point.
long defaultPositionProjectionUs = long defaultPositionProjectionUs =
mediaPeriodHolder.getRendererOffset() + mediaPeriodInfo.durationUs - rendererPositionUs; mediaPeriodHolder.getRendererOffset() + mediaPeriodInfo.durationUs - rendererPositionUs;
Pair<Integer, Long> defaultPosition = Pair<Object, Long> defaultPosition =
timeline.getPeriodPosition( timeline.getPeriodPosition(
window, window,
period, period,
@ -556,7 +565,7 @@ import com.google.android.exoplayer2.util.Assertions;
if (defaultPosition == null) { if (defaultPosition == null) {
return null; return null;
} }
nextPeriodIndex = defaultPosition.first; nextPeriodUid = defaultPosition.first;
startPositionUs = defaultPosition.second; startPositionUs = defaultPosition.second;
if (mediaPeriodHolder.next != null && mediaPeriodHolder.next.uid.equals(nextPeriodUid)) { if (mediaPeriodHolder.next != null && mediaPeriodHolder.next.uid.equals(nextPeriodUid)) {
windowSequenceNumber = mediaPeriodHolder.next.info.id.windowSequenceNumber; windowSequenceNumber = mediaPeriodHolder.next.info.id.windowSequenceNumber;
@ -567,12 +576,12 @@ import com.google.android.exoplayer2.util.Assertions;
startPositionUs = 0; startPositionUs = 0;
} }
MediaPeriodId periodId = MediaPeriodId periodId =
resolveMediaPeriodIdForAds(nextPeriodIndex, startPositionUs, windowSequenceNumber); resolveMediaPeriodIdForAds(nextPeriodUid, startPositionUs, windowSequenceNumber);
return getMediaPeriodInfo(periodId, startPositionUs, startPositionUs); return getMediaPeriodInfo(periodId, startPositionUs, startPositionUs);
} }
MediaPeriodId currentPeriodId = mediaPeriodInfo.id; MediaPeriodId currentPeriodId = mediaPeriodInfo.id;
timeline.getPeriod(currentPeriodId.periodIndex, period); timeline.getPeriodByUid(currentPeriodId.periodUid, period);
if (currentPeriodId.isAd()) { if (currentPeriodId.isAd()) {
int adGroupIndex = currentPeriodId.adGroupIndex; int adGroupIndex = currentPeriodId.adGroupIndex;
int adCountInCurrentAdGroup = period.getAdCountInAdGroup(adGroupIndex); int adCountInCurrentAdGroup = period.getAdCountInAdGroup(adGroupIndex);
@ -586,7 +595,7 @@ import com.google.android.exoplayer2.util.Assertions;
return !period.isAdAvailable(adGroupIndex, nextAdIndexInAdGroup) return !period.isAdAvailable(adGroupIndex, nextAdIndexInAdGroup)
? null ? null
: getMediaPeriodInfoForAd( : getMediaPeriodInfoForAd(
currentPeriodId.periodIndex, currentPeriodId.periodUid,
adGroupIndex, adGroupIndex,
nextAdIndexInAdGroup, nextAdIndexInAdGroup,
mediaPeriodInfo.contentPositionUs, mediaPeriodInfo.contentPositionUs,
@ -594,7 +603,7 @@ import com.google.android.exoplayer2.util.Assertions;
} else { } else {
// Play content from the ad group position. // Play content from the ad group position.
return getMediaPeriodInfoForContent( return getMediaPeriodInfoForContent(
currentPeriodId.periodIndex, currentPeriodId.periodUid,
mediaPeriodInfo.contentPositionUs, mediaPeriodInfo.contentPositionUs,
currentPeriodId.windowSequenceNumber); currentPeriodId.windowSequenceNumber);
} }
@ -604,7 +613,7 @@ import com.google.android.exoplayer2.util.Assertions;
if (nextAdGroupIndex == C.INDEX_UNSET) { if (nextAdGroupIndex == C.INDEX_UNSET) {
// The next ad group can't be played. Play content from the ad group position instead. // The next ad group can't be played. Play content from the ad group position instead.
return getMediaPeriodInfoForContent( return getMediaPeriodInfoForContent(
currentPeriodId.periodIndex, currentPeriodId.periodUid,
mediaPeriodInfo.id.endPositionUs, mediaPeriodInfo.id.endPositionUs,
currentPeriodId.windowSequenceNumber); currentPeriodId.windowSequenceNumber);
} }
@ -612,7 +621,7 @@ import com.google.android.exoplayer2.util.Assertions;
return !period.isAdAvailable(nextAdGroupIndex, adIndexInAdGroup) return !period.isAdAvailable(nextAdGroupIndex, adIndexInAdGroup)
? null ? null
: getMediaPeriodInfoForAd( : getMediaPeriodInfoForAd(
currentPeriodId.periodIndex, currentPeriodId.periodUid,
nextAdGroupIndex, nextAdGroupIndex,
adIndexInAdGroup, adIndexInAdGroup,
mediaPeriodInfo.id.endPositionUs, mediaPeriodInfo.id.endPositionUs,
@ -634,7 +643,7 @@ import com.google.android.exoplayer2.util.Assertions;
} }
long contentDurationUs = period.getDurationUs(); long contentDurationUs = period.getDurationUs();
return getMediaPeriodInfoForAd( return getMediaPeriodInfoForAd(
currentPeriodId.periodIndex, currentPeriodId.periodUid,
adGroupIndex, adGroupIndex,
adIndexInAdGroup, adIndexInAdGroup,
contentDurationUs, contentDurationUs,
@ -642,57 +651,37 @@ import com.google.android.exoplayer2.util.Assertions;
} }
} }
private MediaPeriodInfo getUpdatedMediaPeriodInfo(MediaPeriodInfo info, MediaPeriodId newId) {
long startPositionUs = info.startPositionUs;
boolean isLastInPeriod = isLastInPeriod(newId);
boolean isLastInTimeline = isLastInTimeline(newId, isLastInPeriod);
timeline.getPeriod(newId.periodIndex, period);
long durationUs =
newId.isAd()
? period.getAdDurationUs(newId.adGroupIndex, newId.adIndexInAdGroup)
: (newId.endPositionUs == C.TIME_END_OF_SOURCE
? period.getDurationUs()
: newId.endPositionUs);
return new MediaPeriodInfo(
newId,
startPositionUs,
info.contentPositionUs,
durationUs,
isLastInPeriod,
isLastInTimeline);
}
private MediaPeriodInfo getMediaPeriodInfo( private MediaPeriodInfo getMediaPeriodInfo(
MediaPeriodId id, long contentPositionUs, long startPositionUs) { MediaPeriodId id, long contentPositionUs, long startPositionUs) {
timeline.getPeriod(id.periodIndex, period); timeline.getPeriodByUid(id.periodUid, period);
if (id.isAd()) { if (id.isAd()) {
if (!period.isAdAvailable(id.adGroupIndex, id.adIndexInAdGroup)) { if (!period.isAdAvailable(id.adGroupIndex, id.adIndexInAdGroup)) {
return null; return null;
} }
return getMediaPeriodInfoForAd( return getMediaPeriodInfoForAd(
id.periodIndex, id.periodUid,
id.adGroupIndex, id.adGroupIndex,
id.adIndexInAdGroup, id.adIndexInAdGroup,
contentPositionUs, contentPositionUs,
id.windowSequenceNumber); id.windowSequenceNumber);
} else { } else {
return getMediaPeriodInfoForContent(id.periodIndex, startPositionUs, id.windowSequenceNumber); return getMediaPeriodInfoForContent(id.periodUid, startPositionUs, id.windowSequenceNumber);
} }
} }
private MediaPeriodInfo getMediaPeriodInfoForAd( private MediaPeriodInfo getMediaPeriodInfoForAd(
int periodIndex, Object periodUid,
int adGroupIndex, int adGroupIndex,
int adIndexInAdGroup, int adIndexInAdGroup,
long contentPositionUs, long contentPositionUs,
long windowSequenceNumber) { long windowSequenceNumber) {
MediaPeriodId id = MediaPeriodId id =
new MediaPeriodId(periodIndex, adGroupIndex, adIndexInAdGroup, windowSequenceNumber); new MediaPeriodId(periodUid, adGroupIndex, adIndexInAdGroup, windowSequenceNumber);
boolean isLastInPeriod = isLastInPeriod(id); boolean isLastInPeriod = isLastInPeriod(id);
boolean isLastInTimeline = isLastInTimeline(id, isLastInPeriod); boolean isLastInTimeline = isLastInTimeline(id, isLastInPeriod);
long durationUs = long durationUs =
timeline timeline
.getPeriod(id.periodIndex, period) .getPeriodByUid(id.periodUid, period)
.getAdDurationUs(id.adGroupIndex, id.adIndexInAdGroup); .getAdDurationUs(id.adGroupIndex, id.adIndexInAdGroup);
long startPositionUs = long startPositionUs =
adIndexInAdGroup == period.getFirstAdIndexToPlay(adGroupIndex) adIndexInAdGroup == period.getFirstAdIndexToPlay(adGroupIndex)
@ -708,14 +697,14 @@ import com.google.android.exoplayer2.util.Assertions;
} }
private MediaPeriodInfo getMediaPeriodInfoForContent( private MediaPeriodInfo getMediaPeriodInfoForContent(
int periodIndex, long startPositionUs, long windowSequenceNumber) { Object periodUid, long startPositionUs, long windowSequenceNumber) {
int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(startPositionUs); int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(startPositionUs);
long endPositionUs = long endPositionUs =
nextAdGroupIndex == C.INDEX_UNSET nextAdGroupIndex == C.INDEX_UNSET
? C.TIME_END_OF_SOURCE ? C.TIME_END_OF_SOURCE
: period.getAdGroupTimeUs(nextAdGroupIndex); : period.getAdGroupTimeUs(nextAdGroupIndex);
MediaPeriodId id = new MediaPeriodId(periodIndex, windowSequenceNumber, endPositionUs); MediaPeriodId id = new MediaPeriodId(periodUid, windowSequenceNumber, endPositionUs);
timeline.getPeriod(id.periodIndex, period); timeline.getPeriodByUid(id.periodUid, period);
boolean isLastInPeriod = isLastInPeriod(id); boolean isLastInPeriod = isLastInPeriod(id);
boolean isLastInTimeline = isLastInTimeline(id, isLastInPeriod); boolean isLastInTimeline = isLastInTimeline(id, isLastInPeriod);
long durationUs = long durationUs =
@ -725,7 +714,7 @@ import com.google.android.exoplayer2.util.Assertions;
} }
private boolean isLastInPeriod(MediaPeriodId id) { private boolean isLastInPeriod(MediaPeriodId id) {
int adGroupCount = timeline.getPeriod(id.periodIndex, period).getAdGroupCount(); int adGroupCount = timeline.getPeriodByUid(id.periodUid, period).getAdGroupCount();
if (adGroupCount == 0) { if (adGroupCount == 0) {
return true; return true;
} }
@ -749,9 +738,10 @@ import com.google.android.exoplayer2.util.Assertions;
} }
private boolean isLastInTimeline(MediaPeriodId id, boolean isLastMediaPeriodInPeriod) { private boolean isLastInTimeline(MediaPeriodId id, boolean isLastMediaPeriodInPeriod) {
int windowIndex = timeline.getPeriod(id.periodIndex, period).windowIndex; int periodIndex = timeline.getIndexOfPeriod(id.periodUid);
int windowIndex = timeline.getPeriod(periodIndex, period).windowIndex;
return !timeline.getWindow(windowIndex, window).isDynamic return !timeline.getWindow(windowIndex, window).isDynamic
&& timeline.isLastPeriod(id.periodIndex, period, window, repeatMode, shuffleModeEnabled) && timeline.isLastPeriod(periodIndex, period, window, repeatMode, shuffleModeEnabled)
&& isLastMediaPeriodInPeriod; && isLastMediaPeriodInPeriod;
} }
} }

View File

@ -29,7 +29,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
* Dummy media period id used while the timeline is empty and no period id is specified. This id * Dummy media period id used while the timeline is empty and no period id is specified. This id
* is used when playback infos are created with {@link #createDummy(long, TrackSelectorResult)}. * is used when playback infos are created with {@link #createDummy(long, TrackSelectorResult)}.
*/ */
public static final MediaPeriodId DUMMY_MEDIA_PERIOD_ID = new MediaPeriodId(/* periodIndex= */ 0); public static final MediaPeriodId DUMMY_MEDIA_PERIOD_ID =
new MediaPeriodId(/* periodUid= */ new Object());
/** The current {@link Timeline}. */ /** The current {@link Timeline}. */
public final Timeline timeline; public final Timeline timeline;
@ -149,23 +150,6 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
startPositionUs); startPositionUs);
} }
public PlaybackInfo copyWithPeriodIndex(int periodIndex) {
return new PlaybackInfo(
timeline,
manifest,
periodId.copyWithPeriodIndex(periodIndex),
startPositionUs,
contentPositionUs,
playbackState,
isLoading,
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
}
public PlaybackInfo copyWithTimeline(Timeline timeline, Object manifest) { public PlaybackInfo copyWithTimeline(Timeline timeline, Object manifest) {
return new PlaybackInfo( return new PlaybackInfo(
timeline, timeline,

View File

@ -702,13 +702,13 @@ public abstract class Timeline {
* Calls {@link #getPeriodPosition(Window, Period, int, long, long)} with a zero default position * Calls {@link #getPeriodPosition(Window, Period, int, long, long)} with a zero default position
* projection. * projection.
*/ */
public final Pair<Integer, Long> getPeriodPosition(Window window, Period period, int windowIndex, public final Pair<Object, Long> getPeriodPosition(
long windowPositionUs) { Window window, Period period, int windowIndex, long windowPositionUs) {
return getPeriodPosition(window, period, windowIndex, windowPositionUs, 0); return getPeriodPosition(window, period, windowIndex, windowPositionUs, 0);
} }
/** /**
* Converts (windowIndex, windowPositionUs) to the corresponding (periodIndex, periodPositionUs). * Converts (windowIndex, windowPositionUs) to the corresponding (periodUid, periodPositionUs).
* *
* @param window A {@link Window} that may be overwritten. * @param window A {@link Window} that may be overwritten.
* @param period A {@link Period} that may be overwritten. * @param period A {@link Period} that may be overwritten.
@ -717,12 +717,16 @@ public abstract class Timeline {
* start position. * start position.
* @param defaultPositionProjectionUs If {@code windowPositionUs} is {@link C#TIME_UNSET}, the * @param defaultPositionProjectionUs If {@code windowPositionUs} is {@link C#TIME_UNSET}, the
* duration into the future by which the window's position should be projected. * duration into the future by which the window's position should be projected.
* @return The corresponding (periodIndex, periodPositionUs), or null if {@code #windowPositionUs} * @return The corresponding (periodUid, periodPositionUs), or null if {@code #windowPositionUs}
* is {@link C#TIME_UNSET}, {@code defaultPositionProjectionUs} is non-zero, and the window's * is {@link C#TIME_UNSET}, {@code defaultPositionProjectionUs} is non-zero, and the window's
* position could not be projected by {@code defaultPositionProjectionUs}. * position could not be projected by {@code defaultPositionProjectionUs}.
*/ */
public final Pair<Integer, Long> getPeriodPosition(Window window, Period period, int windowIndex, public final Pair<Object, Long> getPeriodPosition(
long windowPositionUs, long defaultPositionProjectionUs) { Window window,
Period period,
int windowIndex,
long windowPositionUs,
long defaultPositionProjectionUs) {
Assertions.checkIndex(windowIndex, 0, getWindowCount()); Assertions.checkIndex(windowIndex, 0, getWindowCount());
getWindow(windowIndex, window, false, defaultPositionProjectionUs); getWindow(windowIndex, window, false, defaultPositionProjectionUs);
if (windowPositionUs == C.TIME_UNSET) { if (windowPositionUs == C.TIME_UNSET) {
@ -733,13 +737,13 @@ public abstract class Timeline {
} }
int periodIndex = window.firstPeriodIndex; int periodIndex = window.firstPeriodIndex;
long periodPositionUs = window.getPositionInFirstPeriodUs() + windowPositionUs; long periodPositionUs = window.getPositionInFirstPeriodUs() + windowPositionUs;
long periodDurationUs = getPeriod(periodIndex, period).getDurationUs(); long periodDurationUs = getPeriod(periodIndex, period, /* setIds= */ true).getDurationUs();
while (periodDurationUs != C.TIME_UNSET && periodPositionUs >= periodDurationUs while (periodDurationUs != C.TIME_UNSET && periodPositionUs >= periodDurationUs
&& periodIndex < window.lastPeriodIndex) { && periodIndex < window.lastPeriodIndex) {
periodPositionUs -= periodDurationUs; periodPositionUs -= periodDurationUs;
periodDurationUs = getPeriod(++periodIndex, period).getDurationUs(); periodDurationUs = getPeriod(++periodIndex, period, /* setIds= */ true).getDurationUs();
} }
return Pair.create(periodIndex, periodPositionUs); return Pair.create(period.uid, periodPositionUs);
} }
/** /**

View File

@ -705,11 +705,10 @@ public class AnalyticsCollector
public @Nullable MediaPeriodId tryResolveWindowIndex(int windowIndex) { public @Nullable MediaPeriodId tryResolveWindowIndex(int windowIndex) {
MediaPeriodId match = null; MediaPeriodId match = null;
if (timeline != null) { if (timeline != null) {
int timelinePeriodCount = timeline.getPeriodCount();
for (int i = 0; i < activeMediaPeriods.size(); i++) { for (int i = 0; i < activeMediaPeriods.size(); i++) {
WindowAndMediaPeriodId mediaPeriod = activeMediaPeriods.get(i); WindowAndMediaPeriodId mediaPeriod = activeMediaPeriods.get(i);
int periodIndex = mediaPeriod.mediaPeriodId.periodIndex; int periodIndex = timeline.getIndexOfPeriod(mediaPeriod.mediaPeriodId.periodUid);
if (periodIndex < timelinePeriodCount if (periodIndex != C.INDEX_UNSET
&& timeline.getPeriod(periodIndex, period).windowIndex == windowIndex) { && timeline.getPeriod(periodIndex, period).windowIndex == windowIndex) {
if (match != null) { if (match != null) {
// Ambiguous match. // Ambiguous match.
@ -731,10 +730,10 @@ public class AnalyticsCollector
public void onTimelineChanged(Timeline timeline) { public void onTimelineChanged(Timeline timeline) {
for (int i = 0; i < activeMediaPeriods.size(); i++) { for (int i = 0; i < activeMediaPeriods.size(); i++) {
activeMediaPeriods.set( activeMediaPeriods.set(
i, updateMediaPeriodToNewTimeline(activeMediaPeriods.get(i), timeline)); i, updateWindowIndexToNewTimeline(activeMediaPeriods.get(i), timeline));
} }
if (readingMediaPeriod != null) { if (readingMediaPeriod != null) {
readingMediaPeriod = updateMediaPeriodToNewTimeline(readingMediaPeriod, timeline); readingMediaPeriod = updateWindowIndexToNewTimeline(readingMediaPeriod, timeline);
} }
this.timeline = timeline; this.timeline = timeline;
updateLastReportedPlayingMediaPeriod(); updateLastReportedPlayingMediaPeriod();
@ -779,19 +778,17 @@ public class AnalyticsCollector
} }
} }
private WindowAndMediaPeriodId updateMediaPeriodToNewTimeline( private WindowAndMediaPeriodId updateWindowIndexToNewTimeline(
WindowAndMediaPeriodId mediaPeriod, Timeline newTimeline) { WindowAndMediaPeriodId mediaPeriod, Timeline newTimeline) {
if (newTimeline.isEmpty() || timeline.isEmpty()) { if (newTimeline.isEmpty() || timeline.isEmpty()) {
return mediaPeriod; return mediaPeriod;
} }
Object uid = timeline.getUidOfPeriod(mediaPeriod.mediaPeriodId.periodIndex); int newPeriodIndex = newTimeline.getIndexOfPeriod(mediaPeriod.mediaPeriodId.periodUid);
int newPeriodIndex = newTimeline.getIndexOfPeriod(uid);
if (newPeriodIndex == C.INDEX_UNSET) { if (newPeriodIndex == C.INDEX_UNSET) {
return mediaPeriod; return mediaPeriod;
} }
int newWindowIndex = newTimeline.getPeriod(newPeriodIndex, period).windowIndex; int newWindowIndex = newTimeline.getPeriod(newPeriodIndex, period).windowIndex;
return new WindowAndMediaPeriodId( return new WindowAndMediaPeriodId(newWindowIndex, mediaPeriod.mediaPeriodId);
newWindowIndex, mediaPeriod.mediaPeriodId.copyWithPeriodIndex(newPeriodIndex));
} }
} }

View File

@ -29,6 +29,37 @@ import com.google.android.exoplayer2.Timeline;
private final ShuffleOrder shuffleOrder; private final ShuffleOrder shuffleOrder;
private final boolean isAtomic; private final boolean isAtomic;
/**
* Returns UID of child timeline from a concatenated period UID.
*
* @param concatenatedUid UID of a period in a concatenated timeline.
* @return UID of the child timeline this period belongs to.
*/
public static Object getChildTimelineUidFromConcatenatedUid(Object concatenatedUid) {
return ((Pair<?, ?>) concatenatedUid).first;
}
/**
* Returns UID of the period in the child timeline from a concatenated period UID.
*
* @param concatenatedUid UID of a period in a concatenated timeline.
* @return UID of the period in the child timeline.
*/
public static Object getChildPeriodUidFromConcatenatedUid(Object concatenatedUid) {
return ((Pair<?, ?>) concatenatedUid).second;
}
/**
* Returns concatenated UID for a period in a child timeline.
*
* @param childTimelineUid UID of the child timeline this period belongs to.
* @param childPeriodUid UID of the period in the child timeline.
* @return UID of the period in the concatenated timeline.
*/
public static Object getConcatenatedUid(Object childTimelineUid, Object childPeriodUid) {
return Pair.create(childTimelineUid, childPeriodUid);
}
/** /**
* Sets up a concatenated timeline with a shuffle order of child timelines. * Sets up a concatenated timeline with a shuffle order of child timelines.
* *
@ -170,9 +201,8 @@ import com.google.android.exoplayer2.Timeline;
@Override @Override
public final Period getPeriodByUid(Object uid, Period period) { public final Period getPeriodByUid(Object uid, Period period) {
Pair<?, ?> childUidAndPeriodUid = (Pair<?, ?>) uid; Object childUid = getChildTimelineUidFromConcatenatedUid(uid);
Object childUid = childUidAndPeriodUid.first; Object periodUid = getChildPeriodUidFromConcatenatedUid(uid);
Object periodUid = childUidAndPeriodUid.second;
int childIndex = getChildIndexByChildUid(childUid); int childIndex = getChildIndexByChildUid(childUid);
int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex); int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex);
getTimelineByChildIndex(childIndex).getPeriodByUid(periodUid, period); getTimelineByChildIndex(childIndex).getPeriodByUid(periodUid, period);
@ -190,7 +220,7 @@ import com.google.android.exoplayer2.Timeline;
setIds); setIds);
period.windowIndex += firstWindowIndexInChild; period.windowIndex += firstWindowIndexInChild;
if (setIds) { if (setIds) {
period.uid = Pair.create(getChildUidByChildIndex(childIndex), period.uid); period.uid = getConcatenatedUid(getChildUidByChildIndex(childIndex), period.uid);
} }
return period; return period;
} }
@ -200,9 +230,8 @@ import com.google.android.exoplayer2.Timeline;
if (!(uid instanceof Pair)) { if (!(uid instanceof Pair)) {
return C.INDEX_UNSET; return C.INDEX_UNSET;
} }
Pair<?, ?> childUidAndPeriodUid = (Pair<?, ?>) uid; Object childUid = getChildTimelineUidFromConcatenatedUid(uid);
Object childUid = childUidAndPeriodUid.first; Object periodUid = getChildPeriodUidFromConcatenatedUid(uid);
Object periodUid = childUidAndPeriodUid.second;
int childIndex = getChildIndexByChildUid(childUid); int childIndex = getChildIndexByChildUid(childUid);
if (childIndex == C.INDEX_UNSET) { if (childIndex == C.INDEX_UNSET) {
return C.INDEX_UNSET; return C.INDEX_UNSET;
@ -218,7 +247,7 @@ import com.google.android.exoplayer2.Timeline;
int firstPeriodIndexInChild = getFirstPeriodIndexByChildIndex(childIndex); int firstPeriodIndexInChild = getFirstPeriodIndexByChildIndex(childIndex);
Object periodUidInChild = Object periodUidInChild =
getTimelineByChildIndex(childIndex).getUidOfPeriod(periodIndex - firstPeriodIndexInChild); getTimelineByChildIndex(childIndex).getUidOfPeriod(periodIndex - firstPeriodIndexInChild);
return Pair.create(getChildUidByChildIndex(childIndex), periodUidInChild); return getConcatenatedUid(getChildUidByChildIndex(childIndex), periodUidInChild);
} }
/** /**

View File

@ -60,8 +60,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
// Accessed on the playback thread. // Accessed on the playback thread.
private final List<MediaSourceHolder> mediaSourceHolders; private final List<MediaSourceHolder> mediaSourceHolders;
private final MediaSourceHolder query;
private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod; private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod;
private final Map<Object, MediaSourceHolder> mediaSourceByUid;
private final List<Runnable> pendingOnCompletionActions; private final List<Runnable> pendingOnCompletionActions;
private final boolean isAtomic; private final boolean isAtomic;
private final boolean useLazyPreparation; private final boolean useLazyPreparation;
@ -125,10 +125,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
} }
this.shuffleOrder = shuffleOrder.getLength() > 0 ? shuffleOrder.cloneAndClear() : shuffleOrder; this.shuffleOrder = shuffleOrder.getLength() > 0 ? shuffleOrder.cloneAndClear() : shuffleOrder;
this.mediaSourceByMediaPeriod = new IdentityHashMap<>(); this.mediaSourceByMediaPeriod = new IdentityHashMap<>();
this.mediaSourceByUid = new HashMap<>();
this.mediaSourcesPublic = new ArrayList<>(); this.mediaSourcesPublic = new ArrayList<>();
this.mediaSourceHolders = new ArrayList<>(); this.mediaSourceHolders = new ArrayList<>();
this.pendingOnCompletionActions = new ArrayList<>(); this.pendingOnCompletionActions = new ArrayList<>();
this.query = new MediaSourceHolder(/* mediaSource= */ null);
this.isAtomic = isAtomic; this.isAtomic = isAtomic;
this.useLazyPreparation = useLazyPreparation; this.useLazyPreparation = useLazyPreparation;
window = new Timeline.Window(); window = new Timeline.Window();
@ -451,8 +451,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
@Override @Override
public final MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { public final MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
int mediaSourceHolderIndex = findMediaSourceHolderByPeriodIndex(id.periodIndex); Object mediaSourceHolderUid = getMediaSourceHolderUid(id.periodUid);
MediaSourceHolder holder = mediaSourceHolders.get(mediaSourceHolderIndex); MediaSourceHolder holder = Assertions.checkNotNull(mediaSourceByUid.get(mediaSourceHolderUid));
DeferredMediaPeriod mediaPeriod = new DeferredMediaPeriod(holder.mediaSource, id, allocator); DeferredMediaPeriod mediaPeriod = new DeferredMediaPeriod(holder.mediaSource, id, allocator);
mediaSourceByMediaPeriod.put(mediaPeriod, holder); mediaSourceByMediaPeriod.put(mediaPeriod, holder);
holder.activeMediaPeriods.add(mediaPeriod); holder.activeMediaPeriods.add(mediaPeriod);
@ -460,8 +460,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
holder.hasStartedPreparing = true; holder.hasStartedPreparing = true;
prepareChildSource(holder, holder.mediaSource); prepareChildSource(holder, holder.mediaSource);
} else if (holder.isPrepared) { } else if (holder.isPrepared) {
MediaPeriodId idInSource = MediaPeriodId idInSource = id.copyWithPeriodUid(getChildPeriodUid(holder, id.periodUid));
id.copyWithPeriodIndex(id.periodIndex - holder.firstPeriodIndexInChild);
mediaPeriod.createPeriod(idInSource); mediaPeriod.createPeriod(idInSource);
} }
return mediaPeriod; return mediaPeriod;
@ -482,6 +481,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
public final void releaseSourceInternal() { public final void releaseSourceInternal() {
super.releaseSourceInternal(); super.releaseSourceInternal();
mediaSourceHolders.clear(); mediaSourceHolders.clear();
mediaSourceByUid.clear();
player = null; player = null;
playerApplicationHandler = null; playerApplicationHandler = null;
shuffleOrder = shuffleOrder.cloneAndClear(); shuffleOrder = shuffleOrder.cloneAndClear();
@ -506,8 +506,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
// by this media source. Otherwise it does not belong to this child source. // by this media source. Otherwise it does not belong to this child source.
if (mediaSourceHolder.activeMediaPeriods.get(i).id.windowSequenceNumber if (mediaSourceHolder.activeMediaPeriods.get(i).id.windowSequenceNumber
== mediaPeriodId.windowSequenceNumber) { == mediaPeriodId.windowSequenceNumber) {
return mediaPeriodId.copyWithPeriodIndex( Object periodUid = getPeriodUid(mediaSourceHolder, mediaPeriodId.periodUid);
mediaPeriodId.periodIndex + mediaSourceHolder.firstPeriodIndexInChild); return mediaPeriodId.copyWithPeriodUid(periodUid);
} }
} }
return null; return null;
@ -633,6 +633,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
newMediaSourceHolder.timeline.getWindowCount(), newMediaSourceHolder.timeline.getWindowCount(),
newMediaSourceHolder.timeline.getPeriodCount()); newMediaSourceHolder.timeline.getPeriodCount());
mediaSourceHolders.add(newIndex, newMediaSourceHolder); mediaSourceHolders.add(newIndex, newMediaSourceHolder);
mediaSourceByUid.put(newMediaSourceHolder.uid, newMediaSourceHolder);
if (!useLazyPreparation) { if (!useLazyPreparation) {
newMediaSourceHolder.hasStartedPreparing = true; newMediaSourceHolder.hasStartedPreparing = true;
prepareChildSource(newMediaSourceHolder, newMediaSourceHolder.mediaSource); prepareChildSource(newMediaSourceHolder, newMediaSourceHolder.mediaSource);
@ -672,8 +673,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
DeferredMediaPeriod deferredMediaPeriod = mediaSourceHolder.activeMediaPeriods.get(i); DeferredMediaPeriod deferredMediaPeriod = mediaSourceHolder.activeMediaPeriods.get(i);
deferredMediaPeriod.setDefaultPreparePositionUs(defaultPeriodPositionUs); deferredMediaPeriod.setDefaultPreparePositionUs(defaultPeriodPositionUs);
MediaPeriodId idInSource = MediaPeriodId idInSource =
deferredMediaPeriod.id.copyWithPeriodIndex( deferredMediaPeriod.id.copyWithPeriodUid(
deferredMediaPeriod.id.periodIndex - mediaSourceHolder.firstPeriodIndexInChild); getChildPeriodUid(mediaSourceHolder, deferredMediaPeriod.id.periodUid));
deferredMediaPeriod.createPeriod(idInSource); deferredMediaPeriod.createPeriod(idInSource);
} }
mediaSourceHolder.isPrepared = true; mediaSourceHolder.isPrepared = true;
@ -689,6 +690,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
private void removeMediaSourceInternal(int index) { private void removeMediaSourceInternal(int index) {
MediaSourceHolder holder = mediaSourceHolders.remove(index); MediaSourceHolder holder = mediaSourceHolders.remove(index);
mediaSourceByUid.remove(holder.uid);
Timeline oldTimeline = holder.timeline; Timeline oldTimeline = holder.timeline;
correctOffsets( correctOffsets(
index, index,
@ -727,17 +729,22 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
} }
} }
private int findMediaSourceHolderByPeriodIndex(int periodIndex) { /** Return uid of media source holder from period uid of concatenated source. */
query.firstPeriodIndexInChild = periodIndex; private static Object getMediaSourceHolderUid(Object periodUid) {
int index = Collections.binarySearch(mediaSourceHolders, query); return ConcatenatedTimeline.getChildTimelineUidFromConcatenatedUid(periodUid);
if (index < 0) { }
return -index - 2;
/** Return uid of child period from period uid of concatenated source. */
private static Object getChildPeriodUid(MediaSourceHolder holder, Object periodUid) {
Object childUid = ConcatenatedTimeline.getChildPeriodUidFromConcatenatedUid(periodUid);
return childUid.equals(DeferredTimeline.DUMMY_ID) ? holder.timeline.replacedId : childUid;
}
private static Object getPeriodUid(MediaSourceHolder holder, Object childPeriodUid) {
if (holder.timeline.replacedId.equals(childPeriodUid)) {
childPeriodUid = DeferredTimeline.DUMMY_ID;
} }
while (index < mediaSourceHolders.size() - 1 return ConcatenatedTimeline.getConcatenatedUid(holder.uid, childPeriodUid);
&& mediaSourceHolders.get(index + 1).firstPeriodIndexInChild == periodIndex) {
index++;
}
return index;
} }
/** Data class to hold playlist media sources together with meta data needed to process them. */ /** Data class to hold playlist media sources together with meta data needed to process them. */

View File

@ -371,7 +371,6 @@ public final class ExtractorMediaSource extends BaseMediaSource
@Override @Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
Assertions.checkArgument(id.periodIndex == 0);
DataSource dataSource = dataSourceFactory.createDataSource(); DataSource dataSource = dataSourceFactory.createDataSource();
if (transferListener != null) { if (transferListener != null) {
dataSource.addTransferListener(transferListener); dataSource.addTransferListener(transferListener);

View File

@ -40,8 +40,6 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> {
private final Map<MediaPeriodId, MediaPeriodId> childMediaPeriodIdToMediaPeriodId; private final Map<MediaPeriodId, MediaPeriodId> childMediaPeriodIdToMediaPeriodId;
private final Map<MediaPeriod, MediaPeriodId> mediaPeriodToChildMediaPeriodId; private final Map<MediaPeriod, MediaPeriodId> mediaPeriodToChildMediaPeriodId;
private int childPeriodCount;
/** /**
* Loops the provided source indefinitely. Note that it is usually better to use * Loops the provided source indefinitely. Note that it is usually better to use
* {@link ExoPlayer#setRepeatMode(int)}. * {@link ExoPlayer#setRepeatMode(int)}.
@ -80,7 +78,8 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> {
if (loopCount == Integer.MAX_VALUE) { if (loopCount == Integer.MAX_VALUE) {
return childSource.createPeriod(id, allocator); return childSource.createPeriod(id, allocator);
} }
MediaPeriodId childMediaPeriodId = id.copyWithPeriodIndex(id.periodIndex % childPeriodCount); Object childPeriodUid = LoopingTimeline.getChildPeriodUidFromConcatenatedUid(id.periodUid);
MediaPeriodId childMediaPeriodId = id.copyWithPeriodUid(childPeriodUid);
childMediaPeriodIdToMediaPeriodId.put(childMediaPeriodId, id); childMediaPeriodIdToMediaPeriodId.put(childMediaPeriodId, id);
MediaPeriod mediaPeriod = childSource.createPeriod(childMediaPeriodId, allocator); MediaPeriod mediaPeriod = childSource.createPeriod(childMediaPeriodId, allocator);
mediaPeriodToChildMediaPeriodId.put(mediaPeriod, childMediaPeriodId); mediaPeriodToChildMediaPeriodId.put(mediaPeriod, childMediaPeriodId);
@ -96,16 +95,9 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> {
} }
} }
@Override
public void releaseSourceInternal() {
super.releaseSourceInternal();
childPeriodCount = 0;
}
@Override @Override
protected void onChildSourceInfoRefreshed( protected void onChildSourceInfoRefreshed(
Void id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest) { Void id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest) {
childPeriodCount = timeline.getPeriodCount();
Timeline loopingTimeline = Timeline loopingTimeline =
loopCount != Integer.MAX_VALUE loopCount != Integer.MAX_VALUE
? new LoopingTimeline(timeline, loopCount) ? new LoopingTimeline(timeline, loopCount)

View File

@ -66,10 +66,8 @@ public interface MediaSource {
*/ */
final class MediaPeriodId { final class MediaPeriodId {
/** /** The unique id of the timeline period. */
* The timeline period index. public final Object periodUid;
*/
public final int periodIndex;
/** /**
* If the media period is in an ad group, the index of the ad group in the period. * If the media period is in an ad group, the index of the ad group in the period.
@ -103,72 +101,70 @@ public interface MediaSource {
* Creates a media period identifier for a dummy period which is not part of a buffered sequence * Creates a media period identifier for a dummy period which is not part of a buffered sequence
* of windows. * of windows.
* *
* @param periodIndex The period index. * @param periodUid The unique id of the timeline period.
*/ */
public MediaPeriodId(int periodIndex) { public MediaPeriodId(Object periodUid) {
this(periodIndex, C.INDEX_UNSET); this(periodUid, C.INDEX_UNSET);
} }
/** /**
* Creates a media period identifier for the specified period in the timeline. * Creates a media period identifier for the specified period in the timeline.
* *
* @param periodIndex The timeline period index. * @param periodUid The unique id of the timeline period.
* @param windowSequenceNumber The sequence number of the window in the buffered sequence of * @param windowSequenceNumber The sequence number of the window in the buffered sequence of
* windows this media period is part of. * windows this media period is part of.
*/ */
public MediaPeriodId(int periodIndex, long windowSequenceNumber) { public MediaPeriodId(Object periodUid, long windowSequenceNumber) {
this(periodIndex, C.INDEX_UNSET, C.INDEX_UNSET, windowSequenceNumber, C.TIME_END_OF_SOURCE); this(periodUid, C.INDEX_UNSET, C.INDEX_UNSET, windowSequenceNumber, C.TIME_END_OF_SOURCE);
} }
/** /**
* Creates a media period identifier for the specified clipped period in the timeline. * Creates a media period identifier for the specified clipped period in the timeline.
* *
* @param periodIndex The timeline period index. * @param periodUid The unique id of the timeline period.
* @param windowSequenceNumber The sequence number of the window in the buffered sequence of * @param windowSequenceNumber The sequence number of the window in the buffered sequence of
* windows this media period is part of. * windows this media period is part of.
* @param endPositionUs The end position of the media period within the timeline period, in * @param endPositionUs The end position of the media period within the timeline period, in
* microseconds. * microseconds.
*/ */
public MediaPeriodId(int periodIndex, long windowSequenceNumber, long endPositionUs) { public MediaPeriodId(Object periodUid, long windowSequenceNumber, long endPositionUs) {
this(periodIndex, C.INDEX_UNSET, C.INDEX_UNSET, windowSequenceNumber, endPositionUs); this(periodUid, C.INDEX_UNSET, C.INDEX_UNSET, windowSequenceNumber, endPositionUs);
} }
/** /**
* Creates a media period identifier that identifies an ad within an ad group at the specified * Creates a media period identifier that identifies an ad within an ad group at the specified
* timeline period. * timeline period.
* *
* @param periodIndex The index of the timeline period that contains the ad group. * @param periodUid The unique id of the timeline period that contains the ad group.
* @param adGroupIndex The index of the ad group. * @param adGroupIndex The index of the ad group.
* @param adIndexInAdGroup The index of the ad in the ad group. * @param adIndexInAdGroup The index of the ad in the ad group.
* @param windowSequenceNumber The sequence number of the window in the buffered sequence of * @param windowSequenceNumber The sequence number of the window in the buffered sequence of
* windows this media period is part of. * windows this media period is part of.
*/ */
public MediaPeriodId( public MediaPeriodId(
int periodIndex, int adGroupIndex, int adIndexInAdGroup, long windowSequenceNumber) { Object periodUid, int adGroupIndex, int adIndexInAdGroup, long windowSequenceNumber) {
this(periodIndex, adGroupIndex, adIndexInAdGroup, windowSequenceNumber, C.TIME_END_OF_SOURCE); this(periodUid, adGroupIndex, adIndexInAdGroup, windowSequenceNumber, C.TIME_END_OF_SOURCE);
} }
private MediaPeriodId( private MediaPeriodId(
int periodIndex, Object periodUid,
int adGroupIndex, int adGroupIndex,
int adIndexInAdGroup, int adIndexInAdGroup,
long windowSequenceNumber, long windowSequenceNumber,
long endPositionUs) { long endPositionUs) {
this.periodIndex = periodIndex; this.periodUid = periodUid;
this.adGroupIndex = adGroupIndex; this.adGroupIndex = adGroupIndex;
this.adIndexInAdGroup = adIndexInAdGroup; this.adIndexInAdGroup = adIndexInAdGroup;
this.windowSequenceNumber = windowSequenceNumber; this.windowSequenceNumber = windowSequenceNumber;
this.endPositionUs = endPositionUs; this.endPositionUs = endPositionUs;
} }
/** /** Returns a copy of this period identifier but with {@code newPeriodUid} as its period uid. */
* Returns a copy of this period identifier but with {@code newPeriodIndex} as its period index. public MediaPeriodId copyWithPeriodUid(Object newPeriodUid) {
*/ return periodUid.equals(newPeriodUid)
public MediaPeriodId copyWithPeriodIndex(int newPeriodIndex) {
return periodIndex == newPeriodIndex
? this ? this
: new MediaPeriodId( : new MediaPeriodId(
newPeriodIndex, adGroupIndex, adIndexInAdGroup, windowSequenceNumber, endPositionUs); newPeriodUid, adGroupIndex, adIndexInAdGroup, windowSequenceNumber, endPositionUs);
} }
/** /**
@ -188,7 +184,7 @@ public interface MediaSource {
} }
MediaPeriodId periodId = (MediaPeriodId) obj; MediaPeriodId periodId = (MediaPeriodId) obj;
return periodIndex == periodId.periodIndex return periodUid.equals(periodId.periodUid)
&& adGroupIndex == periodId.adGroupIndex && adGroupIndex == periodId.adGroupIndex
&& adIndexInAdGroup == periodId.adIndexInAdGroup && adIndexInAdGroup == periodId.adIndexInAdGroup
&& windowSequenceNumber == periodId.windowSequenceNumber && windowSequenceNumber == periodId.windowSequenceNumber
@ -198,14 +194,13 @@ public interface MediaSource {
@Override @Override
public int hashCode() { public int hashCode() {
int result = 17; int result = 17;
result = 31 * result + periodIndex; result = 31 * result + periodUid.hashCode();
result = 31 * result + adGroupIndex; result = 31 * result + adGroupIndex;
result = 31 * result + adIndexInAdGroup; result = 31 * result + adIndexInAdGroup;
result = 31 * result + (int) windowSequenceNumber; result = 31 * result + (int) windowSequenceNumber;
result = 31 * result + (int) endPositionUs; result = 31 * result + (int) endPositionUs;
return result; return result;
} }
} }
/** /**

View File

@ -68,10 +68,10 @@ public final class MergingMediaSource extends CompositeMediaSource<Integer> {
private static final int PERIOD_COUNT_UNSET = -1; private static final int PERIOD_COUNT_UNSET = -1;
private final MediaSource[] mediaSources; private final MediaSource[] mediaSources;
private final Timeline[] timelines;
private final ArrayList<MediaSource> pendingTimelineSources; private final ArrayList<MediaSource> pendingTimelineSources;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private Timeline primaryTimeline;
private Object primaryManifest; private Object primaryManifest;
private int periodCount; private int periodCount;
private IllegalMergeException mergeError; private IllegalMergeException mergeError;
@ -95,6 +95,7 @@ public final class MergingMediaSource extends CompositeMediaSource<Integer> {
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
pendingTimelineSources = new ArrayList<>(Arrays.asList(mediaSources)); pendingTimelineSources = new ArrayList<>(Arrays.asList(mediaSources));
periodCount = PERIOD_COUNT_UNSET; periodCount = PERIOD_COUNT_UNSET;
timelines = new Timeline[mediaSources.length];
} }
@Override @Override
@ -119,8 +120,11 @@ public final class MergingMediaSource extends CompositeMediaSource<Integer> {
@Override @Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
MediaPeriod[] periods = new MediaPeriod[mediaSources.length]; MediaPeriod[] periods = new MediaPeriod[mediaSources.length];
int periodIndex = timelines[0].getIndexOfPeriod(id.periodUid);
for (int i = 0; i < periods.length; i++) { for (int i = 0; i < periods.length; i++) {
periods[i] = mediaSources[i].createPeriod(id, allocator); MediaPeriodId childMediaPeriodId =
id.copyWithPeriodUid(timelines[i].getUidOfPeriod(periodIndex));
periods[i] = mediaSources[i].createPeriod(childMediaPeriodId, allocator);
} }
return new MergingMediaPeriod(compositeSequenceableLoaderFactory, periods); return new MergingMediaPeriod(compositeSequenceableLoaderFactory, periods);
} }
@ -136,7 +140,7 @@ public final class MergingMediaSource extends CompositeMediaSource<Integer> {
@Override @Override
public void releaseSourceInternal() { public void releaseSourceInternal() {
super.releaseSourceInternal(); super.releaseSourceInternal();
primaryTimeline = null; Arrays.fill(timelines, null);
primaryManifest = null; primaryManifest = null;
periodCount = PERIOD_COUNT_UNSET; periodCount = PERIOD_COUNT_UNSET;
mergeError = null; mergeError = null;
@ -154,12 +158,12 @@ public final class MergingMediaSource extends CompositeMediaSource<Integer> {
return; return;
} }
pendingTimelineSources.remove(mediaSource); pendingTimelineSources.remove(mediaSource);
timelines[id] = timeline;
if (mediaSource == mediaSources[0]) { if (mediaSource == mediaSources[0]) {
primaryTimeline = timeline;
primaryManifest = manifest; primaryManifest = manifest;
} }
if (pendingTimelineSources.isEmpty()) { if (pendingTimelineSources.isEmpty()) {
refreshSourceInfo(primaryTimeline, primaryManifest); refreshSourceInfo(timelines[0], primaryManifest);
} }
} }

View File

@ -308,7 +308,6 @@ public final class SingleSampleMediaSource extends BaseMediaSource {
@Override @Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
Assertions.checkArgument(id.periodIndex == 0);
return new SingleSampleMediaPeriod( return new SingleSampleMediaPeriod(
dataSpec, dataSpec,
dataSourceFactory, dataSourceFactory,

View File

@ -176,7 +176,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
// Used to identify the content "child" source for CompositeMediaSource. // Used to identify the content "child" source for CompositeMediaSource.
private static final MediaPeriodId DUMMY_CONTENT_MEDIA_PERIOD_ID = private static final MediaPeriodId DUMMY_CONTENT_MEDIA_PERIOD_ID =
new MediaPeriodId(/* periodIndex= */ 0); new MediaPeriodId(/* periodUid= */ new Object());
private final MediaSource contentMediaSource; private final MediaSource contentMediaSource;
private final MediaSourceFactory adMediaSourceFactory; private final MediaSourceFactory adMediaSourceFactory;
@ -194,7 +194,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
private Object contentManifest; private Object contentManifest;
private AdPlaybackState adPlaybackState; private AdPlaybackState adPlaybackState;
private MediaSource[][] adGroupMediaSources; private MediaSource[][] adGroupMediaSources;
private long[][] adDurationsUs; private Timeline[][] adGroupTimelines;
/** /**
* Constructs a new source that inserts ads linearly with the content specified by {@code * Constructs a new source that inserts ads linearly with the content specified by {@code
@ -309,7 +309,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
deferredMediaPeriodByAdMediaSource = new HashMap<>(); deferredMediaPeriodByAdMediaSource = new HashMap<>();
period = new Timeline.Period(); period = new Timeline.Period();
adGroupMediaSources = new MediaSource[0][]; adGroupMediaSources = new MediaSource[0][];
adDurationsUs = new long[0][]; adGroupTimelines = new Timeline[0][];
adsLoader.setSupportedContentTypes(adMediaSourceFactory.getSupportedTypes()); adsLoader.setSupportedContentTypes(adMediaSourceFactory.getSupportedTypes());
} }
@ -341,8 +341,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
int adCount = adIndexInAdGroup + 1; int adCount = adIndexInAdGroup + 1;
adGroupMediaSources[adGroupIndex] = adGroupMediaSources[adGroupIndex] =
Arrays.copyOf(adGroupMediaSources[adGroupIndex], adCount); Arrays.copyOf(adGroupMediaSources[adGroupIndex], adCount);
adDurationsUs[adGroupIndex] = Arrays.copyOf(adDurationsUs[adGroupIndex], adCount); adGroupTimelines[adGroupIndex] = Arrays.copyOf(adGroupTimelines[adGroupIndex], adCount);
Arrays.fill(adDurationsUs[adGroupIndex], oldAdCount, adCount, C.TIME_UNSET);
} }
adGroupMediaSources[adGroupIndex][adIndexInAdGroup] = adMediaSource; adGroupMediaSources[adGroupIndex][adIndexInAdGroup] = adMediaSource;
deferredMediaPeriodByAdMediaSource.put(adMediaSource, new ArrayList<>()); deferredMediaPeriodByAdMediaSource.put(adMediaSource, new ArrayList<>());
@ -354,8 +353,9 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
new AdPrepareErrorListener(adUri, adGroupIndex, adIndexInAdGroup)); new AdPrepareErrorListener(adUri, adGroupIndex, adIndexInAdGroup));
List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.get(mediaSource); List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.get(mediaSource);
if (mediaPeriods == null) { if (mediaPeriods == null) {
MediaPeriodId adSourceMediaPeriodId = Object periodUid =
new MediaPeriodId(/* periodIndex= */ 0, id.windowSequenceNumber); adGroupTimelines[adGroupIndex][adIndexInAdGroup].getUidOfPeriod(/* periodIndex= */ 0);
MediaPeriodId adSourceMediaPeriodId = new MediaPeriodId(periodUid, id.windowSequenceNumber);
deferredMediaPeriod.createPeriod(adSourceMediaPeriodId); deferredMediaPeriod.createPeriod(adSourceMediaPeriodId);
} else { } else {
// Keep track of the deferred media period so it can be populated with the real media period // Keep track of the deferred media period so it can be populated with the real media period
@ -391,7 +391,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
contentManifest = null; contentManifest = null;
adPlaybackState = null; adPlaybackState = null;
adGroupMediaSources = new MediaSource[0][]; adGroupMediaSources = new MediaSource[0][];
adDurationsUs = new long[0][]; adGroupTimelines = new Timeline[0][];
mainHandler.post(adsLoader::detachPlayer); mainHandler.post(adsLoader::detachPlayer);
} }
@ -424,8 +424,8 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
if (this.adPlaybackState == null) { if (this.adPlaybackState == null) {
adGroupMediaSources = new MediaSource[adPlaybackState.adGroupCount][]; adGroupMediaSources = new MediaSource[adPlaybackState.adGroupCount][];
Arrays.fill(adGroupMediaSources, new MediaSource[0]); Arrays.fill(adGroupMediaSources, new MediaSource[0]);
adDurationsUs = new long[adPlaybackState.adGroupCount][]; adGroupTimelines = new Timeline[adPlaybackState.adGroupCount][];
Arrays.fill(adDurationsUs, new long[0]); Arrays.fill(adGroupTimelines, new Timeline[0]);
} }
this.adPlaybackState = adPlaybackState; this.adPlaybackState = adPlaybackState;
maybeUpdateSourceInfo(); maybeUpdateSourceInfo();
@ -440,13 +440,14 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
private void onAdSourceInfoRefreshed(MediaSource mediaSource, int adGroupIndex, private void onAdSourceInfoRefreshed(MediaSource mediaSource, int adGroupIndex,
int adIndexInAdGroup, Timeline timeline) { int adIndexInAdGroup, Timeline timeline) {
Assertions.checkArgument(timeline.getPeriodCount() == 1); Assertions.checkArgument(timeline.getPeriodCount() == 1);
adDurationsUs[adGroupIndex][adIndexInAdGroup] = timeline.getPeriod(0, period).getDurationUs(); adGroupTimelines[adGroupIndex][adIndexInAdGroup] = timeline;
List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.remove(mediaSource); List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.remove(mediaSource);
if (mediaPeriods != null) { if (mediaPeriods != null) {
Object periodUid = timeline.getUidOfPeriod(/* periodIndex= */ 0);
for (int i = 0; i < mediaPeriods.size(); i++) { for (int i = 0; i < mediaPeriods.size(); i++) {
DeferredMediaPeriod mediaPeriod = mediaPeriods.get(i); DeferredMediaPeriod mediaPeriod = mediaPeriods.get(i);
MediaPeriodId adSourceMediaPeriodId = MediaPeriodId adSourceMediaPeriodId =
new MediaPeriodId(/* periodIndex= */ 0, mediaPeriod.id.windowSequenceNumber); new MediaPeriodId(periodUid, mediaPeriod.id.windowSequenceNumber);
mediaPeriod.createPeriod(adSourceMediaPeriodId); mediaPeriod.createPeriod(adSourceMediaPeriodId);
} }
} }
@ -455,7 +456,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
private void maybeUpdateSourceInfo() { private void maybeUpdateSourceInfo() {
if (adPlaybackState != null && contentTimeline != null) { if (adPlaybackState != null && contentTimeline != null) {
adPlaybackState = adPlaybackState.withAdDurationsUs(adDurationsUs); adPlaybackState = adPlaybackState.withAdDurationsUs(getAdDurations(adGroupTimelines, period));
Timeline timeline = Timeline timeline =
adPlaybackState.adGroupCount == 0 adPlaybackState.adGroupCount == 0
? contentTimeline ? contentTimeline
@ -464,6 +465,20 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
} }
} }
private static long[][] getAdDurations(Timeline[][] adTimelines, Timeline.Period period) {
long[][] adDurations = new long[adTimelines.length][];
for (int i = 0; i < adTimelines.length; i++) {
adDurations[i] = new long[adTimelines[i].length];
for (int j = 0; j < adTimelines[i].length; j++) {
adDurations[i][j] =
adTimelines[i][j] == null
? C.TIME_UNSET
: adTimelines[i][j].getPeriod(/* periodIndex= */ 0, period).getDurationUs();
}
}
return adDurations;
}
/** Listener for component events. All methods are called on the main thread. */ /** Listener for component events. All methods are called on the main thread. */
private final class ComponentListener implements AdsLoader.EventListener { private final class ComponentListener implements AdsLoader.EventListener {

View File

@ -456,7 +456,8 @@ public class EventLogger implements AnalyticsListener {
private String getEventTimeString(EventTime eventTime) { private String getEventTimeString(EventTime eventTime) {
String windowPeriodString = "window=" + eventTime.windowIndex; String windowPeriodString = "window=" + eventTime.windowIndex;
if (eventTime.mediaPeriodId != null) { if (eventTime.mediaPeriodId != null) {
windowPeriodString += ", period=" + eventTime.mediaPeriodId.periodIndex; windowPeriodString +=
", period=" + eventTime.timeline.getIndexOfPeriod(eventTime.mediaPeriodId.periodUid);
if (eventTime.mediaPeriodId.isAd()) { if (eventTime.mediaPeriodId.isAd()) {
windowPeriodString += ", adGroup=" + eventTime.mediaPeriodId.adGroupIndex; windowPeriodString += ", adGroup=" + eventTime.mediaPeriodId.adGroupIndex;
windowPeriodString += ", ad=" + eventTime.mediaPeriodId.adIndexInAdGroup; windowPeriodString += ", ad=" + eventTime.mediaPeriodId.adIndexInAdGroup;

View File

@ -187,10 +187,28 @@ public final class ExoPlayerTest {
@Test @Test
public void testReadAheadToEndDoesNotResetRenderer() throws Exception { public void testReadAheadToEndDoesNotResetRenderer() throws Exception {
// Use sufficiently short periods to ensure the player attempts to read all at once. // Use sufficiently short periods to ensure the player attempts to read all at once.
TimelineWindowDefinition windowDefinition = TimelineWindowDefinition windowDefinition0 =
new TimelineWindowDefinition( new TimelineWindowDefinition(
/* isSeekable= */ false, /* isDynamic= */ false, /* durationUs= */ 100_000); /* periodCount= */ 1,
Timeline timeline = new FakeTimeline(windowDefinition, windowDefinition, windowDefinition); /* id= */ 0,
/* isSeekable= */ false,
/* isDynamic= */ false,
/* durationUs= */ 100_000);
TimelineWindowDefinition windowDefinition1 =
new TimelineWindowDefinition(
/* periodCount= */ 1,
/* id= */ 1,
/* isSeekable= */ false,
/* isDynamic= */ false,
/* durationUs= */ 100_000);
TimelineWindowDefinition windowDefinition2 =
new TimelineWindowDefinition(
/* periodCount= */ 1,
/* id= */ 2,
/* isSeekable= */ false,
/* isDynamic= */ false,
/* durationUs= */ 100_000);
Timeline timeline = new FakeTimeline(windowDefinition0, windowDefinition1, windowDefinition2);
final FakeRenderer videoRenderer = new FakeRenderer(Builder.VIDEO_FORMAT); final FakeRenderer videoRenderer = new FakeRenderer(Builder.VIDEO_FORMAT);
FakeMediaClockRenderer audioRenderer = FakeMediaClockRenderer audioRenderer =
new FakeMediaClockRenderer(Builder.AUDIO_FORMAT) { new FakeMediaClockRenderer(Builder.AUDIO_FORMAT) {
@ -2019,9 +2037,12 @@ public final class ExoPlayerTest {
// Assert that the second period was re-created from the new timeline. // Assert that the second period was re-created from the new timeline.
assertThat(mediaSource.getCreatedMediaPeriods()) assertThat(mediaSource.getCreatedMediaPeriods())
.containsExactly( .containsExactly(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0), new MediaPeriodId(
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 1), timeline1.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 2)) new MediaPeriodId(
timeline1.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1),
new MediaPeriodId(
timeline2.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 2))
.inOrder(); .inOrder();
} }
@ -2057,10 +2078,14 @@ public final class ExoPlayerTest {
testRunner.assertPlayedPeriodIndices(0, 1, 0, 1); testRunner.assertPlayedPeriodIndices(0, 1, 0, 1);
assertThat(mediaSource.getCreatedMediaPeriods()) assertThat(mediaSource.getCreatedMediaPeriods())
.containsAllOf( .containsAllOf(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0), new MediaPeriodId(
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 0)); timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0),
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0));
assertThat(mediaSource.getCreatedMediaPeriods()) assertThat(mediaSource.getCreatedMediaPeriods())
.doesNotContain(new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 1)); .doesNotContain(
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1));
} }
@Test @Test

View File

@ -108,28 +108,16 @@ public final class AnalyticsCollectorTest {
new EventWindowAndPeriodId(/* windowIndex= */ 0, /* mediaPeriodId= */ null); new EventWindowAndPeriodId(/* windowIndex= */ 0, /* mediaPeriodId= */ null);
private static final EventWindowAndPeriodId WINDOW_1 = private static final EventWindowAndPeriodId WINDOW_1 =
new EventWindowAndPeriodId(/* windowIndex= */ 1, /* mediaPeriodId= */ null); new EventWindowAndPeriodId(/* windowIndex= */ 1, /* mediaPeriodId= */ null);
private static final EventWindowAndPeriodId PERIOD_0 =
new EventWindowAndPeriodId( private EventWindowAndPeriodId period0;
/* windowIndex= */ 0, private EventWindowAndPeriodId period1;
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); private EventWindowAndPeriodId period0Seq0;
private static final EventWindowAndPeriodId PERIOD_1 = private EventWindowAndPeriodId period1Seq1;
new EventWindowAndPeriodId( private EventWindowAndPeriodId period0Seq1;
/* windowIndex= */ 1, private EventWindowAndPeriodId period1Seq0;
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 1)); private EventWindowAndPeriodId period1Seq2;
private static final EventWindowAndPeriodId PERIOD_0_SEQ_0 = PERIOD_0; private EventWindowAndPeriodId window0Period1Seq0;
private static final EventWindowAndPeriodId PERIOD_1_SEQ_1 = PERIOD_1; private EventWindowAndPeriodId window1Period0Seq1;
private static final EventWindowAndPeriodId PERIOD_0_SEQ_1 =
new EventWindowAndPeriodId(
/* windowIndex= */ 0,
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 1));
private static final EventWindowAndPeriodId PERIOD_1_SEQ_0 =
new EventWindowAndPeriodId(
/* windowIndex= */ 1,
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 0));
private static final EventWindowAndPeriodId PERIOD_1_SEQ_2 =
new EventWindowAndPeriodId(
/* windowIndex= */ 1,
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 2));
@Test @Test
public void testEmptyTimeline() throws Exception { public void testEmptyTimeline() throws Exception {
@ -155,34 +143,35 @@ public final class AnalyticsCollectorTest {
Builder.AUDIO_FORMAT); Builder.AUDIO_FORMAT);
TestAnalyticsListener listener = runAnalyticsTest(mediaSource); TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
populateEventIds(SINGLE_PERIOD_TIMELINE);
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED)) assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
.containsExactly( .containsExactly(
WINDOW_0 /* setPlayWhenReady */, WINDOW_0 /* setPlayWhenReady */,
WINDOW_0 /* BUFFERING */, WINDOW_0 /* BUFFERING */,
PERIOD_0 /* READY */, period0 /* READY */,
PERIOD_0 /* ENDED */); period0 /* ENDED */);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)).containsExactly(WINDOW_0); assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)).containsExactly(WINDOW_0);
assertThat(listener.getEvents(EVENT_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_LOADING_CHANGED))
.containsExactly(PERIOD_0 /* started */, PERIOD_0 /* stopped */); .containsExactly(period0 /* started */, period0 /* stopped */);
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_LOAD_STARTED)) assertThat(listener.getEvents(EVENT_LOAD_STARTED))
.containsExactly(WINDOW_0 /* manifest */, PERIOD_0 /* media */); .containsExactly(WINDOW_0 /* manifest */, period0 /* media */);
assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) assertThat(listener.getEvents(EVENT_LOAD_COMPLETED))
.containsExactly(WINDOW_0 /* manifest */, PERIOD_0 /* media */); .containsExactly(WINDOW_0 /* manifest */, period0 /* media */);
assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED))
.containsExactly(PERIOD_0 /* audio */, PERIOD_0 /* video */); .containsExactly(period0 /* audio */, period0 /* video */);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_READING_STARTED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_READING_STARTED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_DECODER_ENABLED)) assertThat(listener.getEvents(EVENT_DECODER_ENABLED))
.containsExactly(PERIOD_0 /* audio */, PERIOD_0 /* video */); .containsExactly(period0 /* audio */, period0 /* video */);
assertThat(listener.getEvents(EVENT_DECODER_INIT)) assertThat(listener.getEvents(EVENT_DECODER_INIT))
.containsExactly(PERIOD_0 /* audio */, PERIOD_0 /* video */); .containsExactly(period0 /* audio */, period0 /* video */);
assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED))
.containsExactly(PERIOD_0 /* audio */, PERIOD_0 /* video */); .containsExactly(period0 /* audio */, period0 /* video */);
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -202,47 +191,48 @@ public final class AnalyticsCollectorTest {
Builder.AUDIO_FORMAT)); Builder.AUDIO_FORMAT));
TestAnalyticsListener listener = runAnalyticsTest(mediaSource); TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
populateEventIds(listener.lastReportedTimeline);
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED)) assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
.containsExactly( .containsExactly(
WINDOW_0 /* setPlayWhenReady */, WINDOW_0 /* setPlayWhenReady */,
WINDOW_0 /* BUFFERING */, WINDOW_0 /* BUFFERING */,
PERIOD_0 /* READY */, period0 /* READY */,
PERIOD_1 /* ENDED */); period1 /* ENDED */);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)).containsExactly(WINDOW_0); assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)).containsExactly(WINDOW_0);
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(PERIOD_1); assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_LOADING_CHANGED))
.containsExactly(PERIOD_0, PERIOD_0, PERIOD_0, PERIOD_0); .containsExactly(period0, period0, period0, period0);
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(PERIOD_0, PERIOD_1); assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(period0, period1);
assertThat(listener.getEvents(EVENT_LOAD_STARTED)) assertThat(listener.getEvents(EVENT_LOAD_STARTED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0 /* media */, period0 /* media */,
WINDOW_1 /* manifest */, WINDOW_1 /* manifest */,
PERIOD_1 /* media */); period1 /* media */);
assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) assertThat(listener.getEvents(EVENT_LOAD_COMPLETED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0 /* media */, period0 /* media */,
WINDOW_1 /* manifest */, WINDOW_1 /* manifest */,
PERIOD_1 /* media */); period1 /* media */);
assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED))
.containsExactly( .containsExactly(
PERIOD_0 /* audio */, PERIOD_0 /* video */, PERIOD_1 /* audio */, PERIOD_1 /* video */); period0 /* audio */, period0 /* video */, period1 /* audio */, period1 /* video */);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)).containsExactly(PERIOD_0, PERIOD_1); assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)).containsExactly(period0, period1);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_READING_STARTED)).containsExactly(PERIOD_0, PERIOD_1); assertThat(listener.getEvents(EVENT_READING_STARTED)).containsExactly(period0, period1);
assertThat(listener.getEvents(EVENT_DECODER_ENABLED)) assertThat(listener.getEvents(EVENT_DECODER_ENABLED))
.containsExactly(PERIOD_0 /* audio */, PERIOD_0 /* video */); .containsExactly(period0 /* audio */, period0 /* video */);
assertThat(listener.getEvents(EVENT_DECODER_INIT)) assertThat(listener.getEvents(EVENT_DECODER_INIT))
.containsExactly( .containsExactly(
PERIOD_0 /* audio */, PERIOD_0 /* video */, PERIOD_1 /* audio */, PERIOD_1 /* video */); period0 /* audio */, period0 /* video */, period1 /* audio */, period1 /* video */);
assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED))
.containsExactly( .containsExactly(
PERIOD_0 /* audio */, PERIOD_0 /* video */, PERIOD_1 /* audio */, PERIOD_1 /* video */); period0 /* audio */, period0 /* video */, period1 /* audio */, period1 /* video */);
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(PERIOD_1); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -255,47 +245,48 @@ public final class AnalyticsCollectorTest {
SINGLE_PERIOD_TIMELINE, /* manifest= */ null, Builder.AUDIO_FORMAT)); SINGLE_PERIOD_TIMELINE, /* manifest= */ null, Builder.AUDIO_FORMAT));
TestAnalyticsListener listener = runAnalyticsTest(mediaSource); TestAnalyticsListener listener = runAnalyticsTest(mediaSource);
populateEventIds(listener.lastReportedTimeline);
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED)) assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
.containsExactly( .containsExactly(
WINDOW_0 /* setPlayWhenReady */, WINDOW_0 /* setPlayWhenReady */,
WINDOW_0 /* BUFFERING */, WINDOW_0 /* BUFFERING */,
PERIOD_0 /* READY */, period0 /* READY */,
PERIOD_1 /* BUFFERING */, period1 /* BUFFERING */,
PERIOD_1 /* READY */, period1 /* READY */,
PERIOD_1 /* ENDED */); period1 /* ENDED */);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)).containsExactly(WINDOW_0); assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)).containsExactly(WINDOW_0);
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(PERIOD_1); assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_LOADING_CHANGED))
.containsExactly(PERIOD_0, PERIOD_0, PERIOD_0, PERIOD_0); .containsExactly(period0, period0, period0, period0);
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(PERIOD_0, PERIOD_1); assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(period0, period1);
assertThat(listener.getEvents(EVENT_LOAD_STARTED)) assertThat(listener.getEvents(EVENT_LOAD_STARTED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0 /* media */, period0 /* media */,
WINDOW_1 /* manifest */, WINDOW_1 /* manifest */,
PERIOD_1 /* media */); period1 /* media */);
assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) assertThat(listener.getEvents(EVENT_LOAD_COMPLETED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0 /* media */, period0 /* media */,
WINDOW_1 /* manifest */, WINDOW_1 /* manifest */,
PERIOD_1 /* media */); period1 /* media */);
assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED))
.containsExactly(PERIOD_0 /* video */, PERIOD_1 /* audio */); .containsExactly(period0 /* video */, period1 /* audio */);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)).containsExactly(PERIOD_0, PERIOD_1); assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)).containsExactly(period0, period1);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_READING_STARTED)).containsExactly(PERIOD_0, PERIOD_1); assertThat(listener.getEvents(EVENT_READING_STARTED)).containsExactly(period0, period1);
assertThat(listener.getEvents(EVENT_DECODER_ENABLED)) assertThat(listener.getEvents(EVENT_DECODER_ENABLED))
.containsExactly(PERIOD_0 /* video */, PERIOD_1 /* audio */); .containsExactly(period0 /* video */, period1 /* audio */);
assertThat(listener.getEvents(EVENT_DECODER_INIT)) assertThat(listener.getEvents(EVENT_DECODER_INIT))
.containsExactly(PERIOD_0 /* video */, PERIOD_1 /* audio */); .containsExactly(period0 /* video */, period1 /* audio */);
assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED))
.containsExactly(PERIOD_0 /* video */, PERIOD_1 /* audio */); .containsExactly(period0 /* video */, period1 /* audio */);
assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(PERIOD_1); assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -315,51 +306,52 @@ public final class AnalyticsCollectorTest {
.build(); .build();
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule); TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
populateEventIds(listener.lastReportedTimeline);
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED)) assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
.containsExactly( .containsExactly(
WINDOW_0 /* setPlayWhenReady=true */, WINDOW_0 /* setPlayWhenReady=true */,
WINDOW_0 /* BUFFERING */, WINDOW_0 /* BUFFERING */,
WINDOW_0 /* setPlayWhenReady=false */, WINDOW_0 /* setPlayWhenReady=false */,
PERIOD_0 /* READY */, period0 /* READY */,
PERIOD_1 /* BUFFERING */, period1 /* BUFFERING */,
PERIOD_1 /* READY */, period1 /* READY */,
PERIOD_1 /* setPlayWhenReady=true */, period1 /* setPlayWhenReady=true */,
PERIOD_1 /* ENDED */); period1 /* ENDED */);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)).containsExactly(WINDOW_0); assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)).containsExactly(WINDOW_0);
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(PERIOD_1); assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(PERIOD_1); assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(period1);
List<EventWindowAndPeriodId> loadingEvents = listener.getEvents(EVENT_LOADING_CHANGED); List<EventWindowAndPeriodId> loadingEvents = listener.getEvents(EVENT_LOADING_CHANGED);
assertThat(loadingEvents).hasSize(4); assertThat(loadingEvents).hasSize(4);
assertThat(loadingEvents).containsAllOf(PERIOD_0, PERIOD_0); assertThat(loadingEvents).containsAllOf(period0, period0);
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(PERIOD_0, PERIOD_1); assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(period0, period1);
assertThat(listener.getEvents(EVENT_LOAD_STARTED)) assertThat(listener.getEvents(EVENT_LOAD_STARTED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0 /* media */, period0 /* media */,
WINDOW_1 /* manifest */, WINDOW_1 /* manifest */,
PERIOD_1 /* media */); period1 /* media */);
assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) assertThat(listener.getEvents(EVENT_LOAD_COMPLETED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0 /* media */, period0 /* media */,
WINDOW_1 /* manifest */, WINDOW_1 /* manifest */,
PERIOD_1 /* media */); period1 /* media */);
assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED))
.containsExactly(PERIOD_0 /* video */, PERIOD_1 /* audio */); .containsExactly(period0 /* video */, period1 /* audio */);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)).containsExactly(PERIOD_0, PERIOD_1); assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)).containsExactly(period0, period1);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_READING_STARTED)).containsExactly(PERIOD_0, PERIOD_1); assertThat(listener.getEvents(EVENT_READING_STARTED)).containsExactly(period0, period1);
assertThat(listener.getEvents(EVENT_DECODER_ENABLED)) assertThat(listener.getEvents(EVENT_DECODER_ENABLED))
.containsExactly(PERIOD_0 /* video */, PERIOD_1 /* audio */); .containsExactly(period0 /* video */, period1 /* audio */);
assertThat(listener.getEvents(EVENT_DECODER_INIT)) assertThat(listener.getEvents(EVENT_DECODER_INIT))
.containsExactly(PERIOD_0 /* video */, PERIOD_1 /* audio */); .containsExactly(period0 /* video */, period1 /* audio */);
assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED))
.containsExactly(PERIOD_0 /* video */, PERIOD_1 /* audio */); .containsExactly(period0 /* video */, period1 /* audio */);
assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(PERIOD_1); assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(period1);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -386,64 +378,64 @@ public final class AnalyticsCollectorTest {
.build(); .build();
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule); TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
populateEventIds(listener.lastReportedTimeline);
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED)) assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
.containsExactly( .containsExactly(
WINDOW_0 /* setPlayWhenReady=true */, WINDOW_0 /* setPlayWhenReady=true */,
WINDOW_0 /* BUFFERING */, WINDOW_0 /* BUFFERING */,
WINDOW_0 /* setPlayWhenReady=false */, WINDOW_0 /* setPlayWhenReady=false */,
PERIOD_0 /* READY */, period0 /* READY */,
PERIOD_0 /* setPlayWhenReady=true */, period0 /* setPlayWhenReady=true */,
PERIOD_0 /* setPlayWhenReady=false */, period0 /* setPlayWhenReady=false */,
PERIOD_0 /* BUFFERING */, period0 /* BUFFERING */,
PERIOD_0 /* READY */, period0 /* READY */,
PERIOD_0 /* setPlayWhenReady=true */, period0 /* setPlayWhenReady=true */,
PERIOD_1_SEQ_2 /* BUFFERING */, period1Seq2 /* BUFFERING */,
PERIOD_1_SEQ_2 /* READY */, period1Seq2 /* READY */,
PERIOD_1_SEQ_2 /* ENDED */); period1Seq2 /* ENDED */);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)).containsExactly(WINDOW_0); assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)).containsExactly(WINDOW_0);
assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY)) assertThat(listener.getEvents(EVENT_POSITION_DISCONTINUITY))
.containsExactly(PERIOD_0, PERIOD_1_SEQ_2); .containsExactly(period0, period1Seq2);
assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_LOADING_CHANGED))
.containsExactly(PERIOD_0, PERIOD_0, PERIOD_0, PERIOD_0, PERIOD_0, PERIOD_0); .containsExactly(period0, period0, period0, period0, period0, period0);
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(PERIOD_0, PERIOD_1_SEQ_2); assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(period0, period1Seq2);
assertThat(listener.getEvents(EVENT_LOAD_STARTED)) assertThat(listener.getEvents(EVENT_LOAD_STARTED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0 /* media */, period0 /* media */,
WINDOW_1 /* manifest */, WINDOW_1 /* manifest */,
PERIOD_1_SEQ_1 /* media */, period1Seq1 /* media */,
PERIOD_1_SEQ_2 /* media */); period1Seq2 /* media */);
assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) assertThat(listener.getEvents(EVENT_LOAD_COMPLETED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0 /* media */, period0 /* media */,
WINDOW_1 /* manifest */, WINDOW_1 /* manifest */,
PERIOD_1_SEQ_1 /* media */, period1Seq1 /* media */,
PERIOD_1_SEQ_2 /* media */); period1Seq2 /* media */);
assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED))
.containsExactly(PERIOD_0, PERIOD_1_SEQ_1, PERIOD_1_SEQ_2, PERIOD_1_SEQ_2); .containsExactly(period0, period1Seq1, period1Seq2, period1Seq2);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)) assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED))
.containsExactly(PERIOD_0, PERIOD_1_SEQ_1, PERIOD_1_SEQ_2); .containsExactly(period0, period1Seq1, period1Seq2);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)) assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED))
.containsExactly(PERIOD_0, PERIOD_1_SEQ_1); .containsExactly(period0, period1Seq1);
assertThat(listener.getEvents(EVENT_READING_STARTED)) assertThat(listener.getEvents(EVENT_READING_STARTED))
.containsExactly(PERIOD_0, PERIOD_1_SEQ_1, PERIOD_1_SEQ_2); .containsExactly(period0, period1Seq1, period1Seq2);
assertThat(listener.getEvents(EVENT_DECODER_ENABLED)) assertThat(listener.getEvents(EVENT_DECODER_ENABLED))
.containsExactly(PERIOD_0, PERIOD_0, PERIOD_1_SEQ_2); .containsExactly(period0, period0, period1Seq2);
assertThat(listener.getEvents(EVENT_DECODER_INIT)) assertThat(listener.getEvents(EVENT_DECODER_INIT))
.containsExactly(PERIOD_0, PERIOD_1_SEQ_1, PERIOD_1_SEQ_2, PERIOD_1_SEQ_2); .containsExactly(period0, period1Seq1, period1Seq2, period1Seq2);
assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED))
.containsExactly(PERIOD_0, PERIOD_1_SEQ_1, PERIOD_1_SEQ_2, PERIOD_1_SEQ_2); .containsExactly(period0, period1Seq1, period1Seq2, period1Seq2);
assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(PERIOD_1_SEQ_2); assertThat(listener.getEvents(EVENT_AUDIO_SESSION_ID)).containsExactly(period1Seq2);
assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)) assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES))
.containsExactly(PERIOD_0, PERIOD_0, PERIOD_1_SEQ_2); .containsExactly(period0, period0, period1Seq2);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0, period1Seq2);
.containsExactly(PERIOD_0, PERIOD_1_SEQ_2);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME))
.containsExactly(PERIOD_0, PERIOD_1_SEQ_2); .containsExactly(period0, period1Seq2);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -462,54 +454,52 @@ public final class AnalyticsCollectorTest {
.build(); .build();
TestAnalyticsListener listener = runAnalyticsTest(mediaSource1, actionSchedule); TestAnalyticsListener listener = runAnalyticsTest(mediaSource1, actionSchedule);
populateEventIds(SINGLE_PERIOD_TIMELINE);
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED)) assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
.containsExactly( .containsExactly(
WINDOW_0 /* setPlayWhenReady=true */, WINDOW_0 /* setPlayWhenReady=true */,
WINDOW_0 /* BUFFERING */, WINDOW_0 /* BUFFERING */,
WINDOW_0 /* setPlayWhenReady=false */, WINDOW_0 /* setPlayWhenReady=false */,
PERIOD_0_SEQ_0 /* READY */, period0Seq0 /* READY */,
WINDOW_0 /* BUFFERING */, WINDOW_0 /* BUFFERING */,
WINDOW_0 /* setPlayWhenReady=true */, WINDOW_0 /* setPlayWhenReady=true */,
PERIOD_0_SEQ_1 /* READY */, period0Seq1 /* READY */,
PERIOD_0_SEQ_1 /* ENDED */); period0Seq1 /* ENDED */);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)) assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
.containsExactly(WINDOW_0 /* prepared */, WINDOW_0 /* reset */, WINDOW_0 /* prepared */); .containsExactly(WINDOW_0 /* prepared */, WINDOW_0 /* reset */, WINDOW_0 /* prepared */);
assertThat(listener.getEvents(EVENT_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_LOADING_CHANGED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0, PERIOD_0_SEQ_1, PERIOD_0_SEQ_1); .containsExactly(period0Seq0, period0Seq0, period0Seq1, period0Seq1);
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)) assertThat(listener.getEvents(EVENT_TRACKS_CHANGED))
.containsExactly( .containsExactly(
PERIOD_0_SEQ_0 /* prepared */, WINDOW_0 /* reset */, PERIOD_0_SEQ_1 /* prepared */); period0Seq0 /* prepared */, WINDOW_0 /* reset */, period0Seq1 /* prepared */);
assertThat(listener.getEvents(EVENT_LOAD_STARTED)) assertThat(listener.getEvents(EVENT_LOAD_STARTED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0_SEQ_0 /* media */, period0Seq0 /* media */,
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0_SEQ_1 /* media */); period0Seq1 /* media */);
assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) assertThat(listener.getEvents(EVENT_LOAD_COMPLETED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0_SEQ_0 /* media */, period0Seq0 /* media */,
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0_SEQ_1 /* media */); period0Seq1 /* media */);
assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_1); .containsExactly(period0Seq0, period0Seq1);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)) assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_1); .containsExactly(period0Seq0, period0Seq1);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(PERIOD_0_SEQ_0); assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(period0Seq0);
assertThat(listener.getEvents(EVENT_READING_STARTED)) assertThat(listener.getEvents(EVENT_READING_STARTED)).containsExactly(period0Seq0, period0Seq1);
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_1); assertThat(listener.getEvents(EVENT_DECODER_ENABLED)).containsExactly(period0Seq0, period0Seq1);
assertThat(listener.getEvents(EVENT_DECODER_ENABLED)) assertThat(listener.getEvents(EVENT_DECODER_INIT)).containsExactly(period0Seq0, period0Seq1);
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_1);
assertThat(listener.getEvents(EVENT_DECODER_INIT))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_1);
assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_1); .containsExactly(period0Seq0, period0Seq1);
assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(PERIOD_0_SEQ_0); assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(period0Seq0);
assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(PERIOD_0_SEQ_1); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0Seq1);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_1); .containsExactly(period0Seq0, period0Seq1);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_1); .containsExactly(period0Seq0, period0Seq1);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -527,55 +517,51 @@ public final class AnalyticsCollectorTest {
.build(); .build();
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule); TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
populateEventIds(SINGLE_PERIOD_TIMELINE);
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED)) assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
.containsExactly( .containsExactly(
WINDOW_0 /* setPlayWhenReady=true */, WINDOW_0 /* setPlayWhenReady=true */,
WINDOW_0 /* BUFFERING */, WINDOW_0 /* BUFFERING */,
PERIOD_0_SEQ_0 /* READY */, period0Seq0 /* READY */,
WINDOW_0 /* IDLE */, WINDOW_0 /* IDLE */,
WINDOW_0 /* BUFFERING */, WINDOW_0 /* BUFFERING */,
PERIOD_0_SEQ_0 /* READY */, period0Seq0 /* READY */,
PERIOD_0_SEQ_0 /* ENDED */); period0Seq0 /* ENDED */);
// assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED)).doesNotContain(PERIOD_0_SEQ_1);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)) assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
.containsExactly(WINDOW_0 /* prepared */, WINDOW_0 /* prepared */); .containsExactly(WINDOW_0 /* prepared */, WINDOW_0 /* prepared */);
assertThat(listener.getEvents(EVENT_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_LOADING_CHANGED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0, PERIOD_0_SEQ_0, PERIOD_0_SEQ_0); .containsExactly(period0Seq0, period0Seq0, period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_PLAYER_ERROR)).containsExactly(WINDOW_0); assertThat(listener.getEvents(EVENT_PLAYER_ERROR)).containsExactly(WINDOW_0);
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)) assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(period0Seq0, period0Seq0);
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0);
assertThat(listener.getEvents(EVENT_LOAD_STARTED)) assertThat(listener.getEvents(EVENT_LOAD_STARTED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0_SEQ_0 /* media */, period0Seq0 /* media */,
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0_SEQ_0 /* media */); period0Seq0 /* media */);
assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) assertThat(listener.getEvents(EVENT_LOAD_COMPLETED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0_SEQ_0 /* media */, period0Seq0 /* media */,
WINDOW_0 /* manifest */, WINDOW_0 /* manifest */,
PERIOD_0_SEQ_0 /* media */); period0Seq0 /* media */);
assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0); .containsExactly(period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)) assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0); .containsExactly(period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(PERIOD_0_SEQ_0); assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(period0Seq0);
assertThat(listener.getEvents(EVENT_READING_STARTED)) assertThat(listener.getEvents(EVENT_READING_STARTED)).containsExactly(period0Seq0, period0Seq0);
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0); assertThat(listener.getEvents(EVENT_DECODER_ENABLED)).containsExactly(period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_DECODER_ENABLED)) assertThat(listener.getEvents(EVENT_DECODER_INIT)).containsExactly(period0Seq0, period0Seq0);
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0);
assertThat(listener.getEvents(EVENT_DECODER_INIT))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0);
assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0); .containsExactly(period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(PERIOD_0_SEQ_0); assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(period0Seq0);
assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)) assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0); .containsExactly(period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0); .containsExactly(period0Seq0, period0Seq0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0); .containsExactly(period0Seq0, period0Seq0);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -602,45 +588,50 @@ public final class AnalyticsCollectorTest {
.build(); .build();
TestAnalyticsListener listener = runAnalyticsTest(concatenatedMediaSource, actionSchedule); TestAnalyticsListener listener = runAnalyticsTest(concatenatedMediaSource, actionSchedule);
populateEventIds(listener.lastReportedTimeline);
assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED)) assertThat(listener.getEvents(EVENT_PLAYER_STATE_CHANGED))
.containsExactly( .containsExactly(
WINDOW_0 /* setPlayWhenReady=true */, WINDOW_0 /* setPlayWhenReady=true */,
WINDOW_0 /* BUFFERING */, WINDOW_0 /* BUFFERING */,
WINDOW_0 /* setPlayWhenReady=false */, WINDOW_0 /* setPlayWhenReady=false */,
PERIOD_0_SEQ_0 /* READY */, window0Period1Seq0 /* READY */,
PERIOD_0_SEQ_0 /* setPlayWhenReady=true */, window0Period1Seq0 /* setPlayWhenReady=true */,
PERIOD_0_SEQ_0 /* setPlayWhenReady=false */, window0Period1Seq0 /* setPlayWhenReady=false */,
PERIOD_1_SEQ_0 /* setPlayWhenReady=true */, period1Seq0 /* setPlayWhenReady=true */,
PERIOD_1_SEQ_0 /* BUFFERING */, period1Seq0 /* BUFFERING */,
PERIOD_1_SEQ_0 /* ENDED */); period1Seq0 /* ENDED */);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)) assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED)).containsExactly(WINDOW_0, period1Seq0);
.containsExactly(WINDOW_0, PERIOD_1_SEQ_0);
assertThat(listener.getEvents(EVENT_LOADING_CHANGED)) assertThat(listener.getEvents(EVENT_LOADING_CHANGED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0, PERIOD_0_SEQ_0, PERIOD_0_SEQ_0); .containsExactly(
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(PERIOD_0_SEQ_0); window0Period1Seq0, window0Period1Seq0, window0Period1Seq0, window0Period1Seq0);
assertThat(listener.getEvents(EVENT_TRACKS_CHANGED)).containsExactly(window0Period1Seq0);
assertThat(listener.getEvents(EVENT_LOAD_STARTED)) assertThat(listener.getEvents(EVENT_LOAD_STARTED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, PERIOD_0_SEQ_0 /* media */, PERIOD_1_SEQ_1 /* media */); WINDOW_0 /* manifest */,
window0Period1Seq0 /* media */,
window1Period0Seq1 /* media */);
assertThat(listener.getEvents(EVENT_LOAD_COMPLETED)) assertThat(listener.getEvents(EVENT_LOAD_COMPLETED))
.containsExactly( .containsExactly(
WINDOW_0 /* manifest */, PERIOD_0_SEQ_0 /* media */, PERIOD_1_SEQ_1 /* media */); WINDOW_0 /* manifest */,
window0Period1Seq0 /* media */,
window1Period0Seq1 /* media */);
assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DOWNSTREAM_FORMAT_CHANGED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_1_SEQ_1); .containsExactly(window0Period1Seq0, window1Period0Seq1);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED)) assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_CREATED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_1_SEQ_1); .containsExactly(window0Period1Seq0, window1Period0Seq1);
assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(PERIOD_0_SEQ_1); assertThat(listener.getEvents(EVENT_MEDIA_PERIOD_RELEASED)).containsExactly(period0Seq1);
assertThat(listener.getEvents(EVENT_READING_STARTED)) assertThat(listener.getEvents(EVENT_READING_STARTED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_1_SEQ_1); .containsExactly(window0Period1Seq0, window1Period0Seq1);
assertThat(listener.getEvents(EVENT_DECODER_ENABLED)) assertThat(listener.getEvents(EVENT_DECODER_ENABLED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_0_SEQ_0); .containsExactly(window0Period1Seq0, window0Period1Seq0);
assertThat(listener.getEvents(EVENT_DECODER_INIT)) assertThat(listener.getEvents(EVENT_DECODER_INIT))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_1_SEQ_1); .containsExactly(window0Period1Seq0, window1Period0Seq1);
assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED)) assertThat(listener.getEvents(EVENT_DECODER_FORMAT_CHANGED))
.containsExactly(PERIOD_0_SEQ_0, PERIOD_1_SEQ_1); .containsExactly(window0Period1Seq0, window1Period0Seq1);
assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(PERIOD_0_SEQ_0); assertThat(listener.getEvents(EVENT_DECODER_DISABLED)).containsExactly(window0Period1Seq0);
assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(PERIOD_0_SEQ_0); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(window0Period1Seq0);
assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(PERIOD_0_SEQ_0); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(window0Period1Seq0);
assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(PERIOD_0_SEQ_0); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(window0Period1Seq0);
listener.assertNoMoreEvents(); listener.assertNoMoreEvents();
} }
@ -663,8 +654,51 @@ public final class AnalyticsCollectorTest {
.build(); .build();
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule); TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(PERIOD_0); populateEventIds(SINGLE_PERIOD_TIMELINE);
assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(PERIOD_0); assertThat(listener.getEvents(EVENT_SEEK_STARTED)).containsExactly(period0);
assertThat(listener.getEvents(EVENT_SEEK_PROCESSED)).containsExactly(period0);
}
private void populateEventIds(Timeline timeline) {
period0 =
new EventWindowAndPeriodId(
/* windowIndex= */ 0,
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
period0Seq0 = period0;
period0Seq1 =
new EventWindowAndPeriodId(
/* windowIndex= */ 0,
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 1));
window1Period0Seq1 =
new EventWindowAndPeriodId(
/* windowIndex= */ 1,
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 1));
if (timeline.getPeriodCount() > 1) {
period1 =
new EventWindowAndPeriodId(
/* windowIndex= */ 1,
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1));
period1Seq1 = period1;
period1Seq0 =
new EventWindowAndPeriodId(
/* windowIndex= */ 1,
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0));
period1Seq2 =
new EventWindowAndPeriodId(
/* windowIndex= */ 1,
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 2));
window0Period1Seq0 =
new EventWindowAndPeriodId(
/* windowIndex= */ 0,
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0));
}
} }
private static TestAnalyticsListener runAnalyticsTest(MediaSource mediaSource) throws Exception { private static TestAnalyticsListener runAnalyticsTest(MediaSource mediaSource) throws Exception {
@ -834,7 +868,7 @@ public final class AnalyticsCollectorTest {
+ "window=" + "window="
+ windowIndex + windowIndex
+ ", period=" + ", period="
+ mediaPeriodId.periodIndex + mediaPeriodId.periodUid
+ ", sequence=" + ", sequence="
+ mediaPeriodId.windowSequenceNumber + mediaPeriodId.windowSequenceNumber
+ '}' + '}'
@ -849,10 +883,13 @@ public final class AnalyticsCollectorTest {
private static final class TestAnalyticsListener implements AnalyticsListener { private static final class TestAnalyticsListener implements AnalyticsListener {
public Timeline lastReportedTimeline;
private final ArrayList<ReportedEvent> reportedEvents; private final ArrayList<ReportedEvent> reportedEvents;
public TestAnalyticsListener() { public TestAnalyticsListener() {
reportedEvents = new ArrayList<>(); reportedEvents = new ArrayList<>();
lastReportedTimeline = Timeline.EMPTY;
} }
public List<EventWindowAndPeriodId> getEvents(int eventType) { public List<EventWindowAndPeriodId> getEvents(int eventType) {
@ -880,6 +917,7 @@ public final class AnalyticsCollectorTest {
@Override @Override
public void onTimelineChanged(EventTime eventTime, int reason) { public void onTimelineChanged(EventTime eventTime, int reason) {
lastReportedTimeline = eventTime.timeline;
reportedEvents.add(new ReportedEvent(EVENT_TIMELINE_CHANGED, eventTime)); reportedEvents.add(new ReportedEvent(EVENT_TIMELINE_CHANGED, eventTime));
} }

View File

@ -469,11 +469,11 @@ public final class ClippingMediaSourceTest {
private static MediaLoadData getClippingMediaSourceMediaLoadData( private static MediaLoadData getClippingMediaSourceMediaLoadData(
long clippingStartUs, long clippingEndUs, final long eventStartUs, final long eventEndUs) long clippingStartUs, long clippingEndUs, final long eventStartUs, final long eventEndUs)
throws IOException { throws IOException {
Timeline timeline =
new SinglePeriodTimeline(
TEST_PERIOD_DURATION_US, /* isSeekable= */ true, /* isDynamic= */ false);
FakeMediaSource fakeMediaSource = FakeMediaSource fakeMediaSource =
new FakeMediaSource( new FakeMediaSource(timeline, /* manifest= */ null) {
new SinglePeriodTimeline(
TEST_PERIOD_DURATION_US, /* isSeekable= */ true, /* isDynamic= */ false),
/* manifest= */ null) {
@Override @Override
protected FakeMediaPeriod createFakeMediaPeriod( protected FakeMediaPeriod createFakeMediaPeriod(
MediaPeriodId id, MediaPeriodId id,
@ -516,7 +516,8 @@ public final class ClippingMediaSourceTest {
testRunner.prepareSource(); testRunner.prepareSource();
// Create period to send the test event configured above. // Create period to send the test event configured above.
testRunner.createPeriod( testRunner.createPeriod(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
assertThat(reportedMediaLoadData[0]).isNotNull(); assertThat(reportedMediaLoadData[0]).isNotNull();
} finally { } finally {
testRunner.release(); testRunner.release();
@ -580,7 +581,9 @@ public final class ClippingMediaSourceTest {
clippedTimelines[0] = testRunner.prepareSource(); clippedTimelines[0] = testRunner.prepareSource();
MediaPeriod mediaPeriod = MediaPeriod mediaPeriod =
testRunner.createPeriod( testRunner.createPeriod(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); new MediaPeriodId(
clippedTimelines[0].getUidOfPeriod(/* periodIndex= */ 0),
/* windowSequenceNumber= */ 0));
for (int i = 0; i < additionalTimelines.length; i++) { for (int i = 0; i < additionalTimelines.length; i++) {
fakeMediaSource.setNewSourceInfo(additionalTimelines[i], /* newManifest= */ null); fakeMediaSource.setNewSourceInfo(additionalTimelines[i], /* newManifest= */ null);
clippedTimelines[i + 1] = testRunner.assertTimelineChangeBlocking(); clippedTimelines[i + 1] = testRunner.assertTimelineChangeBlocking();

View File

@ -274,14 +274,16 @@ public final class ConcatenatingMediaSourceTest {
// called yet. // called yet.
MediaPeriod lazyPeriod = MediaPeriod lazyPeriod =
testRunner.createPeriod( testRunner.createPeriod(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
CountDownLatch preparedCondition = testRunner.preparePeriod(lazyPeriod, 0); CountDownLatch preparedCondition = testRunner.preparePeriod(lazyPeriod, 0);
assertThat(preparedCondition.getCount()).isEqualTo(1); assertThat(preparedCondition.getCount()).isEqualTo(1);
// Assert that a second period can also be created and released without problems. // Assert that a second period can also be created and released without problems.
MediaPeriod secondLazyPeriod = MediaPeriod secondLazyPeriod =
testRunner.createPeriod( testRunner.createPeriod(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
testRunner.releasePeriod(secondLazyPeriod); testRunner.releasePeriod(secondLazyPeriod);
// Trigger source info refresh for lazy media source. Assert that now all information is // Trigger source info refresh for lazy media source. Assert that now all information is
@ -605,23 +607,27 @@ public final class ConcatenatingMediaSourceTest {
// Create all periods and assert period creation of child media sources has been called. // Create all periods and assert period creation of child media sources has been called.
testRunner.assertPrepareAndReleaseAllPeriods(); testRunner.assertPrepareAndReleaseAllPeriods();
Object timelineContentOnlyPeriodUid0 = timelineContentOnly.getUidOfPeriod(/* periodIndex= */ 0);
Object timelineContentOnlyPeriodUid1 = timelineContentOnly.getUidOfPeriod(/* periodIndex= */ 1);
Object timelineWithAdsPeriodUid0 = timelineWithAds.getUidOfPeriod(/* periodIndex= */ 0);
Object timelineWithAdsPeriodUid1 = timelineWithAds.getUidOfPeriod(/* periodIndex= */ 1);
mediaSourceContentOnly.assertMediaPeriodCreated( mediaSourceContentOnly.assertMediaPeriodCreated(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); new MediaPeriodId(timelineContentOnlyPeriodUid0, /* windowSequenceNumber= */ 0));
mediaSourceContentOnly.assertMediaPeriodCreated( mediaSourceContentOnly.assertMediaPeriodCreated(
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 0)); new MediaPeriodId(timelineContentOnlyPeriodUid1, /* windowSequenceNumber= */ 0));
mediaSourceWithAds.assertMediaPeriodCreated( mediaSourceWithAds.assertMediaPeriodCreated(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 1)); new MediaPeriodId(timelineWithAdsPeriodUid0, /* windowSequenceNumber= */ 1));
mediaSourceWithAds.assertMediaPeriodCreated( mediaSourceWithAds.assertMediaPeriodCreated(
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 1)); new MediaPeriodId(timelineWithAdsPeriodUid1, /* windowSequenceNumber= */ 1));
mediaSourceWithAds.assertMediaPeriodCreated( mediaSourceWithAds.assertMediaPeriodCreated(
new MediaPeriodId( new MediaPeriodId(
/* periodIndex= */ 0, timelineWithAdsPeriodUid0,
/* adGroupIndex= */ 0, /* adGroupIndex= */ 0,
/* adIndexInAdGroup= */ 0, /* adIndexInAdGroup= */ 0,
/* windowSequenceNumber= */ 1)); /* windowSequenceNumber= */ 1));
mediaSourceWithAds.assertMediaPeriodCreated( mediaSourceWithAds.assertMediaPeriodCreated(
new MediaPeriodId( new MediaPeriodId(
/* periodIndex= */ 1, timelineWithAdsPeriodUid1,
/* adGroupIndex= */ 0, /* adGroupIndex= */ 0,
/* adIndexInAdGroup= */ 0, /* adIndexInAdGroup= */ 0,
/* windowSequenceNumber= */ 1)); /* windowSequenceNumber= */ 1));
@ -721,10 +727,11 @@ public final class ConcatenatingMediaSourceTest {
public void testRemoveChildSourceWithActiveMediaPeriod() throws IOException { public void testRemoveChildSourceWithActiveMediaPeriod() throws IOException {
FakeMediaSource childSource = createFakeMediaSource(); FakeMediaSource childSource = createFakeMediaSource();
mediaSource.addMediaSource(childSource); mediaSource.addMediaSource(childSource);
testRunner.prepareSource(); Timeline timeline = testRunner.prepareSource();
MediaPeriod mediaPeriod = MediaPeriod mediaPeriod =
testRunner.createPeriod( testRunner.createPeriod(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
mediaSource.removeMediaSource(/* index= */ 0); mediaSource.removeMediaSource(/* index= */ 0);
testRunner.assertTimelineChangeBlocking(); testRunner.assertTimelineChangeBlocking();
testRunner.releasePeriod(mediaPeriod); testRunner.releasePeriod(mediaPeriod);
@ -734,8 +741,8 @@ public final class ConcatenatingMediaSourceTest {
@Test @Test
public void testDuplicateMediaSources() throws IOException, InterruptedException { public void testDuplicateMediaSources() throws IOException, InterruptedException {
FakeMediaSource childSource = Timeline childTimeline = new FakeTimeline(/* windowCount= */ 2);
new FakeMediaSource(new FakeTimeline(/* windowCount= */ 2), /* manifest= */ null); FakeMediaSource childSource = new FakeMediaSource(childTimeline, /* manifest= */ null);
mediaSource.addMediaSource(childSource); mediaSource.addMediaSource(childSource);
mediaSource.addMediaSource(childSource); mediaSource.addMediaSource(childSource);
@ -745,16 +752,18 @@ public final class ConcatenatingMediaSourceTest {
TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1, 1, 1, 1, 1, 1); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1, 1, 1, 1, 1, 1);
testRunner.assertPrepareAndReleaseAllPeriods(); testRunner.assertPrepareAndReleaseAllPeriods();
Object childPeriodUid0 = childTimeline.getUidOfPeriod(/* periodIndex= */ 0);
Object childPeriodUid1 = childTimeline.getUidOfPeriod(/* periodIndex= */ 1);
assertThat(childSource.getCreatedMediaPeriods()) assertThat(childSource.getCreatedMediaPeriods())
.containsAllOf( .containsAllOf(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0), new MediaPeriodId(childPeriodUid0, /* windowSequenceNumber= */ 0),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 2), new MediaPeriodId(childPeriodUid0, /* windowSequenceNumber= */ 2),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 4), new MediaPeriodId(childPeriodUid0, /* windowSequenceNumber= */ 4),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 6), new MediaPeriodId(childPeriodUid0, /* windowSequenceNumber= */ 6),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 1), new MediaPeriodId(childPeriodUid1, /* windowSequenceNumber= */ 1),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 3), new MediaPeriodId(childPeriodUid1, /* windowSequenceNumber= */ 3),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 5), new MediaPeriodId(childPeriodUid1, /* windowSequenceNumber= */ 5),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 7)); new MediaPeriodId(childPeriodUid1, /* windowSequenceNumber= */ 7));
// Assert that only one manifest load is reported because the source is reused. // Assert that only one manifest load is reported because the source is reused.
testRunner.assertCompletedManifestLoads(/* windowIndices= */ 0); testRunner.assertCompletedManifestLoads(/* windowIndices= */ 0);
assertCompletedAllMediaPeriodLoads(timeline); assertCompletedAllMediaPeriodLoads(timeline);
@ -765,8 +774,8 @@ public final class ConcatenatingMediaSourceTest {
@Test @Test
public void testDuplicateNestedMediaSources() throws IOException, InterruptedException { public void testDuplicateNestedMediaSources() throws IOException, InterruptedException {
FakeMediaSource childSource = Timeline childTimeline = new FakeTimeline(/* windowCount= */ 1);
new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1), /* manifest= */ null); FakeMediaSource childSource = new FakeMediaSource(childTimeline, /* manifest= */ null);
ConcatenatingMediaSource nestedConcatenation = new ConcatenatingMediaSource(); ConcatenatingMediaSource nestedConcatenation = new ConcatenatingMediaSource();
testRunner.prepareSource(); testRunner.prepareSource();
@ -780,13 +789,14 @@ public final class ConcatenatingMediaSourceTest {
TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1, 1, 1); TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1, 1, 1);
testRunner.assertPrepareAndReleaseAllPeriods(); testRunner.assertPrepareAndReleaseAllPeriods();
Object childPeriodUid = childTimeline.getUidOfPeriod(/* periodIndex= */ 0);
assertThat(childSource.getCreatedMediaPeriods()) assertThat(childSource.getCreatedMediaPeriods())
.containsAllOf( .containsAllOf(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0), new MediaPeriodId(childPeriodUid, /* windowSequenceNumber= */ 0),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 1), new MediaPeriodId(childPeriodUid, /* windowSequenceNumber= */ 1),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 2), new MediaPeriodId(childPeriodUid, /* windowSequenceNumber= */ 2),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 3), new MediaPeriodId(childPeriodUid, /* windowSequenceNumber= */ 3),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 4)); new MediaPeriodId(childPeriodUid, /* windowSequenceNumber= */ 4));
// Assert that only one manifest load is needed because the source is reused. // Assert that only one manifest load is needed because the source is reused.
testRunner.assertCompletedManifestLoads(/* windowIndices= */ 0); testRunner.assertCompletedManifestLoads(/* windowIndices= */ 0);
assertCompletedAllMediaPeriodLoads(timeline); assertCompletedAllMediaPeriodLoads(timeline);
@ -844,16 +854,18 @@ public final class ConcatenatingMediaSourceTest {
ConcatenatingMediaSource childSource = new ConcatenatingMediaSource(nestedChildSources); ConcatenatingMediaSource childSource = new ConcatenatingMediaSource(nestedChildSources);
mediaSource.addMediaSource(childSource); mediaSource.addMediaSource(childSource);
testRunner.prepareSource(); Timeline timeline = testRunner.prepareSource();
MediaPeriod mediaPeriod = MediaPeriod mediaPeriod =
testRunner.createPeriod( testRunner.createPeriod(
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 0)); new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0));
childSource.moveMediaSource(/* currentIndex= */ 0, /* newIndex= */ 1); childSource.moveMediaSource(/* currentIndex= */ 0, /* newIndex= */ 1);
testRunner.assertTimelineChangeBlocking(); timeline = testRunner.assertTimelineChangeBlocking();
testRunner.preparePeriod(mediaPeriod, /* positionUs= */ 0); testRunner.preparePeriod(mediaPeriod, /* positionUs= */ 0);
testRunner.assertCompletedMediaPeriodLoads( testRunner.assertCompletedMediaPeriodLoads(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
} }
@Test @Test
@ -882,8 +894,10 @@ public final class ConcatenatingMediaSourceTest {
new DefaultShuffleOrder(0), new DefaultShuffleOrder(0),
childSources); childSources);
testRunner = new MediaSourceTestRunner(mediaSource, /* allocator= */ null); testRunner = new MediaSourceTestRunner(mediaSource, /* allocator= */ null);
testRunner.prepareSource(); Timeline timeline = testRunner.prepareSource();
testRunner.createPeriod(new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); testRunner.createPeriod(
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
assertThat(childSources[0].isPrepared()).isTrue(); assertThat(childSources[0].isPrepared()).isTrue();
assertThat(childSources[1].isPrepared()).isFalse(); assertThat(childSources[1].isPrepared()).isFalse();
@ -899,13 +913,16 @@ public final class ConcatenatingMediaSourceTest {
new DefaultShuffleOrder(0), new DefaultShuffleOrder(0),
childSources); childSources);
testRunner = new MediaSourceTestRunner(mediaSource, /* allocator= */ null); testRunner = new MediaSourceTestRunner(mediaSource, /* allocator= */ null);
testRunner.prepareSource(); Timeline timeline = testRunner.prepareSource();
// The lazy preparation must only be triggered once, even if we create multiple periods from // The lazy preparation must only be triggered once, even if we create multiple periods from
// the media source. FakeMediaSource.prepareSource asserts that it's not called twice, so // the media source. FakeMediaSource.prepareSource asserts that it's not called twice, so
// creating two periods shouldn't throw. // creating two periods shouldn't throw.
testRunner.createPeriod(new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); MediaPeriodId mediaPeriodId =
testRunner.createPeriod(new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0)); new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
testRunner.createPeriod(mediaPeriodId);
testRunner.createPeriod(mediaPeriodId);
} }
private void assertCompletedAllMediaPeriodLoads(Timeline timeline) { private void assertCompletedAllMediaPeriodLoads(Timeline timeline) {
@ -918,11 +935,12 @@ public final class ConcatenatingMediaSourceTest {
periodIndex <= window.lastPeriodIndex; periodIndex <= window.lastPeriodIndex;
periodIndex++) { periodIndex++) {
timeline.getPeriod(periodIndex, period); timeline.getPeriod(periodIndex, period);
expectedMediaPeriodIds.add(new MediaPeriodId(periodIndex, windowIndex)); Object periodUid = timeline.getUidOfPeriod(periodIndex);
expectedMediaPeriodIds.add(new MediaPeriodId(periodUid, windowIndex));
for (int adGroupIndex = 0; adGroupIndex < period.getAdGroupCount(); adGroupIndex++) { for (int adGroupIndex = 0; adGroupIndex < period.getAdGroupCount(); adGroupIndex++) {
for (int adIndex = 0; adIndex < period.getAdCountInAdGroup(adGroupIndex); adIndex++) { for (int adIndex = 0; adIndex < period.getAdCountInAdGroup(adGroupIndex); adIndex++) {
expectedMediaPeriodIds.add( expectedMediaPeriodIds.add(
new MediaPeriodId(periodIndex, adGroupIndex, adIndex, windowIndex)); new MediaPeriodId(periodUid, adGroupIndex, adIndex, windowIndex));
} }
} }
} }

View File

@ -45,11 +45,11 @@ public final class SinglePeriodTimelineTest {
public void testGetPeriodPositionDynamicWindowUnknownDuration() { public void testGetPeriodPositionDynamicWindowUnknownDuration() {
SinglePeriodTimeline timeline = new SinglePeriodTimeline(C.TIME_UNSET, false, true); SinglePeriodTimeline timeline = new SinglePeriodTimeline(C.TIME_UNSET, false, true);
// Should return null with any positive position projection. // Should return null with any positive position projection.
Pair<Integer, Long> position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, 1); Pair<Object, Long> position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, 1);
assertThat(position).isNull(); assertThat(position).isNull();
// Should return (0, 0) without a position projection. // Should return (0, 0) without a position projection.
position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, 0); position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, 0);
assertThat(position.first).isEqualTo(0); assertThat(position.first).isEqualTo(timeline.getUidOfPeriod(0));
assertThat(position.second).isEqualTo(0); assertThat(position.second).isEqualTo(0);
} }
@ -66,16 +66,16 @@ public final class SinglePeriodTimelineTest {
/* isDynamic= */ true, /* isDynamic= */ true,
/* tag= */ null); /* tag= */ null);
// Should return null with a positive position projection beyond window duration. // Should return null with a positive position projection beyond window duration.
Pair<Integer, Long> position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, Pair<Object, Long> position =
windowDurationUs + 1); timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, windowDurationUs + 1);
assertThat(position).isNull(); assertThat(position).isNull();
// Should return (0, duration) with a projection equal to window duration. // Should return (0, duration) with a projection equal to window duration.
position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, windowDurationUs); position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, windowDurationUs);
assertThat(position.first).isEqualTo(0); assertThat(position.first).isEqualTo(timeline.getUidOfPeriod(0));
assertThat(position.second).isEqualTo(windowDurationUs); assertThat(position.second).isEqualTo(windowDurationUs);
// Should return (0, 0) without a position projection. // Should return (0, 0) without a position projection.
position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, 0); position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, 0);
assertThat(position.first).isEqualTo(0); assertThat(position.first).isEqualTo(timeline.getUidOfPeriod(0));
assertThat(position.second).isEqualTo(0); assertThat(position.second).isEqualTo(0);
} }

View File

@ -73,8 +73,8 @@ import java.util.List;
private final PlayerEmsgHandler playerEmsgHandler; private final PlayerEmsgHandler playerEmsgHandler;
private final IdentityHashMap<ChunkSampleStream<DashChunkSource>, PlayerTrackEmsgHandler> private final IdentityHashMap<ChunkSampleStream<DashChunkSource>, PlayerTrackEmsgHandler>
trackEmsgHandlerBySampleStream; trackEmsgHandlerBySampleStream;
private final EventDispatcher eventDispatcher;
private EventDispatcher eventDispatcher;
private @Nullable Callback callback; private @Nullable Callback callback;
private ChunkSampleStream<DashChunkSource>[] sampleStreams; private ChunkSampleStream<DashChunkSource>[] sampleStreams;
private EventSampleStream[] eventSampleStreams; private EventSampleStream[] eventSampleStreams;
@ -131,13 +131,6 @@ import java.util.List;
*/ */
public void updateManifest(DashManifest manifest, int periodIndex) { public void updateManifest(DashManifest manifest, int periodIndex) {
this.manifest = manifest; this.manifest = manifest;
if (this.periodIndex != periodIndex) {
eventDispatcher =
eventDispatcher.withParameters(
/* windowIndex= */ 0,
eventDispatcher.mediaPeriodId.copyWithPeriodIndex(periodIndex),
manifest.getPeriod(periodIndex).startMs);
}
this.periodIndex = periodIndex; this.periodIndex = periodIndex;
playerEmsgHandler.updateManifest(manifest); playerEmsgHandler.updateManifest(manifest);
if (sampleStreams != null) { if (sampleStreams != null) {

View File

@ -631,7 +631,7 @@ public final class DashMediaSource extends BaseMediaSource {
@Override @Override
public MediaPeriod createPeriod(MediaPeriodId periodId, Allocator allocator) { public MediaPeriod createPeriod(MediaPeriodId periodId, Allocator allocator) {
int periodIndex = periodId.periodIndex; int periodIndex = (Integer) periodId.periodUid - firstPeriodId;
EventDispatcher periodEventDispatcher = EventDispatcher periodEventDispatcher =
createEventDispatcher(periodId, manifest.getPeriod(periodIndex).startMs); createEventDispatcher(periodId, manifest.getPeriod(periodIndex).startMs);
DashMediaPeriod mediaPeriod = DashMediaPeriod mediaPeriod =

View File

@ -425,7 +425,6 @@ public final class HlsMediaSource extends BaseMediaSource
@Override @Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
Assertions.checkArgument(id.periodIndex == 0);
EventDispatcher eventDispatcher = createEventDispatcher(id); EventDispatcher eventDispatcher = createEventDispatcher(id);
return new HlsMediaPeriod( return new HlsMediaPeriod(
extractorFactory, extractorFactory,

View File

@ -525,7 +525,6 @@ public final class SsMediaSource extends BaseMediaSource
@Override @Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
Assertions.checkArgument(id.periodIndex == 0);
EventDispatcher eventDispatcher = createEventDispatcher(id); EventDispatcher eventDispatcher = createEventDispatcher(id);
SsMediaPeriod period = SsMediaPeriod period =
new SsMediaPeriod( new SsMediaPeriod(

View File

@ -53,7 +53,7 @@ public class FakeAdaptiveMediaSource extends FakeMediaSource {
Allocator allocator, Allocator allocator,
EventDispatcher eventDispatcher, EventDispatcher eventDispatcher,
@Nullable TransferListener transferListener) { @Nullable TransferListener transferListener) {
Period period = timeline.getPeriod(id.periodIndex, new Period()); Period period = timeline.getPeriodByUid(id.periodUid, new Period());
return new FakeAdaptiveMediaPeriod( return new FakeAdaptiveMediaPeriod(
trackGroupArray, trackGroupArray,
eventDispatcher, eventDispatcher,

View File

@ -111,8 +111,9 @@ public class FakeMediaSource extends BaseMediaSource {
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) { public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
assertThat(preparedSource).isTrue(); assertThat(preparedSource).isTrue();
assertThat(releasedSource).isFalse(); assertThat(releasedSource).isFalse();
Assertions.checkIndex(id.periodIndex, 0, timeline.getPeriodCount()); int periodIndex = timeline.getIndexOfPeriod(id.periodUid);
Period period = timeline.getPeriod(id.periodIndex, new Period()); Assertions.checkArgument(periodIndex != C.INDEX_UNSET);
Period period = timeline.getPeriod(periodIndex, new Period());
EventDispatcher eventDispatcher = EventDispatcher eventDispatcher =
createEventDispatcher(period.windowIndex, id, period.getPositionInWindowMs()); createEventDispatcher(period.windowIndex, id, period.getPositionInWindowMs());
FakeMediaPeriod mediaPeriod = FakeMediaPeriod mediaPeriod =

View File

@ -251,12 +251,12 @@ public class MediaSourceTestRunner {
public void assertPrepareAndReleaseAllPeriods() throws InterruptedException { public void assertPrepareAndReleaseAllPeriods() throws InterruptedException {
Timeline.Period period = new Timeline.Period(); Timeline.Period period = new Timeline.Period();
for (int i = 0; i < timeline.getPeriodCount(); i++) { for (int i = 0; i < timeline.getPeriodCount(); i++) {
timeline.getPeriod(i, period); timeline.getPeriod(i, period, /* setIds= */ true);
assertPrepareAndReleasePeriod(new MediaPeriodId(i, period.windowIndex)); assertPrepareAndReleasePeriod(new MediaPeriodId(period.uid, period.windowIndex));
for (int adGroupIndex = 0; adGroupIndex < period.getAdGroupCount(); adGroupIndex++) { for (int adGroupIndex = 0; adGroupIndex < period.getAdGroupCount(); adGroupIndex++) {
for (int adIndex = 0; adIndex < period.getAdCountInAdGroup(adGroupIndex); adIndex++) { for (int adIndex = 0; adIndex < period.getAdCountInAdGroup(adGroupIndex); adIndex++) {
assertPrepareAndReleasePeriod( assertPrepareAndReleasePeriod(
new MediaPeriodId(i, adGroupIndex, adIndex, period.windowIndex)); new MediaPeriodId(period.uid, adGroupIndex, adIndex, period.windowIndex));
} }
} }
} }
@ -272,7 +272,7 @@ public class MediaSourceTestRunner {
// to releasePeriod. // to releasePeriod.
MediaPeriodId secondMediaPeriodId = MediaPeriodId secondMediaPeriodId =
new MediaPeriodId( new MediaPeriodId(
mediaPeriodId.periodIndex, mediaPeriodId.periodUid,
mediaPeriodId.adGroupIndex, mediaPeriodId.adGroupIndex,
mediaPeriodId.adIndexInAdGroup, mediaPeriodId.adIndexInAdGroup,
mediaPeriodId.windowSequenceNumber + 1000); mediaPeriodId.windowSequenceNumber + 1000);
@ -322,8 +322,8 @@ public class MediaSourceTestRunner {
int windowIndex = windowIndexAndMediaPeriodId.first; int windowIndex = windowIndexAndMediaPeriodId.first;
MediaPeriodId mediaPeriodId = windowIndexAndMediaPeriodId.second; MediaPeriodId mediaPeriodId = windowIndexAndMediaPeriodId.second;
if (expectedLoads.remove(mediaPeriodId)) { if (expectedLoads.remove(mediaPeriodId)) {
assertThat(windowIndex) int periodIndex = timeline.getIndexOfPeriod(mediaPeriodId.periodUid);
.isEqualTo(timeline.getPeriod(mediaPeriodId.periodIndex, period).windowIndex); assertThat(windowIndex).isEqualTo(timeline.getPeriod(periodIndex, period).windowIndex);
} }
} }
assertWithMessage("Not all expected media source loads have been completed.") assertWithMessage("Not all expected media source loads have been completed.")