Guard playWhenReady masking against regular status updates

PiperOrigin-RevId: 275863101
This commit is contained in:
aquilescanta 2019-10-21 17:51:45 +01:00 committed by Oliver Woodman
parent dbfc101e04
commit a0843d0e2f

View File

@ -16,7 +16,6 @@
package com.google.android.exoplayer2.ext.cast; package com.google.android.exoplayer2.ext.cast;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.BasePlayer; import com.google.android.exoplayer2.BasePlayer;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
@ -99,6 +98,7 @@ public final class CastPlayer extends BasePlayer {
@Nullable private SessionAvailabilityListener sessionAvailabilityListener; @Nullable private SessionAvailabilityListener sessionAvailabilityListener;
// Internal state. // Internal state.
private final StateHolder<Boolean> playWhenReady;
@Nullable private RemoteMediaClient remoteMediaClient; @Nullable private RemoteMediaClient remoteMediaClient;
private CastTimeline currentTimeline; private CastTimeline currentTimeline;
private TrackGroupArray currentTrackGroups; private TrackGroupArray currentTrackGroups;
@ -106,7 +106,6 @@ public final class CastPlayer extends BasePlayer {
@Player.State private int playbackState; @Player.State private int playbackState;
private int repeatMode; private int repeatMode;
private int currentWindowIndex; private int currentWindowIndex;
private boolean playWhenReady;
private long lastReportedPositionMs; private long lastReportedPositionMs;
private int pendingSeekCount; private int pendingSeekCount;
private int pendingSeekWindowIndex; private int pendingSeekWindowIndex;
@ -131,6 +130,7 @@ public final class CastPlayer extends BasePlayer {
CastSession session = sessionManager.getCurrentCastSession(); CastSession session = sessionManager.getCurrentCastSession();
remoteMediaClient = session != null ? session.getRemoteMediaClient() : null; remoteMediaClient = session != null ? session.getRemoteMediaClient() : null;
playWhenReady = new StateHolder<>(false);
playbackState = STATE_IDLE; playbackState = STATE_IDLE;
repeatMode = REPEAT_MODE_OFF; repeatMode = REPEAT_MODE_OFF;
currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE; currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE;
@ -357,18 +357,22 @@ public final class CastPlayer extends BasePlayer {
flushNotifications(); flushNotifications();
PendingResult<MediaChannelResult> pendingResult = PendingResult<MediaChannelResult> pendingResult =
playWhenReady ? remoteMediaClient.play() : remoteMediaClient.pause(); playWhenReady ? remoteMediaClient.play() : remoteMediaClient.pause();
pendingResult.setResultCallback( this.playWhenReady.pendingResultCallback =
mediaChannelResult -> { new ResultCallback<MediaChannelResult>() {
@Override
public void onResult(MediaChannelResult mediaChannelResult) {
if (remoteMediaClient != null) { if (remoteMediaClient != null) {
updatePlayerStateAndNotifyIfChanged(); updatePlayerStateAndNotifyIfChanged(this);
flushNotifications(); flushNotifications();
} }
}); }
};
pendingResult.setResultCallback(this.playWhenReady.pendingResultCallback);
} }
@Override @Override
public boolean getPlayWhenReady() { public boolean getPlayWhenReady() {
return playWhenReady; return playWhenReady.value;
} }
@Override @Override
@ -571,9 +575,9 @@ public final class CastPlayer extends BasePlayer {
// There is no session. We leave the state of the player as it is now. // There is no session. We leave the state of the player as it is now.
return; return;
} }
boolean wasPlaying = playbackState == Player.STATE_READY && playWhenReady; boolean wasPlaying = playbackState == Player.STATE_READY && playWhenReady.value;
updatePlayerStateAndNotifyIfChanged(); updatePlayerStateAndNotifyIfChanged(/* resultCallback= */ null);
boolean isPlaying = playbackState == Player.STATE_READY && playWhenReady; boolean isPlaying = playbackState == Player.STATE_READY && playWhenReady.value;
if (wasPlaying != isPlaying) { if (wasPlaying != isPlaying) {
notificationsBatch.add( notificationsBatch.add(
new ListenerNotificationTask(listener -> listener.onIsPlayingChanged(isPlaying))); new ListenerNotificationTask(listener -> listener.onIsPlayingChanged(isPlaying)));
@ -605,10 +609,22 @@ public final class CastPlayer extends BasePlayer {
flushNotifications(); flushNotifications();
} }
/**
* Updates {@link #playWhenReady} and {@link #playbackState} to match the Cast {@code
* remoteMediaClient} state, and notifies listeners of any state changes.
*
* <p>This method will only update values whose {@link StateHolder#pendingResultCallback} matches
* the given {@code resultCallback}.
*/
@RequiresNonNull("remoteMediaClient") @RequiresNonNull("remoteMediaClient")
private void updatePlayerStateAndNotifyIfChanged() { private void updatePlayerStateAndNotifyIfChanged(ResultCallback<?> resultCallback) {
setPlayerStateAndNotifyIfChanged( boolean newPlayWhenReadyValue = playWhenReady.value;
!remoteMediaClient.isPaused(), fetchPlaybackState(remoteMediaClient)); if (playWhenReady.acceptsUpdate(resultCallback)) {
newPlayWhenReadyValue = !remoteMediaClient.isPaused();
playWhenReady.clearPendingResultCallback();
}
// We do not mask the playback state, so try setting it regardless of the playWhenReady masking.
setPlayerStateAndNotifyIfChanged(newPlayWhenReadyValue, fetchPlaybackState(remoteMediaClient));
} }
@RequiresNonNull("remoteMediaClient") @RequiresNonNull("remoteMediaClient")
@ -700,8 +716,8 @@ public final class CastPlayer extends BasePlayer {
private void setPlayerStateAndNotifyIfChanged( private void setPlayerStateAndNotifyIfChanged(
boolean playWhenReady, @Player.State int playbackState) { boolean playWhenReady, @Player.State int playbackState) {
if (this.playWhenReady != playWhenReady || this.playbackState != playbackState) { if (this.playWhenReady.value != playWhenReady || this.playbackState != playbackState) {
this.playWhenReady = playWhenReady; this.playWhenReady.value = playWhenReady;
this.playbackState = playbackState; this.playbackState = playbackState;
notificationsBatch.add( notificationsBatch.add(
new ListenerNotificationTask( new ListenerNotificationTask(
@ -918,7 +934,7 @@ public final class CastPlayer extends BasePlayer {
private final class SeekResultCallback implements ResultCallback<MediaChannelResult> { private final class SeekResultCallback implements ResultCallback<MediaChannelResult> {
@Override @Override
public void onResult(@NonNull MediaChannelResult result) { public void onResult(MediaChannelResult result) {
int statusCode = result.getStatus().getStatusCode(); int statusCode = result.getStatus().getStatusCode();
if (statusCode != CastStatusCodes.SUCCESS && statusCode != CastStatusCodes.REPLACED) { if (statusCode != CastStatusCodes.SUCCESS && statusCode != CastStatusCodes.REPLACED) {
Log.e(TAG, "Seek failed. Error code " + statusCode + ": " Log.e(TAG, "Seek failed. Error code " + statusCode + ": "
@ -933,6 +949,37 @@ public final class CastPlayer extends BasePlayer {
} }
} }
/** Holds the value and the masking status of a specific part of the {@link CastPlayer} state. */
private static final class StateHolder<T> {
/** The user-facing value of a specific part of the {@link CastPlayer} state. */
public T value;
/**
* If {@link #value} is being masked, holds the result callback for the operation that triggered
* the masking. Or null if {@link #value} is not being masked.
*/
@Nullable public ResultCallback<MediaChannelResult> pendingResultCallback;
public StateHolder(T initialValue) {
value = initialValue;
}
public void clearPendingResultCallback() {
pendingResultCallback = null;
}
/**
* Returns whether {@link #value} is being masked by the operation associated to the given
* result callback.
*
* @param resultCallback A result callback.
*/
public boolean acceptsUpdate(ResultCallback<?> resultCallback) {
return pendingResultCallback == resultCallback;
}
}
private final class ListenerNotificationTask { private final class ListenerNotificationTask {
private final Iterator<ListenerHolder> listenersSnapshot; private final Iterator<ListenerHolder> listenersSnapshot;