Skip to content

Commit

Permalink
Use listener notification batching in CastPlayer
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 251399230
  • Loading branch information
AquilesCanta authored and ojw28 committed Jun 6, 2019
1 parent edee3dd commit 83a6d51
Showing 1 changed file with 76 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@
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.ResultCallback;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CopyOnWriteArrayList;

/**
* {@link Player} implementation that communicates with a Cast receiver app.
Expand Down Expand Up @@ -86,8 +89,10 @@ public final class CastPlayer extends BasePlayer {
private final StatusListener statusListener;
private final SeekResultCallback seekResultCallback;

// Listeners.
private final CopyOnWriteArraySet<EventListener> listeners;
// Listeners and notification.
private final CopyOnWriteArrayList<ListenerHolder> listeners;
private final ArrayList<ListenerNotificationTask> notificationsBatch;
private final ArrayDeque<ListenerNotificationTask> ongoingNotificationsTasks;
private SessionAvailabilityListener sessionAvailabilityListener;

// Internal state.
Expand All @@ -113,7 +118,9 @@ public CastPlayer(CastContext castContext) {
period = new Timeline.Period();
statusListener = new StatusListener();
seekResultCallback = new SeekResultCallback();
listeners = new CopyOnWriteArraySet<>();
listeners = new CopyOnWriteArrayList<>();
notificationsBatch = new ArrayList<>();
ongoingNotificationsTasks = new ArrayDeque<>();

SessionManager sessionManager = castContext.getSessionManager();
sessionManager.addSessionManagerListener(statusListener, CastSession.class);
Expand Down Expand Up @@ -296,12 +303,17 @@ public Looper getApplicationLooper() {

@Override
public void addListener(EventListener listener) {
listeners.add(listener);
listeners.addIfAbsent(new ListenerHolder(listener));
}

@Override
public void removeListener(EventListener listener) {
listeners.remove(listener);
for (ListenerHolder listenerHolder : listeners) {
if (listenerHolder.listener.equals(listener)) {
listenerHolder.release();
listeners.remove(listenerHolder);
}
}
}

@Override
Expand Down Expand Up @@ -347,14 +359,13 @@ public void seekTo(int windowIndex, long positionMs) {
pendingSeekCount++;
pendingSeekWindowIndex = windowIndex;
pendingSeekPositionMs = positionMs;
for (EventListener listener : listeners) {
listener.onPositionDiscontinuity(Player.DISCONTINUITY_REASON_SEEK);
}
notificationsBatch.add(
new ListenerNotificationTask(
listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_SEEK)));
} else if (pendingSeekCount == 0) {
for (EventListener listener : listeners) {
listener.onSeekProcessed();
}
notificationsBatch.add(new ListenerNotificationTask(EventListener::onSeekProcessed));
}
flushNotifications();
}

@Override
Expand Down Expand Up @@ -530,40 +541,42 @@ public void updateInternalState() {
|| this.playWhenReady != playWhenReady) {
this.playbackState = playbackState;
this.playWhenReady = playWhenReady;
for (EventListener listener : listeners) {
listener.onPlayerStateChanged(this.playWhenReady, this.playbackState);
}
notificationsBatch.add(
new ListenerNotificationTask(
listener -> listener.onPlayerStateChanged(this.playWhenReady, this.playbackState)));
}
@RepeatMode int repeatMode = fetchRepeatMode(remoteMediaClient);
if (this.repeatMode != repeatMode) {
this.repeatMode = repeatMode;
for (EventListener listener : listeners) {
listener.onRepeatModeChanged(repeatMode);
}
notificationsBatch.add(
new ListenerNotificationTask(listener -> listener.onRepeatModeChanged(this.repeatMode)));
}
int currentWindowIndex = fetchCurrentWindowIndex(getMediaStatus());
if (this.currentWindowIndex != currentWindowIndex && pendingSeekCount == 0) {
this.currentWindowIndex = currentWindowIndex;
for (EventListener listener : listeners) {
listener.onPositionDiscontinuity(DISCONTINUITY_REASON_PERIOD_TRANSITION);
}
notificationsBatch.add(
new ListenerNotificationTask(
listener ->
listener.onPositionDiscontinuity(DISCONTINUITY_REASON_PERIOD_TRANSITION)));
}
if (updateTracksAndSelections()) {
for (EventListener listener : listeners) {
listener.onTracksChanged(currentTrackGroups, currentTrackSelection);
}
notificationsBatch.add(
new ListenerNotificationTask(
listener -> listener.onTracksChanged(currentTrackGroups, currentTrackSelection)));
}
maybeUpdateTimelineAndNotify();
flushNotifications();
}

private void maybeUpdateTimelineAndNotify() {
if (updateTimeline()) {
@Player.TimelineChangeReason int reason = waitingForInitialTimeline
? Player.TIMELINE_CHANGE_REASON_PREPARED : Player.TIMELINE_CHANGE_REASON_DYNAMIC;
waitingForInitialTimeline = false;
for (EventListener listener : listeners) {
listener.onTimelineChanged(currentTimeline, null, reason);
}
notificationsBatch.add(
new ListenerNotificationTask(
listener ->
listener.onTimelineChanged(currentTimeline, /* manifest= */ null, reason)));
}
}

Expand Down Expand Up @@ -826,7 +839,23 @@ public void onSessionResuming(CastSession castSession, String s) {

}

// 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> {

Expand All @@ -840,9 +869,25 @@ public void onResult(@NonNull MediaChannelResult result) {
if (--pendingSeekCount == 0) {
pendingSeekWindowIndex = C.INDEX_UNSET;
pendingSeekPositionMs = C.TIME_UNSET;
for (EventListener listener : listeners) {
listener.onSeekProcessed();
}
notificationsBatch.add(new ListenerNotificationTask(EventListener::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);
}
}
}
Expand Down

0 comments on commit 83a6d51

Please sign in to comment.