From d51b98dd1f6290504b4be9d86d71e70ebe217513 Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 20 Aug 2018 09:24:58 -0700 Subject: [PATCH] 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 --- .../android/exoplayer2/ExoPlayerImpl.java | 17 +- .../exoplayer2/ExoPlayerImplInternal.java | 106 ++-- .../android/exoplayer2/MediaPeriodHolder.java | 4 +- .../android/exoplayer2/MediaPeriodInfo.java | 14 - .../android/exoplayer2/MediaPeriodQueue.java | 156 +++--- .../android/exoplayer2/PlaybackInfo.java | 20 +- .../google/android/exoplayer2/Timeline.java | 22 +- .../analytics/AnalyticsCollector.java | 17 +- .../source/AbstractConcatenatedTimeline.java | 45 +- .../source/ConcatenatingMediaSource.java | 47 +- .../source/ExtractorMediaSource.java | 1 - .../exoplayer2/source/LoopingMediaSource.java | 12 +- .../exoplayer2/source/MediaSource.java | 49 +- .../exoplayer2/source/MergingMediaSource.java | 14 +- .../source/SingleSampleMediaSource.java | 1 - .../exoplayer2/source/ads/AdsMediaSource.java | 41 +- .../android/exoplayer2/util/EventLogger.java | 3 +- .../android/exoplayer2/ExoPlayerTest.java | 43 +- .../analytics/AnalyticsCollectorTest.java | 464 ++++++++++-------- .../source/ClippingMediaSourceTest.java | 15 +- .../source/ConcatenatingMediaSourceTest.java | 94 ++-- .../source/SinglePeriodTimelineTest.java | 12 +- .../source/dash/DashMediaPeriod.java | 9 +- .../source/dash/DashMediaSource.java | 2 +- .../exoplayer2/source/hls/HlsMediaSource.java | 1 - .../source/smoothstreaming/SsMediaSource.java | 1 - .../testutil/FakeAdaptiveMediaSource.java | 2 +- .../exoplayer2/testutil/FakeMediaSource.java | 5 +- .../testutil/MediaSourceTestRunner.java | 12 +- 29 files changed, 649 insertions(+), 580 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 648168816f..7149cf5cc2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -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 periodIndexAndPosition = + Pair 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; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 0a5b4b72d4..f2aeb7321d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -594,7 +594,7 @@ import java.util.Collections; long periodPositionUs; long contentPositionUs; boolean seekPositionAdjusted; - Pair resolvedSeekPosition = + Pair 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 periodPosition = + Pair 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 periodPosition; + Pair 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 defaultPosition = getPeriodPosition(timeline, - timeline.getFirstWindowIndex(shuffleModeEnabled), C.TIME_UNSET); - int periodIndex = defaultPosition.first; + Pair 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 defaultPosition = + Pair 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 defaultPosition = getPeriodPosition(timeline, - timeline.getPeriod(newPeriodIndex, period).windowIndex, C.TIME_UNSET); - newPeriodIndex = defaultPosition.first; + Pair 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 resolveSeekPosition( + private Pair 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 periodPosition; + Pair 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 getPeriodPosition( + private Pair 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); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java index a74a2ac1ca..e42dd03dbe 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodHolder.java @@ -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]; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodInfo.java index b887e8222e..ba19b54c3f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodInfo.java @@ -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( diff --git a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java index e9be2d985e..2edf7bb8c6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java @@ -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 defaultPosition = + Pair 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; } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/PlaybackInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/PlaybackInfo.java index b338de15b4..02058c0484 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/PlaybackInfo.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/PlaybackInfo.java @@ -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, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java b/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java index a1a0e9b152..1639920aaa 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java @@ -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 getPeriodPosition(Window window, Period period, int windowIndex, - long windowPositionUs) { + public final Pair 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 getPeriodPosition(Window window, Period period, int windowIndex, - long windowPositionUs, long defaultPositionProjectionUs) { + public final Pair 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); } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java index 262187586b..3a158060cf 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java @@ -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); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java b/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java index 305a249e4a..4a3505749a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/AbstractConcatenatedTimeline.java @@ -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); } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java index 8987e9cb56..9850427063 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java @@ -60,8 +60,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource mediaSourceHolders; - private final MediaSourceHolder query; private final Map mediaSourceByMediaPeriod; + private final Map mediaSourceByUid; private final List pendingOnCompletionActions; private final boolean isAtomic; private final boolean useLazyPreparation; @@ -125,10 +125,10 @@ public class ConcatenatingMediaSource extends CompositeMediaSource 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 { private final Map childMediaPeriodIdToMediaPeriodId; private final Map 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 { 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 { } } - @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) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSource.java index fb4c64ae6e..6b0f5c8eeb 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSource.java @@ -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; } - } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java index d33cfb8abd..b1367cb19f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MergingMediaSource.java @@ -68,10 +68,10 @@ public final class MergingMediaSource extends CompositeMediaSource { private static final int PERIOD_COUNT_UNSET = -1; private final MediaSource[] mediaSources; + private final Timeline[] timelines; private final ArrayList 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 { 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 { @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 { @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 { 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); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java index 24f49cb086..c75c9541a4 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaSource.java @@ -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, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java index e3df34bed8..0e218d5efe 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java @@ -176,7 +176,7 @@ public final class AdsMediaSource extends CompositeMediaSource { // 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 { 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 { 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 { 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 { new AdPrepareErrorListener(adUri, adGroupIndex, adIndexInAdGroup)); List 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 { 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 { 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 { 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 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 { 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 { } } + 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 { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java b/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java index 3ca463e5e4..0324630f1f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java @@ -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; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 5cba9267b5..6409c1b604 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -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 diff --git a/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java index 8a2612cacc..9ec22c0d51 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/analytics/AnalyticsCollectorTest.java @@ -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 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 reportedEvents; public TestAnalyticsListener() { reportedEvents = new ArrayList<>(); + lastReportedTimeline = Timeline.EMPTY; } public List 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)); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java index 1008ac26bf..ee8cdf4887 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java @@ -469,11 +469,11 @@ public final class ClippingMediaSourceTest { private static MediaLoadData getClippingMediaSourceMediaLoadData( long clippingStartUs, long clippingEndUs, final long eventStartUs, final long eventEndUs) throws IOException { + Timeline timeline = + new SinglePeriodTimeline( + TEST_PERIOD_DURATION_US, /* isSeekable= */ true, /* isDynamic= */ false); FakeMediaSource fakeMediaSource = - new FakeMediaSource( - new SinglePeriodTimeline( - TEST_PERIOD_DURATION_US, /* isSeekable= */ true, /* isDynamic= */ false), - /* manifest= */ null) { + 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(); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java index 49b4a0d24f..3ac962f6c3 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java @@ -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)); } } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java index 2587b78d99..1b57341d33 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/SinglePeriodTimelineTest.java @@ -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 position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, 1); + Pair 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 position = timeline.getPeriodPosition(window, period, 0, C.TIME_UNSET, - windowDurationUs + 1); + Pair 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); } diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java index d52049931f..a501435262 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java @@ -73,8 +73,8 @@ import java.util.List; private final PlayerEmsgHandler playerEmsgHandler; private final IdentityHashMap, PlayerTrackEmsgHandler> trackEmsgHandlerBySampleStream; + private final EventDispatcher eventDispatcher; - private EventDispatcher eventDispatcher; private @Nullable Callback callback; private ChunkSampleStream[] 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) { diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index ec73c3a05e..e9d9c42e0b 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -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 = diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index 670554fe73..24067fcab1 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -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, diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java index dcaecf6d60..efd733d651 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java @@ -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( diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java index 6510c8b425..e6e4ea53cc 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java @@ -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, diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java index 90e86d6c5a..82d6026482 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java @@ -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 = diff --git a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java index 4f9012ab27..70e7669dfb 100644 --- a/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java +++ b/testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/MediaSourceTestRunner.java @@ -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.")