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

View File

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

View File

@ -62,7 +62,6 @@ import com.google.android.exoplayer2.util.Assertions;
* @param trackSelector The track selector.
* @param allocator The allocator.
* @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.
*/
public MediaPeriodHolder(
@ -71,13 +70,12 @@ import com.google.android.exoplayer2.util.Assertions;
TrackSelector trackSelector,
Allocator allocator,
MediaSource mediaSource,
Object uid,
MediaPeriodInfo info) {
this.rendererCapabilities = rendererCapabilities;
this.rendererPositionOffsetUs = rendererPositionOffsetUs - info.startPositionUs;
this.trackSelector = trackSelector;
this.mediaSource = mediaSource;
this.uid = Assertions.checkNotNull(uid);
this.uid = Assertions.checkNotNull(info.id.periodUid);
this.info = info;
sampleStreams = new SampleStream[rendererCapabilities.length];
mayRetainStreamFlags = new boolean[rendererCapabilities.length];

View File

@ -62,20 +62,6 @@ import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
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. */
public MediaPeriodInfo copyWithStartPositionUs(long startPositionUs) {
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
* the queue. Also has a reference to the media period currently being read.
*/
@SuppressWarnings("UngroupedOverloads")
/* package */ final class MediaPeriodQueue {
/**
@ -135,7 +134,6 @@ import com.google.android.exoplayer2.util.Assertions;
* @param trackSelector The track selector.
* @param allocator The allocator.
* @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.
*/
public MediaPeriod enqueueNextMediaPeriod(
@ -143,7 +141,6 @@ import com.google.android.exoplayer2.util.Assertions;
TrackSelector trackSelector,
Allocator allocator,
MediaSource mediaSource,
Object uid,
MediaPeriodInfo info) {
long rendererPositionOffsetUs =
loading == null
@ -156,7 +153,6 @@ import com.google.android.exoplayer2.util.Assertions;
trackSelector,
allocator,
mediaSource,
uid,
info);
if (loading != null) {
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
// is set, once all cases handled by ExoPlayerImplInternal.handleSourceInfoRefreshed can be
// 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
// period.
MediaPeriodHolder previousPeriodHolder = null;
MediaPeriodHolder periodHolder = getFrontPeriod();
while (periodHolder != null) {
if (previousPeriodHolder == null) {
periodHolder.info = getUpdatedMediaPeriodInfo(periodHolder.info, periodIndex);
periodHolder.info = getUpdatedMediaPeriodInfo(periodHolder.info);
} else {
// Check this period holder still follows the previous one, based on the new timeline.
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.
return !removeAfter(previousPeriodHolder);
}
// Update the period index.
periodHolder.info = getUpdatedMediaPeriodInfo(periodHolder.info, periodIndex);
// Update the period holder.
periodHolder.info = getUpdatedMediaPeriodInfo(periodHolder.info);
// Check the media period information matches the new timeline.
if (!canKeepMediaPeriodHolder(periodHolder, periodInfo)) {
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
* 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 newPeriodIndex The new period index in the new timeline for the existing media period.
* @param info Media period info for a media period based on an old timeline.
* @return The updated media period info for the current timeline.
*/
public MediaPeriodInfo getUpdatedMediaPeriodInfo(
MediaPeriodInfo mediaPeriodInfo, int newPeriodIndex) {
return getUpdatedMediaPeriodInfo(
mediaPeriodInfo, mediaPeriodInfo.id.copyWithPeriodIndex(newPeriodIndex));
public MediaPeriodInfo getUpdatedMediaPeriodInfo(MediaPeriodInfo info) {
boolean isLastInPeriod = isLastInPeriod(info.id);
boolean isLastInTimeline = isLastInTimeline(info.id, isLastInPeriod);
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
* 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.
* @return The identifier for the first media period to play, taking into account unplayed ads.
*/
public MediaPeriodId resolveMediaPeriodIdForAds(int periodIndex, long positionUs) {
long windowSequenceNumber = resolvePeriodIndexToWindowSequenceNumber(periodIndex);
return resolveMediaPeriodIdForAds(periodIndex, positionUs, windowSequenceNumber);
public MediaPeriodId resolveMediaPeriodIdForAds(Object periodUid, long positionUs) {
long windowSequenceNumber = resolvePeriodIndexToWindowSequenceNumber(periodUid);
return resolveMediaPeriodIdForAds(periodUid, positionUs, windowSequenceNumber);
}
// 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
* 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 windowSequenceNumber The sequence number of the window in the buffered sequence of
* windows this period is part of.
* @return The identifier for the first media period to play, taking into account unplayed ads.
*/
private MediaPeriodId resolveMediaPeriodIdForAds(
int periodIndex, long positionUs, long windowSequenceNumber) {
timeline.getPeriod(periodIndex, period);
Object periodUid, long positionUs, long windowSequenceNumber) {
timeline.getPeriodByUid(periodUid, period);
int adGroupIndex = period.getAdGroupIndexForPositionUs(positionUs);
if (adGroupIndex == C.INDEX_UNSET) {
int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(positionUs);
@ -397,24 +406,23 @@ import com.google.android.exoplayer2.util.Assertions;
nextAdGroupIndex == C.INDEX_UNSET
? C.TIME_END_OF_SOURCE
: period.getAdGroupTimeUs(nextAdGroupIndex);
return new MediaPeriodId(periodIndex, windowSequenceNumber, endPositionUs);
return new MediaPeriodId(periodUid, windowSequenceNumber, endPositionUs);
} else {
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
* reusing the window sequence number of an existing matching media period or by creating a new
* window sequence number.
* Resolves the specified period uid to a corresponding window sequence number. Either by reusing
* the window sequence number of an existing matching media period or by creating a new window
* 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.
*/
private long resolvePeriodIndexToWindowSequenceNumber(int periodIndex) {
Object periodUid = timeline.getPeriod(periodIndex, period, /* setIds= */ true).uid;
int windowIndex = period.windowIndex;
private long resolvePeriodIndexToWindowSequenceNumber(Object periodUid) {
int windowIndex = timeline.getPeriodByUid(periodUid, period).windowIndex;
if (oldFrontPeriodUid != null) {
int oldFrontPeriodIndex = timeline.getIndexOfPeriod(oldFrontPeriodUid);
if (oldFrontPeriodIndex != C.INDEX_UNSET) {
@ -469,32 +477,32 @@ import com.google.android.exoplayer2.util.Assertions;
if (lastValidPeriodHolder == null) {
return true;
}
int currentPeriodIndex = timeline.getIndexOfPeriod(lastValidPeriodHolder.uid);
while (true) {
int nextPeriodIndex =
timeline.getNextPeriodIndex(
lastValidPeriodHolder.info.id.periodIndex,
period,
window,
repeatMode,
shuffleModeEnabled);
currentPeriodIndex, period, window, repeatMode, shuffleModeEnabled);
while (lastValidPeriodHolder.next != null
&& !lastValidPeriodHolder.info.isLastInTimelinePeriod) {
lastValidPeriodHolder = lastValidPeriodHolder.next;
}
if (nextPeriodIndex == C.INDEX_UNSET
|| lastValidPeriodHolder.next == null
|| lastValidPeriodHolder.next.info.id.periodIndex != nextPeriodIndex) {
if (nextPeriodIndex == C.INDEX_UNSET || lastValidPeriodHolder.next == null) {
break;
}
int nextPeriodHolderPeriodIndex = timeline.getIndexOfPeriod(lastValidPeriodHolder.next.uid);
if (nextPeriodHolderPeriodIndex != nextPeriodIndex) {
break;
}
lastValidPeriodHolder = lastValidPeriodHolder.next;
currentPeriodIndex = nextPeriodIndex;
}
// Release any period holders that don't match the new period order.
boolean readingPeriodRemoved = removeAfter(lastValidPeriodHolder);
// Update the period info for the last holder, as it may now be the last period in the timeline.
lastValidPeriodHolder.info =
getUpdatedMediaPeriodInfo(lastValidPeriodHolder.info, lastValidPeriodHolder.info.id);
lastValidPeriodHolder.info = getUpdatedMediaPeriodInfo(lastValidPeriodHolder.info);
// If renderers may have read from a period that's been removed, it is necessary to restart.
return !readingPeriodRemoved || !hasPlayingPeriod();
@ -525,9 +533,10 @@ import com.google.android.exoplayer2.util.Assertions;
// timeline is updated, to avoid repeatedly checking the same timeline.
MediaPeriodInfo mediaPeriodInfo = mediaPeriodHolder.info;
if (mediaPeriodInfo.isLastInTimelinePeriod) {
int currentPeriodIndex = timeline.getIndexOfPeriod(mediaPeriodInfo.id.periodUid);
int nextPeriodIndex =
timeline.getNextPeriodIndex(
mediaPeriodInfo.id.periodIndex, period, window, repeatMode, shuffleModeEnabled);
currentPeriodIndex, period, window, repeatMode, shuffleModeEnabled);
if (nextPeriodIndex == C.INDEX_UNSET) {
// We can't create a next period yet.
return null;
@ -546,7 +555,7 @@ import com.google.android.exoplayer2.util.Assertions;
// the buffer, and start buffering from this point.
long defaultPositionProjectionUs =
mediaPeriodHolder.getRendererOffset() + mediaPeriodInfo.durationUs - rendererPositionUs;
Pair<Integer, Long> defaultPosition =
Pair<Object, Long> defaultPosition =
timeline.getPeriodPosition(
window,
period,
@ -556,7 +565,7 @@ import com.google.android.exoplayer2.util.Assertions;
if (defaultPosition == null) {
return null;
}
nextPeriodIndex = defaultPosition.first;
nextPeriodUid = defaultPosition.first;
startPositionUs = defaultPosition.second;
if (mediaPeriodHolder.next != null && mediaPeriodHolder.next.uid.equals(nextPeriodUid)) {
windowSequenceNumber = mediaPeriodHolder.next.info.id.windowSequenceNumber;
@ -567,12 +576,12 @@ import com.google.android.exoplayer2.util.Assertions;
startPositionUs = 0;
}
MediaPeriodId periodId =
resolveMediaPeriodIdForAds(nextPeriodIndex, startPositionUs, windowSequenceNumber);
resolveMediaPeriodIdForAds(nextPeriodUid, startPositionUs, windowSequenceNumber);
return getMediaPeriodInfo(periodId, startPositionUs, startPositionUs);
}
MediaPeriodId currentPeriodId = mediaPeriodInfo.id;
timeline.getPeriod(currentPeriodId.periodIndex, period);
timeline.getPeriodByUid(currentPeriodId.periodUid, period);
if (currentPeriodId.isAd()) {
int adGroupIndex = currentPeriodId.adGroupIndex;
int adCountInCurrentAdGroup = period.getAdCountInAdGroup(adGroupIndex);
@ -586,7 +595,7 @@ import com.google.android.exoplayer2.util.Assertions;
return !period.isAdAvailable(adGroupIndex, nextAdIndexInAdGroup)
? null
: getMediaPeriodInfoForAd(
currentPeriodId.periodIndex,
currentPeriodId.periodUid,
adGroupIndex,
nextAdIndexInAdGroup,
mediaPeriodInfo.contentPositionUs,
@ -594,7 +603,7 @@ import com.google.android.exoplayer2.util.Assertions;
} else {
// Play content from the ad group position.
return getMediaPeriodInfoForContent(
currentPeriodId.periodIndex,
currentPeriodId.periodUid,
mediaPeriodInfo.contentPositionUs,
currentPeriodId.windowSequenceNumber);
}
@ -604,7 +613,7 @@ import com.google.android.exoplayer2.util.Assertions;
if (nextAdGroupIndex == C.INDEX_UNSET) {
// The next ad group can't be played. Play content from the ad group position instead.
return getMediaPeriodInfoForContent(
currentPeriodId.periodIndex,
currentPeriodId.periodUid,
mediaPeriodInfo.id.endPositionUs,
currentPeriodId.windowSequenceNumber);
}
@ -612,7 +621,7 @@ import com.google.android.exoplayer2.util.Assertions;
return !period.isAdAvailable(nextAdGroupIndex, adIndexInAdGroup)
? null
: getMediaPeriodInfoForAd(
currentPeriodId.periodIndex,
currentPeriodId.periodUid,
nextAdGroupIndex,
adIndexInAdGroup,
mediaPeriodInfo.id.endPositionUs,
@ -634,7 +643,7 @@ import com.google.android.exoplayer2.util.Assertions;
}
long contentDurationUs = period.getDurationUs();
return getMediaPeriodInfoForAd(
currentPeriodId.periodIndex,
currentPeriodId.periodUid,
adGroupIndex,
adIndexInAdGroup,
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(
MediaPeriodId id, long contentPositionUs, long startPositionUs) {
timeline.getPeriod(id.periodIndex, period);
timeline.getPeriodByUid(id.periodUid, period);
if (id.isAd()) {
if (!period.isAdAvailable(id.adGroupIndex, id.adIndexInAdGroup)) {
return null;
}
return getMediaPeriodInfoForAd(
id.periodIndex,
id.periodUid,
id.adGroupIndex,
id.adIndexInAdGroup,
contentPositionUs,
id.windowSequenceNumber);
} else {
return getMediaPeriodInfoForContent(id.periodIndex, startPositionUs, id.windowSequenceNumber);
return getMediaPeriodInfoForContent(id.periodUid, startPositionUs, id.windowSequenceNumber);
}
}
private MediaPeriodInfo getMediaPeriodInfoForAd(
int periodIndex,
Object periodUid,
int adGroupIndex,
int adIndexInAdGroup,
long contentPositionUs,
long windowSequenceNumber) {
MediaPeriodId id =
new MediaPeriodId(periodIndex, adGroupIndex, adIndexInAdGroup, windowSequenceNumber);
new MediaPeriodId(periodUid, adGroupIndex, adIndexInAdGroup, windowSequenceNumber);
boolean isLastInPeriod = isLastInPeriod(id);
boolean isLastInTimeline = isLastInTimeline(id, isLastInPeriod);
long durationUs =
timeline
.getPeriod(id.periodIndex, period)
.getPeriodByUid(id.periodUid, period)
.getAdDurationUs(id.adGroupIndex, id.adIndexInAdGroup);
long startPositionUs =
adIndexInAdGroup == period.getFirstAdIndexToPlay(adGroupIndex)
@ -708,14 +697,14 @@ import com.google.android.exoplayer2.util.Assertions;
}
private MediaPeriodInfo getMediaPeriodInfoForContent(
int periodIndex, long startPositionUs, long windowSequenceNumber) {
Object periodUid, long startPositionUs, long windowSequenceNumber) {
int nextAdGroupIndex = period.getAdGroupIndexAfterPositionUs(startPositionUs);
long endPositionUs =
nextAdGroupIndex == C.INDEX_UNSET
? C.TIME_END_OF_SOURCE
: period.getAdGroupTimeUs(nextAdGroupIndex);
MediaPeriodId id = new MediaPeriodId(periodIndex, windowSequenceNumber, endPositionUs);
timeline.getPeriod(id.periodIndex, period);
MediaPeriodId id = new MediaPeriodId(periodUid, windowSequenceNumber, endPositionUs);
timeline.getPeriodByUid(id.periodUid, period);
boolean isLastInPeriod = isLastInPeriod(id);
boolean isLastInTimeline = isLastInTimeline(id, isLastInPeriod);
long durationUs =
@ -725,7 +714,7 @@ import com.google.android.exoplayer2.util.Assertions;
}
private boolean isLastInPeriod(MediaPeriodId id) {
int adGroupCount = timeline.getPeriod(id.periodIndex, period).getAdGroupCount();
int adGroupCount = timeline.getPeriodByUid(id.periodUid, period).getAdGroupCount();
if (adGroupCount == 0) {
return true;
}
@ -749,9 +738,10 @@ import com.google.android.exoplayer2.util.Assertions;
}
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
&& timeline.isLastPeriod(id.periodIndex, period, window, repeatMode, shuffleModeEnabled)
&& timeline.isLastPeriod(periodIndex, period, window, repeatMode, shuffleModeEnabled)
&& 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
* 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}. */
public final Timeline timeline;
@ -149,23 +150,6 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
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) {
return new PlaybackInfo(
timeline,

View File

@ -702,13 +702,13 @@ public abstract class Timeline {
* Calls {@link #getPeriodPosition(Window, Period, int, long, long)} with a zero default position
* projection.
*/
public final Pair<Integer, Long> getPeriodPosition(Window window, Period period, int windowIndex,
long windowPositionUs) {
public final Pair<Object, Long> getPeriodPosition(
Window window, Period period, int windowIndex, long windowPositionUs) {
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 period A {@link Period} that may be overwritten.
@ -717,12 +717,16 @@ public abstract class Timeline {
* start position.
* @param defaultPositionProjectionUs If {@code windowPositionUs} is {@link C#TIME_UNSET}, the
* 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
* position could not be projected by {@code defaultPositionProjectionUs}.
*/
public final Pair<Integer, Long> getPeriodPosition(Window window, Period period, int windowIndex,
long windowPositionUs, long defaultPositionProjectionUs) {
public final Pair<Object, Long> getPeriodPosition(
Window window,
Period period,
int windowIndex,
long windowPositionUs,
long defaultPositionProjectionUs) {
Assertions.checkIndex(windowIndex, 0, getWindowCount());
getWindow(windowIndex, window, false, defaultPositionProjectionUs);
if (windowPositionUs == C.TIME_UNSET) {
@ -733,13 +737,13 @@ public abstract class Timeline {
}
int periodIndex = window.firstPeriodIndex;
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
&& periodIndex < window.lastPeriodIndex) {
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) {
MediaPeriodId match = null;
if (timeline != null) {
int timelinePeriodCount = timeline.getPeriodCount();
for (int i = 0; i < activeMediaPeriods.size(); i++) {
WindowAndMediaPeriodId mediaPeriod = activeMediaPeriods.get(i);
int periodIndex = mediaPeriod.mediaPeriodId.periodIndex;
if (periodIndex < timelinePeriodCount
int periodIndex = timeline.getIndexOfPeriod(mediaPeriod.mediaPeriodId.periodUid);
if (periodIndex != C.INDEX_UNSET
&& timeline.getPeriod(periodIndex, period).windowIndex == windowIndex) {
if (match != null) {
// Ambiguous match.
@ -731,10 +730,10 @@ public class AnalyticsCollector
public void onTimelineChanged(Timeline timeline) {
for (int i = 0; i < activeMediaPeriods.size(); i++) {
activeMediaPeriods.set(
i, updateMediaPeriodToNewTimeline(activeMediaPeriods.get(i), timeline));
i, updateWindowIndexToNewTimeline(activeMediaPeriods.get(i), timeline));
}
if (readingMediaPeriod != null) {
readingMediaPeriod = updateMediaPeriodToNewTimeline(readingMediaPeriod, timeline);
readingMediaPeriod = updateWindowIndexToNewTimeline(readingMediaPeriod, timeline);
}
this.timeline = timeline;
updateLastReportedPlayingMediaPeriod();
@ -779,19 +778,17 @@ public class AnalyticsCollector
}
}
private WindowAndMediaPeriodId updateMediaPeriodToNewTimeline(
private WindowAndMediaPeriodId updateWindowIndexToNewTimeline(
WindowAndMediaPeriodId mediaPeriod, Timeline newTimeline) {
if (newTimeline.isEmpty() || timeline.isEmpty()) {
return mediaPeriod;
}
Object uid = timeline.getUidOfPeriod(mediaPeriod.mediaPeriodId.periodIndex);
int newPeriodIndex = newTimeline.getIndexOfPeriod(uid);
int newPeriodIndex = newTimeline.getIndexOfPeriod(mediaPeriod.mediaPeriodId.periodUid);
if (newPeriodIndex == C.INDEX_UNSET) {
return mediaPeriod;
}
int newWindowIndex = newTimeline.getPeriod(newPeriodIndex, period).windowIndex;
return new WindowAndMediaPeriodId(
newWindowIndex, mediaPeriod.mediaPeriodId.copyWithPeriodIndex(newPeriodIndex));
return new WindowAndMediaPeriodId(newWindowIndex, mediaPeriod.mediaPeriodId);
}
}

View File

@ -29,6 +29,37 @@ import com.google.android.exoplayer2.Timeline;
private final ShuffleOrder shuffleOrder;
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.
*
@ -170,9 +201,8 @@ import com.google.android.exoplayer2.Timeline;
@Override
public final Period getPeriodByUid(Object uid, Period period) {
Pair<?, ?> childUidAndPeriodUid = (Pair<?, ?>) uid;
Object childUid = childUidAndPeriodUid.first;
Object periodUid = childUidAndPeriodUid.second;
Object childUid = getChildTimelineUidFromConcatenatedUid(uid);
Object periodUid = getChildPeriodUidFromConcatenatedUid(uid);
int childIndex = getChildIndexByChildUid(childUid);
int firstWindowIndexInChild = getFirstWindowIndexByChildIndex(childIndex);
getTimelineByChildIndex(childIndex).getPeriodByUid(periodUid, period);
@ -190,7 +220,7 @@ import com.google.android.exoplayer2.Timeline;
setIds);
period.windowIndex += firstWindowIndexInChild;
if (setIds) {
period.uid = Pair.create(getChildUidByChildIndex(childIndex), period.uid);
period.uid = getConcatenatedUid(getChildUidByChildIndex(childIndex), period.uid);
}
return period;
}
@ -200,9 +230,8 @@ import com.google.android.exoplayer2.Timeline;
if (!(uid instanceof Pair)) {
return C.INDEX_UNSET;
}
Pair<?, ?> childUidAndPeriodUid = (Pair<?, ?>) uid;
Object childUid = childUidAndPeriodUid.first;
Object periodUid = childUidAndPeriodUid.second;
Object childUid = getChildTimelineUidFromConcatenatedUid(uid);
Object periodUid = getChildPeriodUidFromConcatenatedUid(uid);
int childIndex = getChildIndexByChildUid(childUid);
if (childIndex == C.INDEX_UNSET) {
return C.INDEX_UNSET;
@ -218,7 +247,7 @@ import com.google.android.exoplayer2.Timeline;
int firstPeriodIndexInChild = getFirstPeriodIndexByChildIndex(childIndex);
Object periodUidInChild =
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.
private final List<MediaSourceHolder> mediaSourceHolders;
private final MediaSourceHolder query;
private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod;
private final Map<Object, MediaSourceHolder> mediaSourceByUid;
private final List<Runnable> pendingOnCompletionActions;
private final boolean isAtomic;
private final boolean useLazyPreparation;
@ -125,10 +125,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
}
this.shuffleOrder = shuffleOrder.getLength() > 0 ? shuffleOrder.cloneAndClear() : shuffleOrder;
this.mediaSourceByMediaPeriod = new IdentityHashMap<>();
this.mediaSourceByUid = new HashMap<>();
this.mediaSourcesPublic = new ArrayList<>();
this.mediaSourceHolders = new ArrayList<>();
this.pendingOnCompletionActions = new ArrayList<>();
this.query = new MediaSourceHolder(/* mediaSource= */ null);
this.isAtomic = isAtomic;
this.useLazyPreparation = useLazyPreparation;
window = new Timeline.Window();
@ -451,8 +451,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
@Override
public final MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
int mediaSourceHolderIndex = findMediaSourceHolderByPeriodIndex(id.periodIndex);
MediaSourceHolder holder = mediaSourceHolders.get(mediaSourceHolderIndex);
Object mediaSourceHolderUid = getMediaSourceHolderUid(id.periodUid);
MediaSourceHolder holder = Assertions.checkNotNull(mediaSourceByUid.get(mediaSourceHolderUid));
DeferredMediaPeriod mediaPeriod = new DeferredMediaPeriod(holder.mediaSource, id, allocator);
mediaSourceByMediaPeriod.put(mediaPeriod, holder);
holder.activeMediaPeriods.add(mediaPeriod);
@ -460,8 +460,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
holder.hasStartedPreparing = true;
prepareChildSource(holder, holder.mediaSource);
} else if (holder.isPrepared) {
MediaPeriodId idInSource =
id.copyWithPeriodIndex(id.periodIndex - holder.firstPeriodIndexInChild);
MediaPeriodId idInSource = id.copyWithPeriodUid(getChildPeriodUid(holder, id.periodUid));
mediaPeriod.createPeriod(idInSource);
}
return mediaPeriod;
@ -482,6 +481,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
public final void releaseSourceInternal() {
super.releaseSourceInternal();
mediaSourceHolders.clear();
mediaSourceByUid.clear();
player = null;
playerApplicationHandler = null;
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.
if (mediaSourceHolder.activeMediaPeriods.get(i).id.windowSequenceNumber
== mediaPeriodId.windowSequenceNumber) {
return mediaPeriodId.copyWithPeriodIndex(
mediaPeriodId.periodIndex + mediaSourceHolder.firstPeriodIndexInChild);
Object periodUid = getPeriodUid(mediaSourceHolder, mediaPeriodId.periodUid);
return mediaPeriodId.copyWithPeriodUid(periodUid);
}
}
return null;
@ -633,6 +633,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
newMediaSourceHolder.timeline.getWindowCount(),
newMediaSourceHolder.timeline.getPeriodCount());
mediaSourceHolders.add(newIndex, newMediaSourceHolder);
mediaSourceByUid.put(newMediaSourceHolder.uid, newMediaSourceHolder);
if (!useLazyPreparation) {
newMediaSourceHolder.hasStartedPreparing = true;
prepareChildSource(newMediaSourceHolder, newMediaSourceHolder.mediaSource);
@ -672,8 +673,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
DeferredMediaPeriod deferredMediaPeriod = mediaSourceHolder.activeMediaPeriods.get(i);
deferredMediaPeriod.setDefaultPreparePositionUs(defaultPeriodPositionUs);
MediaPeriodId idInSource =
deferredMediaPeriod.id.copyWithPeriodIndex(
deferredMediaPeriod.id.periodIndex - mediaSourceHolder.firstPeriodIndexInChild);
deferredMediaPeriod.id.copyWithPeriodUid(
getChildPeriodUid(mediaSourceHolder, deferredMediaPeriod.id.periodUid));
deferredMediaPeriod.createPeriod(idInSource);
}
mediaSourceHolder.isPrepared = true;
@ -689,6 +690,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
private void removeMediaSourceInternal(int index) {
MediaSourceHolder holder = mediaSourceHolders.remove(index);
mediaSourceByUid.remove(holder.uid);
Timeline oldTimeline = holder.timeline;
correctOffsets(
index,
@ -727,17 +729,22 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
}
}
private int findMediaSourceHolderByPeriodIndex(int periodIndex) {
query.firstPeriodIndexInChild = periodIndex;
int index = Collections.binarySearch(mediaSourceHolders, query);
if (index < 0) {
return -index - 2;
/** Return uid of media source holder from period uid of concatenated source. */
private static Object getMediaSourceHolderUid(Object periodUid) {
return ConcatenatedTimeline.getChildTimelineUidFromConcatenatedUid(periodUid);
}
while (index < mediaSourceHolders.size() - 1
&& mediaSourceHolders.get(index + 1).firstPeriodIndexInChild == periodIndex) {
index++;
/** 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;
}
return index;
private static Object getPeriodUid(MediaSourceHolder holder, Object childPeriodUid) {
if (holder.timeline.replacedId.equals(childPeriodUid)) {
childPeriodUid = DeferredTimeline.DUMMY_ID;
}
return ConcatenatedTimeline.getConcatenatedUid(holder.uid, childPeriodUid);
}
/** 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
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
Assertions.checkArgument(id.periodIndex == 0);
DataSource dataSource = dataSourceFactory.createDataSource();
if (transferListener != null) {
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<MediaPeriod, MediaPeriodId> mediaPeriodToChildMediaPeriodId;
private int childPeriodCount;
/**
* Loops the provided source indefinitely. Note that it is usually better to use
* {@link ExoPlayer#setRepeatMode(int)}.
@ -80,7 +78,8 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> {
if (loopCount == Integer.MAX_VALUE) {
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);
MediaPeriod mediaPeriod = childSource.createPeriod(childMediaPeriodId, allocator);
mediaPeriodToChildMediaPeriodId.put(mediaPeriod, childMediaPeriodId);
@ -96,16 +95,9 @@ public final class LoopingMediaSource extends CompositeMediaSource<Void> {
}
}
@Override
public void releaseSourceInternal() {
super.releaseSourceInternal();
childPeriodCount = 0;
}
@Override
protected void onChildSourceInfoRefreshed(
Void id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest) {
childPeriodCount = timeline.getPeriodCount();
Timeline loopingTimeline =
loopCount != Integer.MAX_VALUE
? new LoopingTimeline(timeline, loopCount)

View File

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

View File

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

View File

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

View File

@ -176,7 +176,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
// Used to identify the content "child" source for CompositeMediaSource.
private static final MediaPeriodId DUMMY_CONTENT_MEDIA_PERIOD_ID =
new MediaPeriodId(/* periodIndex= */ 0);
new MediaPeriodId(/* periodUid= */ new Object());
private final MediaSource contentMediaSource;
private final MediaSourceFactory adMediaSourceFactory;
@ -194,7 +194,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
private Object contentManifest;
private AdPlaybackState adPlaybackState;
private MediaSource[][] adGroupMediaSources;
private long[][] adDurationsUs;
private Timeline[][] adGroupTimelines;
/**
* 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<>();
period = new Timeline.Period();
adGroupMediaSources = new MediaSource[0][];
adDurationsUs = new long[0][];
adGroupTimelines = new Timeline[0][];
adsLoader.setSupportedContentTypes(adMediaSourceFactory.getSupportedTypes());
}
@ -341,8 +341,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
int adCount = adIndexInAdGroup + 1;
adGroupMediaSources[adGroupIndex] =
Arrays.copyOf(adGroupMediaSources[adGroupIndex], adCount);
adDurationsUs[adGroupIndex] = Arrays.copyOf(adDurationsUs[adGroupIndex], adCount);
Arrays.fill(adDurationsUs[adGroupIndex], oldAdCount, adCount, C.TIME_UNSET);
adGroupTimelines[adGroupIndex] = Arrays.copyOf(adGroupTimelines[adGroupIndex], adCount);
}
adGroupMediaSources[adGroupIndex][adIndexInAdGroup] = adMediaSource;
deferredMediaPeriodByAdMediaSource.put(adMediaSource, new ArrayList<>());
@ -354,8 +353,9 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
new AdPrepareErrorListener(adUri, adGroupIndex, adIndexInAdGroup));
List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.get(mediaSource);
if (mediaPeriods == null) {
MediaPeriodId adSourceMediaPeriodId =
new MediaPeriodId(/* periodIndex= */ 0, id.windowSequenceNumber);
Object periodUid =
adGroupTimelines[adGroupIndex][adIndexInAdGroup].getUidOfPeriod(/* periodIndex= */ 0);
MediaPeriodId adSourceMediaPeriodId = new MediaPeriodId(periodUid, id.windowSequenceNumber);
deferredMediaPeriod.createPeriod(adSourceMediaPeriodId);
} else {
// 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;
adPlaybackState = null;
adGroupMediaSources = new MediaSource[0][];
adDurationsUs = new long[0][];
adGroupTimelines = new Timeline[0][];
mainHandler.post(adsLoader::detachPlayer);
}
@ -424,8 +424,8 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
if (this.adPlaybackState == null) {
adGroupMediaSources = new MediaSource[adPlaybackState.adGroupCount][];
Arrays.fill(adGroupMediaSources, new MediaSource[0]);
adDurationsUs = new long[adPlaybackState.adGroupCount][];
Arrays.fill(adDurationsUs, new long[0]);
adGroupTimelines = new Timeline[adPlaybackState.adGroupCount][];
Arrays.fill(adGroupTimelines, new Timeline[0]);
}
this.adPlaybackState = adPlaybackState;
maybeUpdateSourceInfo();
@ -440,13 +440,14 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
private void onAdSourceInfoRefreshed(MediaSource mediaSource, int adGroupIndex,
int adIndexInAdGroup, Timeline timeline) {
Assertions.checkArgument(timeline.getPeriodCount() == 1);
adDurationsUs[adGroupIndex][adIndexInAdGroup] = timeline.getPeriod(0, period).getDurationUs();
adGroupTimelines[adGroupIndex][adIndexInAdGroup] = timeline;
List<DeferredMediaPeriod> mediaPeriods = deferredMediaPeriodByAdMediaSource.remove(mediaSource);
if (mediaPeriods != null) {
Object periodUid = timeline.getUidOfPeriod(/* periodIndex= */ 0);
for (int i = 0; i < mediaPeriods.size(); i++) {
DeferredMediaPeriod mediaPeriod = mediaPeriods.get(i);
MediaPeriodId adSourceMediaPeriodId =
new MediaPeriodId(/* periodIndex= */ 0, mediaPeriod.id.windowSequenceNumber);
new MediaPeriodId(periodUid, mediaPeriod.id.windowSequenceNumber);
mediaPeriod.createPeriod(adSourceMediaPeriodId);
}
}
@ -455,7 +456,7 @@ public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
private void maybeUpdateSourceInfo() {
if (adPlaybackState != null && contentTimeline != null) {
adPlaybackState = adPlaybackState.withAdDurationsUs(adDurationsUs);
adPlaybackState = adPlaybackState.withAdDurationsUs(getAdDurations(adGroupTimelines, period));
Timeline timeline =
adPlaybackState.adGroupCount == 0
? 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. */
private final class ComponentListener implements AdsLoader.EventListener {

View File

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

View File

@ -187,10 +187,28 @@ public final class ExoPlayerTest {
@Test
public void testReadAheadToEndDoesNotResetRenderer() throws Exception {
// Use sufficiently short periods to ensure the player attempts to read all at once.
TimelineWindowDefinition windowDefinition =
TimelineWindowDefinition windowDefinition0 =
new TimelineWindowDefinition(
/* isSeekable= */ false, /* isDynamic= */ false, /* durationUs= */ 100_000);
Timeline timeline = new FakeTimeline(windowDefinition, windowDefinition, windowDefinition);
/* periodCount= */ 1,
/* 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);
FakeMediaClockRenderer audioRenderer =
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.
assertThat(mediaSource.getCreatedMediaPeriods())
.containsExactly(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 1),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 2))
new MediaPeriodId(
timeline1.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0),
new MediaPeriodId(
timeline1.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1),
new MediaPeriodId(
timeline2.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 2))
.inOrder();
}
@ -2057,10 +2078,14 @@ public final class ExoPlayerTest {
testRunner.assertPlayedPeriodIndices(0, 1, 0, 1);
assertThat(mediaSource.getCreatedMediaPeriods())
.containsAllOf(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 0));
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0),
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0));
assertThat(mediaSource.getCreatedMediaPeriods())
.doesNotContain(new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 1));
.doesNotContain(
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 1));
}
@Test

View File

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

View File

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

View File

@ -274,14 +274,16 @@ public final class ConcatenatingMediaSourceTest {
// called yet.
MediaPeriod lazyPeriod =
testRunner.createPeriod(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
CountDownLatch preparedCondition = testRunner.preparePeriod(lazyPeriod, 0);
assertThat(preparedCondition.getCount()).isEqualTo(1);
// Assert that a second period can also be created and released without problems.
MediaPeriod secondLazyPeriod =
testRunner.createPeriod(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
testRunner.releasePeriod(secondLazyPeriod);
// 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.
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(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
new MediaPeriodId(timelineContentOnlyPeriodUid0, /* windowSequenceNumber= */ 0));
mediaSourceContentOnly.assertMediaPeriodCreated(
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 0));
new MediaPeriodId(timelineContentOnlyPeriodUid1, /* windowSequenceNumber= */ 0));
mediaSourceWithAds.assertMediaPeriodCreated(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 1));
new MediaPeriodId(timelineWithAdsPeriodUid0, /* windowSequenceNumber= */ 1));
mediaSourceWithAds.assertMediaPeriodCreated(
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 1));
new MediaPeriodId(timelineWithAdsPeriodUid1, /* windowSequenceNumber= */ 1));
mediaSourceWithAds.assertMediaPeriodCreated(
new MediaPeriodId(
/* periodIndex= */ 0,
timelineWithAdsPeriodUid0,
/* adGroupIndex= */ 0,
/* adIndexInAdGroup= */ 0,
/* windowSequenceNumber= */ 1));
mediaSourceWithAds.assertMediaPeriodCreated(
new MediaPeriodId(
/* periodIndex= */ 1,
timelineWithAdsPeriodUid1,
/* adGroupIndex= */ 0,
/* adIndexInAdGroup= */ 0,
/* windowSequenceNumber= */ 1));
@ -721,10 +727,11 @@ public final class ConcatenatingMediaSourceTest {
public void testRemoveChildSourceWithActiveMediaPeriod() throws IOException {
FakeMediaSource childSource = createFakeMediaSource();
mediaSource.addMediaSource(childSource);
testRunner.prepareSource();
Timeline timeline = testRunner.prepareSource();
MediaPeriod mediaPeriod =
testRunner.createPeriod(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
mediaSource.removeMediaSource(/* index= */ 0);
testRunner.assertTimelineChangeBlocking();
testRunner.releasePeriod(mediaPeriod);
@ -734,8 +741,8 @@ public final class ConcatenatingMediaSourceTest {
@Test
public void testDuplicateMediaSources() throws IOException, InterruptedException {
FakeMediaSource childSource =
new FakeMediaSource(new FakeTimeline(/* windowCount= */ 2), /* manifest= */ null);
Timeline childTimeline = new FakeTimeline(/* windowCount= */ 2);
FakeMediaSource childSource = new FakeMediaSource(childTimeline, /* manifest= */ null);
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);
testRunner.assertPrepareAndReleaseAllPeriods();
Object childPeriodUid0 = childTimeline.getUidOfPeriod(/* periodIndex= */ 0);
Object childPeriodUid1 = childTimeline.getUidOfPeriod(/* periodIndex= */ 1);
assertThat(childSource.getCreatedMediaPeriods())
.containsAllOf(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 2),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 4),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 6),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 1),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 3),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 5),
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 7));
new MediaPeriodId(childPeriodUid0, /* windowSequenceNumber= */ 0),
new MediaPeriodId(childPeriodUid0, /* windowSequenceNumber= */ 2),
new MediaPeriodId(childPeriodUid0, /* windowSequenceNumber= */ 4),
new MediaPeriodId(childPeriodUid0, /* windowSequenceNumber= */ 6),
new MediaPeriodId(childPeriodUid1, /* windowSequenceNumber= */ 1),
new MediaPeriodId(childPeriodUid1, /* windowSequenceNumber= */ 3),
new MediaPeriodId(childPeriodUid1, /* windowSequenceNumber= */ 5),
new MediaPeriodId(childPeriodUid1, /* windowSequenceNumber= */ 7));
// Assert that only one manifest load is reported because the source is reused.
testRunner.assertCompletedManifestLoads(/* windowIndices= */ 0);
assertCompletedAllMediaPeriodLoads(timeline);
@ -765,8 +774,8 @@ public final class ConcatenatingMediaSourceTest {
@Test
public void testDuplicateNestedMediaSources() throws IOException, InterruptedException {
FakeMediaSource childSource =
new FakeMediaSource(new FakeTimeline(/* windowCount= */ 1), /* manifest= */ null);
Timeline childTimeline = new FakeTimeline(/* windowCount= */ 1);
FakeMediaSource childSource = new FakeMediaSource(childTimeline, /* manifest= */ null);
ConcatenatingMediaSource nestedConcatenation = new ConcatenatingMediaSource();
testRunner.prepareSource();
@ -780,13 +789,14 @@ public final class ConcatenatingMediaSourceTest {
TimelineAsserts.assertPeriodCounts(timeline, 1, 1, 1, 1, 1);
testRunner.assertPrepareAndReleaseAllPeriods();
Object childPeriodUid = childTimeline.getUidOfPeriod(/* periodIndex= */ 0);
assertThat(childSource.getCreatedMediaPeriods())
.containsAllOf(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 1),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 2),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 3),
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 4));
new MediaPeriodId(childPeriodUid, /* windowSequenceNumber= */ 0),
new MediaPeriodId(childPeriodUid, /* windowSequenceNumber= */ 1),
new MediaPeriodId(childPeriodUid, /* windowSequenceNumber= */ 2),
new MediaPeriodId(childPeriodUid, /* windowSequenceNumber= */ 3),
new MediaPeriodId(childPeriodUid, /* windowSequenceNumber= */ 4));
// Assert that only one manifest load is needed because the source is reused.
testRunner.assertCompletedManifestLoads(/* windowIndices= */ 0);
assertCompletedAllMediaPeriodLoads(timeline);
@ -844,16 +854,18 @@ public final class ConcatenatingMediaSourceTest {
ConcatenatingMediaSource childSource = new ConcatenatingMediaSource(nestedChildSources);
mediaSource.addMediaSource(childSource);
testRunner.prepareSource();
Timeline timeline = testRunner.prepareSource();
MediaPeriod mediaPeriod =
testRunner.createPeriod(
new MediaPeriodId(/* periodIndex= */ 1, /* windowSequenceNumber= */ 0));
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 1), /* windowSequenceNumber= */ 0));
childSource.moveMediaSource(/* currentIndex= */ 0, /* newIndex= */ 1);
testRunner.assertTimelineChangeBlocking();
timeline = testRunner.assertTimelineChangeBlocking();
testRunner.preparePeriod(mediaPeriod, /* positionUs= */ 0);
testRunner.assertCompletedMediaPeriodLoads(
new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
}
@Test
@ -882,8 +894,10 @@ public final class ConcatenatingMediaSourceTest {
new DefaultShuffleOrder(0),
childSources);
testRunner = new MediaSourceTestRunner(mediaSource, /* allocator= */ null);
testRunner.prepareSource();
testRunner.createPeriod(new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
Timeline timeline = testRunner.prepareSource();
testRunner.createPeriod(
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0));
assertThat(childSources[0].isPrepared()).isTrue();
assertThat(childSources[1].isPrepared()).isFalse();
@ -899,13 +913,16 @@ public final class ConcatenatingMediaSourceTest {
new DefaultShuffleOrder(0),
childSources);
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 media source. FakeMediaSource.prepareSource asserts that it's not called twice, so
// creating two periods shouldn't throw.
testRunner.createPeriod(new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
testRunner.createPeriod(new MediaPeriodId(/* periodIndex= */ 0, /* windowSequenceNumber= */ 0));
MediaPeriodId mediaPeriodId =
new MediaPeriodId(
timeline.getUidOfPeriod(/* periodIndex= */ 0), /* windowSequenceNumber= */ 0);
testRunner.createPeriod(mediaPeriodId);
testRunner.createPeriod(mediaPeriodId);
}
private void assertCompletedAllMediaPeriodLoads(Timeline timeline) {
@ -918,11 +935,12 @@ public final class ConcatenatingMediaSourceTest {
periodIndex <= window.lastPeriodIndex;
periodIndex++) {
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 adIndex = 0; adIndex < period.getAdCountInAdGroup(adGroupIndex); adIndex++) {
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() {
SinglePeriodTimeline timeline = new SinglePeriodTimeline(C.TIME_UNSET, false, true);
// 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();
// Should return (0, 0) without a position projection.
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);
}
@ -66,16 +66,16 @@ public final class SinglePeriodTimelineTest {
/* isDynamic= */ true,
/* tag= */ null);
// Should return null with a positive position projection beyond window duration.
Pair<Integer, Long> position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET,
windowDurationUs + 1);
Pair<Object, Long> position =
timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, windowDurationUs + 1);
assertThat(position).isNull();
// Should return (0, duration) with a projection equal to window duration.
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);
// Should return (0, 0) without a position projection.
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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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