Commit b489f58c by tonihei Committed by Andrew Lewis

Change listener notification in DynamicConcatenatingMediaSource.

Up to now we use a boolean "preventListenerNotification" to suppress updates
while other operations are still in progress. This ensures, for example, that
only one initial timeline is issued even for multiple child sources.

As soon as we allow to reuse the same instance, it becomes increasingly difficult
to manage this manual listener notification suppression. Therefore, this change
schedules an update as a new message on the playback thread immediately after the
current message. This way, we also ensure that all simultaneous updates within one
looper message iteration are reported together.

Issue:#3498

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=187180342
parent 322e15f6
...@@ -48,7 +48,8 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -48,7 +48,8 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
private static final int MSG_ADD_MULTIPLE = 1; private static final int MSG_ADD_MULTIPLE = 1;
private static final int MSG_REMOVE = 2; private static final int MSG_REMOVE = 2;
private static final int MSG_MOVE = 3; private static final int MSG_MOVE = 3;
private static final int MSG_ON_COMPLETION = 4; private static final int MSG_NOTIFY_LISTENER = 4;
private static final int MSG_ON_COMPLETION = 5;
// Accessed on the app thread. // Accessed on the app thread.
private final List<MediaSource> mediaSourcesPublic; private final List<MediaSource> mediaSourcesPublic;
...@@ -58,12 +59,13 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -58,12 +59,13 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
private final MediaSourceHolder query; private final MediaSourceHolder query;
private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod; private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod;
private final List<DeferredMediaPeriod> deferredMediaPeriods; private final List<DeferredMediaPeriod> deferredMediaPeriods;
private final List<EventDispatcher> pendingOnCompletionActions;
private final boolean isAtomic; private final boolean isAtomic;
private ExoPlayer player; private ExoPlayer player;
private Listener listener; private Listener listener;
private boolean listenerNotificationScheduled;
private ShuffleOrder shuffleOrder; private ShuffleOrder shuffleOrder;
private boolean preventListenerNotification;
private int windowCount; private int windowCount;
private int periodCount; private int periodCount;
...@@ -98,6 +100,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -98,6 +100,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
this.mediaSourcesPublic = new ArrayList<>(); this.mediaSourcesPublic = new ArrayList<>();
this.mediaSourceHolders = new ArrayList<>(); this.mediaSourceHolders = new ArrayList<>();
this.deferredMediaPeriods = new ArrayList<>(1); this.deferredMediaPeriods = new ArrayList<>(1);
this.pendingOnCompletionActions = new ArrayList<>();
this.query = new MediaSourceHolder(null, null, -1, -1, -1); this.query = new MediaSourceHolder(null, null, -1, -1, -1);
this.isAtomic = isAtomic; this.isAtomic = isAtomic;
} }
...@@ -349,11 +352,9 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -349,11 +352,9 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
super.prepareSource(player, isTopLevelSource, listener); super.prepareSource(player, isTopLevelSource, listener);
this.player = player; this.player = player;
this.listener = listener; this.listener = listener;
preventListenerNotification = true;
shuffleOrder = shuffleOrder.cloneAndInsert(0, mediaSourcesPublic.size()); shuffleOrder = shuffleOrder.cloneAndInsert(0, mediaSourcesPublic.size());
addMediaSourcesInternal(0, mediaSourcesPublic); addMediaSourcesInternal(0, mediaSourcesPublic);
preventListenerNotification = false; scheduleListenerNotification(/* actionOnCompletion= */ null);
maybeNotifyListener(null);
} }
@Override @Override
...@@ -412,62 +413,73 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -412,62 +413,73 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void handleMessage(int messageType, Object message) throws ExoPlaybackException { public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
if (messageType == MSG_ON_COMPLETION) {
((EventDispatcher) message).dispatchEvent();
return;
}
preventListenerNotification = true;
EventDispatcher actionOnCompletion;
switch (messageType) { switch (messageType) {
case MSG_ADD: { case MSG_ADD:
MessageData<MediaSource> messageData = (MessageData<MediaSource>) message; MessageData<MediaSource> addMessage = (MessageData<MediaSource>) message;
shuffleOrder = shuffleOrder.cloneAndInsert(messageData.index, 1); shuffleOrder = shuffleOrder.cloneAndInsert(addMessage.index, 1);
addMediaSourceInternal(messageData.index, messageData.customData); addMediaSourceInternal(addMessage.index, addMessage.customData);
actionOnCompletion = messageData.actionOnCompletion; scheduleListenerNotification(addMessage.actionOnCompletion);
break; break;
} case MSG_ADD_MULTIPLE:
case MSG_ADD_MULTIPLE: { MessageData<Collection<MediaSource>> addMultipleMessage =
MessageData<Collection<MediaSource>> messageData =
(MessageData<Collection<MediaSource>>) message; (MessageData<Collection<MediaSource>>) message;
shuffleOrder = shuffleOrder.cloneAndInsert(messageData.index, shuffleOrder =
messageData.customData.size()); shuffleOrder.cloneAndInsert(
addMediaSourcesInternal(messageData.index, messageData.customData); addMultipleMessage.index, addMultipleMessage.customData.size());
actionOnCompletion = messageData.actionOnCompletion; addMediaSourcesInternal(addMultipleMessage.index, addMultipleMessage.customData);
scheduleListenerNotification(addMultipleMessage.actionOnCompletion);
break; break;
} case MSG_REMOVE:
case MSG_REMOVE: { MessageData<Void> removeMessage = (MessageData<Void>) message;
MessageData<Void> messageData = (MessageData<Void>) message; shuffleOrder = shuffleOrder.cloneAndRemove(removeMessage.index);
shuffleOrder = shuffleOrder.cloneAndRemove(messageData.index); removeMediaSourceInternal(removeMessage.index);
removeMediaSourceInternal(messageData.index); scheduleListenerNotification(removeMessage.actionOnCompletion);
actionOnCompletion = messageData.actionOnCompletion;
break; break;
} case MSG_MOVE:
case MSG_MOVE: { MessageData<Integer> moveMessage = (MessageData<Integer>) message;
MessageData<Integer> messageData = (MessageData<Integer>) message; shuffleOrder = shuffleOrder.cloneAndRemove(moveMessage.index);
shuffleOrder = shuffleOrder.cloneAndRemove(messageData.index); shuffleOrder = shuffleOrder.cloneAndInsert(moveMessage.customData, 1);
shuffleOrder = shuffleOrder.cloneAndInsert(messageData.customData, 1); moveMediaSourceInternal(moveMessage.index, moveMessage.customData);
moveMediaSourceInternal(messageData.index, messageData.customData); scheduleListenerNotification(moveMessage.actionOnCompletion);
actionOnCompletion = messageData.actionOnCompletion;
break; break;
} case MSG_NOTIFY_LISTENER:
default: { notifyListener();
break;
case MSG_ON_COMPLETION:
List<EventDispatcher> actionsOnCompletion = ((List<EventDispatcher>) message);
for (int i = 0; i < actionsOnCompletion.size(); i++) {
actionsOnCompletion.get(i).dispatchEvent();
}
break;
default:
throw new IllegalStateException(); throw new IllegalStateException();
}
} }
preventListenerNotification = false;
maybeNotifyListener(actionOnCompletion);
} }
private void maybeNotifyListener(@Nullable EventDispatcher actionOnCompletion) { private void scheduleListenerNotification(@Nullable EventDispatcher actionOnCompletion) {
if (!preventListenerNotification) { if (!listenerNotificationScheduled) {
listener.onSourceInfoRefreshed( player.createMessage(this).setType(MSG_NOTIFY_LISTENER).send();
this, listenerNotificationScheduled = true;
new ConcatenatedTimeline( }
mediaSourceHolders, windowCount, periodCount, shuffleOrder, isAtomic), if (actionOnCompletion != null) {
null); pendingOnCompletionActions.add(actionOnCompletion);
if (actionOnCompletion != null) { }
player.createMessage(this).setType(MSG_ON_COMPLETION).setPayload(actionOnCompletion).send(); }
}
private void notifyListener() {
listenerNotificationScheduled = false;
List<EventDispatcher> actionsOnCompletion =
pendingOnCompletionActions.isEmpty()
? Collections.<EventDispatcher>emptyList()
: new ArrayList<>(pendingOnCompletionActions);
pendingOnCompletionActions.clear();
listener.onSourceInfoRefreshed(
this,
new ConcatenatedTimeline(
mediaSourceHolders, windowCount, periodCount, shuffleOrder, isAtomic),
/* manifest= */ null);
if (!actionsOnCompletion.isEmpty()) {
player.createMessage(this).setType(MSG_ON_COMPLETION).setPayload(actionsOnCompletion).send();
} }
} }
...@@ -528,7 +540,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< ...@@ -528,7 +540,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<
} }
} }
mediaSourceHolder.isPrepared = true; mediaSourceHolder.isPrepared = true;
maybeNotifyListener(null); scheduleListenerNotification(/* actionOnCompletion= */ null);
} }
private void removeMediaSourceInternal(int index) { private void removeMediaSourceInternal(int index) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment