Use listener notification batching in CastPlayer
PiperOrigin-RevId: 251399230
This commit is contained in:
parent
9ca6f60c3a
commit
44aa731476
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user