mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Handle duration change in updateQueuedPeriods.
We can keep the reading media period and continue playing if we haven't read beyond the new duration. Otherwise, we can keep the period, but need to reset the renderers as we already read too far. PiperOrigin-RevId: 231406252
This commit is contained in:
parent
d49e7ebae2
commit
f4e7af3fd0
@ -1376,12 +1376,34 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
}
|
||||
}
|
||||
|
||||
if (!queue.updateQueuedPeriods(rendererPositionUs)) {
|
||||
if (!queue.updateQueuedPeriods(rendererPositionUs, getMaxRendererReadPositionUs())) {
|
||||
seekToCurrentPosition(/* sendDiscontinuity= */ false);
|
||||
}
|
||||
handleLoadingMediaPeriodChanged(/* loadingTrackSelectionChanged= */ false);
|
||||
}
|
||||
|
||||
private long getMaxRendererReadPositionUs() {
|
||||
MediaPeriodHolder readingHolder = queue.getReadingPeriod();
|
||||
if (readingHolder == null) {
|
||||
return 0;
|
||||
}
|
||||
long maxReadPositionUs = readingHolder.getRendererOffset();
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
if (renderers[i].getState() == Renderer.STATE_DISABLED
|
||||
|| renderers[i].getStream() != readingHolder.sampleStreams[i]) {
|
||||
// Ignore disabled renderers and renderers with sample streams from previous periods.
|
||||
continue;
|
||||
}
|
||||
long readingPositionUs = renderers[i].getReadingPositionUs();
|
||||
if (readingPositionUs == C.TIME_END_OF_SOURCE) {
|
||||
return C.TIME_END_OF_SOURCE;
|
||||
} else {
|
||||
maxReadPositionUs = Math.max(readingPositionUs, maxReadPositionUs);
|
||||
}
|
||||
}
|
||||
return maxReadPositionUs;
|
||||
}
|
||||
|
||||
private void handleSourceInfoRefreshEndedPlayback() {
|
||||
setState(Player.STATE_ENDED);
|
||||
// Reset, but retain the source so that it can still be used should a seek occur.
|
||||
|
@ -61,8 +61,8 @@ import com.google.android.exoplayer2.util.Assertions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Timeline}. Call {@link #updateQueuedPeriods(long)} to update the queued media
|
||||
* periods to take into account the new timeline.
|
||||
* Sets the {@link Timeline}. Call {@link #updateQueuedPeriods(long, long)} to update the queued
|
||||
* media periods to take into account the new timeline.
|
||||
*/
|
||||
public void setTimeline(Timeline timeline) {
|
||||
this.timeline = timeline;
|
||||
@ -293,9 +293,12 @@ import com.google.android.exoplayer2.util.Assertions;
|
||||
* consistent with the new timeline.
|
||||
*
|
||||
* @param rendererPositionUs The current renderer position in microseconds.
|
||||
* @param maxRendererReadPositionUs The maximum renderer position up to which renderers have read
|
||||
* the current reading media period in microseconds, or {@link C#TIME_END_OF_SOURCE} if they
|
||||
* have read to the end.
|
||||
* @return Whether the timeline change has been handled completely.
|
||||
*/
|
||||
public boolean updateQueuedPeriods(long rendererPositionUs) {
|
||||
public boolean updateQueuedPeriods(long rendererPositionUs, long maxRendererReadPositionUs) {
|
||||
// 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.
|
||||
@ -327,8 +330,18 @@ import com.google.android.exoplayer2.util.Assertions;
|
||||
periodHolder.info = newPeriodInfo.copyWithContentPositionUs(oldPeriodInfo.contentPositionUs);
|
||||
|
||||
if (!areDurationsCompatible(oldPeriodInfo.durationUs, newPeriodInfo.durationUs)) {
|
||||
// The period duration changed. Remove all subsequent periods.
|
||||
return !removeAfter(periodHolder);
|
||||
// The period duration changed. Remove all subsequent periods and check whether we read
|
||||
// beyond the new duration.
|
||||
long newDurationInRendererTime =
|
||||
newPeriodInfo.durationUs == C.TIME_UNSET
|
||||
? Long.MAX_VALUE
|
||||
: periodHolder.toRendererTime(newPeriodInfo.durationUs);
|
||||
boolean isReadingAndReadBeyondNewDuration =
|
||||
periodHolder == reading
|
||||
&& (maxRendererReadPositionUs == C.TIME_END_OF_SOURCE
|
||||
|| maxRendererReadPositionUs >= newDurationInRendererTime);
|
||||
boolean readingPeriodRemoved = removeAfter(periodHolder);
|
||||
return !readingPeriodRemoved && !isReadingAndReadBeyondNewDuration;
|
||||
}
|
||||
|
||||
previousPeriodHolder = periodHolder;
|
||||
|
@ -37,6 +37,7 @@ import org.robolectric.RobolectricTestRunner;
|
||||
public final class MediaPeriodQueueTest {
|
||||
|
||||
private static final long CONTENT_DURATION_US = 30 * C.MICROS_PER_SECOND;
|
||||
private static final long AD_DURATION_US = 10 * C.MICROS_PER_SECOND;
|
||||
private static final long FIRST_AD_START_TIME_US = 10 * C.MICROS_PER_SECOND;
|
||||
private static final long SECOND_AD_START_TIME_US = 20 * C.MICROS_PER_SECOND;
|
||||
|
||||
@ -65,8 +66,8 @@ public final class MediaPeriodQueueTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNextMediaPeriodInfo_withoutAds_returnsLastMediaPeriodInfo() {
|
||||
setupInitialTimeline(/* initialPositionUs= */ 0);
|
||||
public void getNextMediaPeriodInfo_withoutAds_returnsLastMediaPeriodInfo() {
|
||||
setupTimeline(/* initialPositionUs= */ 0);
|
||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||
/* startPositionUs= */ 0,
|
||||
/* endPositionUs= */ C.TIME_UNSET,
|
||||
@ -76,8 +77,8 @@ public final class MediaPeriodQueueTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNextMediaPeriodInfo_withPrerollAd_returnsCorrectMediaPeriodInfos() {
|
||||
setupInitialTimeline(/* initialPositionUs= */ 0, /* adGroupTimesUs= */ 0);
|
||||
public void getNextMediaPeriodInfo_withPrerollAd_returnsCorrectMediaPeriodInfos() {
|
||||
setupTimeline(/* initialPositionUs= */ 0, /* adGroupTimesUs= */ 0);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||
assertNextMediaPeriodInfoIsAd(/* adGroupIndex= */ 0, /* contentPositionUs= */ 0);
|
||||
advance();
|
||||
@ -90,8 +91,8 @@ public final class MediaPeriodQueueTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNextMediaPeriodInfo_withMidrollAds_returnsCorrectMediaPeriodInfos() {
|
||||
setupInitialTimeline(
|
||||
public void getNextMediaPeriodInfo_withMidrollAds_returnsCorrectMediaPeriodInfos() {
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US,
|
||||
SECOND_AD_START_TIME_US);
|
||||
@ -128,8 +129,8 @@ public final class MediaPeriodQueueTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNextMediaPeriodInfo_withMidrollAndPostroll_returnsCorrectMediaPeriodInfos() {
|
||||
setupInitialTimeline(
|
||||
public void getNextMediaPeriodInfo_withMidrollAndPostroll_returnsCorrectMediaPeriodInfos() {
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US,
|
||||
C.TIME_END_OF_SOURCE);
|
||||
@ -164,8 +165,8 @@ public final class MediaPeriodQueueTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNextMediaPeriodInfo_withPostrollLoadError_returnsEmptyFinalMediaPeriodInfo() {
|
||||
setupInitialTimeline(/* initialPositionUs= */ 0, /* adGroupTimesUs= */ C.TIME_END_OF_SOURCE);
|
||||
public void getNextMediaPeriodInfo_withPostrollLoadError_returnsEmptyFinalMediaPeriodInfo() {
|
||||
setupTimeline(/* initialPositionUs= */ 0, /* adGroupTimesUs= */ C.TIME_END_OF_SOURCE);
|
||||
assertGetNextMediaPeriodInfoReturnsContentMediaPeriod(
|
||||
/* startPositionUs= */ 0,
|
||||
/* endPositionUs= */ C.TIME_END_OF_SOURCE,
|
||||
@ -182,7 +183,168 @@ public final class MediaPeriodQueueTest {
|
||||
/* nextAdGroupIndex= */ C.INDEX_UNSET);
|
||||
}
|
||||
|
||||
private void setupInitialTimeline(long initialPositionUs, long... adGroupTimesUs) {
|
||||
@Test
|
||||
public void
|
||||
updateQueuedPeriods_withDurationChangeAfterReadingPeriod_handlesChangeAndRemovesPeriodsAfterChangedPeriod() {
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US,
|
||||
SECOND_AD_START_TIME_US);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||
enqueueNext(); // Content before first ad.
|
||||
advancePlaying();
|
||||
enqueueNext(); // First ad.
|
||||
enqueueNext(); // Content between ads.
|
||||
enqueueNext(); // Second ad.
|
||||
|
||||
// Change position of second ad (= change duration of content between ads).
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US,
|
||||
SECOND_AD_START_TIME_US + 1);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||
boolean changeHandled =
|
||||
mediaPeriodQueue.updateQueuedPeriods(
|
||||
/* rendererPositionUs= */ 0, /* maxRendererReadPositionUs= */ 0);
|
||||
|
||||
assertThat(changeHandled).isTrue();
|
||||
assertThat(getQueueLength()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
updateQueuedPeriods_withDurationChangeBeforeReadingPeriod_doesntHandleChangeAndRemovesPeriodsAfterChangedPeriod() {
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US,
|
||||
SECOND_AD_START_TIME_US);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||
enqueueNext(); // Content before first ad.
|
||||
advancePlaying();
|
||||
enqueueNext(); // First ad.
|
||||
enqueueNext(); // Content between ads.
|
||||
enqueueNext(); // Second ad.
|
||||
advanceReading(); // Reading first ad.
|
||||
|
||||
// Change position of first ad (= change duration of content before first ad).
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US + 1,
|
||||
SECOND_AD_START_TIME_US);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||
boolean changeHandled =
|
||||
mediaPeriodQueue.updateQueuedPeriods(
|
||||
/* rendererPositionUs= */ 0, /* maxRendererReadPositionUs= */ FIRST_AD_START_TIME_US);
|
||||
|
||||
assertThat(changeHandled).isFalse();
|
||||
assertThat(getQueueLength()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
updateQueuedPeriods_withDurationChangeInReadingPeriodAfterReadingPosition_handlesChangeAndRemovesPeriodsAfterChangedPeriod() {
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US,
|
||||
SECOND_AD_START_TIME_US);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||
enqueueNext(); // Content before first ad.
|
||||
advancePlaying();
|
||||
enqueueNext(); // First ad.
|
||||
enqueueNext(); // Content between ads.
|
||||
enqueueNext(); // Second ad.
|
||||
advanceReading(); // Reading first ad.
|
||||
advanceReading(); // Reading content between ads.
|
||||
|
||||
// Change position of second ad (= change duration of content between ads).
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US,
|
||||
SECOND_AD_START_TIME_US - 1000);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||
long readingPositionAtStartOfContentBetweenAds = FIRST_AD_START_TIME_US + AD_DURATION_US;
|
||||
boolean changeHandled =
|
||||
mediaPeriodQueue.updateQueuedPeriods(
|
||||
/* rendererPositionUs= */ 0,
|
||||
/* maxRendererReadPositionUs= */ readingPositionAtStartOfContentBetweenAds);
|
||||
|
||||
assertThat(changeHandled).isTrue();
|
||||
assertThat(getQueueLength()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
updateQueuedPeriods_withDurationChangeInReadingPeriodBeforeReadingPosition_doesntHandleChangeAndRemovesPeriodsAfterChangedPeriod() {
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US,
|
||||
SECOND_AD_START_TIME_US);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||
enqueueNext(); // Content before first ad.
|
||||
advancePlaying();
|
||||
enqueueNext(); // First ad.
|
||||
enqueueNext(); // Content between ads.
|
||||
enqueueNext(); // Second ad.
|
||||
advanceReading(); // Reading first ad.
|
||||
advanceReading(); // Reading content between ads.
|
||||
|
||||
// Change position of second ad (= change duration of content between ads).
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US,
|
||||
SECOND_AD_START_TIME_US - 1000);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||
long readingPositionAtEndOfContentBetweenAds = SECOND_AD_START_TIME_US + AD_DURATION_US;
|
||||
boolean changeHandled =
|
||||
mediaPeriodQueue.updateQueuedPeriods(
|
||||
/* rendererPositionUs= */ 0,
|
||||
/* maxRendererReadPositionUs= */ readingPositionAtEndOfContentBetweenAds);
|
||||
|
||||
assertThat(changeHandled).isFalse();
|
||||
assertThat(getQueueLength()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
updateQueuedPeriods_withDurationChangeInReadingPeriodReadToEnd_doesntHandleChangeAndRemovesPeriodsAfterChangedPeriod() {
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US,
|
||||
SECOND_AD_START_TIME_US);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||
enqueueNext(); // Content before first ad.
|
||||
advancePlaying();
|
||||
enqueueNext(); // First ad.
|
||||
enqueueNext(); // Content between ads.
|
||||
enqueueNext(); // Second ad.
|
||||
advanceReading(); // Reading first ad.
|
||||
advanceReading(); // Reading content between ads.
|
||||
|
||||
// Change position of second ad (= change duration of content between ads).
|
||||
setupTimeline(
|
||||
/* initialPositionUs= */ 0,
|
||||
/* adGroupTimesUs= */ FIRST_AD_START_TIME_US,
|
||||
SECOND_AD_START_TIME_US - 1000);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 0);
|
||||
setAdGroupLoaded(/* adGroupIndex= */ 1);
|
||||
boolean changeHandled =
|
||||
mediaPeriodQueue.updateQueuedPeriods(
|
||||
/* rendererPositionUs= */ 0, /* maxRendererReadPositionUs= */ C.TIME_END_OF_SOURCE);
|
||||
|
||||
assertThat(changeHandled).isFalse();
|
||||
assertThat(getQueueLength()).isEqualTo(3);
|
||||
}
|
||||
|
||||
private void setupTimeline(long initialPositionUs, long... adGroupTimesUs) {
|
||||
adPlaybackState =
|
||||
new AdPlaybackState(adGroupTimesUs).withContentDurationUs(CONTENT_DURATION_US);
|
||||
timeline = new SinglePeriodAdTimeline(CONTENT_TIMELINE, adPlaybackState);
|
||||
@ -206,9 +368,21 @@ public final class MediaPeriodQueueTest {
|
||||
}
|
||||
|
||||
private void advance() {
|
||||
enqueueNext();
|
||||
advancePlaying();
|
||||
}
|
||||
|
||||
private void advancePlaying() {
|
||||
mediaPeriodQueue.advancePlayingPeriod();
|
||||
}
|
||||
|
||||
private void advanceReading() {
|
||||
mediaPeriodQueue.advanceReadingPeriod();
|
||||
}
|
||||
|
||||
private void enqueueNext() {
|
||||
mediaPeriodQueue.enqueueNextMediaPeriod(
|
||||
rendererCapabilities, trackSelector, allocator, mediaSource, getNextMediaPeriodInfo());
|
||||
mediaPeriodQueue.advancePlayingPeriod();
|
||||
}
|
||||
|
||||
private MediaPeriodInfo getNextMediaPeriodInfo() {
|
||||
@ -216,10 +390,16 @@ public final class MediaPeriodQueueTest {
|
||||
}
|
||||
|
||||
private void setAdGroupLoaded(int adGroupIndex) {
|
||||
long[][] newDurations = new long[adPlaybackState.adGroupCount][];
|
||||
for (int i = 0; i < adPlaybackState.adGroupCount; i++) {
|
||||
newDurations[i] =
|
||||
i == adGroupIndex ? new long[] {AD_DURATION_US} : adPlaybackState.adGroups[i].durationsUs;
|
||||
}
|
||||
adPlaybackState =
|
||||
adPlaybackState
|
||||
.withAdCount(adGroupIndex, /* adCount= */ 1)
|
||||
.withAdUri(adGroupIndex, /* adIndexInAdGroup= */ 0, AD_URI);
|
||||
.withAdUri(adGroupIndex, /* adIndexInAdGroup= */ 0, AD_URI)
|
||||
.withAdDurationsUs(newDurations);
|
||||
updateTimeline();
|
||||
}
|
||||
|
||||
@ -266,8 +446,18 @@ public final class MediaPeriodQueueTest {
|
||||
/* startPositionUs= */ 0,
|
||||
contentPositionUs,
|
||||
/* endPositionUs= */ C.TIME_UNSET,
|
||||
/* durationUs= */ C.TIME_UNSET,
|
||||
/* durationUs= */ AD_DURATION_US,
|
||||
/* isLastInTimelinePeriod= */ false,
|
||||
/* isFinal= */ false));
|
||||
}
|
||||
|
||||
private int getQueueLength() {
|
||||
int length = 0;
|
||||
MediaPeriodHolder periodHolder = mediaPeriodQueue.getFrontPeriod();
|
||||
while (periodHolder != null) {
|
||||
length++;
|
||||
periodHolder = periodHolder.getNext();
|
||||
}
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user