mirror of
https://github.com/samsonjs/media.git
synced 2026-04-09 11:55:46 +00:00
Rollback of 5485133e5c
*** Original commit ***
Rollback of 949bbcfb2e
*** Original commit ***
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 c...
***
PiperOrigin-RevId: 300330307
This commit is contained in:
parent
b46f64ab70
commit
7b82a3c889
6 changed files with 304 additions and 120 deletions
|
|
@ -70,12 +70,13 @@ import java.util.concurrent.TimeoutException;
|
|||
private final List<Playlist.MediaSourceHolder> mediaSourceHolders;
|
||||
private final boolean useLazyPreparation;
|
||||
|
||||
private boolean playWhenReady;
|
||||
@PlaybackSuppressionReason private int playbackSuppressionReason;
|
||||
@RepeatMode private int repeatMode;
|
||||
private boolean shuffleModeEnabled;
|
||||
private int pendingOperationAcks;
|
||||
private boolean hasPendingSeek;
|
||||
private boolean hasPendingDiscontinuity;
|
||||
@DiscontinuityReason private int pendingDiscontinuityReason;
|
||||
@PlayWhenReadyChangeReason private int pendingPlayWhenReadyChangeReason;
|
||||
private boolean foregroundMode;
|
||||
private int pendingSetPlaybackParametersAcks;
|
||||
private PlaybackParameters playbackParameters;
|
||||
|
|
@ -121,9 +122,7 @@ import java.util.concurrent.TimeoutException;
|
|||
this.renderers = Assertions.checkNotNull(renderers);
|
||||
this.trackSelector = Assertions.checkNotNull(trackSelector);
|
||||
this.useLazyPreparation = useLazyPreparation;
|
||||
playWhenReady = false;
|
||||
repeatMode = Player.REPEAT_MODE_OFF;
|
||||
shuffleModeEnabled = false;
|
||||
listeners = new CopyOnWriteArrayList<>();
|
||||
mediaSourceHolders = new ArrayList<>();
|
||||
shuffleOrder = new ShuffleOrder.DefaultShuffleOrder(/* length= */ 0);
|
||||
|
|
@ -135,7 +134,6 @@ import java.util.concurrent.TimeoutException;
|
|||
period = new Timeline.Period();
|
||||
playbackParameters = PlaybackParameters.DEFAULT;
|
||||
seekParameters = SeekParameters.DEFAULT;
|
||||
playbackSuppressionReason = PLAYBACK_SUPPRESSION_REASON_NONE;
|
||||
maskingWindowIndex = C.INDEX_UNSET;
|
||||
eventHandler =
|
||||
new Handler(looper) {
|
||||
|
|
@ -156,7 +154,6 @@ import java.util.concurrent.TimeoutException;
|
|||
emptyTrackSelectorResult,
|
||||
loadControl,
|
||||
bandwidthMeter,
|
||||
playWhenReady,
|
||||
repeatMode,
|
||||
shuffleModeEnabled,
|
||||
analyticsCollector,
|
||||
|
|
@ -237,7 +234,7 @@ import java.util.concurrent.TimeoutException;
|
|||
@Override
|
||||
@PlaybackSuppressionReason
|
||||
public int getPlaybackSuppressionReason() {
|
||||
return playbackSuppressionReason;
|
||||
return playbackInfo.playbackSuppressionReason;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
|
|
@ -283,6 +280,7 @@ import java.util.concurrent.TimeoutException;
|
|||
/* positionDiscontinuity= */ false,
|
||||
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
|
||||
/* ignored */ TIMELINE_CHANGE_REASON_SOURCE_UPDATE,
|
||||
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||
/* seekProcessed= */ false);
|
||||
}
|
||||
|
||||
|
|
@ -421,9 +419,10 @@ import java.util.concurrent.TimeoutException;
|
|||
|
||||
@Override
|
||||
public void setShuffleOrder(ShuffleOrder shuffleOrder) {
|
||||
Timeline timeline = maskTimeline();
|
||||
maskWithCurrentPosition();
|
||||
pendingOperationAcks++;
|
||||
this.shuffleOrder = shuffleOrder;
|
||||
Timeline timeline = maskTimeline();
|
||||
internalPlayer.setShuffleOrder(shuffleOrder);
|
||||
notifyListeners(
|
||||
listener -> listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED));
|
||||
|
|
@ -443,40 +442,37 @@ import java.util.concurrent.TimeoutException;
|
|||
@PlaybackSuppressionReason int playbackSuppressionReason,
|
||||
@PlayWhenReadyChangeReason int playWhenReadyChangeReason) {
|
||||
boolean oldIsPlaying = isPlaying();
|
||||
boolean oldInternalPlayWhenReady =
|
||||
this.playWhenReady && this.playbackSuppressionReason == PLAYBACK_SUPPRESSION_REASON_NONE;
|
||||
boolean internalPlayWhenReady =
|
||||
playWhenReady && playbackSuppressionReason == PLAYBACK_SUPPRESSION_REASON_NONE;
|
||||
if (oldInternalPlayWhenReady != internalPlayWhenReady) {
|
||||
internalPlayer.setPlayWhenReady(internalPlayWhenReady);
|
||||
boolean playWhenReadyChanged = playbackInfo.playWhenReady != playWhenReady;
|
||||
boolean suppressionReasonChanged =
|
||||
playbackInfo.playbackSuppressionReason != playbackSuppressionReason;
|
||||
if (!playWhenReadyChanged && !suppressionReasonChanged) {
|
||||
return;
|
||||
}
|
||||
boolean playWhenReadyChanged = this.playWhenReady != playWhenReady;
|
||||
boolean suppressionReasonChanged = this.playbackSuppressionReason != playbackSuppressionReason;
|
||||
this.playWhenReady = playWhenReady;
|
||||
this.playbackSuppressionReason = playbackSuppressionReason;
|
||||
maskWithCurrentPosition();
|
||||
pendingOperationAcks++;
|
||||
playbackInfo = playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason);
|
||||
internalPlayer.setPlayWhenReady(playWhenReady, playbackSuppressionReason);
|
||||
boolean isPlaying = isPlaying();
|
||||
boolean isPlayingChanged = oldIsPlaying != isPlaying;
|
||||
if (playWhenReadyChanged || suppressionReasonChanged || isPlayingChanged) {
|
||||
int playbackState = playbackInfo.playbackState;
|
||||
notifyListeners(
|
||||
listener -> {
|
||||
if (playWhenReadyChanged) {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
listener.onPlayWhenReadyChanged(playWhenReady, playWhenReadyChangeReason);
|
||||
}
|
||||
if (suppressionReasonChanged) {
|
||||
listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason);
|
||||
}
|
||||
if (isPlayingChanged) {
|
||||
listener.onIsPlayingChanged(isPlaying);
|
||||
}
|
||||
});
|
||||
}
|
||||
int playbackState = playbackInfo.playbackState;
|
||||
notifyListeners(
|
||||
listener -> {
|
||||
if (playWhenReadyChanged) {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
listener.onPlayWhenReadyChanged(playWhenReady, playWhenReadyChangeReason);
|
||||
}
|
||||
if (suppressionReasonChanged) {
|
||||
listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason);
|
||||
}
|
||||
if (isPlayingChanged) {
|
||||
listener.onIsPlayingChanged(isPlaying);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getPlayWhenReady() {
|
||||
return playWhenReady;
|
||||
return playbackInfo.playWhenReady;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -601,6 +597,7 @@ import java.util.concurrent.TimeoutException;
|
|||
/* positionDiscontinuity= */ false,
|
||||
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
|
||||
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||
/* seekProcessed= */ false);
|
||||
}
|
||||
|
||||
|
|
@ -763,14 +760,9 @@ import java.util.concurrent.TimeoutException;
|
|||
|
||||
// Not private so it can be called from an inner class without going through a thunk method.
|
||||
/* package */ void handleEvent(Message msg) {
|
||||
|
||||
switch (msg.what) {
|
||||
case ExoPlayerImplInternal.MSG_PLAYBACK_INFO_CHANGED:
|
||||
handlePlaybackInfo(
|
||||
/* playbackInfo= */ (PlaybackInfo) msg.obj,
|
||||
/* operationAcks= */ msg.arg1,
|
||||
/* positionDiscontinuity= */ msg.arg2 != C.INDEX_UNSET,
|
||||
/* positionDiscontinuityReason= */ msg.arg2);
|
||||
handlePlaybackInfo((ExoPlayerImplInternal.PlaybackInfoUpdate) msg.obj);
|
||||
break;
|
||||
case ExoPlayerImplInternal.MSG_PLAYBACK_PARAMETERS_CHANGED:
|
||||
handlePlaybackParameters((PlaybackParameters) msg.obj, /* operationAck= */ msg.arg1 != 0);
|
||||
|
|
@ -802,24 +794,31 @@ import java.util.concurrent.TimeoutException;
|
|||
}
|
||||
}
|
||||
|
||||
private void handlePlaybackInfo(
|
||||
PlaybackInfo playbackInfo,
|
||||
int operationAcks,
|
||||
boolean positionDiscontinuity,
|
||||
@DiscontinuityReason int positionDiscontinuityReason) {
|
||||
pendingOperationAcks -= operationAcks;
|
||||
private void handlePlaybackInfo(ExoPlayerImplInternal.PlaybackInfoUpdate playbackInfoUpdate) {
|
||||
pendingOperationAcks -= playbackInfoUpdate.operationAcks;
|
||||
if (playbackInfoUpdate.positionDiscontinuity) {
|
||||
hasPendingDiscontinuity = true;
|
||||
pendingDiscontinuityReason = playbackInfoUpdate.discontinuityReason;
|
||||
}
|
||||
if (playbackInfoUpdate.hasPlayWhenReadyChangeReason) {
|
||||
pendingPlayWhenReadyChangeReason = playbackInfoUpdate.playWhenReadyChangeReason;
|
||||
}
|
||||
if (pendingOperationAcks == 0) {
|
||||
if (!this.playbackInfo.timeline.isEmpty() && playbackInfo.timeline.isEmpty()) {
|
||||
if (!this.playbackInfo.timeline.isEmpty()
|
||||
&& playbackInfoUpdate.playbackInfo.timeline.isEmpty()) {
|
||||
// Update the masking variables, which are used when the timeline becomes empty.
|
||||
resetMaskingPosition();
|
||||
}
|
||||
boolean seekProcessed = hasPendingSeek;
|
||||
boolean positionDiscontinuity = hasPendingDiscontinuity;
|
||||
hasPendingSeek = false;
|
||||
hasPendingDiscontinuity = false;
|
||||
updatePlaybackInfo(
|
||||
playbackInfo,
|
||||
playbackInfoUpdate.playbackInfo,
|
||||
positionDiscontinuity,
|
||||
positionDiscontinuityReason,
|
||||
pendingDiscontinuityReason,
|
||||
TIMELINE_CHANGE_REASON_SOURCE_UPDATE,
|
||||
pendingPlayWhenReadyChangeReason,
|
||||
seekProcessed);
|
||||
}
|
||||
}
|
||||
|
|
@ -832,9 +831,7 @@ import java.util.concurrent.TimeoutException;
|
|||
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolders.size());
|
||||
resetMaskingPosition();
|
||||
} else {
|
||||
maskingWindowIndex = getCurrentWindowIndex();
|
||||
maskingPeriodIndex = getCurrentPeriodIndex();
|
||||
maskingWindowPositionMs = getCurrentPosition();
|
||||
maskWithCurrentPosition();
|
||||
}
|
||||
Timeline timeline = playbackInfo.timeline;
|
||||
MediaPeriodId mediaPeriodId = playbackInfo.periodId;
|
||||
|
|
@ -856,6 +853,8 @@ import java.util.concurrent.TimeoutException;
|
|||
clearPlaylist ? TrackGroupArray.EMPTY : playbackInfo.trackGroups,
|
||||
clearPlaylist ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult,
|
||||
mediaPeriodId,
|
||||
playbackInfo.playWhenReady,
|
||||
playbackInfo.playbackSuppressionReason,
|
||||
positionUs,
|
||||
/* totalBufferedDurationUs= */ 0,
|
||||
positionUs);
|
||||
|
|
@ -866,12 +865,11 @@ import java.util.concurrent.TimeoutException;
|
|||
boolean positionDiscontinuity,
|
||||
@DiscontinuityReason int positionDiscontinuityReason,
|
||||
@TimelineChangeReason int timelineChangeReason,
|
||||
@PlayWhenReadyChangeReason int playWhenReadyChangeReason,
|
||||
boolean seekProcessed) {
|
||||
boolean previousIsPlaying = isPlaying();
|
||||
// Assign playback info immediately such that all getters return the right values.
|
||||
PlaybackInfo previousPlaybackInfo = this.playbackInfo;
|
||||
this.playbackInfo = playbackInfo;
|
||||
boolean isPlaying = isPlaying();
|
||||
notifyListeners(
|
||||
new PlaybackInfoUpdate(
|
||||
playbackInfo,
|
||||
|
|
@ -881,9 +879,8 @@ import java.util.concurrent.TimeoutException;
|
|||
positionDiscontinuity,
|
||||
positionDiscontinuityReason,
|
||||
timelineChangeReason,
|
||||
seekProcessed,
|
||||
playWhenReady,
|
||||
/* isPlayingChanged= */ previousIsPlaying != isPlaying));
|
||||
playWhenReadyChangeReason,
|
||||
seekProcessed));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
|
@ -1085,6 +1082,12 @@ import java.util.concurrent.TimeoutException;
|
|||
}
|
||||
}
|
||||
|
||||
private void maskWithCurrentPosition() {
|
||||
maskingWindowIndex = getCurrentWindowIndexInternal();
|
||||
maskingPeriodIndex = getCurrentPeriodIndex();
|
||||
maskingWindowPositionMs = getCurrentPosition();
|
||||
}
|
||||
|
||||
private void maskWithDefaultPosition(Timeline timeline) {
|
||||
if (timeline.isEmpty()) {
|
||||
resetMaskingPosition();
|
||||
|
|
@ -1136,16 +1139,18 @@ import java.util.concurrent.TimeoutException;
|
|||
private final CopyOnWriteArrayList<ListenerHolder> listenerSnapshot;
|
||||
private final TrackSelector trackSelector;
|
||||
private final boolean positionDiscontinuity;
|
||||
private final @Player.DiscontinuityReason int positionDiscontinuityReason;
|
||||
private final int timelineChangeReason;
|
||||
@DiscontinuityReason private final int positionDiscontinuityReason;
|
||||
@TimelineChangeReason private final int timelineChangeReason;
|
||||
@PlayWhenReadyChangeReason private final int playWhenReadyChangeReason;
|
||||
private final boolean seekProcessed;
|
||||
private final boolean playbackStateChanged;
|
||||
private final boolean playbackErrorChanged;
|
||||
private final boolean timelineChanged;
|
||||
private final boolean isLoadingChanged;
|
||||
private final boolean trackSelectorResultChanged;
|
||||
private final boolean playWhenReady;
|
||||
private final boolean isPlayingChanged;
|
||||
private final boolean playWhenReadyChanged;
|
||||
private final boolean playbackSuppressionReasonChanged;
|
||||
|
||||
public PlaybackInfoUpdate(
|
||||
PlaybackInfo playbackInfo,
|
||||
|
|
@ -1155,18 +1160,16 @@ import java.util.concurrent.TimeoutException;
|
|||
boolean positionDiscontinuity,
|
||||
@DiscontinuityReason int positionDiscontinuityReason,
|
||||
@TimelineChangeReason int timelineChangeReason,
|
||||
boolean seekProcessed,
|
||||
boolean playWhenReady,
|
||||
boolean isPlayingChanged) {
|
||||
@PlayWhenReadyChangeReason int playWhenReadyChangeReason,
|
||||
boolean seekProcessed) {
|
||||
this.playbackInfo = playbackInfo;
|
||||
this.listenerSnapshot = new CopyOnWriteArrayList<>(listeners);
|
||||
this.trackSelector = trackSelector;
|
||||
this.positionDiscontinuity = positionDiscontinuity;
|
||||
this.positionDiscontinuityReason = positionDiscontinuityReason;
|
||||
this.timelineChangeReason = timelineChangeReason;
|
||||
this.playWhenReadyChangeReason = playWhenReadyChangeReason;
|
||||
this.seekProcessed = seekProcessed;
|
||||
this.playWhenReady = playWhenReady;
|
||||
this.isPlayingChanged = isPlayingChanged;
|
||||
playbackStateChanged = previousPlaybackInfo.playbackState != playbackInfo.playbackState;
|
||||
playbackErrorChanged =
|
||||
previousPlaybackInfo.playbackError != playbackInfo.playbackError
|
||||
|
|
@ -1175,6 +1178,10 @@ import java.util.concurrent.TimeoutException;
|
|||
timelineChanged = !previousPlaybackInfo.timeline.equals(playbackInfo.timeline);
|
||||
trackSelectorResultChanged =
|
||||
previousPlaybackInfo.trackSelectorResult != playbackInfo.trackSelectorResult;
|
||||
playWhenReadyChanged = previousPlaybackInfo.playWhenReady != playbackInfo.playWhenReady;
|
||||
playbackSuppressionReasonChanged =
|
||||
previousPlaybackInfo.playbackSuppressionReason != playbackInfo.playbackSuppressionReason;
|
||||
isPlayingChanged = isPlaying(previousPlaybackInfo) != isPlaying(playbackInfo);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
|
@ -1202,30 +1209,49 @@ import java.util.concurrent.TimeoutException;
|
|||
playbackInfo.trackGroups, playbackInfo.trackSelectorResult.selections));
|
||||
}
|
||||
if (isLoadingChanged) {
|
||||
invokeAll(
|
||||
listenerSnapshot, listener -> listener.onIsLoadingChanged(playbackInfo.isLoading));
|
||||
}
|
||||
if (playbackStateChanged || playWhenReadyChanged) {
|
||||
invokeAll(
|
||||
listenerSnapshot,
|
||||
listener -> {
|
||||
listener.onIsLoadingChanged(playbackInfo.isLoading);
|
||||
});
|
||||
listener ->
|
||||
listener.onPlayerStateChanged(
|
||||
playbackInfo.playWhenReady, playbackInfo.playbackState));
|
||||
}
|
||||
if (playbackStateChanged) {
|
||||
invokeAll(
|
||||
listenerSnapshot,
|
||||
listener -> {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackInfo.playbackState);
|
||||
listener.onPlaybackStateChanged(playbackInfo.playbackState);
|
||||
});
|
||||
listener -> listener.onPlaybackStateChanged(playbackInfo.playbackState));
|
||||
}
|
||||
if (isPlayingChanged) {
|
||||
if (playWhenReadyChanged) {
|
||||
invokeAll(
|
||||
listenerSnapshot,
|
||||
listener ->
|
||||
listener.onIsPlayingChanged(playbackInfo.playbackState == Player.STATE_READY));
|
||||
listener.onPlayWhenReadyChanged(
|
||||
playbackInfo.playWhenReady, playWhenReadyChangeReason));
|
||||
}
|
||||
if (playbackSuppressionReasonChanged) {
|
||||
invokeAll(
|
||||
listenerSnapshot,
|
||||
listener ->
|
||||
listener.onPlaybackSuppressionReasonChanged(
|
||||
playbackInfo.playbackSuppressionReason));
|
||||
}
|
||||
if (isPlayingChanged) {
|
||||
invokeAll(
|
||||
listenerSnapshot, listener -> listener.onIsPlayingChanged(isPlaying(playbackInfo)));
|
||||
}
|
||||
if (seekProcessed) {
|
||||
invokeAll(listenerSnapshot, EventListener::onSeekProcessed);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPlaying(PlaybackInfo playbackInfo) {
|
||||
return playbackInfo.playbackState == Player.STATE_READY
|
||||
&& playbackInfo.playWhenReady
|
||||
&& playbackInfo.playbackSuppressionReason == PLAYBACK_SUPPRESSION_REASON_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private static void invokeAll(
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import androidx.annotation.CheckResult;
|
|||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.DefaultMediaClock.PlaybackParameterListener;
|
||||
import com.google.android.exoplayer2.Player.DiscontinuityReason;
|
||||
import com.google.android.exoplayer2.Player.PlayWhenReadyChangeReason;
|
||||
import com.google.android.exoplayer2.Player.PlaybackSuppressionReason;
|
||||
import com.google.android.exoplayer2.Player.RepeatMode;
|
||||
import com.google.android.exoplayer2.analytics.AnalyticsCollector;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
|
|
@ -107,7 +109,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
private final long backBufferDurationUs;
|
||||
private final boolean retainBackBufferFromKeyframe;
|
||||
private final DefaultMediaClock mediaClock;
|
||||
private final PlaybackInfoUpdate playbackInfoUpdate;
|
||||
private final ArrayList<PendingMessageInfo> pendingMessages;
|
||||
private final Clock clock;
|
||||
private final MediaPeriodQueue queue;
|
||||
|
|
@ -117,9 +118,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
private SeekParameters seekParameters;
|
||||
|
||||
private PlaybackInfo playbackInfo;
|
||||
private PlaybackInfoUpdate playbackInfoUpdate;
|
||||
private Renderer[] enabledRenderers;
|
||||
private boolean released;
|
||||
private boolean playWhenReady;
|
||||
private boolean pauseAtEndOfWindow;
|
||||
private boolean pendingPauseAtEndOfPeriod;
|
||||
private boolean rebuffering;
|
||||
|
|
@ -141,7 +142,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
TrackSelectorResult emptyTrackSelectorResult,
|
||||
LoadControl loadControl,
|
||||
BandwidthMeter bandwidthMeter,
|
||||
boolean playWhenReady,
|
||||
@Player.RepeatMode int repeatMode,
|
||||
boolean shuffleModeEnabled,
|
||||
@Nullable AnalyticsCollector analyticsCollector,
|
||||
|
|
@ -152,7 +152,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
this.emptyTrackSelectorResult = emptyTrackSelectorResult;
|
||||
this.loadControl = loadControl;
|
||||
this.bandwidthMeter = bandwidthMeter;
|
||||
this.playWhenReady = playWhenReady;
|
||||
this.repeatMode = repeatMode;
|
||||
this.shuffleModeEnabled = shuffleModeEnabled;
|
||||
this.eventHandler = eventHandler;
|
||||
|
|
@ -164,7 +163,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
|
||||
seekParameters = SeekParameters.DEFAULT;
|
||||
playbackInfo = PlaybackInfo.createDummy(emptyTrackSelectorResult);
|
||||
playbackInfoUpdate = new PlaybackInfoUpdate();
|
||||
playbackInfoUpdate = new PlaybackInfoUpdate(playbackInfo);
|
||||
rendererCapabilities = new RendererCapabilities[renderers.length];
|
||||
for (int i = 0; i < renderers.length; i++) {
|
||||
renderers[i].setIndex(i);
|
||||
|
|
@ -198,8 +197,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
handler.obtainMessage(MSG_PREPARE).sendToTarget();
|
||||
}
|
||||
|
||||
public void setPlayWhenReady(boolean playWhenReady) {
|
||||
handler.obtainMessage(MSG_SET_PLAY_WHEN_READY, playWhenReady ? 1 : 0, 0).sendToTarget();
|
||||
public void setPlayWhenReady(
|
||||
boolean playWhenReady, @PlaybackSuppressionReason int playbackSuppressionReason) {
|
||||
handler
|
||||
.obtainMessage(MSG_SET_PLAY_WHEN_READY, playWhenReady ? 1 : 0, playbackSuppressionReason)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
public void setPauseAtEndOfWindow(boolean pauseAtEndOfWindow) {
|
||||
|
|
@ -381,7 +383,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
prepareInternal();
|
||||
break;
|
||||
case MSG_SET_PLAY_WHEN_READY:
|
||||
setPlayWhenReadyInternal(msg.arg1 != 0);
|
||||
setPlayWhenReadyInternal(
|
||||
/* playWhenReady= */ msg.arg1 != 0,
|
||||
/* playbackSuppressionReason= */ msg.arg2,
|
||||
/* operationAck= */ true,
|
||||
Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
break;
|
||||
case MSG_SET_REPEAT_MODE:
|
||||
setRepeatModeInternal(msg.arg1);
|
||||
|
|
@ -571,17 +577,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
}
|
||||
|
||||
private void maybeNotifyPlaybackInfoChanged() {
|
||||
if (playbackInfoUpdate.hasPendingUpdate(playbackInfo)) {
|
||||
eventHandler
|
||||
.obtainMessage(
|
||||
MSG_PLAYBACK_INFO_CHANGED,
|
||||
playbackInfoUpdate.operationAcks,
|
||||
playbackInfoUpdate.positionDiscontinuity
|
||||
? playbackInfoUpdate.discontinuityReason
|
||||
: C.INDEX_UNSET,
|
||||
playbackInfo)
|
||||
.sendToTarget();
|
||||
playbackInfoUpdate.reset(playbackInfo);
|
||||
playbackInfoUpdate.setPlaybackInfo(playbackInfo);
|
||||
if (playbackInfoUpdate.hasPendingChange) {
|
||||
eventHandler.obtainMessage(MSG_PLAYBACK_INFO_CHANGED, playbackInfoUpdate).sendToTarget();
|
||||
playbackInfoUpdate = new PlaybackInfoUpdate(playbackInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -656,10 +655,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
handlePlaylistInfoRefreshed(timeline);
|
||||
}
|
||||
|
||||
private void setPlayWhenReadyInternal(boolean playWhenReady) throws ExoPlaybackException {
|
||||
private void setPlayWhenReadyInternal(
|
||||
boolean playWhenReady,
|
||||
@PlaybackSuppressionReason int playbackSuppressionReason,
|
||||
boolean operationAck,
|
||||
@Player.PlayWhenReadyChangeReason int reason)
|
||||
throws ExoPlaybackException {
|
||||
playbackInfoUpdate.incrementPendingOperationAcks(operationAck ? 1 : 0);
|
||||
playbackInfoUpdate.setPlayWhenReadyChangeReason(reason);
|
||||
playbackInfo = playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason);
|
||||
rebuffering = false;
|
||||
this.playWhenReady = playWhenReady;
|
||||
if (!playWhenReady) {
|
||||
if (!shouldPlayWhenReady()) {
|
||||
stopRenderers();
|
||||
updatePlaybackPositions();
|
||||
} else {
|
||||
|
|
@ -840,7 +846,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
|| playingPeriodDurationUs <= playbackInfo.positionUs);
|
||||
if (finishedRendering && pendingPauseAtEndOfPeriod) {
|
||||
pendingPauseAtEndOfPeriod = false;
|
||||
setPlayWhenReadyInternal(false);
|
||||
// TODO: Add new change reason for timed pause requests.
|
||||
setPlayWhenReadyInternal(
|
||||
/* playWhenReady= */ false,
|
||||
playbackInfo.playbackSuppressionReason,
|
||||
/* operationAck= */ false,
|
||||
Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
||||
}
|
||||
if (finishedRendering && playingPeriodHolder.info.isFinal) {
|
||||
setState(Player.STATE_ENDED);
|
||||
|
|
@ -848,12 +859,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
} else if (playbackInfo.playbackState == Player.STATE_BUFFERING
|
||||
&& shouldTransitionToReadyState(renderersAllowPlayback)) {
|
||||
setState(Player.STATE_READY);
|
||||
if (playWhenReady) {
|
||||
if (shouldPlayWhenReady()) {
|
||||
startRenderers();
|
||||
}
|
||||
} else if (playbackInfo.playbackState == Player.STATE_READY
|
||||
&& !(enabledRenderers.length == 0 ? isTimelineReady() : renderersAllowPlayback)) {
|
||||
rebuffering = playWhenReady;
|
||||
rebuffering = shouldPlayWhenReady();
|
||||
setState(Player.STATE_BUFFERING);
|
||||
stopRenderers();
|
||||
}
|
||||
|
|
@ -864,7 +875,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
}
|
||||
}
|
||||
|
||||
if ((playWhenReady && playbackInfo.playbackState == Player.STATE_READY)
|
||||
if ((shouldPlayWhenReady() && playbackInfo.playbackState == Player.STATE_READY)
|
||||
|| playbackInfo.playbackState == Player.STATE_BUFFERING) {
|
||||
scheduleNextWork(operationStartTimeMs, ACTIVE_INTERVAL_MS);
|
||||
} else if (enabledRenderers.length != 0 && playbackInfo.playbackState != Player.STATE_ENDED) {
|
||||
|
|
@ -906,7 +917,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
periodId = firstPeriodAndPosition.first;
|
||||
periodPositionUs = firstPeriodAndPosition.second;
|
||||
requestedContentPosition = C.TIME_UNSET;
|
||||
seekPositionAdjusted = true;
|
||||
seekPositionAdjusted = !playbackInfo.timeline.isEmpty();
|
||||
} else {
|
||||
// Update the resolved seek position to take ads into account.
|
||||
Object periodUid = resolvedSeekPosition.first;
|
||||
|
|
@ -1209,6 +1220,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
resetTrackInfo ? TrackGroupArray.EMPTY : playbackInfo.trackGroups,
|
||||
resetTrackInfo ? emptyTrackSelectorResult : playbackInfo.trackSelectorResult,
|
||||
mediaPeriodId,
|
||||
playbackInfo.playWhenReady,
|
||||
playbackInfo.playbackSuppressionReason,
|
||||
startPositionUs,
|
||||
/* totalBufferedDurationUs= */ 0,
|
||||
startPositionUs);
|
||||
|
|
@ -1806,7 +1819,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
}
|
||||
|
||||
private boolean shouldAdvancePlayingPeriod() {
|
||||
if (!playWhenReady) {
|
||||
if (!shouldPlayWhenReady()) {
|
||||
return false;
|
||||
}
|
||||
if (pendingPauseAtEndOfPeriod) {
|
||||
|
|
@ -2048,7 +2061,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
TrackSelection newSelection = trackSelectorResult.selections.get(rendererIndex);
|
||||
Format[] formats = getFormats(newSelection);
|
||||
// The renderer needs enabling with its new track selection.
|
||||
boolean playing = playWhenReady && playbackInfo.playbackState == Player.STATE_READY;
|
||||
boolean playing = shouldPlayWhenReady() && playbackInfo.playbackState == Player.STATE_READY;
|
||||
// Consider as joining only if the renderer was previously disabled.
|
||||
boolean joining = !wasRendererEnabled && playing;
|
||||
// Enable the renderer.
|
||||
|
|
@ -2121,6 +2134,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
.sendToTarget();
|
||||
}
|
||||
|
||||
private boolean shouldPlayWhenReady() {
|
||||
return playbackInfo.playWhenReady
|
||||
&& playbackInfo.playbackSuppressionReason == Player.PLAYBACK_SUPPRESSION_REASON_NONE;
|
||||
}
|
||||
|
||||
private static PositionUpdateForPlaylistChange resolvePositionForPlaylistChange(
|
||||
Timeline timeline,
|
||||
PlaybackInfo playbackInfo,
|
||||
|
|
@ -2606,27 +2624,31 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
}
|
||||
}
|
||||
|
||||
private static final class PlaybackInfoUpdate {
|
||||
/* package */ static final class PlaybackInfoUpdate {
|
||||
|
||||
private PlaybackInfo lastPlaybackInfo;
|
||||
private int operationAcks;
|
||||
private boolean positionDiscontinuity;
|
||||
@DiscontinuityReason private int discontinuityReason;
|
||||
private boolean hasPendingChange;
|
||||
|
||||
public boolean hasPendingUpdate(PlaybackInfo playbackInfo) {
|
||||
return playbackInfo != lastPlaybackInfo || operationAcks > 0 || positionDiscontinuity;
|
||||
}
|
||||
public PlaybackInfo playbackInfo;
|
||||
public int operationAcks;
|
||||
public boolean positionDiscontinuity;
|
||||
@DiscontinuityReason public int discontinuityReason;
|
||||
public boolean hasPlayWhenReadyChangeReason;
|
||||
@PlayWhenReadyChangeReason public int playWhenReadyChangeReason;
|
||||
|
||||
public void reset(PlaybackInfo playbackInfo) {
|
||||
lastPlaybackInfo = playbackInfo;
|
||||
operationAcks = 0;
|
||||
positionDiscontinuity = false;
|
||||
public PlaybackInfoUpdate(PlaybackInfo playbackInfo) {
|
||||
this.playbackInfo = playbackInfo;
|
||||
}
|
||||
|
||||
public void incrementPendingOperationAcks(int operationAcks) {
|
||||
hasPendingChange |= operationAcks > 0;
|
||||
this.operationAcks += operationAcks;
|
||||
}
|
||||
|
||||
public void setPlaybackInfo(PlaybackInfo playbackInfo) {
|
||||
hasPendingChange |= this.playbackInfo != playbackInfo;
|
||||
this.playbackInfo = playbackInfo;
|
||||
}
|
||||
|
||||
public void setPositionDiscontinuity(@DiscontinuityReason int discontinuityReason) {
|
||||
if (positionDiscontinuity
|
||||
&& this.discontinuityReason != Player.DISCONTINUITY_REASON_INTERNAL) {
|
||||
|
|
@ -2635,8 +2657,16 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
Assertions.checkArgument(discontinuityReason == Player.DISCONTINUITY_REASON_INTERNAL);
|
||||
return;
|
||||
}
|
||||
hasPendingChange = true;
|
||||
positionDiscontinuity = true;
|
||||
this.discontinuityReason = discontinuityReason;
|
||||
}
|
||||
|
||||
public void setPlayWhenReadyChangeReason(
|
||||
@PlayWhenReadyChangeReason int playWhenReadyChangeReason) {
|
||||
hasPendingChange = true;
|
||||
this.hasPlayWhenReadyChangeReason = true;
|
||||
this.playWhenReadyChangeReason = playWhenReadyChangeReason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<>();
|
||||
|
|
@ -4270,6 +4272,55 @@ public final class ExoPlayerTest {
|
|||
assertArrayEquals(new long[] {5_000, 5_000}, currentPlaybackPositions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPlayWhenReady_keepsCurrentPosition() throws Exception {
|
||||
AtomicLong positionAfterSetPlayWhenReady = new AtomicLong(C.TIME_UNSET);
|
||||
ActionSchedule actionSchedule =
|
||||
new ActionSchedule.Builder(TAG)
|
||||
.playUntilPosition(0, 5000)
|
||||
.play()
|
||||
.executeRunnable(
|
||||
new PlayerRunnable() {
|
||||
@Override
|
||||
public void run(SimpleExoPlayer player) {
|
||||
positionAfterSetPlayWhenReady.set(player.getCurrentPosition());
|
||||
}
|
||||
})
|
||||
.build();
|
||||
new Builder()
|
||||
.setActionSchedule(actionSchedule)
|
||||
.build(context)
|
||||
.start()
|
||||
.blockUntilEnded(TIMEOUT_MS);
|
||||
|
||||
assertThat(positionAfterSetPlayWhenReady.get()).isAtLeast(5000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setShuffleOrder_keepsCurrentPosition() throws Exception {
|
||||
AtomicLong positionAfterSetShuffleOrder = new AtomicLong(C.TIME_UNSET);
|
||||
ActionSchedule actionSchedule =
|
||||
new ActionSchedule.Builder(TAG)
|
||||
.playUntilPosition(0, 5000)
|
||||
.setShuffleOrder(new FakeShuffleOrder(/* length= */ 1))
|
||||
.executeRunnable(
|
||||
new PlayerRunnable() {
|
||||
@Override
|
||||
public void run(SimpleExoPlayer player) {
|
||||
positionAfterSetShuffleOrder.set(player.getCurrentPosition());
|
||||
}
|
||||
})
|
||||
.play()
|
||||
.build();
|
||||
new Builder()
|
||||
.setActionSchedule(actionSchedule)
|
||||
.build(context)
|
||||
.start()
|
||||
.blockUntilEnded(TIMEOUT_MS);
|
||||
|
||||
assertThat(positionAfterSetShuffleOrder.get()).isAtLeast(5000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMediaSources_empty_whenEmpty_correctMaskingWindowIndex() throws Exception {
|
||||
Timeline secondTimeline = new FakeTimeline(/* windowCount= */ 1);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
Loading…
Reference in a new issue