From b489f58cdbf3581ef7091e3d1af5bb671869ca6d Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 27 Feb 2018 08:37:07 -0800 Subject: [PATCH] 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 --- .../DynamicConcatenatingMediaSource.java | 116 ++++++++++-------- 1 file changed, 64 insertions(+), 52 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java index f52c1bfd0f..6d57cca440 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java @@ -48,7 +48,8 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< private static final int MSG_ADD_MULTIPLE = 1; private static final int MSG_REMOVE = 2; 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. private final List mediaSourcesPublic; @@ -58,12 +59,13 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< private final MediaSourceHolder query; private final Map mediaSourceByMediaPeriod; private final List deferredMediaPeriods; + private final List pendingOnCompletionActions; private final boolean isAtomic; private ExoPlayer player; private Listener listener; + private boolean listenerNotificationScheduled; private ShuffleOrder shuffleOrder; - private boolean preventListenerNotification; private int windowCount; private int periodCount; @@ -98,6 +100,7 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< this.mediaSourcesPublic = new ArrayList<>(); this.mediaSourceHolders = new ArrayList<>(); this.deferredMediaPeriods = new ArrayList<>(1); + this.pendingOnCompletionActions = new ArrayList<>(); this.query = new MediaSourceHolder(null, null, -1, -1, -1); this.isAtomic = isAtomic; } @@ -349,11 +352,9 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< super.prepareSource(player, isTopLevelSource, listener); this.player = player; this.listener = listener; - preventListenerNotification = true; shuffleOrder = shuffleOrder.cloneAndInsert(0, mediaSourcesPublic.size()); addMediaSourcesInternal(0, mediaSourcesPublic); - preventListenerNotification = false; - maybeNotifyListener(null); + scheduleListenerNotification(/* actionOnCompletion= */ null); } @Override @@ -412,62 +413,73 @@ public final class DynamicConcatenatingMediaSource extends CompositeMediaSource< @Override @SuppressWarnings("unchecked") public void handleMessage(int messageType, Object message) throws ExoPlaybackException { - if (messageType == MSG_ON_COMPLETION) { - ((EventDispatcher) message).dispatchEvent(); - return; - } - preventListenerNotification = true; - EventDispatcher actionOnCompletion; switch (messageType) { - case MSG_ADD: { - MessageData messageData = (MessageData) message; - shuffleOrder = shuffleOrder.cloneAndInsert(messageData.index, 1); - addMediaSourceInternal(messageData.index, messageData.customData); - actionOnCompletion = messageData.actionOnCompletion; + case MSG_ADD: + MessageData addMessage = (MessageData) message; + shuffleOrder = shuffleOrder.cloneAndInsert(addMessage.index, 1); + addMediaSourceInternal(addMessage.index, addMessage.customData); + scheduleListenerNotification(addMessage.actionOnCompletion); break; - } - case MSG_ADD_MULTIPLE: { - MessageData> messageData = + case MSG_ADD_MULTIPLE: + MessageData> addMultipleMessage = (MessageData>) message; - shuffleOrder = shuffleOrder.cloneAndInsert(messageData.index, - messageData.customData.size()); - addMediaSourcesInternal(messageData.index, messageData.customData); - actionOnCompletion = messageData.actionOnCompletion; + shuffleOrder = + shuffleOrder.cloneAndInsert( + addMultipleMessage.index, addMultipleMessage.customData.size()); + addMediaSourcesInternal(addMultipleMessage.index, addMultipleMessage.customData); + scheduleListenerNotification(addMultipleMessage.actionOnCompletion); break; - } - case MSG_REMOVE: { - MessageData messageData = (MessageData) message; - shuffleOrder = shuffleOrder.cloneAndRemove(messageData.index); - removeMediaSourceInternal(messageData.index); - actionOnCompletion = messageData.actionOnCompletion; + case MSG_REMOVE: + MessageData removeMessage = (MessageData) message; + shuffleOrder = shuffleOrder.cloneAndRemove(removeMessage.index); + removeMediaSourceInternal(removeMessage.index); + scheduleListenerNotification(removeMessage.actionOnCompletion); break; - } - case MSG_MOVE: { - MessageData messageData = (MessageData) message; - shuffleOrder = shuffleOrder.cloneAndRemove(messageData.index); - shuffleOrder = shuffleOrder.cloneAndInsert(messageData.customData, 1); - moveMediaSourceInternal(messageData.index, messageData.customData); - actionOnCompletion = messageData.actionOnCompletion; + case MSG_MOVE: + MessageData moveMessage = (MessageData) message; + shuffleOrder = shuffleOrder.cloneAndRemove(moveMessage.index); + shuffleOrder = shuffleOrder.cloneAndInsert(moveMessage.customData, 1); + moveMediaSourceInternal(moveMessage.index, moveMessage.customData); + scheduleListenerNotification(moveMessage.actionOnCompletion); break; - } - default: { + case MSG_NOTIFY_LISTENER: + notifyListener(); + break; + case MSG_ON_COMPLETION: + List actionsOnCompletion = ((List) message); + for (int i = 0; i < actionsOnCompletion.size(); i++) { + actionsOnCompletion.get(i).dispatchEvent(); + } + break; + default: throw new IllegalStateException(); - } } - preventListenerNotification = false; - maybeNotifyListener(actionOnCompletion); } - private void maybeNotifyListener(@Nullable EventDispatcher actionOnCompletion) { - if (!preventListenerNotification) { - listener.onSourceInfoRefreshed( - this, - new ConcatenatedTimeline( - mediaSourceHolders, windowCount, periodCount, shuffleOrder, isAtomic), - null); - if (actionOnCompletion != null) { - player.createMessage(this).setType(MSG_ON_COMPLETION).setPayload(actionOnCompletion).send(); - } + private void scheduleListenerNotification(@Nullable EventDispatcher actionOnCompletion) { + if (!listenerNotificationScheduled) { + player.createMessage(this).setType(MSG_NOTIFY_LISTENER).send(); + listenerNotificationScheduled = true; + } + if (actionOnCompletion != null) { + pendingOnCompletionActions.add(actionOnCompletion); + } + } + + private void notifyListener() { + listenerNotificationScheduled = false; + List actionsOnCompletion = + pendingOnCompletionActions.isEmpty() + ? Collections.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< } } mediaSourceHolder.isPrepared = true; - maybeNotifyListener(null); + scheduleListenerNotification(/* actionOnCompletion= */ null); } private void removeMediaSourceInternal(int index) {