Use listener notification batching in CastPlayer

PiperOrigin-RevId: 251399230
This commit is contained in:
aquilescanta 2019-06-04 10:19:29 +01:00 committed by Oliver Woodman
parent 9ca6f60c3a
commit 44aa731476

View File

@ -45,8 +45,11 @@ import com.google.android.gms.cast.framework.media.RemoteMediaClient;
import com.google.android.gms.cast.framework.media.RemoteMediaClient.MediaChannelResult; import com.google.android.gms.cast.framework.media.RemoteMediaClient.MediaChannelResult;
import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.ResultCallback;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArrayList;
/** /**
* {@link Player} implementation that communicates with a Cast receiver app. * {@link Player} implementation that communicates with a Cast receiver app.
@ -86,8 +89,10 @@ public final class CastPlayer extends BasePlayer {
private final StatusListener statusListener; private final StatusListener statusListener;
private final SeekResultCallback seekResultCallback; private final SeekResultCallback seekResultCallback;
// Listeners. // Listeners and notification.
private final CopyOnWriteArraySet<EventListener> listeners; private final CopyOnWriteArrayList<ListenerHolder> listeners;
private final ArrayList<ListenerNotificationTask> notificationsBatch;
private final ArrayDeque<ListenerNotificationTask> ongoingNotificationsTasks;
private SessionAvailabilityListener sessionAvailabilityListener; private SessionAvailabilityListener sessionAvailabilityListener;
// Internal state. // Internal state.
@ -113,7 +118,9 @@ public final class CastPlayer extends BasePlayer {
period = new Timeline.Period(); period = new Timeline.Period();
statusListener = new StatusListener(); statusListener = new StatusListener();
seekResultCallback = new SeekResultCallback(); seekResultCallback = new SeekResultCallback();
listeners = new CopyOnWriteArraySet<>(); listeners = new CopyOnWriteArrayList<>();
notificationsBatch = new ArrayList<>();
ongoingNotificationsTasks = new ArrayDeque<>();
SessionManager sessionManager = castContext.getSessionManager(); SessionManager sessionManager = castContext.getSessionManager();
sessionManager.addSessionManagerListener(statusListener, CastSession.class); sessionManager.addSessionManagerListener(statusListener, CastSession.class);
@ -296,12 +303,17 @@ public final class CastPlayer extends BasePlayer {
@Override @Override
public void addListener(EventListener listener) { public void addListener(EventListener listener) {
listeners.add(listener); listeners.addIfAbsent(new ListenerHolder(listener));
} }
@Override @Override
public void removeListener(EventListener listener) { public void removeListener(EventListener listener) {
listeners.remove(listener); for (ListenerHolder listenerHolder : listeners) {
if (listenerHolder.listener.equals(listener)) {
listenerHolder.release();
listeners.remove(listenerHolder);
}
}
} }
@Override @Override
@ -348,14 +360,13 @@ public final class CastPlayer extends BasePlayer {
pendingSeekCount++; pendingSeekCount++;
pendingSeekWindowIndex = windowIndex; pendingSeekWindowIndex = windowIndex;
pendingSeekPositionMs = positionMs; pendingSeekPositionMs = positionMs;
for (EventListener listener : listeners) { notificationsBatch.add(
listener.onPositionDiscontinuity(Player.DISCONTINUITY_REASON_SEEK); new ListenerNotificationTask(
} listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_SEEK)));
} else if (pendingSeekCount == 0) { } else if (pendingSeekCount == 0) {
for (EventListener listener : listeners) { notificationsBatch.add(new ListenerNotificationTask(EventListener::onSeekProcessed));
listener.onSeekProcessed();
}
} }
flushNotifications();
} }
@Override @Override
@ -531,30 +542,31 @@ public final class CastPlayer extends BasePlayer {
|| this.playWhenReady != playWhenReady) { || this.playWhenReady != playWhenReady) {
this.playbackState = playbackState; this.playbackState = playbackState;
this.playWhenReady = playWhenReady; this.playWhenReady = playWhenReady;
for (EventListener listener : listeners) { notificationsBatch.add(
listener.onPlayerStateChanged(this.playWhenReady, this.playbackState); new ListenerNotificationTask(
} listener -> listener.onPlayerStateChanged(this.playWhenReady, this.playbackState)));
} }
@RepeatMode int repeatMode = fetchRepeatMode(remoteMediaClient); @RepeatMode int repeatMode = fetchRepeatMode(remoteMediaClient);
if (this.repeatMode != repeatMode) { if (this.repeatMode != repeatMode) {
this.repeatMode = repeatMode; this.repeatMode = repeatMode;
for (EventListener listener : listeners) { notificationsBatch.add(
listener.onRepeatModeChanged(repeatMode); new ListenerNotificationTask(listener -> listener.onRepeatModeChanged(this.repeatMode)));
}
} }
int currentWindowIndex = fetchCurrentWindowIndex(getMediaStatus()); int currentWindowIndex = fetchCurrentWindowIndex(getMediaStatus());
if (this.currentWindowIndex != currentWindowIndex && pendingSeekCount == 0) { if (this.currentWindowIndex != currentWindowIndex && pendingSeekCount == 0) {
this.currentWindowIndex = currentWindowIndex; this.currentWindowIndex = currentWindowIndex;
for (EventListener listener : listeners) { notificationsBatch.add(
listener.onPositionDiscontinuity(DISCONTINUITY_REASON_PERIOD_TRANSITION); new ListenerNotificationTask(
} listener ->
listener.onPositionDiscontinuity(DISCONTINUITY_REASON_PERIOD_TRANSITION)));
} }
if (updateTracksAndSelections()) { if (updateTracksAndSelections()) {
for (EventListener listener : listeners) { notificationsBatch.add(
listener.onTracksChanged(currentTrackGroups, currentTrackSelection); new ListenerNotificationTask(
} listener -> listener.onTracksChanged(currentTrackGroups, currentTrackSelection)));
} }
maybeUpdateTimelineAndNotify(); maybeUpdateTimelineAndNotify();
flushNotifications();
} }
private void maybeUpdateTimelineAndNotify() { private void maybeUpdateTimelineAndNotify() {
@ -562,9 +574,10 @@ public final class CastPlayer extends BasePlayer {
@Player.TimelineChangeReason int reason = waitingForInitialTimeline @Player.TimelineChangeReason int reason = waitingForInitialTimeline
? Player.TIMELINE_CHANGE_REASON_PREPARED : Player.TIMELINE_CHANGE_REASON_DYNAMIC; ? Player.TIMELINE_CHANGE_REASON_PREPARED : Player.TIMELINE_CHANGE_REASON_DYNAMIC;
waitingForInitialTimeline = false; waitingForInitialTimeline = false;
for (EventListener listener : listeners) { notificationsBatch.add(
listener.onTimelineChanged(currentTimeline, null, reason); new ListenerNotificationTask(
} listener ->
listener.onTimelineChanged(currentTimeline, /* manifest= */ null, reason)));
} }
} }
@ -827,7 +840,23 @@ public final class CastPlayer extends BasePlayer {
} }
// Result callbacks hooks. // Internal methods.
private void flushNotifications() {
boolean recursiveNotification = !ongoingNotificationsTasks.isEmpty();
ongoingNotificationsTasks.addAll(notificationsBatch);
notificationsBatch.clear();
if (recursiveNotification) {
// This will be handled once the current notification task is finished.
return;
}
while (!ongoingNotificationsTasks.isEmpty()) {
ongoingNotificationsTasks.peekFirst().execute();
ongoingNotificationsTasks.removeFirst();
}
}
// Internal classes.
private final class SeekResultCallback implements ResultCallback<MediaChannelResult> { private final class SeekResultCallback implements ResultCallback<MediaChannelResult> {
@ -841,11 +870,27 @@ public final class CastPlayer extends BasePlayer {
if (--pendingSeekCount == 0) { if (--pendingSeekCount == 0) {
pendingSeekWindowIndex = C.INDEX_UNSET; pendingSeekWindowIndex = C.INDEX_UNSET;
pendingSeekPositionMs = C.TIME_UNSET; pendingSeekPositionMs = C.TIME_UNSET;
for (EventListener listener : listeners) { notificationsBatch.add(new ListenerNotificationTask(EventListener::onSeekProcessed));
listener.onSeekProcessed(); flushNotifications();
} }
} }
} }
private final class ListenerNotificationTask {
private final Iterator<ListenerHolder> listenersSnapshot;
private final ListenerInvocation listenerInvocation;
private ListenerNotificationTask(ListenerInvocation listenerInvocation) {
this.listenersSnapshot = listeners.iterator();
this.listenerInvocation = listenerInvocation;
}
public void execute() {
while (listenersSnapshot.hasNext()) {
listenersSnapshot.next().invoke(listenerInvocation);
}
}
} }
} }