From 78d7754f2934d4a029115a56eec4a8c33e8ba0d3 Mon Sep 17 00:00:00 2001 From: tonihei Date: Thu, 12 Jul 2018 00:37:16 -0700 Subject: [PATCH] Add addListener/removeListener to BandwidthMeter. That allows to add listeners after the BandwidthMeter has been created which is helpful as the BandwidthMeter instances are often long-lived static instances. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=204255299 --- RELEASENOTES.md | 3 +- .../exoplayer2/drm/DefaultDrmSession.java | 14 +-- .../drm/DefaultDrmSessionEventListener.java | 96 ------------------- .../drm/DefaultDrmSessionManager.java | 8 +- .../exoplayer2/upstream/BandwidthMeter.java | 16 ++++ .../upstream/DefaultBandwidthMeter.java | 61 ++++++------ .../exoplayer2/util/EventDispatcher.java | 85 ++++++++++++++++ 7 files changed, 146 insertions(+), 137 deletions(-) create mode 100644 library/core/src/main/java/com/google/android/exoplayer2/util/EventDispatcher.java diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2a30fb0353..fad7466e17 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -24,7 +24,8 @@ * Pass `BandwidthMeter` to `TrackSelection.Factory` which can be used to obtain bandwidth estimates in the future. Always null at the moment. * Add method to `BandwidthMeter` to return the `TransferListener` used to - gather bandwidth information. + gather bandwidth information. Also add methods to add and remove event + listeners. * Pass `TransferListener` to `MediaSource`s to listen to media data transfers. Always null at the moment. * Add method to `DataSource` to add `TransferListener`s. Custom `DataSource`s diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java index c4be283548..44867b5039 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java @@ -26,10 +26,10 @@ import android.support.annotation.Nullable; import android.util.Log; import android.util.Pair; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener.EventDispatcher; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest; import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest; +import com.google.android.exoplayer2.util.EventDispatcher; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -81,7 +81,7 @@ import java.util.UUID; private final SchemeData schemeData; private final @DefaultDrmSessionManager.Mode int mode; private final HashMap optionalKeyRequestParameters; - private final EventDispatcher eventDispatcher; + private final EventDispatcher eventDispatcher; private final int initialDrmRequestRetryCount; /* package */ final MediaDrmCallback callback; @@ -128,7 +128,7 @@ import java.util.UUID; HashMap optionalKeyRequestParameters, MediaDrmCallback callback, Looper playbackLooper, - EventDispatcher eventDispatcher, + EventDispatcher eventDispatcher, int initialDrmRequestRetryCount) { this.uuid = uuid; this.provisioningManager = provisioningManager; @@ -333,7 +333,7 @@ import java.util.UUID; onError(new KeysExpiredException()); } else { state = STATE_OPENED_WITH_KEYS; - eventDispatcher.drmKeysRestored(); + eventDispatcher.dispatch(DefaultDrmSessionEventListener::onDrmKeysRestored); } } break; @@ -414,7 +414,7 @@ import java.util.UUID; byte[] responseData = (byte[]) response; if (mode == DefaultDrmSessionManager.MODE_RELEASE) { mediaDrm.provideKeyResponse(offlineLicenseKeySetId, responseData); - eventDispatcher.drmKeysRemoved(); + eventDispatcher.dispatch(DefaultDrmSessionEventListener::onDrmKeysRestored); } else { byte[] keySetId = mediaDrm.provideKeyResponse(sessionId, responseData); if ((mode == DefaultDrmSessionManager.MODE_DOWNLOAD @@ -423,7 +423,7 @@ import java.util.UUID; offlineLicenseKeySetId = keySetId; } state = STATE_OPENED_WITH_KEYS; - eventDispatcher.drmKeysLoaded(); + eventDispatcher.dispatch(DefaultDrmSessionEventListener::onDrmKeysLoaded); } } catch (Exception e) { onKeysError(e); @@ -447,7 +447,7 @@ import java.util.UUID; private void onError(final Exception e) { lastException = new DrmSessionException(e); - eventDispatcher.drmSessionManagerError(e); + eventDispatcher.dispatch(listener -> listener.onDrmSessionManagerError(e)); if (state != STATE_OPENED_WITH_KEYS) { state = STATE_ERROR; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionEventListener.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionEventListener.java index 7cdee7c537..afec4b6114 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionEventListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionEventListener.java @@ -15,10 +15,7 @@ */ package com.google.android.exoplayer2.drm; -import android.os.Handler; import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.util.Assertions; -import java.util.concurrent.CopyOnWriteArrayList; /** Listener of {@link DefaultDrmSessionManager} events. */ public interface DefaultDrmSessionEventListener { @@ -45,97 +42,4 @@ public interface DefaultDrmSessionEventListener { /** Called each time offline keys are removed. */ void onDrmKeysRemoved(); - - /** Dispatches drm events to all registered listeners. */ - final class EventDispatcher { - - private final CopyOnWriteArrayList listeners; - - /** Creates event dispatcher. */ - public EventDispatcher() { - listeners = new CopyOnWriteArrayList<>(); - } - - /** Adds listener to event dispatcher. */ - public void addListener(Handler handler, DefaultDrmSessionEventListener eventListener) { - Assertions.checkArgument(handler != null && eventListener != null); - listeners.add(new HandlerAndListener(handler, eventListener)); - } - - /** Removes listener from event dispatcher. */ - public void removeListener(DefaultDrmSessionEventListener eventListener) { - for (HandlerAndListener handlerAndListener : listeners) { - if (handlerAndListener.listener == eventListener) { - listeners.remove(handlerAndListener); - } - } - } - - /** Dispatches {@link DefaultDrmSessionEventListener#onDrmKeysLoaded()}. */ - public void drmKeysLoaded() { - for (HandlerAndListener handlerAndListener : listeners) { - final DefaultDrmSessionEventListener listener = handlerAndListener.listener; - handlerAndListener.handler.post( - new Runnable() { - @Override - public void run() { - listener.onDrmKeysLoaded(); - } - }); - } - } - - /** Dispatches {@link DefaultDrmSessionEventListener#onDrmSessionManagerError(Exception)}. */ - public void drmSessionManagerError(final Exception e) { - for (HandlerAndListener handlerAndListener : listeners) { - final DefaultDrmSessionEventListener listener = handlerAndListener.listener; - handlerAndListener.handler.post( - new Runnable() { - @Override - public void run() { - listener.onDrmSessionManagerError(e); - } - }); - } - } - - /** Dispatches {@link DefaultDrmSessionEventListener#onDrmKeysRestored()}. */ - public void drmKeysRestored() { - for (HandlerAndListener handlerAndListener : listeners) { - final DefaultDrmSessionEventListener listener = handlerAndListener.listener; - handlerAndListener.handler.post( - new Runnable() { - @Override - public void run() { - listener.onDrmKeysRestored(); - } - }); - } - } - - /** Dispatches {@link DefaultDrmSessionEventListener#onDrmKeysRemoved()}. */ - public void drmKeysRemoved() { - for (HandlerAndListener handlerAndListener : listeners) { - final DefaultDrmSessionEventListener listener = handlerAndListener.listener; - handlerAndListener.handler.post( - new Runnable() { - @Override - public void run() { - listener.onDrmKeysRemoved(); - } - }); - } - } - - private static final class HandlerAndListener { - - public final Handler handler; - public final DefaultDrmSessionEventListener listener; - - public HandlerAndListener(Handler handler, DefaultDrmSessionEventListener eventListener) { - this.handler = handler; - this.listener = eventListener; - } - } - } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java index 28fd7e15ab..895c27ad93 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java @@ -26,12 +26,12 @@ import android.text.TextUtils; import android.util.Log; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.drm.DefaultDrmSession.ProvisioningManager; -import com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener.EventDispatcher; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException; import com.google.android.exoplayer2.drm.ExoMediaDrm.OnEventListener; import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil; import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.EventDispatcher; import com.google.android.exoplayer2.util.Util; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -93,7 +93,7 @@ public class DefaultDrmSessionManager implements DrmSe private final ExoMediaDrm mediaDrm; private final MediaDrmCallback callback; private final HashMap optionalKeyRequestParameters; - private final EventDispatcher eventDispatcher; + private final EventDispatcher eventDispatcher; private final boolean multiSession; private final int initialDrmRequestRetryCount; @@ -354,7 +354,7 @@ public class DefaultDrmSessionManager implements DrmSe this.mediaDrm = mediaDrm; this.callback = callback; this.optionalKeyRequestParameters = optionalKeyRequestParameters; - this.eventDispatcher = new EventDispatcher(); + this.eventDispatcher = new EventDispatcher<>(); this.multiSession = multiSession; this.initialDrmRequestRetryCount = initialDrmRequestRetryCount; mode = MODE_PLAYBACK; @@ -512,7 +512,7 @@ public class DefaultDrmSessionManager implements DrmSe schemeData = getSchemeData(drmInitData, uuid, false); if (schemeData == null) { final MissingSchemeDataException error = new MissingSchemeDataException(uuid); - eventDispatcher.drmSessionManagerError(error); + eventDispatcher.dispatch(listener -> listener.onDrmSessionManagerError(error)); return new ErrorStateDrmSession<>(new DrmSessionException(error)); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/BandwidthMeter.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/BandwidthMeter.java index 73e9225b41..78c8a2790b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/BandwidthMeter.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/BandwidthMeter.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.upstream; +import android.os.Handler; import android.support.annotation.Nullable; /** @@ -49,4 +50,19 @@ public interface BandwidthMeter { */ @Nullable TransferListener getTransferListener(); + + /** + * Adds an {@link EventListener} to be informed of bandwidth samples. + * + * @param eventHandler A handler for events. + * @param eventListener A listener of events. + */ + void addEventListener(Handler eventHandler, EventListener eventListener); + + /** + * Removes an {@link EventListener}. + * + * @param eventListener The listener to be removed. + */ + void removeEventListener(EventListener eventListener); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultBandwidthMeter.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultBandwidthMeter.java index 080165164d..6542c6777e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultBandwidthMeter.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultBandwidthMeter.java @@ -19,6 +19,7 @@ import android.os.Handler; import android.support.annotation.Nullable; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Clock; +import com.google.android.exoplayer2.util.EventDispatcher; import com.google.android.exoplayer2.util.SlidingPercentile; /** @@ -105,16 +106,19 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList * @return A bandwidth meter with the configured properties. */ public DefaultBandwidthMeter build() { - return new DefaultBandwidthMeter( - eventHandler, eventListener, initialBitrateEstimate, slidingWindowMaxWeight, clock); + DefaultBandwidthMeter bandwidthMeter = + new DefaultBandwidthMeter(initialBitrateEstimate, slidingWindowMaxWeight, clock); + if (eventHandler != null && eventListener != null) { + bandwidthMeter.addEventListener(eventHandler, eventListener); + } + return bandwidthMeter; } } private static final int ELAPSED_MILLIS_FOR_ESTIMATE = 2000; private static final int BYTES_TRANSFERRED_FOR_ESTIMATE = 512 * 1024; - private final @Nullable Handler eventHandler; - private final @Nullable EventListener eventListener; + private final EventDispatcher eventDispatcher; private final SlidingPercentile slidingPercentile; private final Clock clock; @@ -128,39 +132,32 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList /** Creates a bandwidth meter with default parameters. */ public DefaultBandwidthMeter() { - this( - /* eventHandler= */ null, - /* eventListener= */ null, - DEFAULT_INITIAL_BITRATE_ESTIMATE, - DEFAULT_SLIDING_WINDOW_MAX_WEIGHT, - Clock.DEFAULT); + this(DEFAULT_INITIAL_BITRATE_ESTIMATE, DEFAULT_SLIDING_WINDOW_MAX_WEIGHT, Clock.DEFAULT); } /** @deprecated Use {@link Builder} instead. */ @Deprecated public DefaultBandwidthMeter(Handler eventHandler, EventListener eventListener) { - this( - eventHandler, - eventListener, - DEFAULT_INITIAL_BITRATE_ESTIMATE, - DEFAULT_SLIDING_WINDOW_MAX_WEIGHT, - Clock.DEFAULT); + this(DEFAULT_INITIAL_BITRATE_ESTIMATE, DEFAULT_SLIDING_WINDOW_MAX_WEIGHT, Clock.DEFAULT); + if (eventHandler != null && eventListener != null) { + addEventListener(eventHandler, eventListener); + } } /** @deprecated Use {@link Builder} instead. */ @Deprecated public DefaultBandwidthMeter(Handler eventHandler, EventListener eventListener, int maxWeight) { - this(eventHandler, eventListener, DEFAULT_INITIAL_BITRATE_ESTIMATE, maxWeight, Clock.DEFAULT); + this(DEFAULT_INITIAL_BITRATE_ESTIMATE, maxWeight, Clock.DEFAULT); + if (eventHandler != null && eventListener != null) { + addEventListener(eventHandler, eventListener); + } } private DefaultBandwidthMeter( - @Nullable Handler eventHandler, - @Nullable EventListener eventListener, long initialBitrateEstimate, int maxWeight, Clock clock) { - this.eventHandler = eventHandler; - this.eventListener = eventListener; + this.eventDispatcher = new EventDispatcher<>(); this.slidingPercentile = new SlidingPercentile(maxWeight); this.clock = clock; bitrateEstimate = initialBitrateEstimate; @@ -176,6 +173,16 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList return this; } + @Override + public void addEventListener(Handler eventHandler, EventListener eventListener) { + eventDispatcher.addListener(eventHandler, eventListener); + } + + @Override + public void removeEventListener(EventListener eventListener) { + eventDispatcher.removeListener(eventListener); + } + @Override public void onTransferInitializing(Object source, DataSpec dataSpec, boolean isNetwork) { // Do nothing. @@ -219,17 +226,13 @@ public final class DefaultBandwidthMeter implements BandwidthMeter, TransferList bitrateEstimate = (long) slidingPercentile.getPercentile(0.5f); } } - notifyBandwidthSample(sampleElapsedTimeMs, sampleBytesTransferred, bitrateEstimate); + eventDispatcher.dispatch( + listener -> + listener.onBandwidthSample( + sampleElapsedTimeMs, sampleBytesTransferred, bitrateEstimate)); if (--streamCount > 0) { sampleStartTimeMs = nowMs; } sampleBytesTransferred = 0; } - - private void notifyBandwidthSample(int elapsedMs, long bytes, long bitrate) { - if (eventHandler != null && eventListener != null) { - EventListener eventListener = this.eventListener; - eventHandler.post(() -> eventListener.onBandwidthSample(elapsedMs, bytes, bitrate)); - } - } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/EventDispatcher.java b/library/core/src/main/java/com/google/android/exoplayer2/util/EventDispatcher.java new file mode 100644 index 0000000000..26c02d8ae9 --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/EventDispatcher.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.util; + +import android.os.Handler; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Event dispatcher which allows listener registration. + * + * @param The type of listener. + */ +public final class EventDispatcher { + + /** Functional interface to send an event. */ + public interface Event { + + /** + * Sends the event to a listener. + * + * @param listener The listener to send the event to. + */ + void sendTo(T listener); + } + + /** The list of listeners and handlers. */ + private final CopyOnWriteArrayList> listeners; + + /** Creates event dispatcher. */ + public EventDispatcher() { + listeners = new CopyOnWriteArrayList<>(); + } + + /** Adds listener to event dispatcher. */ + public void addListener(Handler handler, T eventListener) { + Assertions.checkArgument(handler != null && eventListener != null); + removeListener(eventListener); + listeners.add(new HandlerAndListener<>(handler, eventListener)); + } + + /** Removes listener from event dispatcher. */ + public void removeListener(T eventListener) { + for (HandlerAndListener handlerAndListener : listeners) { + if (handlerAndListener.listener == eventListener) { + listeners.remove(handlerAndListener); + } + } + } + + /** + * Dispatches an event to all registered listeners. + * + * @param event The {@link Event}. + */ + public void dispatch(Event event) { + for (HandlerAndListener handlerAndListener : listeners) { + T eventListener = handlerAndListener.listener; + handlerAndListener.handler.post(() -> event.sendTo(eventListener)); + } + } + + private static final class HandlerAndListener { + + public final Handler handler; + public final T listener; + + public HandlerAndListener(Handler handler, T eventListener) { + this.handler = handler; + this.listener = eventListener; + } + } +}