Commit 949bbcfb by tonihei Committed by Oliver Woodman

Add masking for playWhenReady.

Masking is needed as soon as updates to a value can happen both in EPI
and EPII. PlayWhenReady is currently not masked because all updates
happen in EPI only. As soon as we allow pausing at certain times
(e.g. end of a stream), playWhenReady changes may be triggered by EPII
as well and that's why we need masking.

To know when the value actually changed, we also need to update the
internal state to include whether playback is supppressed.

PiperOrigin-RevId: 300284613
parent f82bc244
......@@ -17,6 +17,7 @@ package com.google.android.exoplayer2;
import androidx.annotation.CheckResult;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Player.PlaybackSuppressionReason;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
......@@ -58,6 +59,10 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
public final TrackSelectorResult trackSelectorResult;
/** The {@link MediaPeriodId} of the currently loading media period in the {@link #timeline}. */
public final MediaPeriodId loadingMediaPeriodId;
/** Whether playback should proceed when {@link #playbackState} == {@link Player#STATE_READY}. */
public final boolean playWhenReady;
/** Reason why playback is suppressed even though {@link #playWhenReady} is {@code true}. */
@PlaybackSuppressionReason public final int playbackSuppressionReason;
/**
* Position up to which media is buffered in {@link #loadingMediaPeriodId) relative to the start
......@@ -94,6 +99,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
TrackGroupArray.EMPTY,
emptyTrackSelectorResult,
DUMMY_MEDIA_PERIOD_ID,
/* playWhenReady= */ false,
Player.PLAYBACK_SUPPRESSION_REASON_NONE,
/* bufferedPositionUs= */ 0,
/* totalBufferedDurationUs= */ 0,
/* positionUs= */ 0);
......@@ -124,6 +131,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
TrackGroupArray trackGroups,
TrackSelectorResult trackSelectorResult,
MediaPeriodId loadingMediaPeriodId,
boolean playWhenReady,
@PlaybackSuppressionReason int playbackSuppressionReason,
long bufferedPositionUs,
long totalBufferedDurationUs,
long positionUs) {
......@@ -136,6 +145,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
this.trackGroups = trackGroups;
this.trackSelectorResult = trackSelectorResult;
this.loadingMediaPeriodId = loadingMediaPeriodId;
this.playWhenReady = playWhenReady;
this.playbackSuppressionReason = playbackSuppressionReason;
this.bufferedPositionUs = bufferedPositionUs;
this.totalBufferedDurationUs = totalBufferedDurationUs;
this.positionUs = positionUs;
......@@ -177,6 +188,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
......@@ -200,6 +213,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
......@@ -223,6 +238,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
......@@ -246,6 +263,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
......@@ -269,6 +288,8 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
......@@ -292,6 +313,37 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
}
/**
* Copies playback info with new information about whether playback should proceed when ready.
*
* @param playWhenReady Whether playback should proceed when {@link #playbackState} == {@link
* Player#STATE_READY}.
* @param playbackSuppressionReason Reason why playback is suppressed even though {@link
* #playWhenReady} is {@code true}.
* @return Copied playback info with new information.
*/
@CheckResult
public PlaybackInfo copyWithPlayWhenReady(
boolean playWhenReady, @PlaybackSuppressionReason int playbackSuppressionReason) {
return new PlaybackInfo(
timeline,
periodId,
requestedContentPositionUs,
playbackState,
playbackError,
isLoading,
trackGroups,
trackSelectorResult,
loadingMediaPeriodId,
playWhenReady,
playbackSuppressionReason,
bufferedPositionUs,
totalBufferedDurationUs,
positionUs);
......
......@@ -548,6 +548,7 @@ public final class ExoPlayerTest {
// only on seek processed callback).
.seek(5)
.seek(60)
.waitForSeekProcessed()
.play()
.build();
final List<Integer> playbackStatesWhenSeekProcessed = new ArrayList<>();
......@@ -2790,6 +2791,7 @@ public final class ExoPlayerTest {
.pause()
.waitForPlaybackState(Player.STATE_READY)
.seek(/* windowIndex= */ 1, /* positionMs= */ 0)
.waitForSeekProcessed()
.play()
.build();
List<TrackGroupArray> trackGroupsList = new ArrayList<>();
......
......@@ -421,6 +421,8 @@ public final class MediaPeriodQueueTest {
/* trackGroups= */ null,
/* trackSelectorResult= */ null,
/* loadingMediaPeriodId= */ null,
/* playWhenReady= */ false,
Player.PLAYBACK_SUPPRESSION_REASON_NONE,
/* bufferedPositionUs= */ 0,
/* totalBufferedDurationUs= */ 0,
/* positionUs= */ 0);
......
......@@ -315,6 +315,7 @@ public final class AnalyticsCollectorTest {
.pause()
.waitForPlaybackState(Player.STATE_READY)
.seek(/* windowIndex= */ 1, /* positionMs= */ 0)
.waitForSeekProcessed()
.play()
.build();
TestAnalyticsListener listener = runAnalyticsTest(mediaSource, actionSchedule);
......@@ -327,8 +328,8 @@ public final class AnalyticsCollectorTest {
WINDOW_0 /* setPlayWhenReady=false */,
period0 /* READY */,
period1 /* BUFFERING */,
period1 /* READY */,
period1 /* setPlayWhenReady=true */,
period1 /* READY */,
period1 /* ENDED */);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
.containsExactly(WINDOW_0 /* PLAYLIST_CHANGED */, period0 /* SOURCE_UPDATE */);
......@@ -466,6 +467,9 @@ public final class AnalyticsCollectorTest {
.pause()
.waitForPlaybackState(Player.STATE_READY)
.setMediaSources(/* resetPosition= */ false, mediaSource2)
.waitForTimelineChanged()
// Wait until loading started to prevent flakiness caused by loading finishing too fast.
.waitForIsLoading(true)
.play()
.build();
TestAnalyticsListener listener = runAnalyticsTest(mediaSource1, actionSchedule);
......@@ -486,7 +490,7 @@ public final class AnalyticsCollectorTest {
WINDOW_0 /* setPlayWhenReady=false */,
period0Seq0 /* READY */,
WINDOW_0 /* BUFFERING */,
WINDOW_0 /* setPlayWhenReady=true */,
period0Seq1 /* setPlayWhenReady=true */,
period0Seq1 /* READY */,
period0Seq1 /* ENDED */);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
......@@ -545,6 +549,8 @@ public final class AnalyticsCollectorTest {
.waitForPlaybackState(Player.STATE_IDLE)
.seek(/* positionMs= */ 0)
.prepare()
// Wait until loading started to assert loading events without flakiness.
.waitForIsLoading(true)
.play()
.waitForPlaybackState(Player.STATE_ENDED)
.build();
......@@ -698,6 +704,8 @@ public final class AnalyticsCollectorTest {
.waitForIsLoading(true)
.waitForIsLoading(false)
.removeMediaItem(/* index= */ 0)
.waitForPlaybackState(Player.STATE_BUFFERING)
.waitForPlaybackState(Player.STATE_READY)
.play()
.build();
TestAnalyticsListener listener = runAnalyticsTest(fakeMediaSource, actionSchedule);
......@@ -719,8 +727,8 @@ public final class AnalyticsCollectorTest {
WINDOW_0 /* BUFFERING */,
period0Seq0 /* READY */,
period0Seq1 /* BUFFERING */,
period0Seq1 /* setPlayWhenReady=true */,
period0Seq1 /* READY */,
period0Seq1 /* setPlayWhenReady=true */,
period0Seq1 /* ENDED */);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
.containsExactly(
......@@ -815,6 +823,19 @@ public final class AnalyticsCollectorTest {
}
})
.pause()
// Ensure everything is preloaded.
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForIsLoading(true)
.waitForIsLoading(false)
.waitForPlaybackState(Player.STATE_READY)
// Wait in each content part to ensure previously triggered events get a chance to be
// delivered. This prevents flakiness caused by playback progressing too fast.
......@@ -1018,6 +1039,8 @@ public final class AnalyticsCollectorTest {
.waitForIsLoading(false)
// Seek behind the midroll.
.seek(6 * C.MICROS_PER_SECOND)
// Wait until loading started again to assert loading events without flakiness.
.waitForIsLoading(true)
.play()
.waitForPlaybackState(Player.STATE_ENDED)
.build();
......@@ -1047,8 +1070,8 @@ public final class AnalyticsCollectorTest {
WINDOW_0 /* setPlayWhenReady=false */,
WINDOW_0 /* BUFFERING */,
contentBeforeMidroll /* READY */,
contentAfterMidroll /* setPlayWhenReady=true */,
midrollAd /* BUFFERING */,
midrollAd /* setPlayWhenReady=true */,
midrollAd /* READY */,
contentAfterMidroll /* ENDED */);
assertThat(listener.getEvents(EVENT_TIMELINE_CHANGED))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment