From fa594489d9a07e254e9fa3c85636cadd9d6dd842 Mon Sep 17 00:00:00 2001 From: ibaker Date: Tue, 7 Jul 2020 16:40:27 +0100 Subject: [PATCH] Completely separate MediaSource & DrmSession EventDispatchers PiperOrigin-RevId: 319989989 --- .../android/exoplayer2/util/Consumer.java | 26 ++ .../android/exoplayer2/MediaSourceList.java | 69 +++-- .../exoplayer2/drm/DefaultDrmSession.java | 35 ++- .../drm/DefaultDrmSessionManager.java | 16 +- .../android/exoplayer2/drm/DrmSession.java | 26 +- .../drm/DrmSessionEventListener.java | 149 +++++++++++ .../exoplayer2/drm/DrmSessionManager.java | 14 +- .../exoplayer2/drm/ErrorStateDrmSession.java | 5 +- .../exoplayer2/drm/OfflineLicenseHelper.java | 34 ++- .../exoplayer2/source/BaseMediaSource.java | 60 +++-- .../source/CompositeMediaSource.java | 61 ++--- .../source/MediaSourceEventListener.java | 222 ++++++++++++---- .../source/ProgressiveMediaPeriod.java | 34 +-- .../source/ProgressiveMediaSource.java | 1 + .../exoplayer2/source/SampleQueue.java | 18 +- .../source/chunk/ChunkSampleStream.java | 38 +-- .../util/MediaSourceEventDispatcher.java | 203 --------------- .../android/exoplayer2/ExoPlayerTest.java | 53 ++-- .../audio/DecoderAudioRendererTest.java | 4 +- .../audio/MediaCodecAudioRendererTest.java | 10 +- .../drm/OfflineLicenseHelperTest.java | 3 +- .../metadata/MetadataRendererTest.java | 4 +- .../source/ClippingMediaSourceTest.java | 10 +- .../source/MergingMediaPeriodTest.java | 6 +- .../source/ProgressiveMediaPeriodTest.java | 9 +- .../exoplayer2/source/SampleQueueTest.java | 6 +- .../util/MediaSourceEventDispatcherTest.java | 236 ------------------ .../video/DecoderVideoRendererTest.java | 22 +- .../video/MediaCodecVideoRendererTest.java | 37 ++- .../source/dash/DashMediaPeriod.java | 20 +- .../source/dash/DashMediaSource.java | 5 +- .../source/dash/PlayerEmsgHandler.java | 4 +- .../source/dash/DashMediaPeriodTest.java | 13 +- .../exoplayer2/source/hls/HlsMediaPeriod.java | 5 + .../exoplayer2/source/hls/HlsMediaSource.java | 11 +- .../source/hls/HlsSampleStreamWrapper.java | 38 +-- .../source/hls/HlsMediaPeriodTest.java | 13 +- .../source/smoothstreaming/SsMediaPeriod.java | 21 +- .../source/smoothstreaming/SsMediaSource.java | 7 +- .../smoothstreaming/SsMediaPeriodTest.java | 34 +-- .../gts/DashWidevineOfflineTest.java | 6 +- .../testutil/FakeAdaptiveMediaPeriod.java | 14 +- .../testutil/FakeAdaptiveMediaSource.java | 8 +- .../exoplayer2/testutil/FakeMediaPeriod.java | 80 ++++-- .../exoplayer2/testutil/FakeMediaSource.java | 29 ++- .../exoplayer2/testutil/FakeSampleStream.java | 27 +- 46 files changed, 878 insertions(+), 868 deletions(-) create mode 100644 library/common/src/main/java/com/google/android/exoplayer2/util/Consumer.java delete mode 100644 library/core/src/main/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcher.java delete mode 100644 library/core/src/test/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcherTest.java diff --git a/library/common/src/main/java/com/google/android/exoplayer2/util/Consumer.java b/library/common/src/main/java/com/google/android/exoplayer2/util/Consumer.java new file mode 100644 index 0000000000..8e982fc646 --- /dev/null +++ b/library/common/src/main/java/com/google/android/exoplayer2/util/Consumer.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2020 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; + +/** + * Represents an operation that accepts a single input argument and returns no result. Unlike most + * other functional interfaces, Consumer is expected to operate via side-effects. + */ +public interface Consumer { + + /** Performs this operation on the given argument. */ + void accept(T t); +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/MediaSourceList.java b/library/core/src/main/java/com/google/android/exoplayer2/MediaSourceList.java index 4e0e99320e..85a0b52789 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/MediaSourceList.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaSourceList.java @@ -70,7 +70,8 @@ import java.util.Set; private final IdentityHashMap mediaSourceByMediaPeriod; private final Map mediaSourceByUid; private final MediaSourceListInfoRefreshListener mediaSourceListInfoListener; - private final MediaSourceEventListener.EventDispatcher eventDispatcher; + private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; + private final DrmSessionEventListener.EventDispatcher drmEventDispatcher; private final HashMap childSources; private final Set enabledMediaSourceHolders; @@ -98,14 +99,13 @@ import java.util.Set; mediaSourceByMediaPeriod = new IdentityHashMap<>(); mediaSourceByUid = new HashMap<>(); mediaSourceHolders = new ArrayList<>(); - eventDispatcher = new MediaSourceEventListener.EventDispatcher(); + mediaSourceEventDispatcher = new MediaSourceEventListener.EventDispatcher(); + drmEventDispatcher = new DrmSessionEventListener.EventDispatcher(); childSources = new HashMap<>(); enabledMediaSourceHolders = new HashSet<>(); if (analyticsCollector != null) { - eventDispatcher.addEventListener( - analyticsCollectorHandler, analyticsCollector, MediaSourceEventListener.class); - eventDispatcher.addEventListener( - analyticsCollectorHandler, analyticsCollector, DrmSessionEventListener.class); + mediaSourceEventDispatcher.addEventListener(analyticsCollectorHandler, analyticsCollector); + drmEventDispatcher.addEventListener(analyticsCollectorHandler, analyticsCollector); } } @@ -523,10 +523,12 @@ import java.util.Set; implements MediaSourceEventListener, DrmSessionEventListener { private final MediaSourceList.MediaSourceHolder id; - private EventDispatcher eventDispatcher; + private MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; + private DrmSessionEventListener.EventDispatcher drmEventDispatcher; public ForwardingEventListener(MediaSourceList.MediaSourceHolder id) { - eventDispatcher = MediaSourceList.this.eventDispatcher; + mediaSourceEventDispatcher = MediaSourceList.this.mediaSourceEventDispatcher; + drmEventDispatcher = MediaSourceList.this.drmEventDispatcher; this.id = id; } @@ -535,14 +537,14 @@ import java.util.Set; @Override public void onMediaPeriodCreated(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.mediaPeriodCreated(); + mediaSourceEventDispatcher.mediaPeriodCreated(); } } @Override public void onMediaPeriodReleased(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.mediaPeriodReleased(); + mediaSourceEventDispatcher.mediaPeriodReleased(); } } @@ -553,7 +555,7 @@ import java.util.Set; LoadEventInfo loadEventData, MediaLoadData mediaLoadData) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.loadStarted(loadEventData, mediaLoadData); + mediaSourceEventDispatcher.loadStarted(loadEventData, mediaLoadData); } } @@ -564,7 +566,7 @@ import java.util.Set; LoadEventInfo loadEventData, MediaLoadData mediaLoadData) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.loadCompleted(loadEventData, mediaLoadData); + mediaSourceEventDispatcher.loadCompleted(loadEventData, mediaLoadData); } } @@ -575,7 +577,7 @@ import java.util.Set; LoadEventInfo loadEventData, MediaLoadData mediaLoadData) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.loadCanceled(loadEventData, mediaLoadData); + mediaSourceEventDispatcher.loadCanceled(loadEventData, mediaLoadData); } } @@ -588,14 +590,14 @@ import java.util.Set; IOException error, boolean wasCanceled) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.loadError(loadEventData, mediaLoadData, error, wasCanceled); + mediaSourceEventDispatcher.loadError(loadEventData, mediaLoadData, error, wasCanceled); } } @Override public void onReadingStarted(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.readingStarted(); + mediaSourceEventDispatcher.readingStarted(); } } @@ -605,7 +607,7 @@ import java.util.Set; @Nullable MediaSource.MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.upstreamDiscarded(mediaLoadData); + mediaSourceEventDispatcher.upstreamDiscarded(mediaLoadData); } } @@ -615,7 +617,7 @@ import java.util.Set; @Nullable MediaSource.MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.downstreamFormatChanged(mediaLoadData); + mediaSourceEventDispatcher.downstreamFormatChanged(mediaLoadData); } } @@ -625,8 +627,7 @@ import java.util.Set; public void onDrmSessionAcquired( int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmSessionAcquired, DrmSessionEventListener.class); + drmEventDispatcher.drmSessionAcquired(); } } @@ -634,8 +635,7 @@ import java.util.Set; public void onDrmKeysLoaded( int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmKeysLoaded, DrmSessionEventListener.class); + drmEventDispatcher.drmKeysLoaded(); } } @@ -643,10 +643,7 @@ import java.util.Set; public void onDrmSessionManagerError( int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId, Exception error) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - (listener, innerWindowIndex, innerMediaPeriodId) -> - listener.onDrmSessionManagerError(innerWindowIndex, innerMediaPeriodId, error), - DrmSessionEventListener.class); + drmEventDispatcher.drmSessionManagerError(error); } } @@ -654,8 +651,7 @@ import java.util.Set; public void onDrmKeysRestored( int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmKeysRestored, DrmSessionEventListener.class); + drmEventDispatcher.drmKeysRestored(); } } @@ -663,8 +659,7 @@ import java.util.Set; public void onDrmKeysRemoved( int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmKeysRemoved, DrmSessionEventListener.class); + drmEventDispatcher.drmKeysRemoved(); } } @@ -672,8 +667,7 @@ import java.util.Set; public void onDrmSessionReleased( int windowIndex, @Nullable MediaSource.MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmSessionReleased, DrmSessionEventListener.class); + drmEventDispatcher.drmSessionReleased(); } } @@ -689,12 +683,17 @@ import java.util.Set; } } int windowIndex = getWindowIndexForChildWindowIndex(id, childWindowIndex); - if (eventDispatcher.windowIndex != windowIndex - || !Util.areEqual(eventDispatcher.mediaPeriodId, mediaPeriodId)) { - eventDispatcher = - MediaSourceList.this.eventDispatcher.withParameters( + if (mediaSourceEventDispatcher.windowIndex != windowIndex + || !Util.areEqual(mediaSourceEventDispatcher.mediaPeriodId, mediaPeriodId)) { + mediaSourceEventDispatcher = + MediaSourceList.this.mediaSourceEventDispatcher.withParameters( windowIndex, mediaPeriodId, /* mediaTimeOffsetMs= */ 0L); } + if (drmEventDispatcher.windowIndex != windowIndex + || !Util.areEqual(drmEventDispatcher.mediaPeriodId, mediaPeriodId)) { + drmEventDispatcher = + MediaSourceList.this.drmEventDispatcher.withParameters(windowIndex, mediaPeriodId); + } return true; } } 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 4139db05ad..400bb6c28f 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 @@ -34,9 +34,9 @@ import com.google.android.exoplayer2.source.MediaLoadData; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy.LoadErrorInfo; import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.Consumer; import com.google.android.exoplayer2.util.CopyOnWriteMultiset; import com.google.android.exoplayer2.util.Log; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import com.google.android.exoplayer2.util.Util; import java.io.IOException; import java.util.Arrays; @@ -123,7 +123,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private final boolean playClearSamplesWithoutKeys; private final boolean isPlaceholderSession; private final HashMap keyRequestParameters; - private final CopyOnWriteMultiset eventDispatchers; + private final CopyOnWriteMultiset eventDispatchers; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; /* package */ final MediaDrmCallback callback; @@ -271,7 +271,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } @Override - public void acquire(@Nullable MediaSourceEventDispatcher eventDispatcher) { + public void acquire(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) { Assertions.checkState(referenceCount >= 0); if (eventDispatcher != null) { eventDispatchers.add(eventDispatcher); @@ -288,14 +288,13 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; // If the session is already open then send the acquire event only to the provided dispatcher. // TODO: Add a parameter to onDrmSessionAcquired to indicate whether the session is being // re-used or not. - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmSessionAcquired, DrmSessionEventListener.class); + eventDispatcher.drmSessionAcquired(); } referenceCountListener.onReferenceCountIncremented(this, referenceCount); } @Override - public void release(@Nullable MediaSourceEventDispatcher eventDispatcher) { + public void release(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) { if (--referenceCount == 0) { // Assigning null to various non-null variables for clean-up. state = STATE_RELEASED; @@ -312,14 +311,13 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; mediaDrm.closeSession(sessionId); sessionId = null; } - dispatchEvent(DrmSessionEventListener::onDrmSessionReleased); + dispatchEvent(DrmSessionEventListener.EventDispatcher::drmSessionReleased); } if (eventDispatcher != null) { if (isOpen()) { // If the session is still open then send the release event only to the provided dispatcher // before removing it. - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmSessionReleased, DrmSessionEventListener.class); + eventDispatcher.drmSessionReleased(); } eventDispatchers.remove(eventDispatcher); } @@ -345,7 +343,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; try { sessionId = mediaDrm.openSession(); mediaCrypto = mediaDrm.createMediaCrypto(sessionId); - dispatchEvent(DrmSessionEventListener::onDrmSessionAcquired); + dispatchEvent(DrmSessionEventListener.EventDispatcher::drmSessionAcquired); state = STATE_OPENED; Assertions.checkNotNull(sessionId); return true; @@ -409,7 +407,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; onError(new KeysExpiredException()); } else { state = STATE_OPENED_WITH_KEYS; - dispatchEvent(DrmSessionEventListener::onDrmKeysRestored); + dispatchEvent(DrmSessionEventListener.EventDispatcher::drmKeysRestored); } } break; @@ -479,7 +477,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; byte[] responseData = (byte[]) response; if (mode == DefaultDrmSessionManager.MODE_RELEASE) { mediaDrm.provideKeyResponse(Util.castNonNull(offlineLicenseKeySetId), responseData); - dispatchEvent(DrmSessionEventListener::onDrmKeysRemoved); + dispatchEvent(DrmSessionEventListener.EventDispatcher::drmKeysRemoved); } else { byte[] keySetId = mediaDrm.provideKeyResponse(sessionId, responseData); if ((mode == DefaultDrmSessionManager.MODE_DOWNLOAD @@ -490,7 +488,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; offlineLicenseKeySetId = keySetId; } state = STATE_OPENED_WITH_KEYS; - dispatchEvent(DrmSessionEventListener::onDrmKeysLoaded); + dispatchEvent(DrmSessionEventListener.EventDispatcher::drmKeysLoaded); } } catch (Exception e) { onKeysError(e); @@ -514,9 +512,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private void onError(final Exception e) { lastException = new DrmSessionException(e); - dispatchEvent( - (listener, windowIndex, mediaPeriodId) -> - listener.onDrmSessionManagerError(windowIndex, mediaPeriodId, e)); + dispatchEvent(eventDispatcher -> eventDispatcher.drmSessionManagerError(e)); if (state != STATE_OPENED_WITH_KEYS) { state = STATE_ERROR; } @@ -528,10 +524,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; return state == STATE_OPENED || state == STATE_OPENED_WITH_KEYS; } - private void dispatchEvent( - MediaSourceEventDispatcher.EventWithPeriodId event) { - for (MediaSourceEventDispatcher eventDispatcher : eventDispatchers.elementSet()) { - eventDispatcher.dispatch(event, DrmSessionEventListener.class); + private void dispatchEvent(Consumer event) { + for (DrmSessionEventListener.EventDispatcher eventDispatcher : eventDispatchers.elementSet()) { + event.accept(eventDispatcher); } } 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 2912af4c36..34e8cb2afd 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 @@ -32,7 +32,6 @@ import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Log; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import com.google.android.exoplayer2.util.Util; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; @@ -406,8 +405,8 @@ public class DefaultDrmSessionManager implements DrmSessionManager { /** * Sets the mode, which determines the role of sessions acquired from the instance. This must be - * called before {@link #acquireSession(Looper, MediaSourceEventDispatcher, DrmInitData)} or - * {@link #acquirePlaceholderSession} is called. + * called before {@link #acquireSession(Looper, DrmSessionEventListener.EventDispatcher, + * DrmInitData)} or {@link #acquirePlaceholderSession} is called. * *

By default, the mode is {@link #MODE_PLAYBACK} and a streaming license is requested when * required. @@ -527,7 +526,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { @Override public DrmSession acquireSession( Looper playbackLooper, - @Nullable MediaSourceEventDispatcher eventDispatcher, + @Nullable DrmSessionEventListener.EventDispatcher eventDispatcher, DrmInitData drmInitData) { initPlaybackLooper(playbackLooper); maybeCreateMediaDrmHandler(playbackLooper); @@ -538,10 +537,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { if (schemeDatas.isEmpty()) { final MissingSchemeDataException error = new MissingSchemeDataException(uuid); if (eventDispatcher != null) { - eventDispatcher.dispatch( - (listener, windowIndex, mediaPeriodId) -> - listener.onDrmSessionManagerError(windowIndex, mediaPeriodId, error), - DrmSessionEventListener.class); + eventDispatcher.drmSessionManagerError(error); } return new ErrorStateDrmSession(new DrmSessionException(error)); } @@ -605,7 +601,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { private DefaultDrmSession createAndAcquireSessionWithRetry( @Nullable List schemeDatas, boolean isPlaceholderSession, - @Nullable MediaSourceEventDispatcher eventDispatcher) { + @Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) { DefaultDrmSession session = createAndAcquireSession(schemeDatas, isPlaceholderSession, eventDispatcher); if (session.getState() == DrmSession.STATE_ERROR @@ -644,7 +640,7 @@ public class DefaultDrmSessionManager implements DrmSessionManager { private DefaultDrmSession createAndAcquireSession( @Nullable List schemeDatas, boolean isPlaceholderSession, - @Nullable MediaSourceEventDispatcher eventDispatcher) { + @Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) { Assertions.checkNotNull(exoMediaDrm); // Placeholder sessions should always play clear samples without keys. boolean playClearSamplesWithoutKeys = this.playClearSamplesWithoutKeys | isPlaceholderSession; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java index 3f2aae7b30..97bb4b3dd1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer2.drm; import android.media.MediaDrm; import androidx.annotation.IntDef; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -31,10 +30,10 @@ public interface DrmSession { /** * Acquires {@code newSession} then releases {@code previousSession}. * - *

Invokes {@code newSession's} {@link #acquire(MediaSourceEventDispatcher)} and {@code - * previousSession's} {@link #release(MediaSourceEventDispatcher)} in that order (passing {@code - * eventDispatcher = null}). Null arguments are ignored. Does nothing if {@code previousSession} - * and {@code newSession} are the same session. + *

Invokes {@code newSession's} {@link #acquire(DrmSessionEventListener.EventDispatcher)} and + * {@code previousSession's} {@link #release(DrmSessionEventListener.EventDispatcher)} in that + * order (passing {@code eventDispatcher = null}). Null arguments are ignored. Does nothing if + * {@code previousSession} and {@code newSession} are the same session. */ static void replaceSession( @Nullable DrmSession previousSession, @Nullable DrmSession newSession) { @@ -134,20 +133,21 @@ public interface DrmSession { /** * Increments the reference count. When the caller no longer needs to use the instance, it must - * call {@link #release(MediaSourceEventDispatcher)} to decrement the reference count. + * call {@link #release(DrmSessionEventListener.EventDispatcher)} to decrement the reference + * count. * - * @param eventDispatcher The {@link MediaSourceEventDispatcher} used to route DRM-related events - * dispatched from this session, or null if no event handling is needed. + * @param eventDispatcher The {@link DrmSessionEventListener.EventDispatcher} used to route + * DRM-related events dispatched from this session, or null if no event handling is needed. */ - void acquire(@Nullable MediaSourceEventDispatcher eventDispatcher); + void acquire(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher); /** * Decrements the reference count. If the reference count drops to 0 underlying resources are * released, and the instance cannot be re-used. * - * @param eventDispatcher The {@link MediaSourceEventDispatcher} to disconnect when the session is - * released (the same instance (possibly null) that was passed by the caller to {@link - * #acquire(MediaSourceEventDispatcher)}). + * @param eventDispatcher The {@link DrmSessionEventListener.EventDispatcher} to disconnect when + * the session is released (the same instance (possibly null) that was passed by the caller to + * {@link #acquire(DrmSessionEventListener.EventDispatcher)}). */ - void release(@Nullable MediaSourceEventDispatcher eventDispatcher); + void release(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionEventListener.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionEventListener.java index b4482408b1..c32a6fc1a3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionEventListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionEventListener.java @@ -15,9 +15,14 @@ */ package com.google.android.exoplayer2.drm; +import android.os.Handler; +import android.os.Looper; +import androidx.annotation.CheckResult; import androidx.annotation.Nullable; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; +import com.google.android.exoplayer2.util.Assertions; +import java.util.concurrent.CopyOnWriteArrayList; /** Listener of {@link DrmSessionManager} events. */ public interface DrmSessionEventListener { @@ -78,4 +83,148 @@ public interface DrmSessionEventListener { * @param mediaPeriodId The {@link MediaPeriodId} associated with the drm session. */ default void onDrmSessionReleased(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) {} + + /** Dispatches events to {@link DrmSessionEventListener DrmSessionEventListeners}. */ + class EventDispatcher { + + /** The timeline window index reported with the events. */ + public final int windowIndex; + /** The {@link MediaPeriodId} reported with the events. */ + @Nullable public final MediaPeriodId mediaPeriodId; + + private final CopyOnWriteArrayList listenerAndHandlers; + + /** Creates an event dispatcher. */ + public EventDispatcher() { + this( + /* listenerAndHandlers= */ new CopyOnWriteArrayList<>(), + /* windowIndex= */ 0, + /* mediaPeriodId= */ null); + } + + private EventDispatcher( + CopyOnWriteArrayList listenerAndHandlers, + int windowIndex, + @Nullable MediaPeriodId mediaPeriodId) { + this.listenerAndHandlers = listenerAndHandlers; + this.windowIndex = windowIndex; + this.mediaPeriodId = mediaPeriodId; + } + + /** + * Creates a view of the event dispatcher with the provided window index and media period id. + * + * @param windowIndex The timeline window index to be reported with the events. + * @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events. + * @return A view of the event dispatcher with the pre-configured parameters. + */ + @CheckResult + public EventDispatcher withParameters(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { + return new EventDispatcher(listenerAndHandlers, windowIndex, mediaPeriodId); + } + + /** + * Adds a listener to the event dispatcher. + * + * @param handler A handler on the which listener events will be posted. + * @param eventListener The listener to be added. + */ + public void addEventListener(Handler handler, DrmSessionEventListener eventListener) { + Assertions.checkNotNull(handler); + Assertions.checkNotNull(eventListener); + listenerAndHandlers.add(new ListenerAndHandler(handler, eventListener)); + } + + /** + * Removes a listener from the event dispatcher. + * + * @param eventListener The listener to be removed. + */ + public void removeEventListener(DrmSessionEventListener eventListener) { + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + if (listenerAndHandler.listener == eventListener) { + listenerAndHandlers.remove(listenerAndHandler); + } + } + } + + /** Dispatches {@link #onDrmSessionAcquired(int, MediaPeriodId)}. */ + public void drmSessionAcquired() { + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + DrmSessionEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> listener.onDrmSessionAcquired(windowIndex, mediaPeriodId)); + } + } + + /** Dispatches {@link #onDrmKeysLoaded(int, MediaPeriodId)}. */ + public void drmKeysLoaded() { + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + DrmSessionEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, () -> listener.onDrmKeysLoaded(windowIndex, mediaPeriodId)); + } + } + + /** Dispatches {@link #onDrmSessionManagerError(int, MediaPeriodId, Exception)}. */ + public void drmSessionManagerError(Exception error) { + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + DrmSessionEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> listener.onDrmSessionManagerError(windowIndex, mediaPeriodId, error)); + } + } + + /** Dispatches {@link #onDrmKeysRestored(int, MediaPeriodId)}. */ + public void drmKeysRestored() { + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + DrmSessionEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> listener.onDrmKeysRestored(windowIndex, mediaPeriodId)); + } + } + + /** Dispatches {@link #onDrmKeysRemoved(int, MediaPeriodId)}. */ + public void drmKeysRemoved() { + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + DrmSessionEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> listener.onDrmKeysRemoved(windowIndex, mediaPeriodId)); + } + } + + /** Dispatches {@link #onDrmSessionReleased(int, MediaPeriodId)}. */ + public void drmSessionReleased() { + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + DrmSessionEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> listener.onDrmSessionReleased(windowIndex, mediaPeriodId)); + } + } + + /** Dispatches {@link #onDrmSessionAcquired(int, MediaPeriodId)}. */ + private static void postOrRun(Handler handler, Runnable runnable) { + if (handler.getLooper() == Looper.myLooper()) { + runnable.run(); + } else { + handler.post(runnable); + } + } + + private static final class ListenerAndHandler { + + public Handler handler; + public DrmSessionEventListener listener; + + public ListenerAndHandler(Handler handler, DrmSessionEventListener listener) { + this.handler = handler; + this.listener = listener; + } + } + } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java index 0283470765..69da101837 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java @@ -19,7 +19,6 @@ import android.os.Looper; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; /** Manages a DRM session. */ public interface DrmSessionManager { @@ -41,7 +40,7 @@ public interface DrmSessionManager { @Override public DrmSession acquireSession( Looper playbackLooper, - @Nullable MediaSourceEventDispatcher eventDispatcher, + @Nullable DrmSessionEventListener.EventDispatcher eventDispatcher, DrmInitData drmInitData) { return new ErrorStateDrmSession( new DrmSession.DrmSessionException( @@ -83,7 +82,7 @@ public interface DrmSessionManager { /** * Returns a {@link DrmSession} that does not execute key requests, with an incremented reference * count. When the caller no longer needs to use the instance, it must call {@link - * DrmSession#release(MediaSourceEventDispatcher)} to decrement the reference count. + * DrmSession#release(DrmSessionEventListener.EventDispatcher)} to decrement the reference count. * *

Placeholder {@link DrmSession DrmSessions} may be used to configure secure decoders for * playback of clear content periods. This can reduce the cost of transitioning between clear and @@ -103,18 +102,19 @@ public interface DrmSessionManager { /** * Returns a {@link DrmSession} for the specified {@link DrmInitData}, with an incremented * reference count. When the caller no longer needs to use the instance, it must call {@link - * DrmSession#release(MediaSourceEventDispatcher)} to decrement the reference count. + * DrmSession#release(DrmSessionEventListener.EventDispatcher)} to decrement the reference count. * * @param playbackLooper The looper associated with the media playback thread. - * @param eventDispatcher The {@link MediaSourceEventDispatcher} used to distribute events, and - * passed on to {@link DrmSession#acquire(MediaSourceEventDispatcher)}. + * @param eventDispatcher The {@link DrmSessionEventListener.EventDispatcher} used to distribute + * events, and passed on to {@link + * DrmSession#acquire(DrmSessionEventListener.EventDispatcher)}. * @param drmInitData DRM initialization data. All contained {@link SchemeData}s must contain * non-null {@link SchemeData#data}. * @return The DRM session. */ DrmSession acquireSession( Looper playbackLooper, - @Nullable MediaSourceEventDispatcher eventDispatcher, + @Nullable DrmSessionEventListener.EventDispatcher eventDispatcher, DrmInitData drmInitData); /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/ErrorStateDrmSession.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/ErrorStateDrmSession.java index ff0a861f4b..4253d3011c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/ErrorStateDrmSession.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/ErrorStateDrmSession.java @@ -17,7 +17,6 @@ package com.google.android.exoplayer2.drm; import androidx.annotation.Nullable; import com.google.android.exoplayer2.util.Assertions; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import java.util.Map; /** A {@link DrmSession} that's in a terminal error state. */ @@ -64,12 +63,12 @@ public final class ErrorStateDrmSession implements DrmSession { } @Override - public void acquire(@Nullable MediaSourceEventDispatcher eventDispatcher) { + public void acquire(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) { // Do nothing. } @Override - public void release(@Nullable MediaSourceEventDispatcher eventDispatcher) { + public void release(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) { // Do nothing. } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java index 5b4c9daac7..15b060b9e1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java @@ -27,7 +27,6 @@ import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.util.Assertions; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import java.util.Map; import java.util.UUID; @@ -40,7 +39,7 @@ public final class OfflineLicenseHelper { private final ConditionVariable conditionVariable; private final DefaultDrmSessionManager drmSessionManager; private final HandlerThread handlerThread; - private final MediaSourceEventDispatcher eventDispatcher; + private final DrmSessionEventListener.EventDispatcher eventDispatcher; /** * Instantiates a new instance which uses Widevine CDM. Call {@link #release()} when the instance @@ -49,14 +48,14 @@ public final class OfflineLicenseHelper { * @param defaultLicenseUrl The default license URL. Used for key requests that do not specify * their own license URL. * @param httpDataSourceFactory A factory from which to obtain {@link HttpDataSource} instances. - * @param eventDispatcher A {@link MediaSourceEventDispatcher} used to distribute DRM-related - * events. + * @param eventDispatcher A {@link DrmSessionEventListener.EventDispatcher} used to distribute + * DRM-related events. * @return A new instance which uses Widevine CDM. */ public static OfflineLicenseHelper newWidevineInstance( String defaultLicenseUrl, HttpDataSource.Factory httpDataSourceFactory, - MediaSourceEventDispatcher eventDispatcher) { + DrmSessionEventListener.EventDispatcher eventDispatcher) { return newWidevineInstance( defaultLicenseUrl, /* forceDefaultLicenseUrl= */ false, @@ -73,15 +72,15 @@ public final class OfflineLicenseHelper { * @param forceDefaultLicenseUrl Whether to use {@code defaultLicenseUrl} for key requests that * include their own license URL. * @param httpDataSourceFactory A factory from which to obtain {@link HttpDataSource} instances. - * @param eventDispatcher A {@link MediaSourceEventDispatcher} used to distribute DRM-related - * events. + * @param eventDispatcher A {@link DrmSessionEventListener.EventDispatcher} used to distribute + * DRM-related events. * @return A new instance which uses Widevine CDM. */ public static OfflineLicenseHelper newWidevineInstance( String defaultLicenseUrl, boolean forceDefaultLicenseUrl, HttpDataSource.Factory httpDataSourceFactory, - MediaSourceEventDispatcher eventDispatcher) { + DrmSessionEventListener.EventDispatcher eventDispatcher) { return newWidevineInstance( defaultLicenseUrl, forceDefaultLicenseUrl, @@ -100,8 +99,8 @@ public final class OfflineLicenseHelper { * include their own license URL. * @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument * to {@link MediaDrm#getKeyRequest}. May be null. - * @param eventDispatcher A {@link MediaSourceEventDispatcher} used to distribute DRM-related - * events. + * @param eventDispatcher A {@link DrmSessionEventListener.EventDispatcher} used to distribute + * DRM-related events. * @return A new instance which uses Widevine CDM. * @see DefaultDrmSessionManager.Builder */ @@ -110,7 +109,7 @@ public final class OfflineLicenseHelper { boolean forceDefaultLicenseUrl, HttpDataSource.Factory httpDataSourceFactory, @Nullable Map optionalKeyRequestParameters, - MediaSourceEventDispatcher eventDispatcher) { + DrmSessionEventListener.EventDispatcher eventDispatcher) { return new OfflineLicenseHelper( new DefaultDrmSessionManager.Builder() .setKeyRequestParameters(optionalKeyRequestParameters) @@ -122,7 +121,7 @@ public final class OfflineLicenseHelper { /** * @deprecated Use {@link #OfflineLicenseHelper(DefaultDrmSessionManager, - * MediaSourceEventDispatcher)} instead. + * DrmSessionEventListener.EventDispatcher)} instead. */ @Deprecated public OfflineLicenseHelper( @@ -130,7 +129,7 @@ public final class OfflineLicenseHelper { ExoMediaDrm.Provider mediaDrmProvider, MediaDrmCallback callback, @Nullable Map optionalKeyRequestParameters, - MediaSourceEventDispatcher eventDispatcher) { + DrmSessionEventListener.EventDispatcher eventDispatcher) { this( new DefaultDrmSessionManager.Builder() .setUuidAndExoMediaDrmProvider(uuid, mediaDrmProvider) @@ -143,12 +142,12 @@ public final class OfflineLicenseHelper { * Constructs an instance. Call {@link #release()} when the instance is no longer required. * * @param defaultDrmSessionManager The {@link DefaultDrmSessionManager} used to download licenses. - * @param eventDispatcher A {@link MediaSourceEventDispatcher} used to distribute DRM-related - * events. + * @param eventDispatcher A {@link DrmSessionEventListener.EventDispatcher} used to distribute + * DRM-related events. */ public OfflineLicenseHelper( DefaultDrmSessionManager defaultDrmSessionManager, - MediaSourceEventDispatcher eventDispatcher) { + DrmSessionEventListener.EventDispatcher eventDispatcher) { this.drmSessionManager = defaultDrmSessionManager; this.eventDispatcher = eventDispatcher; handlerThread = new HandlerThread("ExoPlayer:OfflineLicenseHelper"); @@ -177,8 +176,7 @@ public final class OfflineLicenseHelper { conditionVariable.open(); } }; - eventDispatcher.addEventListener( - new Handler(handlerThread.getLooper()), eventListener, DrmSessionEventListener.class); + eventDispatcher.addEventListener(new Handler(handlerThread.getLooper()), eventListener); } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/BaseMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/BaseMediaSource.java index 461b146b8d..04b8ad1f83 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/BaseMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/BaseMediaSource.java @@ -22,7 +22,6 @@ import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.Assertions; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import java.util.ArrayList; import java.util.HashSet; @@ -38,6 +37,7 @@ public abstract class BaseMediaSource implements MediaSource { private final ArrayList mediaSourceCallers; private final HashSet enabledMediaSourceCallers; private final MediaSourceEventListener.EventDispatcher eventDispatcher; + private final DrmSessionEventListener.EventDispatcher drmEventDispatcher; @Nullable private Looper looper; @Nullable private Timeline timeline; @@ -46,6 +46,7 @@ public abstract class BaseMediaSource implements MediaSource { mediaSourceCallers = new ArrayList<>(/* initialCapacity= */ 1); enabledMediaSourceCallers = new HashSet<>(/* initialCapacity= */ 1); eventDispatcher = new MediaSourceEventListener.EventDispatcher(); + drmEventDispatcher = new DrmSessionEventListener.EventDispatcher(); } /** @@ -127,6 +128,33 @@ public abstract class BaseMediaSource implements MediaSource { return eventDispatcher.withParameters(windowIndex, mediaPeriodId, mediaTimeOffsetMs); } + /** + * Returns a {@link DrmSessionEventListener.EventDispatcher} which dispatches all events to the + * registered listeners with the specified media period id. + * + * @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events. May be null, if + * the events do not belong to a specific media period. + * @return An event dispatcher with pre-configured media period id. + */ + protected final DrmSessionEventListener.EventDispatcher createDrmEventDispatcher( + @Nullable MediaPeriodId mediaPeriodId) { + return drmEventDispatcher.withParameters(/* windowIndex= */ 0, mediaPeriodId); + } + + /** + * Returns a {@link DrmSessionEventListener.EventDispatcher} which dispatches all events to the + * registered listeners with the specified window index and media period id. + * + * @param windowIndex The timeline window index to be reported with the events. + * @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events. May be null, if + * the events do not belong to a specific media period. + * @return An event dispatcher with pre-configured media period id and time offset. + */ + protected final DrmSessionEventListener.EventDispatcher createDrmEventDispatcher( + int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { + return drmEventDispatcher.withParameters(windowIndex, mediaPeriodId); + } + /** Returns whether the source is enabled. */ protected final boolean isEnabled() { return !enabledMediaSourceCallers.isEmpty(); @@ -134,44 +162,22 @@ public abstract class BaseMediaSource implements MediaSource { @Override public final void addEventListener(Handler handler, MediaSourceEventListener eventListener) { - addEventListenerInternal(handler, eventListener, MediaSourceEventListener.class); + eventDispatcher.addEventListener(handler, eventListener); } @Override public final void removeEventListener(MediaSourceEventListener eventListener) { - removeEventListenerInternal(eventListener, MediaSourceEventListener.class); + eventDispatcher.removeEventListener(eventListener); } @Override public final void addDrmEventListener(Handler handler, DrmSessionEventListener eventListener) { - addEventListenerInternal(handler, eventListener, DrmSessionEventListener.class); + drmEventDispatcher.addEventListener(handler, eventListener); } @Override public final void removeDrmEventListener(DrmSessionEventListener eventListener) { - removeEventListenerInternal(eventListener, DrmSessionEventListener.class); - } - - /** - * Adds a listener to the internal {@link MediaSourceEventDispatcher} with the provided type. - * - *

NOTE: Read the caveats on {@link MediaSourceEventDispatcher#addEventListener(Handler, - * Object, Class)} when deciding what value to pass for {@code listenerClass}. - * - * @see MediaSourceEventDispatcher#addEventListener(Handler, Object, Class) - */ - protected final void addEventListenerInternal( - Handler handler, T eventListener, Class listenerClass) { - eventDispatcher.addEventListener(handler, eventListener, listenerClass); - } - - /** - * Removes a listener from the internal {@link MediaSourceEventDispatcher}. - * - * @see MediaSourceEventDispatcher#removeEventListener(Object, Class) - */ - protected final void removeEventListenerInternal(T eventListener, Class listenerClass) { - eventDispatcher.removeEventListener(eventListener, listenerClass); + drmEventDispatcher.removeEventListener(eventListener); } @Override diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java index 3d31eeba92..dd7f22d2de 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/CompositeMediaSource.java @@ -222,10 +222,12 @@ public abstract class CompositeMediaSource extends BaseMediaSource { implements MediaSourceEventListener, DrmSessionEventListener { @UnknownNull private final T id; - private EventDispatcher eventDispatcher; + private MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; + private DrmSessionEventListener.EventDispatcher drmEventDispatcher; public ForwardingEventListener(@UnknownNull T id) { - this.eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null); + this.mediaSourceEventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null); + this.drmEventDispatcher = createDrmEventDispatcher(/* mediaPeriodId= */ null); this.id = id; } @@ -235,8 +237,8 @@ public abstract class CompositeMediaSource extends BaseMediaSource { public void onMediaPeriodCreated(int windowIndex, MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { if (shouldDispatchCreateOrReleaseEvent( - Assertions.checkNotNull(eventDispatcher.mediaPeriodId))) { - eventDispatcher.mediaPeriodCreated(); + Assertions.checkNotNull(mediaSourceEventDispatcher.mediaPeriodId))) { + mediaSourceEventDispatcher.mediaPeriodCreated(); } } } @@ -245,8 +247,8 @@ public abstract class CompositeMediaSource extends BaseMediaSource { public void onMediaPeriodReleased(int windowIndex, MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { if (shouldDispatchCreateOrReleaseEvent( - Assertions.checkNotNull(eventDispatcher.mediaPeriodId))) { - eventDispatcher.mediaPeriodReleased(); + Assertions.checkNotNull(mediaSourceEventDispatcher.mediaPeriodId))) { + mediaSourceEventDispatcher.mediaPeriodReleased(); } } } @@ -258,7 +260,8 @@ public abstract class CompositeMediaSource extends BaseMediaSource { LoadEventInfo loadEventData, MediaLoadData mediaLoadData) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.loadStarted(loadEventData, maybeUpdateMediaLoadData(mediaLoadData)); + mediaSourceEventDispatcher.loadStarted( + loadEventData, maybeUpdateMediaLoadData(mediaLoadData)); } } @@ -269,7 +272,8 @@ public abstract class CompositeMediaSource extends BaseMediaSource { LoadEventInfo loadEventData, MediaLoadData mediaLoadData) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.loadCompleted(loadEventData, maybeUpdateMediaLoadData(mediaLoadData)); + mediaSourceEventDispatcher.loadCompleted( + loadEventData, maybeUpdateMediaLoadData(mediaLoadData)); } } @@ -280,7 +284,8 @@ public abstract class CompositeMediaSource extends BaseMediaSource { LoadEventInfo loadEventData, MediaLoadData mediaLoadData) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.loadCanceled(loadEventData, maybeUpdateMediaLoadData(mediaLoadData)); + mediaSourceEventDispatcher.loadCanceled( + loadEventData, maybeUpdateMediaLoadData(mediaLoadData)); } } @@ -293,7 +298,7 @@ public abstract class CompositeMediaSource extends BaseMediaSource { IOException error, boolean wasCanceled) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.loadError( + mediaSourceEventDispatcher.loadError( loadEventData, maybeUpdateMediaLoadData(mediaLoadData), error, wasCanceled); } } @@ -301,7 +306,7 @@ public abstract class CompositeMediaSource extends BaseMediaSource { @Override public void onReadingStarted(int windowIndex, MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.readingStarted(); + mediaSourceEventDispatcher.readingStarted(); } } @@ -309,7 +314,7 @@ public abstract class CompositeMediaSource extends BaseMediaSource { public void onUpstreamDiscarded( int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.upstreamDiscarded(maybeUpdateMediaLoadData(mediaLoadData)); + mediaSourceEventDispatcher.upstreamDiscarded(maybeUpdateMediaLoadData(mediaLoadData)); } } @@ -317,7 +322,7 @@ public abstract class CompositeMediaSource extends BaseMediaSource { public void onDownstreamFormatChanged( int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.downstreamFormatChanged(maybeUpdateMediaLoadData(mediaLoadData)); + mediaSourceEventDispatcher.downstreamFormatChanged(maybeUpdateMediaLoadData(mediaLoadData)); } } @@ -326,16 +331,14 @@ public abstract class CompositeMediaSource extends BaseMediaSource { @Override public void onDrmSessionAcquired(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmSessionAcquired, DrmSessionEventListener.class); + drmEventDispatcher.drmSessionAcquired(); } } @Override public void onDrmKeysLoaded(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmKeysLoaded, DrmSessionEventListener.class); + drmEventDispatcher.drmKeysLoaded(); } } @@ -343,34 +346,28 @@ public abstract class CompositeMediaSource extends BaseMediaSource { public void onDrmSessionManagerError( int windowIndex, @Nullable MediaPeriodId mediaPeriodId, Exception error) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - (listener, innerWindowIndex, innerMediaPeriodId) -> - listener.onDrmSessionManagerError(innerWindowIndex, innerMediaPeriodId, error), - DrmSessionEventListener.class); + drmEventDispatcher.drmSessionManagerError(error); } } @Override public void onDrmKeysRestored(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmKeysRestored, DrmSessionEventListener.class); + drmEventDispatcher.drmKeysRestored(); } } @Override public void onDrmKeysRemoved(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmKeysRemoved, DrmSessionEventListener.class); + drmEventDispatcher.drmKeysRemoved(); } } @Override public void onDrmSessionReleased(int windowIndex, @Nullable MediaPeriodId mediaPeriodId) { if (maybeUpdateEventDispatcher(windowIndex, mediaPeriodId)) { - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmSessionReleased, DrmSessionEventListener.class); + drmEventDispatcher.drmSessionReleased(); } } @@ -386,11 +383,15 @@ public abstract class CompositeMediaSource extends BaseMediaSource { } } int windowIndex = getWindowIndexForChildWindowIndex(id, childWindowIndex); - if (eventDispatcher.windowIndex != windowIndex - || !Util.areEqual(eventDispatcher.mediaPeriodId, mediaPeriodId)) { - eventDispatcher = + if (mediaSourceEventDispatcher.windowIndex != windowIndex + || !Util.areEqual(mediaSourceEventDispatcher.mediaPeriodId, mediaPeriodId)) { + mediaSourceEventDispatcher = createEventDispatcher(windowIndex, mediaPeriodId, /* mediaTimeOffsetMs= */ 0); } + if (drmEventDispatcher.windowIndex != windowIndex + || !Util.areEqual(drmEventDispatcher.mediaPeriodId, mediaPeriodId)) { + drmEventDispatcher = createDrmEventDispatcher(windowIndex, mediaPeriodId); + } return true; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceEventListener.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceEventListener.java index 7c9dc34b4f..c976613cd0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceEventListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceEventListener.java @@ -15,15 +15,17 @@ */ package com.google.android.exoplayer2.source; +import android.os.Handler; +import android.os.Looper; +import androidx.annotation.CheckResult; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.util.Assertions; -import com.google.android.exoplayer2.util.CopyOnWriteMultiset; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import java.io.IOException; +import java.util.concurrent.CopyOnWriteArrayList; /** Interface for callbacks to be notified of {@link MediaSource} events. */ public interface MediaSourceEventListener { @@ -160,42 +162,101 @@ public interface MediaSourceEventListener { default void onDownstreamFormatChanged( int windowIndex, @Nullable MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {} - /** @deprecated Use {@link MediaSourceEventDispatcher} directly instead. */ - @Deprecated - final class EventDispatcher extends MediaSourceEventDispatcher { + /** Dispatches events to {@link MediaSourceEventListener MediaSourceEventListeners}. */ + class EventDispatcher { + /** The timeline window index reported with the events. */ + public final int windowIndex; + /** The {@link MediaPeriodId} reported with the events. */ + @Nullable public final MediaPeriodId mediaPeriodId; + + private final CopyOnWriteArrayList listenerAndHandlers; + private final long mediaTimeOffsetMs; + + /** Creates an event dispatcher. */ public EventDispatcher() { - super(); + this( + /* listenerAndHandlers= */ new CopyOnWriteArrayList<>(), + /* windowIndex= */ 0, + /* mediaPeriodId= */ null, + /* mediaTimeOffsetMs= */ 0); } private EventDispatcher( - CopyOnWriteMultiset listeners, + CopyOnWriteArrayList listenerAndHandlers, int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) { - super(listeners, windowIndex, mediaPeriodId, mediaTimeOffsetMs); + this.listenerAndHandlers = listenerAndHandlers; + this.windowIndex = windowIndex; + this.mediaPeriodId = mediaPeriodId; + this.mediaTimeOffsetMs = mediaTimeOffsetMs; } - @Override + /** + * Creates a view of the event dispatcher with pre-configured window index, media period id, and + * media time offset. + * + * @param windowIndex The timeline window index to be reported with the events. + * @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events. + * @param mediaTimeOffsetMs The offset to be added to all media times, in milliseconds. + * @return A view of the event dispatcher with the pre-configured parameters. + */ + @CheckResult public EventDispatcher withParameters( int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) { - return new EventDispatcher(listenerInfos, windowIndex, mediaPeriodId, mediaTimeOffsetMs); + return new EventDispatcher( + listenerAndHandlers, windowIndex, mediaPeriodId, mediaTimeOffsetMs); } + /** + * Adds a listener to the event dispatcher. + * + * @param handler A handler on the which listener events will be posted. + * @param eventListener The listener to be added. + */ + public void addEventListener(Handler handler, MediaSourceEventListener eventListener) { + Assertions.checkNotNull(handler); + Assertions.checkNotNull(eventListener); + listenerAndHandlers.add(new ListenerAndHandler(handler, eventListener)); + } + + /** + * Removes a listener from the event dispatcher. + * + * @param eventListener The listener to be removed. + */ + public void removeEventListener(MediaSourceEventListener eventListener) { + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + if (listenerAndHandler.listener == eventListener) { + listenerAndHandlers.remove(listenerAndHandler); + } + } + } + + /** Dispatches {@link #onMediaPeriodCreated(int, MediaPeriodId)}. */ public void mediaPeriodCreated() { - dispatch( - (listener, windowIndex, mediaPeriodId) -> - listener.onMediaPeriodCreated(windowIndex, Assertions.checkNotNull(mediaPeriodId)), - MediaSourceEventListener.class); + MediaPeriodId mediaPeriodId = Assertions.checkNotNull(this.mediaPeriodId); + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + MediaSourceEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> listener.onMediaPeriodCreated(windowIndex, mediaPeriodId)); + } } + /** Dispatches {@link #onMediaPeriodReleased(int, MediaPeriodId)}. */ public void mediaPeriodReleased() { - dispatch( - (listener, windowIndex, mediaPeriodId) -> - listener.onMediaPeriodReleased(windowIndex, Assertions.checkNotNull(mediaPeriodId)), - MediaSourceEventListener.class); + MediaPeriodId mediaPeriodId = Assertions.checkNotNull(this.mediaPeriodId); + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + MediaSourceEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> listener.onMediaPeriodReleased(windowIndex, mediaPeriodId)); + } } + /** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ public void loadStarted(LoadEventInfo loadEventInfo, int dataType) { loadStarted( loadEventInfo, @@ -208,6 +269,7 @@ public interface MediaSourceEventListener { /* mediaEndTimeUs= */ C.TIME_UNSET); } + /** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ public void loadStarted( LoadEventInfo loadEventInfo, int dataType, @@ -229,13 +291,17 @@ public interface MediaSourceEventListener { adjustMediaTime(mediaEndTimeUs))); } + /** Dispatches {@link #onLoadStarted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ public void loadStarted(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) { - dispatch( - (listener, windowIndex, mediaPeriodId) -> - listener.onLoadStarted(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData), - MediaSourceEventListener.class); + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + MediaSourceEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> listener.onLoadStarted(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData)); + } } + /** Dispatches {@link #onLoadCompleted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ public void loadCompleted(LoadEventInfo loadEventInfo, int dataType) { loadCompleted( loadEventInfo, @@ -248,6 +314,7 @@ public interface MediaSourceEventListener { /* mediaEndTimeUs= */ C.TIME_UNSET); } + /** Dispatches {@link #onLoadCompleted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ public void loadCompleted( LoadEventInfo loadEventInfo, int dataType, @@ -269,13 +336,18 @@ public interface MediaSourceEventListener { adjustMediaTime(mediaEndTimeUs))); } + /** Dispatches {@link #onLoadCompleted(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ public void loadCompleted(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) { - dispatch( - (listener, windowIndex, mediaPeriodId) -> - listener.onLoadCompleted(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData), - MediaSourceEventListener.class); + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + MediaSourceEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> + listener.onLoadCompleted(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData)); + } } + /** Dispatches {@link #onLoadCanceled(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ public void loadCanceled(LoadEventInfo loadEventInfo, int dataType) { loadCanceled( loadEventInfo, @@ -288,6 +360,7 @@ public interface MediaSourceEventListener { /* mediaEndTimeUs= */ C.TIME_UNSET); } + /** Dispatches {@link #onLoadCanceled(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ public void loadCanceled( LoadEventInfo loadEventInfo, int dataType, @@ -309,13 +382,21 @@ public interface MediaSourceEventListener { adjustMediaTime(mediaEndTimeUs))); } + /** Dispatches {@link #onLoadCanceled(int, MediaPeriodId, LoadEventInfo, MediaLoadData)}. */ public void loadCanceled(LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData) { - dispatch( - (listener, windowIndex, mediaPeriodId) -> - listener.onLoadCanceled(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData), - MediaSourceEventListener.class); + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + MediaSourceEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> + listener.onLoadCanceled(windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData)); + } } + /** + * Dispatches {@link #onLoadError(int, MediaPeriodId, LoadEventInfo, MediaLoadData, IOException, + * boolean)}. + */ public void loadError( LoadEventInfo loadEventInfo, int dataType, IOException error, boolean wasCanceled) { loadError( @@ -331,6 +412,10 @@ public interface MediaSourceEventListener { wasCanceled); } + /** + * Dispatches {@link #onLoadError(int, MediaPeriodId, LoadEventInfo, MediaLoadData, IOException, + * boolean)}. + */ public void loadError( LoadEventInfo loadEventInfo, int dataType, @@ -356,25 +441,37 @@ public interface MediaSourceEventListener { wasCanceled); } + /** + * Dispatches {@link #onLoadError(int, MediaPeriodId, LoadEventInfo, MediaLoadData, IOException, + * boolean)}. + */ public void loadError( LoadEventInfo loadEventInfo, MediaLoadData mediaLoadData, IOException error, boolean wasCanceled) { - dispatch( - (listener, windowIndex, mediaPeriodId) -> - listener.onLoadError( - windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData, error, wasCanceled), - MediaSourceEventListener.class); + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + MediaSourceEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> + listener.onLoadError( + windowIndex, mediaPeriodId, loadEventInfo, mediaLoadData, error, wasCanceled)); + } } + /** Dispatches {@link #onReadingStarted(int, MediaPeriodId)}. */ public void readingStarted() { - dispatch( - (listener, windowIndex, mediaPeriodId) -> - listener.onReadingStarted(windowIndex, Assertions.checkNotNull(mediaPeriodId)), - MediaSourceEventListener.class); + MediaPeriodId mediaPeriodId = Assertions.checkNotNull(this.mediaPeriodId); + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + MediaSourceEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> listener.onReadingStarted(windowIndex, mediaPeriodId)); + } } + /** Dispatches {@link #onUpstreamDiscarded(int, MediaPeriodId, MediaLoadData)}. */ public void upstreamDiscarded(int trackType, long mediaStartTimeUs, long mediaEndTimeUs) { upstreamDiscarded( new MediaLoadData( @@ -387,14 +484,18 @@ public interface MediaSourceEventListener { adjustMediaTime(mediaEndTimeUs))); } + /** Dispatches {@link #onUpstreamDiscarded(int, MediaPeriodId, MediaLoadData)}. */ public void upstreamDiscarded(MediaLoadData mediaLoadData) { - dispatch( - (listener, windowIndex, mediaPeriodId) -> - listener.onUpstreamDiscarded( - windowIndex, Assertions.checkNotNull(mediaPeriodId), mediaLoadData), - MediaSourceEventListener.class); + MediaPeriodId mediaPeriodId = Assertions.checkNotNull(this.mediaPeriodId); + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + MediaSourceEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> listener.onUpstreamDiscarded(windowIndex, mediaPeriodId, mediaLoadData)); + } } + /** Dispatches {@link #onDownstreamFormatChanged(int, MediaPeriodId, MediaLoadData)}. */ public void downstreamFormatChanged( int trackType, @Nullable Format trackFormat, @@ -412,15 +513,38 @@ public interface MediaSourceEventListener { /* mediaEndTimeMs= */ C.TIME_UNSET)); } + /** Dispatches {@link #onDownstreamFormatChanged(int, MediaPeriodId, MediaLoadData)}. */ public void downstreamFormatChanged(MediaLoadData mediaLoadData) { - dispatch( - (listener, windowIndex, mediaPeriodId) -> - listener.onDownstreamFormatChanged(windowIndex, mediaPeriodId, mediaLoadData), - MediaSourceEventListener.class); + for (ListenerAndHandler listenerAndHandler : listenerAndHandlers) { + MediaSourceEventListener listener = listenerAndHandler.listener; + postOrRun( + listenerAndHandler.handler, + () -> listener.onDownstreamFormatChanged(windowIndex, mediaPeriodId, mediaLoadData)); + } } private long adjustMediaTime(long mediaTimeUs) { - return adjustMediaTime(mediaTimeUs, mediaTimeOffsetMs); + long mediaTimeMs = C.usToMs(mediaTimeUs); + return mediaTimeMs == C.TIME_UNSET ? C.TIME_UNSET : mediaTimeOffsetMs + mediaTimeMs; + } + + private static void postOrRun(Handler handler, Runnable runnable) { + if (handler.getLooper() == Looper.myLooper()) { + runnable.run(); + } else { + handler.post(runnable); + } + } + + private static final class ListenerAndHandler { + + public Handler handler; + public MediaSourceEventListener listener; + + public ListenerAndHandler(Handler handler, MediaSourceEventListener listener) { + this.handler = handler; + this.listener = listener; + } } } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java index 546d56bccf..7bc1c36ba7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java @@ -24,6 +24,7 @@ import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.ExtractorOutput; @@ -35,7 +36,6 @@ import com.google.android.exoplayer2.extractor.SeekMap.Unseekable; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.icy.IcyHeaders; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.SampleQueue.UpstreamFormatChangedListener; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.upstream.Allocator; @@ -101,7 +101,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private final DataSource dataSource; private final DrmSessionManager drmSessionManager; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; - private final EventDispatcher eventDispatcher; + private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; + private final DrmSessionEventListener.EventDispatcher drmEventDispatcher; private final Listener listener; private final Allocator allocator; @Nullable private final String customCacheKey; @@ -145,8 +146,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * @param uri The {@link Uri} of the media stream. * @param dataSource The data source to read the media. * @param extractorsFactory The {@link ExtractorsFactory} to use to read the data source. + * @param drmSessionManager A {@link DrmSessionManager} to allow DRM interactions. + * @param drmEventDispatcher A dispatcher to notify of {@link DrmSessionEventListener} events. * @param loadErrorHandlingPolicy The {@link LoadErrorHandlingPolicy}. - * @param eventDispatcher A dispatcher to notify of events. + * @param mediaSourceEventDispatcher A dispatcher to notify of {@link MediaSourceEventListener} + * events. * @param listener A listener to notify when information about the period changes. * @param allocator An {@link Allocator} from which to obtain media buffer allocations. * @param customCacheKey A custom key that uniquely identifies the original stream. Used for cache @@ -164,8 +168,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; DataSource dataSource, ExtractorsFactory extractorsFactory, DrmSessionManager drmSessionManager, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, LoadErrorHandlingPolicy loadErrorHandlingPolicy, - EventDispatcher eventDispatcher, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, Listener listener, Allocator allocator, @Nullable String customCacheKey, @@ -173,8 +178,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; this.uri = uri; this.dataSource = dataSource; this.drmSessionManager = drmSessionManager; + this.drmEventDispatcher = drmEventDispatcher; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; - this.eventDispatcher = eventDispatcher; + this.mediaSourceEventDispatcher = mediaSourceEventDispatcher; this.listener = listener; this.allocator = allocator; this.customCacheKey = customCacheKey; @@ -199,7 +205,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; length = C.LENGTH_UNSET; durationUs = C.TIME_UNSET; dataType = C.DATA_TYPE_MEDIA; - eventDispatcher.mediaPeriodCreated(); + mediaSourceEventDispatcher.mediaPeriodCreated(); } public void release() { @@ -214,7 +220,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; handler.removeCallbacksAndMessages(null); callback = null; released = true; - eventDispatcher.mediaPeriodReleased(); + mediaSourceEventDispatcher.mediaPeriodReleased(); } @Override @@ -369,7 +375,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Override public long readDiscontinuity() { if (!notifiedReadingStarted) { - eventDispatcher.readingStarted(); + mediaSourceEventDispatcher.readingStarted(); notifiedReadingStarted = true; } if (notifyDiscontinuity @@ -510,7 +516,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; boolean[] trackNotifiedDownstreamFormats = trackState.trackNotifiedDownstreamFormats; if (!trackNotifiedDownstreamFormats[track]) { Format trackFormat = trackState.tracks.get(track).getFormat(/* index= */ 0); - eventDispatcher.downstreamFormatChanged( + mediaSourceEventDispatcher.downstreamFormatChanged( MimeTypes.getTrackType(trackFormat.sampleMimeType), trackFormat, C.SELECTION_REASON_UNKNOWN, @@ -566,7 +572,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; loadDurationMs, dataSource.getBytesRead()); loadErrorHandlingPolicy.onLoadTaskConcluded(loadable.loadTaskId); - eventDispatcher.loadCompleted( + mediaSourceEventDispatcher.loadCompleted( loadEventInfo, C.DATA_TYPE_MEDIA, C.TRACK_TYPE_UNKNOWN, @@ -594,7 +600,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; loadDurationMs, dataSource.getBytesRead()); loadErrorHandlingPolicy.onLoadTaskConcluded(loadable.loadTaskId); - eventDispatcher.loadCanceled( + mediaSourceEventDispatcher.loadCanceled( loadEventInfo, C.DATA_TYPE_MEDIA, C.TRACK_TYPE_UNKNOWN, @@ -657,7 +663,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } boolean wasCanceled = !loadErrorAction.isRetry(); - eventDispatcher.loadError( + mediaSourceEventDispatcher.loadError( loadEventInfo, C.DATA_TYPE_MEDIA, C.TRACK_TYPE_UNKNOWN, @@ -719,7 +725,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; allocator, /* playbackLooper= */ handler.getLooper(), drmSessionManager, - eventDispatcher); + drmEventDispatcher); trackOutput.setUpstreamFormatChangeListener(this); @NullableType TrackId[] sampleQueueTrackIds = Arrays.copyOf(this.sampleQueueTrackIds, trackCount + 1); @@ -824,7 +830,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; loader.startLoading( loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(dataType)); DataSpec dataSpec = loadable.dataSpec; - eventDispatcher.loadStarted( + mediaSourceEventDispatcher.loadStarted( new LoadEventInfo(loadable.loadTaskId, dataSpec, elapsedRealtimeMs), C.DATA_TYPE_MEDIA, C.TRACK_TYPE_UNKNOWN, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaSource.java index ffa8ede140..125891f09c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaSource.java @@ -283,6 +283,7 @@ public final class ProgressiveMediaSource extends BaseMediaSource dataSource, extractorsFactory, drmSessionManager, + createDrmEventDispatcher(id), loadableLoadErrorHandlingPolicy, createEventDispatcher(id), this, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java index b65d86279a..ab6e0e3d97 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java @@ -26,12 +26,12 @@ import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmSession; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataReader; import com.google.android.exoplayer2.util.Assertions; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; @@ -59,7 +59,7 @@ public class SampleQueue implements TrackOutput { private final SampleExtrasHolder extrasHolder; private final Looper playbackLooper; private final DrmSessionManager drmSessionManager; - private final MediaSourceEventDispatcher eventDispatcher; + private final DrmSessionEventListener.EventDispatcher drmEventDispatcher; @Nullable private UpstreamFormatChangedListener upstreamFormatChangeListener; @Nullable private Format downstreamFormat; @@ -103,17 +103,17 @@ public class SampleQueue implements TrackOutput { * @param playbackLooper The looper associated with the media playback thread. * @param drmSessionManager The {@link DrmSessionManager} to obtain {@link DrmSession DrmSessions} * from. The created instance does not take ownership of this {@link DrmSessionManager}. - * @param eventDispatcher A {@link MediaSourceEventDispatcher} to notify of events related to this - * SampleQueue. + * @param drmEventDispatcher A {@link DrmSessionEventListener.EventDispatcher} to notify of events + * related to this SampleQueue. */ public SampleQueue( Allocator allocator, Looper playbackLooper, DrmSessionManager drmSessionManager, - MediaSourceEventDispatcher eventDispatcher) { + DrmSessionEventListener.EventDispatcher drmEventDispatcher) { this.playbackLooper = playbackLooper; this.drmSessionManager = drmSessionManager; - this.eventDispatcher = eventDispatcher; + this.drmEventDispatcher = drmEventDispatcher; sampleDataQueue = new SampleDataQueue(allocator); extrasHolder = new SampleExtrasHolder(); capacity = SAMPLE_CAPACITY_INCREMENT; @@ -691,7 +691,7 @@ public class SampleQueue implements TrackOutput { private void releaseDrmSessionReferences() { if (currentDrmSession != null) { - currentDrmSession.release(eventDispatcher); + currentDrmSession.release(drmEventDispatcher); currentDrmSession = null; // Clear downstream format to avoid violating the assumption that downstreamFormat.drmInitData // != null implies currentSession != null @@ -826,13 +826,13 @@ public class SampleQueue implements TrackOutput { @Nullable DrmSession previousSession = currentDrmSession; currentDrmSession = newDrmInitData != null - ? drmSessionManager.acquireSession(playbackLooper, eventDispatcher, newDrmInitData) + ? drmSessionManager.acquireSession(playbackLooper, drmEventDispatcher, newDrmInitData) : drmSessionManager.acquirePlaceholderSession( playbackLooper, MimeTypes.getTrackType(newFormat.sampleMimeType)); outputFormatHolder.drmSession = currentDrmSession; if (previousSession != null) { - previousSession.release(eventDispatcher); + previousSession.release(drmEventDispatcher); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java index 155c51eca3..2491432bb7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ChunkSampleStream.java @@ -23,10 +23,11 @@ import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.drm.DrmSession; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.LoadEventInfo; import com.google.android.exoplayer2.source.MediaLoadData; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.SampleQueue; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SequenceableLoader; @@ -71,7 +72,7 @@ public class ChunkSampleStream implements SampleStream, S private final boolean[] embeddedTracksSelected; private final T chunkSource; private final SequenceableLoader.Callback> callback; - private final EventDispatcher eventDispatcher; + private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; private final Loader loader; private final ChunkHolder nextChunkHolder; @@ -102,8 +103,10 @@ public class ChunkSampleStream implements SampleStream, S * @param positionUs The position from which to start loading media. * @param drmSessionManager The {@link DrmSessionManager} to obtain {@link DrmSession DrmSessions} * from. + * @param drmEventDispatcher A dispatcher to notify of {@link DrmSessionEventListener} events. * @param loadErrorHandlingPolicy The {@link LoadErrorHandlingPolicy}. - * @param eventDispatcher A dispatcher to notify of events. + * @param mediaSourceEventDispatcher A dispatcher to notify of {@link MediaSourceEventListener} + * events. */ public ChunkSampleStream( int primaryTrackType, @@ -114,14 +117,15 @@ public class ChunkSampleStream implements SampleStream, S Allocator allocator, long positionUs, DrmSessionManager drmSessionManager, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, LoadErrorHandlingPolicy loadErrorHandlingPolicy, - EventDispatcher eventDispatcher) { + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher) { this.primaryTrackType = primaryTrackType; this.embeddedTrackTypes = embeddedTrackTypes == null ? new int[0] : embeddedTrackTypes; this.embeddedTrackFormats = embeddedTrackFormats == null ? new Format[0] : embeddedTrackFormats; this.chunkSource = chunkSource; this.callback = callback; - this.eventDispatcher = eventDispatcher; + this.mediaSourceEventDispatcher = mediaSourceEventDispatcher; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; loader = new Loader("Loader:ChunkSampleStream"); nextChunkHolder = new ChunkHolder(); @@ -139,7 +143,7 @@ public class ChunkSampleStream implements SampleStream, S allocator, /* playbackLooper= */ Assertions.checkNotNull(Looper.myLooper()), drmSessionManager, - eventDispatcher); + drmEventDispatcher); trackTypes[0] = primaryTrackType; sampleQueues[0] = primarySampleQueue; @@ -149,7 +153,7 @@ public class ChunkSampleStream implements SampleStream, S allocator, /* playbackLooper= */ Assertions.checkNotNull(Looper.myLooper()), DrmSessionManager.getDummyDrmSessionManager(), - eventDispatcher); + drmEventDispatcher); embeddedSampleQueues[i] = sampleQueue; sampleQueues[i + 1] = sampleQueue; trackTypes[i + 1] = this.embeddedTrackTypes[i]; @@ -413,7 +417,7 @@ public class ChunkSampleStream implements SampleStream, S loadDurationMs, loadable.bytesLoaded()); loadErrorHandlingPolicy.onLoadTaskConcluded(loadable.loadTaskId); - eventDispatcher.loadCompleted( + mediaSourceEventDispatcher.loadCompleted( loadEventInfo, loadable.type, primaryTrackType, @@ -438,7 +442,7 @@ public class ChunkSampleStream implements SampleStream, S loadDurationMs, loadable.bytesLoaded()); loadErrorHandlingPolicy.onLoadTaskConcluded(loadable.loadTaskId); - eventDispatcher.loadCanceled( + mediaSourceEventDispatcher.loadCanceled( loadEventInfo, loadable.type, primaryTrackType, @@ -519,7 +523,7 @@ public class ChunkSampleStream implements SampleStream, S } boolean canceled = !loadErrorAction.isRetry(); - eventDispatcher.loadError( + mediaSourceEventDispatcher.loadError( loadEventInfo, loadable.type, primaryTrackType, @@ -593,7 +597,7 @@ public class ChunkSampleStream implements SampleStream, S long elapsedRealtimeMs = loader.startLoading( loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type)); - eventDispatcher.loadStarted( + mediaSourceEventDispatcher.loadStarted( new LoadEventInfo(loadable.loadTaskId, loadable.dataSpec, elapsedRealtimeMs), loadable.type, primaryTrackType, @@ -648,7 +652,8 @@ public class ChunkSampleStream implements SampleStream, S pendingResetPositionUs = lastSeekPositionUs; } loadingFinished = false; - eventDispatcher.upstreamDiscarded(primaryTrackType, firstRemovedChunk.startTimeUs, endTimeUs); + mediaSourceEventDispatcher.upstreamDiscarded( + primaryTrackType, firstRemovedChunk.startTimeUs, endTimeUs); } // Internal methods @@ -701,8 +706,11 @@ public class ChunkSampleStream implements SampleStream, S BaseMediaChunk currentChunk = mediaChunks.get(mediaChunkReadIndex); Format trackFormat = currentChunk.trackFormat; if (!trackFormat.equals(primaryDownstreamTrackFormat)) { - eventDispatcher.downstreamFormatChanged(primaryTrackType, trackFormat, - currentChunk.trackSelectionReason, currentChunk.trackSelectionData, + mediaSourceEventDispatcher.downstreamFormatChanged( + primaryTrackType, + trackFormat, + currentChunk.trackSelectionReason, + currentChunk.trackSelectionData, currentChunk.startTimeUs); } primaryDownstreamTrackFormat = trackFormat; @@ -812,7 +820,7 @@ public class ChunkSampleStream implements SampleStream, S private void maybeNotifyDownstreamFormat() { if (!notifiedDownstreamFormat) { - eventDispatcher.downstreamFormatChanged( + mediaSourceEventDispatcher.downstreamFormatChanged( embeddedTrackTypes[index], embeddedTrackFormats[index], C.SELECTION_REASON_UNKNOWN, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcher.java b/library/core/src/main/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcher.java deleted file mode 100644 index c58221a12c..0000000000 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcher.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2020 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 android.os.Looper; -import androidx.annotation.CheckResult; -import androidx.annotation.Nullable; -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; - -/** - * Event dispatcher which forwards events to a list of registered listeners. - * - *

Adds the correct {@code windowIndex} and {@code mediaPeriodId} values (and {@code - * mediaTimeOffsetMs} if needed). - * - *

Allows listeners of any type to be registered, calls to {@link #dispatch} then provide the - * type of listener to forward to, which is used to filter the registered listeners. - */ -// TODO: Make this final when MediaSourceEventListener.EventDispatcher is deleted. -public class MediaSourceEventDispatcher { - - /** - * Functional interface to send an event with {@code windowIndex} and {@code mediaPeriodId} - * attached. - */ - public interface EventWithPeriodId { - - /** Sends the event to a listener. */ - void sendTo(T listener, int windowIndex, @Nullable MediaPeriodId mediaPeriodId); - } - - /** The timeline window index reported with the events. */ - public final int windowIndex; - /** The {@link MediaPeriodId} reported with the events. */ - @Nullable public final MediaPeriodId mediaPeriodId; - - // TODO: Make these private when MediaSourceEventListener.EventDispatcher is deleted. - protected final CopyOnWriteMultiset listenerInfos; - // TODO: Define exactly what this means, and check it's always set correctly. - protected final long mediaTimeOffsetMs; - - /** Creates an event dispatcher. */ - public MediaSourceEventDispatcher() { - this( - /* listenerInfos= */ new CopyOnWriteMultiset<>(), - /* windowIndex= */ 0, - /* mediaPeriodId= */ null, - /* mediaTimeOffsetMs= */ 0); - } - - protected MediaSourceEventDispatcher( - CopyOnWriteMultiset listenerInfos, - int windowIndex, - @Nullable MediaPeriodId mediaPeriodId, - long mediaTimeOffsetMs) { - this.listenerInfos = listenerInfos; - this.windowIndex = windowIndex; - this.mediaPeriodId = mediaPeriodId; - this.mediaTimeOffsetMs = mediaTimeOffsetMs; - } - - /** - * Creates a view of the event dispatcher with pre-configured window index, media period id, and - * media time offset. - * - * @param windowIndex The timeline window index to be reported with the events. - * @param mediaPeriodId The {@link MediaPeriodId} to be reported with the events. - * @param mediaTimeOffsetMs The offset to be added to all media times, in milliseconds. - * @return A view of the event dispatcher with the pre-configured parameters. - */ - @CheckResult - public MediaSourceEventDispatcher withParameters( - int windowIndex, @Nullable MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs) { - return new MediaSourceEventDispatcher( - listenerInfos, windowIndex, mediaPeriodId, mediaTimeOffsetMs); - } - - /** - * Adds a listener to the event dispatcher. - * - *

Calls to {@link #dispatch(EventWithPeriodId, Class)} will propagate to {@code eventListener} - * if the {@code listenerClass} types are equal. - * - *

The same listener instance can be added multiple times with different {@code listenerClass} - * values (i.e. if the instance implements multiple listener interfaces). - * - *

Duplicate {@code {eventListener, listenerClass}} pairs are also permitted. In this case an - * event dispatched to {@code listenerClass} will only be passed to the {@code eventListener} - * once. - * - *

NOTE: This doesn't interact well with hierarchies of listener interfaces. If a - * listener is registered with a super-class type then it will only receive events dispatched - * directly to that super-class type. Similarly, if a listener is registered with a sub-class type - * then it will only receive events dispatched directly to that sub-class. - * - * @param handler A handler on the which listener events will be posted. - * @param eventListener The listener to be added. - * @param listenerClass The type used to register the listener. Can be a superclass of {@code - * eventListener}. - */ - public void addEventListener(Handler handler, T eventListener, Class listenerClass) { - Assertions.checkNotNull(handler); - Assertions.checkNotNull(eventListener); - listenerInfos.add(new ListenerInfo(handler, eventListener, listenerClass)); - } - - /** - * Removes a listener from the event dispatcher. - * - *

If there are duplicate registrations of {@code {eventListener, listenerClass}} this will - * only remove one (so events dispatched to {@code listenerClass} will still be passed to {@code - * eventListener}). - * - * @param eventListener The listener to be removed. - * @param listenerClass The listener type passed to {@link #addEventListener(Handler, Object, - * Class)}. - */ - public void removeEventListener(T eventListener, Class listenerClass) { - for (ListenerInfo listenerInfo : listenerInfos) { - if (listenerInfo.listener == eventListener - && listenerInfo.listenerClass.equals(listenerClass)) { - listenerInfos.remove(listenerInfo); - return; - } - } - } - - /** Dispatches {@code event} to all registered listeners of type {@code listenerClass}. */ - @SuppressWarnings("unchecked") // The cast is gated with listenerClass.isInstance() - public void dispatch(EventWithPeriodId event, Class listenerClass) { - for (ListenerInfo listenerInfo : listenerInfos.elementSet()) { - if (listenerInfo.listenerClass.equals(listenerClass)) { - postOrRun( - listenerInfo.handler, - () -> event.sendTo((T) listenerInfo.listener, windowIndex, mediaPeriodId)); - } - } - } - - private static void postOrRun(Handler handler, Runnable runnable) { - if (handler.getLooper() == Looper.myLooper()) { - runnable.run(); - } else { - handler.post(runnable); - } - } - - public static long adjustMediaTime(long mediaTimeUs, long mediaTimeOffsetMs) { - long mediaTimeMs = C.usToMs(mediaTimeUs); - return mediaTimeMs == C.TIME_UNSET ? C.TIME_UNSET : mediaTimeOffsetMs + mediaTimeMs; - } - - /** Container class for a {@link Handler}, {@code listener} and {@code listenerClass}. */ - protected static final class ListenerInfo { - - public final Handler handler; - public final Object listener; - public final Class listenerClass; - - public ListenerInfo(Handler handler, Object listener, Class listenerClass) { - this.handler = handler; - this.listener = listener; - this.listenerClass = listenerClass; - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ListenerInfo)) { - return false; - } - - ListenerInfo that = (ListenerInfo) o; - - // We deliberately only consider listener and listenerClass (and not handler) in equals() and - // hashcode() because the handler used to process the callbacks is an implementation detail. - return listener.equals(that.listener) && listenerClass.equals(that.listenerClass); - } - - @Override - public int hashCode() { - int result = 31 * listener.hashCode(); - return result + 31 * listenerClass.hashCode(); - } - } -} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 3f09f71336..39804646f4 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -44,6 +44,7 @@ import com.google.android.exoplayer2.Player.EventListener; import com.google.android.exoplayer2.Timeline.Window; import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.audio.AudioAttributes; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.ClippingMediaSource; import com.google.android.exoplayer2.source.CompositeMediaSource; @@ -53,7 +54,7 @@ import com.google.android.exoplayer2.source.MaskingMediaSource; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SilenceMediaSource; import com.google.android.exoplayer2.source.TrackGroup; @@ -616,15 +617,17 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) { FakeMediaPeriod mediaPeriod = new FakeMediaPeriod( trackGroupArray, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, - eventDispatcher, + mediaSourceEventDispatcher, drmSessionManager, + drmEventDispatcher, /* deferOnPrepared= */ false); mediaPeriod.setSeekToUsOffset(10); return mediaPeriod; @@ -658,14 +661,15 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) { FakeMediaPeriod mediaPeriod = new FakeMediaPeriod( trackGroupArray, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, - eventDispatcher); + mediaSourceEventDispatcher); mediaPeriod.setDiscontinuityPositionUs(10); return mediaPeriod; } @@ -689,14 +693,15 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) { FakeMediaPeriod mediaPeriod = new FakeMediaPeriod( trackGroupArray, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, - eventDispatcher); + mediaSourceEventDispatcher); // Set a discontinuity at the position this period is supposed to start at anyway. mediaPeriod.setDiscontinuityPositionUs( timeline.getWindow(/* windowIndex= */ 0, new Window()).positionInFirstPeriodUs); @@ -938,16 +943,18 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) { // Defer completing preparation of the period until playback parameters have been set. fakeMediaPeriodHolder[0] = new FakeMediaPeriod( trackGroupArray, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, - eventDispatcher, + mediaSourceEventDispatcher, drmSessionManager, + drmEventDispatcher, /* deferOnPrepared= */ true); createPeriodCalledCountDownLatch.countDown(); return fakeMediaPeriodHolder[0]; @@ -990,16 +997,18 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) { // Defer completing preparation of the period until seek has been sent. fakeMediaPeriodHolder[0] = new FakeMediaPeriod( trackGroupArray, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, - eventDispatcher, + mediaSourceEventDispatcher, drmSessionManager, + drmEventDispatcher, /* deferOnPrepared= */ true); createPeriodCalledCountDownLatch.countDown(); return fakeMediaPeriodHolder[0]; @@ -3723,13 +3732,14 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) { return new FakeMediaPeriod( trackGroupArray, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, - eventDispatcher) { + mediaSourceEventDispatcher) { @Override public long getBufferedPositionUs() { // Pretend not to have buffered data yet. @@ -6365,13 +6375,14 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) { return new FakeMediaPeriod( trackGroupArray, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, - eventDispatcher) { + mediaSourceEventDispatcher) { private final List allocations = new ArrayList<>(); @@ -6444,14 +6455,16 @@ public final class ExoPlayerTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) { return new FakeMediaPeriod( trackGroupArray, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, - eventDispatcher, + mediaSourceEventDispatcher, drmSessionManager, + drmEventDispatcher, /* deferOnPrepared= */ false) { private Loader loader = new Loader("oomLoader"); @@ -6466,13 +6479,15 @@ public final class ExoPlayerTest { protected SampleStream createSampleStream( long positionUs, TrackSelection selection, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher) { + DrmSessionEventListener.EventDispatcher drmEventDispatcher) { // Create 3 samples without end of stream signal to test that all 3 samples are // still played before the exception is thrown. return new FakeSampleStream( + mediaSourceEventDispatcher, drmSessionManager, - eventDispatcher, + drmEventDispatcher, selection.getSelectedFormat(), ImmutableList.of( oneByteSample(positionUs), diff --git a/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java index b141f1ac99..f6e3ac941d 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java @@ -34,6 +34,7 @@ import com.google.android.exoplayer2.decoder.DecoderException; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.SimpleDecoder; import com.google.android.exoplayer2.decoder.SimpleOutputBuffer; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.testutil.FakeSampleStream; @@ -107,8 +108,9 @@ public class DecoderAudioRendererTest { RendererConfiguration.DEFAULT, new Format[] {FORMAT}, new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), FORMAT, ImmutableList.of(END_OF_STREAM_ITEM)), /* positionUs= */ 0, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java index 2ff458147a..4306628d72 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/audio/MediaCodecAudioRendererTest.java @@ -32,6 +32,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.RendererConfiguration; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; import com.google.android.exoplayer2.mediacodec.MediaCodecSelector; @@ -111,8 +112,9 @@ public class MediaCodecAudioRendererTest { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ AUDIO_AAC, ImmutableList.of( oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), @@ -158,8 +160,9 @@ public class MediaCodecAudioRendererTest { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ AUDIO_AAC, ImmutableList.of( oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), @@ -225,8 +228,9 @@ public class MediaCodecAudioRendererTest { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ AUDIO_AAC, ImmutableList.of( oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), END_OF_STREAM_ITEM)); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java b/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java index c36c6cff38..f7b249765b 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/drm/OfflineLicenseHelperTest.java @@ -25,7 +25,6 @@ import android.util.Pair; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import java.util.HashMap; import org.junit.After; import org.junit.Before; @@ -57,7 +56,7 @@ public class OfflineLicenseHelperTest { new ExoMediaDrm.AppManagedProvider(mediaDrm), mediaDrmCallback, /* optionalKeyRequestParameters= */ null, - new MediaSourceEventDispatcher()); + new DrmSessionEventListener.EventDispatcher()); } @After diff --git a/library/core/src/test/java/com/google/android/exoplayer2/metadata/MetadataRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/metadata/MetadataRendererTest.java index d664964888..1a6b6e834d 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/metadata/MetadataRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/metadata/MetadataRendererTest.java @@ -22,6 +22,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.metadata.emsg.EventMessage; import com.google.android.exoplayer2.metadata.emsg.EventMessageEncoder; @@ -146,8 +147,9 @@ public class MetadataRendererTest { renderer.replaceStream( new Format[] {EMSG_FORMAT}, new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), EMSG_FORMAT, ImmutableList.of( FakeSampleStreamItem.sample(/* timeUs= */ 0, /* flags= */ 0, input), diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java index 69b38bd2e1..1e27fe7441 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ClippingMediaSourceTest.java @@ -27,11 +27,11 @@ import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Window; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.ClippingMediaSource.IllegalClippingException; import com.google.android.exoplayer2.source.MaskingMediaSource.DummyTimeline; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.testutil.FakeMediaPeriod; import com.google.android.exoplayer2.testutil.FakeMediaSource; import com.google.android.exoplayer2.testutil.FakeTimeline; @@ -569,10 +569,11 @@ public final class ClippingMediaSourceTest { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) { - eventDispatcher.downstreamFormatChanged( + mediaSourceEventDispatcher.downstreamFormatChanged( new MediaLoadData( C.DATA_TYPE_MEDIA, C.TRACK_TYPE_UNKNOWN, @@ -585,8 +586,9 @@ public final class ClippingMediaSourceTest { id, trackGroupArray, allocator, + mediaSourceEventDispatcher, drmSessionManager, - eventDispatcher, + drmEventDispatcher, transferListener); } }; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaPeriodTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaPeriodTest.java index a88fbed27f..12f52cf2c2 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaPeriodTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/MergingMediaPeriodTest.java @@ -24,6 +24,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; @@ -200,13 +201,14 @@ public final class MergingMediaPeriodTest { public FakeMediaPeriodWithSelectTracksPosition( TrackGroupArray trackGroupArray, - EventDispatcher eventDispatcher, + EventDispatcher mediaSourceEventDispatcher, TrackDataFactory trackDataFactory) { super( trackGroupArray, trackDataFactory, - eventDispatcher, + mediaSourceEventDispatcher, DrmSessionManager.DUMMY, + new DrmSessionEventListener.EventDispatcher(), /* deferOnPrepared= */ false); selectTracksPositionUs = C.TIME_UNSET; } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriodTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriodTest.java index 6b09d37e81..1360f66a3e 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriodTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriodTest.java @@ -22,6 +22,7 @@ import android.net.Uri; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor; @@ -44,18 +45,18 @@ public final class ProgressiveMediaPeriodTest { AtomicBoolean sourceInfoRefreshCalled = new AtomicBoolean(false); ProgressiveMediaPeriod.Listener sourceInfoRefreshListener = (durationUs, isSeekable, isLive) -> sourceInfoRefreshCalled.set(true); + MediaPeriodId mediaPeriodId = new MediaPeriodId(/* periodUid= */ new Object()); ProgressiveMediaPeriod mediaPeriod = new ProgressiveMediaPeriod( Uri.parse("asset://android_asset/mp4/sample.mp4"), new AssetDataSource(ApplicationProvider.getApplicationContext()), () -> new Extractor[] {new Mp4Extractor()}, DrmSessionManager.DUMMY, + new DrmSessionEventListener.EventDispatcher() + .withParameters(/* windowIndex= */ 0, mediaPeriodId), new DefaultLoadErrorHandlingPolicy(), new MediaSourceEventListener.EventDispatcher() - .withParameters( - /* windowIndex= */ 0, - new MediaPeriodId(/* periodUid= */ new Object()), - /* mediaTimeOffsetMs= */ 0), + .withParameters(/* windowIndex= */ 0, mediaPeriodId, /* mediaTimeOffsetMs= */ 0), sourceInfoRefreshListener, new DefaultAllocator(/* trimOnReset= */ true, C.DEFAULT_BUFFER_SEGMENT_SIZE), /* customCacheKey= */ null, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java index bc8ed07167..16eb07a509 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java @@ -35,13 +35,13 @@ import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmSession; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.util.Assertions; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.common.primitives.Bytes; @@ -129,7 +129,7 @@ public final class SampleQueueTest { private Allocator allocator; private DrmSessionManager mockDrmSessionManager; private DrmSession mockDrmSession; - private MediaSourceEventDispatcher eventDispatcher; + private DrmSessionEventListener.EventDispatcher eventDispatcher; private SampleQueue sampleQueue; private FormatHolder formatHolder; private DecoderInputBuffer inputBuffer; @@ -142,7 +142,7 @@ public final class SampleQueueTest { when(mockDrmSessionManager.acquireSession( ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any())) .thenReturn(mockDrmSession); - eventDispatcher = new MediaSourceEventDispatcher(); + eventDispatcher = new DrmSessionEventListener.EventDispatcher(); sampleQueue = new SampleQueue( allocator, diff --git a/library/core/src/test/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcherTest.java b/library/core/src/test/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcherTest.java deleted file mode 100644 index debf839a43..0000000000 --- a/library/core/src/test/java/com/google/android/exoplayer2/util/MediaSourceEventDispatcherTest.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2020 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 static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -import android.os.Handler; -import android.os.Looper; -import androidx.annotation.Nullable; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.android.exoplayer2.drm.DrmSessionEventListener; -import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; -import com.google.android.exoplayer2.source.MediaSourceEventListener; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -/** Tests for {@link MediaSourceEventDispatcher}. */ -@RunWith(AndroidJUnit4.class) -public class MediaSourceEventDispatcherTest { - - private static final MediaSource.MediaPeriodId MEDIA_PERIOD_ID = - new MediaSource.MediaPeriodId("test uid"); - private static final int WINDOW_INDEX = 200; - private static final int MEDIA_TIME_OFFSET_MS = 1_000; - - @Rule public final MockitoRule mockito = MockitoJUnit.rule(); - - @Mock private MediaSourceEventListener mediaSourceEventListener; - @Mock private MediaAndDrmEventListener mediaAndDrmEventListener; - - private MediaSourceEventDispatcher eventDispatcher; - - @Before - public void setupEventDispatcher() { - eventDispatcher = new MediaSourceEventDispatcher(); - eventDispatcher = - eventDispatcher.withParameters(WINDOW_INDEX, MEDIA_PERIOD_ID, MEDIA_TIME_OFFSET_MS); - } - - @Test - public void listenerReceivesEventPopulatedWithMediaPeriodInfo() { - eventDispatcher.addEventListener( - Util.createHandlerForCurrentOrMainLooper(), - mediaSourceEventListener, - MediaSourceEventListener.class); - - eventDispatcher.dispatch( - MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class); - - verify(mediaSourceEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID); - } - - @Test - public void sameListenerObjectRegisteredTwiceOnlyReceivesEventsOnce() { - eventDispatcher.addEventListener( - Util.createHandlerForCurrentOrMainLooper(), - mediaSourceEventListener, - MediaSourceEventListener.class); - eventDispatcher.addEventListener( - Util.createHandlerForCurrentOrMainLooper(), - mediaSourceEventListener, - MediaSourceEventListener.class); - - eventDispatcher.dispatch( - MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class); - - verify(mediaSourceEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID); - } - - @Test - public void sameListenerInstanceCanBeRegisteredWithTwoTypes() { - eventDispatcher.addEventListener( - new Handler(Looper.getMainLooper()), - mediaAndDrmEventListener, - MediaSourceEventListener.class); - eventDispatcher.addEventListener( - new Handler(Looper.getMainLooper()), - mediaAndDrmEventListener, - DrmSessionEventListener.class); - - eventDispatcher.dispatch( - MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class); - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmKeysLoaded, DrmSessionEventListener.class); - - verify(mediaAndDrmEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID); - verify(mediaAndDrmEventListener).onDrmKeysLoaded(WINDOW_INDEX, MEDIA_PERIOD_ID); - } - - // If a listener is added that implements multiple types, it should only receive events for the - // type specified at registration time. - @Test - public void listenerOnlyReceivesEventsForRegisteredType() { - eventDispatcher.addEventListener( - new Handler(Looper.getMainLooper()), - mediaAndDrmEventListener, - MediaSourceEventListener.class); - - eventDispatcher.dispatch( - MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class); - eventDispatcher.dispatch( - DrmSessionEventListener::onDrmKeysLoaded, DrmSessionEventListener.class); - - verify(mediaAndDrmEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID); - verify(mediaAndDrmEventListener, never()).onDrmKeysLoaded(WINDOW_INDEX, MEDIA_PERIOD_ID); - } - - @Test - public void listenerDoesntReceiveEventsDispatchedToSubclass() { - SubclassListener subclassListener = mock(SubclassListener.class); - eventDispatcher.addEventListener( - new Handler(Looper.getMainLooper()), subclassListener, MediaSourceEventListener.class); - - eventDispatcher.dispatch(SubclassListener::subclassMethod, SubclassListener.class); - - // subclassListener can handle the call to subclassMethod, but it isn't called because - // it was registered 'as-a' MediaSourceEventListener, not SubclassListener. - verify(subclassListener, never()).subclassMethod(anyInt(), any()); - } - - @Test - public void listenerDoesntReceiveEventsDispatchedToSuperclass() { - SubclassListener subclassListener = mock(SubclassListener.class); - eventDispatcher.addEventListener( - new Handler(Looper.getMainLooper()), subclassListener, SubclassListener.class); - - eventDispatcher.dispatch( - MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class); - - // subclassListener 'is-a' a MediaSourceEventListener, but it isn't called because the event - // is dispatched specifically to listeners registered as MediaSourceEventListener. - verify(subclassListener, never()).onMediaPeriodCreated(anyInt(), any()); - } - - @Test - public void listenersAreCopiedToNewDispatcher() { - eventDispatcher.addEventListener( - Util.createHandlerForCurrentOrMainLooper(), - mediaSourceEventListener, - MediaSourceEventListener.class); - - MediaSource.MediaPeriodId newPeriodId = new MediaSource.MediaPeriodId("different uid"); - MediaSourceEventDispatcher newEventDispatcher = - this.eventDispatcher.withParameters( - /* windowIndex= */ 250, newPeriodId, /* mediaTimeOffsetMs= */ 500); - - newEventDispatcher.dispatch( - MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class); - - verify(mediaSourceEventListener).onMediaPeriodCreated(250, newPeriodId); - } - - @Test - public void removingListenerStopsEventDispatch() { - eventDispatcher.addEventListener( - Util.createHandlerForCurrentOrMainLooper(), - mediaSourceEventListener, - MediaSourceEventListener.class); - eventDispatcher.removeEventListener(mediaSourceEventListener, MediaSourceEventListener.class); - - eventDispatcher.dispatch( - MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class); - - verify(mediaSourceEventListener, never()).onMediaPeriodCreated(anyInt(), any()); - } - - @Test - public void removingListenerWithDifferentTypeToRegistrationDoesntRemove() { - eventDispatcher.addEventListener( - Util.createHandlerForCurrentOrMainLooper(), - mediaAndDrmEventListener, - MediaSourceEventListener.class); - eventDispatcher.removeEventListener(mediaAndDrmEventListener, DrmSessionEventListener.class); - - eventDispatcher.dispatch( - MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class); - - verify(mediaAndDrmEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID); - } - - @Test - public void listenersAreCountedBasedOnListenerAndType() { - // Add the listener twice and remove it once. - eventDispatcher.addEventListener( - Util.createHandlerForCurrentOrMainLooper(), - mediaSourceEventListener, - MediaSourceEventListener.class); - eventDispatcher.addEventListener( - Util.createHandlerForCurrentOrMainLooper(), - mediaSourceEventListener, - MediaSourceEventListener.class); - eventDispatcher.removeEventListener(mediaSourceEventListener, MediaSourceEventListener.class); - - eventDispatcher.dispatch( - MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class); - - verify(mediaSourceEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID); - - // Remove it a second time and confirm the events stop being propagated. - eventDispatcher.removeEventListener(mediaSourceEventListener, MediaSourceEventListener.class); - - verifyNoMoreInteractions(mediaSourceEventListener); - } - - private interface MediaAndDrmEventListener - extends MediaSourceEventListener, DrmSessionEventListener {} - - private interface SubclassListener extends MediaSourceEventListener { - void subclassMethod(int windowIndex, @Nullable MediaPeriodId mediaPeriodId); - } -} diff --git a/library/core/src/test/java/com/google/android/exoplayer2/video/DecoderVideoRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/video/DecoderVideoRendererTest.java index 3d569aba6d..71b32af98b 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/video/DecoderVideoRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/video/DecoderVideoRendererTest.java @@ -35,6 +35,7 @@ import com.google.android.exoplayer2.RendererConfiguration; import com.google.android.exoplayer2.decoder.DecoderException; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.SimpleDecoder; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.testutil.FakeSampleStream; @@ -185,8 +186,9 @@ public final class DecoderVideoRendererTest { public void enable_withMayRenderStartOfStream_rendersFirstFrameBeforeStart() throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ H264_FORMAT, ImmutableList.of(oneByteSample(/* timeUs= */ 0))); @@ -212,8 +214,9 @@ public final class DecoderVideoRendererTest { throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ H264_FORMAT, ImmutableList.of(oneByteSample(/* timeUs= */ 0))); @@ -238,8 +241,9 @@ public final class DecoderVideoRendererTest { public void enable_withoutMayRenderStartOfStream_rendersFirstFrameAfterStart() throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ H264_FORMAT, ImmutableList.of(oneByteSample(/* timeUs= */ 0))); @@ -267,15 +271,17 @@ public final class DecoderVideoRendererTest { public void replaceStream_whenStarted_rendersFirstFrameOfNewStream() throws Exception { FakeSampleStream fakeSampleStream1 = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ H264_FORMAT, ImmutableList.of( oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); FakeSampleStream fakeSampleStream2 = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ H264_FORMAT, ImmutableList.of( oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); @@ -309,15 +315,17 @@ public final class DecoderVideoRendererTest { public void replaceStream_whenNotStarted_doesNotRenderFirstFrameOfNewStream() throws Exception { FakeSampleStream fakeSampleStream1 = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ H264_FORMAT, ImmutableList.of( oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); FakeSampleStream fakeSampleStream2 = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ H264_FORMAT, ImmutableList.of( oneByteSample(/* timeUs= */ 0), FakeSampleStreamItem.END_OF_STREAM_ITEM)); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/video/MediaCodecVideoRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/video/MediaCodecVideoRendererTest.java index efab1309ba..2a9e149eda 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/video/MediaCodecVideoRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/video/MediaCodecVideoRendererTest.java @@ -40,6 +40,7 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererConfiguration; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; import com.google.android.exoplayer2.mediacodec.MediaCodecSelector; @@ -129,8 +130,9 @@ public class MediaCodecVideoRendererTest { public void render_dropsLateBuffer() throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ VIDEO_H264, ImmutableList.of( oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), // First buffer. @@ -165,8 +167,9 @@ public class MediaCodecVideoRendererTest { RendererConfiguration.DEFAULT, new Format[] {VIDEO_H264}, new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ VIDEO_H264, ImmutableList.of( oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), @@ -202,8 +205,9 @@ public class MediaCodecVideoRendererTest { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ pAsp1, ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME))); @@ -246,8 +250,9 @@ public class MediaCodecVideoRendererTest { throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ VIDEO_H264, ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME))); mediaCodecVideoRenderer.enable( @@ -279,8 +284,9 @@ public class MediaCodecVideoRendererTest { public void enable_withMayRenderStartOfStream_rendersFirstFrameBeforeStart() throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ VIDEO_H264, ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME))); @@ -304,8 +310,9 @@ public class MediaCodecVideoRendererTest { throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ VIDEO_H264, ImmutableList.of(oneByteSample(/* timeUs= */ 0))); @@ -328,8 +335,9 @@ public class MediaCodecVideoRendererTest { public void enable_withoutMayRenderStartOfStream_rendersFirstFrameAfterStart() throws Exception { FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ VIDEO_H264, ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME))); @@ -353,16 +361,18 @@ public class MediaCodecVideoRendererTest { public void replaceStream_whenStarted_rendersFirstFrameOfNewStream() throws Exception { FakeSampleStream fakeSampleStream1 = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ VIDEO_H264, ImmutableList.of( oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), FakeSampleStreamItem.END_OF_STREAM_ITEM)); FakeSampleStream fakeSampleStream2 = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ VIDEO_H264, ImmutableList.of( oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), @@ -395,16 +405,18 @@ public class MediaCodecVideoRendererTest { public void replaceStream_whenNotStarted_doesNotRenderFirstFrameOfNewStream() throws Exception { FakeSampleStream fakeSampleStream1 = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ VIDEO_H264, ImmutableList.of( oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), FakeSampleStreamItem.END_OF_STREAM_ITEM)); FakeSampleStream fakeSampleStream2 = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ VIDEO_H264, ImmutableList.of( oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), @@ -443,8 +455,9 @@ public class MediaCodecVideoRendererTest { Format mp4Uhd = VIDEO_H264.buildUpon().setWidth(3840).setHeight(2160).build(); FakeSampleStream fakeSampleStream = new FakeSampleStream( + /* mediaSourceEventDispatcher= */ null, DrmSessionManager.DUMMY, - /* eventDispatcher= */ null, + new DrmSessionEventListener.EventDispatcher(), /* initialFormat= */ mp4Uhd, ImmutableList.of( oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME), diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java index 712e7137cd..3d5f05268b 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java @@ -24,11 +24,13 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.drm.DrmInitData; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.EmptySampleStream; import com.google.android.exoplayer2.source.MediaPeriod; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SequenceableLoader; @@ -90,7 +92,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; private final PlayerEmsgHandler playerEmsgHandler; private final IdentityHashMap, PlayerTrackEmsgHandler> trackEmsgHandlerBySampleStream; - private final EventDispatcher eventDispatcher; + private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; + private final DrmSessionEventListener.EventDispatcher drmEventDispatcher; @Nullable private Callback callback; private ChunkSampleStream[] sampleStreams; @@ -108,8 +111,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; DashChunkSource.Factory chunkSourceFactory, @Nullable TransferListener transferListener, DrmSessionManager drmSessionManager, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, LoadErrorHandlingPolicy loadErrorHandlingPolicy, - EventDispatcher eventDispatcher, + EventDispatcher mediaSourceEventDispatcher, long elapsedRealtimeOffsetMs, LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator, @@ -121,8 +125,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; this.chunkSourceFactory = chunkSourceFactory; this.transferListener = transferListener; this.drmSessionManager = drmSessionManager; + this.drmEventDispatcher = drmEventDispatcher; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; - this.eventDispatcher = eventDispatcher; + this.mediaSourceEventDispatcher = mediaSourceEventDispatcher; this.elapsedRealtimeOffsetMs = elapsedRealtimeOffsetMs; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.allocator = allocator; @@ -139,7 +144,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; buildTrackGroups(drmSessionManager, period.adaptationSets, eventStreams); trackGroups = result.first; trackGroupInfos = result.second; - eventDispatcher.mediaPeriodCreated(); + mediaSourceEventDispatcher.mediaPeriodCreated(); } /** @@ -178,7 +183,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; sampleStream.release(this); } callback = null; - eventDispatcher.mediaPeriodReleased(); + mediaSourceEventDispatcher.mediaPeriodReleased(); } // ChunkSampleStream.ReleaseCallback implementation. @@ -316,7 +321,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Override public long readDiscontinuity() { if (!notifiedReadingStarted) { - eventDispatcher.readingStarted(); + mediaSourceEventDispatcher.readingStarted(); notifiedReadingStarted = true; } return C.TIME_UNSET; @@ -788,8 +793,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; allocator, positionUs, drmSessionManager, + drmEventDispatcher, loadErrorHandlingPolicy, - eventDispatcher); + mediaSourceEventDispatcher); synchronized (this) { // The map is also accessed on the loading thread so synchronize access. trackEmsgHandlerBySampleStream.put(stream, trackPlayerEmsgHandler); diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java index 6baeadd52e..1a6ea0e763 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java @@ -29,6 +29,7 @@ import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.drm.DrmSession; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.offline.FilteringManifestParser; import com.google.android.exoplayer2.offline.StreamKey; @@ -694,8 +695,9 @@ public final class DashMediaSource extends BaseMediaSource { public MediaPeriod createPeriod( MediaPeriodId periodId, Allocator allocator, long startPositionUs) { int periodIndex = (Integer) periodId.periodUid - firstPeriodId; - EventDispatcher periodEventDispatcher = + MediaSourceEventListener.EventDispatcher periodEventDispatcher = createEventDispatcher(periodId, manifest.getPeriod(periodIndex).startMs); + DrmSessionEventListener.EventDispatcher drmEventDispatcher = createDrmEventDispatcher(periodId); DashMediaPeriod mediaPeriod = new DashMediaPeriod( firstPeriodId + periodIndex, @@ -704,6 +706,7 @@ public final class DashMediaSource extends BaseMediaSource { chunkSourceFactory, mediaTransferListener, drmSessionManager, + drmEventDispatcher, loadErrorHandlingPolicy, periodEventDispatcher, elapsedRealtimeOffsetMs, diff --git a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java index 94c908c98a..2185b52f93 100644 --- a/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java +++ b/library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java @@ -24,6 +24,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.ParserException; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.metadata.Metadata; @@ -35,7 +36,6 @@ import com.google.android.exoplayer2.source.chunk.Chunk; import com.google.android.exoplayer2.source.dash.manifest.DashManifest; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.DataReader; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; import java.io.IOException; @@ -290,7 +290,7 @@ public final class PlayerEmsgHandler implements Handler.Callback { allocator, /* playbackLooper= */ handler.getLooper(), DrmSessionManager.getDummyDrmSessionManager(), - new MediaSourceEventDispatcher()); + new DrmSessionEventListener.EventDispatcher()); formatHolder = new FormatHolder(); buffer = new MetadataInputBuffer(); } diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaPeriodTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaPeriodTest.java index 4a4438edd6..de680ad220 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaPeriodTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaPeriodTest.java @@ -22,10 +22,11 @@ import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.dash.PlayerEmsgHandler.PlayerEmsgCallback; @@ -344,6 +345,7 @@ public final class DashMediaPeriodTest { } private static DashMediaPeriod createDashMediaPeriod(DashManifest manifest, int periodIndex) { + MediaPeriodId mediaPeriodId = new MediaPeriodId(/* periodUid= */ new Object()); return new DashMediaPeriod( /* id= */ periodIndex, manifest, @@ -351,12 +353,11 @@ public final class DashMediaPeriodTest { mock(DashChunkSource.Factory.class), mock(TransferListener.class), DrmSessionManager.getDummyDrmSessionManager(), + new DrmSessionEventListener.EventDispatcher() + .withParameters(/* windowIndex= */ 0, mediaPeriodId), mock(LoadErrorHandlingPolicy.class), - new EventDispatcher() - .withParameters( - /* windowIndex= */ 0, - /* mediaPeriodId= */ new MediaPeriodId(/* periodUid= */ new Object()), - /* mediaTimeOffsetMs= */ 0), + new MediaSourceEventListener.EventDispatcher() + .withParameters(/* windowIndex= */ 0, mediaPeriodId, /* mediaTimeOffsetMs= */ 0), /* elapsedRealtimeOffsetMs= */ 0, mock(LoaderErrorThrower.class), mock(Allocator.class), diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java index 367dd242ec..c8707c28f7 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java @@ -23,6 +23,7 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmSession; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.metadata.Metadata; @@ -69,6 +70,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper private final HlsDataSourceFactory dataSourceFactory; @Nullable private final TransferListener mediaTransferListener; private final DrmSessionManager drmSessionManager; + private final DrmSessionEventListener.EventDispatcher drmEventDispatcher; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; private final EventDispatcher eventDispatcher; private final Allocator allocator; @@ -114,6 +116,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper HlsDataSourceFactory dataSourceFactory, @Nullable TransferListener mediaTransferListener, DrmSessionManager drmSessionManager, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, LoadErrorHandlingPolicy loadErrorHandlingPolicy, EventDispatcher eventDispatcher, Allocator allocator, @@ -126,6 +129,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper this.dataSourceFactory = dataSourceFactory; this.mediaTransferListener = mediaTransferListener; this.drmSessionManager = drmSessionManager; + this.drmEventDispatcher = drmEventDispatcher; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; this.eventDispatcher = eventDispatcher; this.allocator = allocator; @@ -758,6 +762,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper positionUs, muxedAudioFormat, drmSessionManager, + drmEventDispatcher, loadErrorHandlingPolicy, eventDispatcher, metadataType); diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java index c321e893bb..b361a5a1d6 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java @@ -26,6 +26,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.drm.DrmSession; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.offline.StreamKey; @@ -35,7 +36,6 @@ import com.google.android.exoplayer2.source.DefaultCompositeSequenceableLoaderFa import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSourceEventListener; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaSourceFactory; import com.google.android.exoplayer2.source.SequenceableLoader; import com.google.android.exoplayer2.source.SinglePeriodTimeline; @@ -446,7 +446,8 @@ public final class HlsMediaSource extends BaseMediaSource protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) { this.mediaTransferListener = mediaTransferListener; drmSessionManager.prepare(); - EventDispatcher eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null); + MediaSourceEventListener.EventDispatcher eventDispatcher = + createEventDispatcher(/* mediaPeriodId= */ null); playlistTracker.start(playbackProperties.uri, eventDispatcher, /* listener= */ this); } @@ -457,15 +458,17 @@ public final class HlsMediaSource extends BaseMediaSource @Override public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) { - EventDispatcher eventDispatcher = createEventDispatcher(id); + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher = createEventDispatcher(id); + DrmSessionEventListener.EventDispatcher drmEventDispatcher = createDrmEventDispatcher(id); return new HlsMediaPeriod( extractorFactory, playlistTracker, dataSourceFactory, mediaTransferListener, drmSessionManager, + drmEventDispatcher, loadErrorHandlingPolicy, - eventDispatcher, + mediaSourceEventDispatcher, allocator, compositeSequenceableLoaderFactory, allowChunklessPreparation, diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index c668c1641d..aeabff8832 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -27,6 +27,7 @@ import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmSession; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.extractor.DummyTrackOutput; import com.google.android.exoplayer2.extractor.Extractor; @@ -39,7 +40,7 @@ import com.google.android.exoplayer2.metadata.emsg.EventMessageDecoder; import com.google.android.exoplayer2.metadata.id3.PrivFrame; import com.google.android.exoplayer2.source.LoadEventInfo; import com.google.android.exoplayer2.source.MediaLoadData; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.SampleQueue; import com.google.android.exoplayer2.source.SampleQueue.UpstreamFormatChangedListener; import com.google.android.exoplayer2.source.SampleStream; @@ -57,7 +58,6 @@ import com.google.android.exoplayer2.upstream.Loader; import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Log; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; @@ -120,9 +120,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private final Allocator allocator; @Nullable private final Format muxedAudioFormat; private final DrmSessionManager drmSessionManager; + private final DrmSessionEventListener.EventDispatcher drmEventDispatcher; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; private final Loader loader; - private final EventDispatcher eventDispatcher; + private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; private final @HlsMediaSource.MetadataType int metadataType; private final HlsChunkSource.HlsChunkHolder nextChunkHolder; private final ArrayList mediaChunks; @@ -185,8 +186,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; * @param muxedAudioFormat Optional muxed audio {@link Format} as defined by the master playlist. * @param drmSessionManager The {@link DrmSessionManager} to acquire {@link DrmSession * DrmSessions} with. + * @param drmEventDispatcher A dispatcher to notify of {@link DrmSessionEventListener} events. * @param loadErrorHandlingPolicy A {@link LoadErrorHandlingPolicy}. - * @param eventDispatcher A dispatcher to notify of events. + * @param mediaSourceEventDispatcher A dispatcher to notify of {@link MediaSourceEventListener} + * events. */ public HlsSampleStreamWrapper( int trackType, @@ -197,8 +200,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; long positionUs, @Nullable Format muxedAudioFormat, DrmSessionManager drmSessionManager, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, LoadErrorHandlingPolicy loadErrorHandlingPolicy, - EventDispatcher eventDispatcher, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, @HlsMediaSource.MetadataType int metadataType) { this.trackType = trackType; this.callback = callback; @@ -207,8 +211,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; this.allocator = allocator; this.muxedAudioFormat = muxedAudioFormat; this.drmSessionManager = drmSessionManager; + this.drmEventDispatcher = drmEventDispatcher; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; - this.eventDispatcher = eventDispatcher; + this.mediaSourceEventDispatcher = mediaSourceEventDispatcher; this.metadataType = metadataType; loader = new Loader("Loader:HlsSampleStreamWrapper"); nextChunkHolder = new HlsChunkSource.HlsChunkHolder(); @@ -552,8 +557,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; HlsMediaChunk currentChunk = mediaChunks.get(0); Format trackFormat = currentChunk.trackFormat; if (!trackFormat.equals(downstreamTrackFormat)) { - eventDispatcher.downstreamFormatChanged(trackType, trackFormat, - currentChunk.trackSelectionReason, currentChunk.trackSelectionData, + mediaSourceEventDispatcher.downstreamFormatChanged( + trackType, + trackFormat, + currentChunk.trackSelectionReason, + currentChunk.trackSelectionData, currentChunk.startTimeUs); } downstreamTrackFormat = trackFormat; @@ -682,7 +690,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; long elapsedRealtimeMs = loader.startLoading( loadable, this, loadErrorHandlingPolicy.getMinimumLoadableRetryCount(loadable.type)); - eventDispatcher.loadStarted( + mediaSourceEventDispatcher.loadStarted( new LoadEventInfo(loadable.loadTaskId, loadable.dataSpec, elapsedRealtimeMs), loadable.type, trackType, @@ -735,7 +743,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; loadDurationMs, loadable.bytesLoaded()); loadErrorHandlingPolicy.onLoadTaskConcluded(loadable.loadTaskId); - eventDispatcher.loadCompleted( + mediaSourceEventDispatcher.loadCompleted( loadEventInfo, loadable.type, trackType, @@ -765,7 +773,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; loadDurationMs, loadable.bytesLoaded()); loadErrorHandlingPolicy.onLoadTaskConcluded(loadable.loadTaskId); - eventDispatcher.loadCanceled( + mediaSourceEventDispatcher.loadCanceled( loadEventInfo, loadable.type, trackType, @@ -838,7 +846,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } boolean wasCanceled = !loadErrorAction.isRetry(); - eventDispatcher.loadError( + mediaSourceEventDispatcher.loadError( loadEventInfo, loadable.type, trackType, @@ -910,7 +918,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } loadingFinished = false; - eventDispatcher.upstreamDiscarded( + mediaSourceEventDispatcher.upstreamDiscarded( primarySampleQueueType, firstRemovedChunk.startTimeUs, endTimeUs); } @@ -989,7 +997,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; allocator, /* playbackLooper= */ handler.getLooper(), drmSessionManager, - eventDispatcher, + drmEventDispatcher, overridingDrmInitData); if (isAudioVideo) { sampleQueue.setDrmInitData(drmInitData); @@ -1496,7 +1504,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; Allocator allocator, Looper playbackLooper, DrmSessionManager drmSessionManager, - MediaSourceEventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher eventDispatcher, Map overridingDrmInitData) { super(allocator, playbackLooper, drmSessionManager, eventDispatcher); this.overridingDrmInitData = overridingDrmInitData; diff --git a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java index fe42ebb07e..36680e9a32 100644 --- a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriodTest.java @@ -22,10 +22,11 @@ import static org.mockito.Mockito.when; import android.net.Uri; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Rendition; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant; @@ -77,18 +78,18 @@ public final class HlsMediaPeriodTest { when(mockDataSourceFactory.createDataSource(anyInt())).thenReturn(mock(DataSource.class)); HlsPlaylistTracker mockPlaylistTracker = mock(HlsPlaylistTracker.class); when(mockPlaylistTracker.getMasterPlaylist()).thenReturn((HlsMasterPlaylist) playlist); + MediaPeriodId mediaPeriodId = new MediaPeriodId(/* periodUid= */ new Object()); return new HlsMediaPeriod( mock(HlsExtractorFactory.class), mockPlaylistTracker, mockDataSourceFactory, mock(TransferListener.class), mock(DrmSessionManager.class), + new DrmSessionEventListener.EventDispatcher() + .withParameters(/* windowIndex= */ 0, mediaPeriodId), mock(LoadErrorHandlingPolicy.class), - new EventDispatcher() - .withParameters( - /* windowIndex= */ 0, - /* mediaPeriodId= */ new MediaPeriodId(/* periodUid= */ new Object()), - /* mediaTimeOffsetMs= */ 0), + new MediaSourceEventListener.EventDispatcher() + .withParameters(/* windowIndex= */ 0, mediaPeriodId, /* mediaTimeOffsetMs= */ 0), mock(Allocator.class), mock(CompositeSequenceableLoaderFactory.class), /* allowChunklessPreparation =*/ true, diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java index 8efff23f43..6fe999661c 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java @@ -19,11 +19,12 @@ import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SeekParameters; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.MediaPeriod; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SequenceableLoader; import com.google.android.exoplayer2.source.TrackGroup; @@ -48,8 +49,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Nullable private final TransferListener transferListener; private final LoaderErrorThrower manifestLoaderErrorThrower; private final DrmSessionManager drmSessionManager; + private final DrmSessionEventListener.EventDispatcher drmEventDispatcher; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; - private final EventDispatcher eventDispatcher; + private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; private final Allocator allocator; private final TrackGroupArray trackGroups; private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; @@ -66,8 +68,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Nullable TransferListener transferListener, CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, DrmSessionManager drmSessionManager, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, LoadErrorHandlingPolicy loadErrorHandlingPolicy, - EventDispatcher eventDispatcher, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) { this.manifest = manifest; @@ -75,15 +78,16 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; this.transferListener = transferListener; this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.drmSessionManager = drmSessionManager; + this.drmEventDispatcher = drmEventDispatcher; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; - this.eventDispatcher = eventDispatcher; + this.mediaSourceEventDispatcher = mediaSourceEventDispatcher; this.allocator = allocator; this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; trackGroups = buildTrackGroups(manifest, drmSessionManager); sampleStreams = newSampleStreamArray(0); compositeSequenceableLoader = compositeSequenceableLoaderFactory.createCompositeSequenceableLoader(sampleStreams); - eventDispatcher.mediaPeriodCreated(); + mediaSourceEventDispatcher.mediaPeriodCreated(); } public void updateManifest(SsManifest manifest) { @@ -99,7 +103,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; sampleStream.release(); } callback = null; - eventDispatcher.mediaPeriodReleased(); + mediaSourceEventDispatcher.mediaPeriodReleased(); } // MediaPeriod implementation. @@ -197,7 +201,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; @Override public long readDiscontinuity() { if (!notifiedReadingStarted) { - eventDispatcher.readingStarted(); + mediaSourceEventDispatcher.readingStarted(); notifiedReadingStarted = true; } return C.TIME_UNSET; @@ -254,8 +258,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; allocator, positionUs, drmSessionManager, + drmEventDispatcher, loadErrorHandlingPolicy, - eventDispatcher); + mediaSourceEventDispatcher); } private static TrackGroupArray buildTrackGroups( diff --git a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java index 019421b35f..9ffc483117 100644 --- a/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java +++ b/library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaSource.java @@ -26,6 +26,7 @@ import com.google.android.exoplayer2.ExoPlayerLibraryInfo; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.drm.DrmSession; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.offline.FilteringManifestParser; import com.google.android.exoplayer2.offline.StreamKey; @@ -632,7 +633,8 @@ public final class SsMediaSource extends BaseMediaSource @Override public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) { - EventDispatcher eventDispatcher = createEventDispatcher(id); + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher = createEventDispatcher(id); + DrmSessionEventListener.EventDispatcher drmEventDispatcher = createDrmEventDispatcher(id); SsMediaPeriod period = new SsMediaPeriod( manifest, @@ -640,8 +642,9 @@ public final class SsMediaSource extends BaseMediaSource mediaTransferListener, compositeSequenceableLoaderFactory, drmSessionManager, + drmEventDispatcher, loadErrorHandlingPolicy, - eventDispatcher, + mediaSourceEventDispatcher, manifestLoaderErrorThrower, allocator); mediaPeriods.add(period); diff --git a/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriodTest.java b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriodTest.java index a20e5790a7..7042af8aa6 100644 --- a/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriodTest.java +++ b/library/smoothstreaming/src/test/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriodTest.java @@ -22,10 +22,11 @@ import static org.mockito.Mockito.mock; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest; import com.google.android.exoplayer2.testutil.MediaPeriodAsserts; import com.google.android.exoplayer2.testutil.MediaPeriodAsserts.FilterableManifestMediaPeriodFactory; @@ -61,21 +62,22 @@ public class SsMediaPeriodTest { createStreamElement( /* name= */ "text", C.TRACK_TYPE_TEXT, createTextFormat(/* language= */ "eng"))); FilterableManifestMediaPeriodFactory mediaPeriodFactory = - (manifest, periodIndex) -> - new SsMediaPeriod( - manifest, - mock(SsChunkSource.Factory.class), - mock(TransferListener.class), - mock(CompositeSequenceableLoaderFactory.class), - mock(DrmSessionManager.class), - mock(LoadErrorHandlingPolicy.class), - new EventDispatcher() - .withParameters( - /* windowIndex= */ 0, - /* mediaPeriodId= */ new MediaPeriodId(/* periodUid= */ new Object()), - /* mediaTimeOffsetMs= */ 0), - mock(LoaderErrorThrower.class), - mock(Allocator.class)); + (manifest, periodIndex) -> { + MediaPeriodId mediaPeriodId = new MediaPeriodId(/* periodUid= */ new Object()); + return new SsMediaPeriod( + manifest, + mock(SsChunkSource.Factory.class), + mock(TransferListener.class), + mock(CompositeSequenceableLoaderFactory.class), + mock(DrmSessionManager.class), + new DrmSessionEventListener.EventDispatcher() + .withParameters(/* windowIndex= */ 0, mediaPeriodId), + mock(LoadErrorHandlingPolicy.class), + new MediaSourceEventListener.EventDispatcher() + .withParameters(/* windowIndex= */ 0, mediaPeriodId, /* mediaTimeOffsetMs= */ 0), + mock(LoaderErrorThrower.class), + mock(Allocator.class)); + }; MediaPeriodAsserts.assertGetStreamKeysAndManifestFilterIntegration( mediaPeriodFactory, testManifest); diff --git a/playbacktests/src/androidTest/java/com/google/android/exoplayer2/playbacktests/gts/DashWidevineOfflineTest.java b/playbacktests/src/androidTest/java/com/google/android/exoplayer2/playbacktests/gts/DashWidevineOfflineTest.java index 40ec1ed9bb..bc0fc6a95e 100644 --- a/playbacktests/src/androidTest/java/com/google/android/exoplayer2/playbacktests/gts/DashWidevineOfflineTest.java +++ b/playbacktests/src/androidTest/java/com/google/android/exoplayer2/playbacktests/gts/DashWidevineOfflineTest.java @@ -27,6 +27,7 @@ import androidx.test.rule.ActivityTestRule; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.OfflineLicenseHelper; import com.google.android.exoplayer2.source.dash.DashUtil; import com.google.android.exoplayer2.source.dash.manifest.DashManifest; @@ -34,7 +35,6 @@ import com.google.android.exoplayer2.testutil.ActionSchedule; import com.google.android.exoplayer2.testutil.HostActivity; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; -import com.google.android.exoplayer2.util.MediaSourceEventDispatcher; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; import java.io.IOException; @@ -77,7 +77,9 @@ public final class DashWidevineOfflineTest { if (Util.SDK_INT >= 18) { offlineLicenseHelper = OfflineLicenseHelper.newWidevineInstance( - widevineLicenseUrl, httpDataSourceFactory, new MediaSourceEventDispatcher()); + widevineLicenseUrl, + httpDataSourceFactory, + new DrmSessionEventListener.EventDispatcher()); } } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java index e4e7002d8c..d3eec0b85b 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaPeriod.java @@ -16,10 +16,11 @@ package com.google.android.exoplayer2.testutil; import androidx.annotation.Nullable; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.CompositeSequenceableLoader; import com.google.android.exoplayer2.source.MediaPeriod; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SequenceableLoader; import com.google.android.exoplayer2.source.TrackGroupArray; @@ -53,7 +54,7 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod public FakeAdaptiveMediaPeriod( TrackGroupArray trackGroupArray, - EventDispatcher eventDispatcher, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, Allocator allocator, FakeChunkSource.Factory chunkSourceFactory, long durationUs, @@ -63,8 +64,9 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod /* trackDataFactory= */ (unusedFormat, unusedMediaPeriodId) -> { throw new RuntimeException("unused track data"); }, - eventDispatcher, + mediaSourceEventDispatcher, DrmSessionManager.DUMMY, + new DrmSessionEventListener.EventDispatcher(), /* deferOnPrepared= */ false); this.allocator = allocator; this.chunkSourceFactory = chunkSourceFactory; @@ -143,8 +145,9 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod protected final SampleStream createSampleStream( long positionUs, TrackSelection trackSelection, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher) { + DrmSessionEventListener.EventDispatcher drmEventDispatcher) { FakeChunkSource chunkSource = chunkSourceFactory.createChunkSource(trackSelection, durationUs, transferListener); return new ChunkSampleStream<>( @@ -156,8 +159,9 @@ public class FakeAdaptiveMediaPeriod extends FakeMediaPeriod allocator, positionUs, /* drmSessionManager= */ DrmSessionManager.getDummyDrmSessionManager(), + drmEventDispatcher, new DefaultLoadErrorHandlingPolicy(/* minimumLoadableRetryCount= */ 3), - eventDispatcher); + mediaSourceEventDispatcher); } @Override diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java index 216f823f5b..d331b33ff4 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeAdaptiveMediaSource.java @@ -18,9 +18,10 @@ package com.google.android.exoplayer2.testutil; import androidx.annotation.Nullable; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline.Period; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.TransferListener; @@ -53,13 +54,14 @@ public class FakeAdaptiveMediaSource extends FakeMediaSource { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) { Period period = Util.castNonNull(getTimeline()).getPeriodByUid(id.periodUid, new Period()); return new FakeAdaptiveMediaPeriod( trackGroupArray, - eventDispatcher, + mediaSourceEventDispatcher, allocator, chunkSourceFactory, period.durationUs, diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaPeriod.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaPeriod.java index a69265fb3b..cc2ce99683 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaPeriod.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaPeriod.java @@ -26,11 +26,12 @@ import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.SeekParameters; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.LoadEventInfo; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; @@ -57,9 +58,10 @@ public class FakeMediaPeriod implements MediaPeriod { private final TrackGroupArray trackGroupArray; private final List sampleStreams; - private final DrmSessionManager drmSessionManager; - private final EventDispatcher eventDispatcher; private final TrackDataFactory trackDataFactory; + private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; + private final DrmSessionManager drmSessionManager; + private final DrmSessionEventListener.EventDispatcher drmEventDispatcher; private final long fakePreparationLoadTaskId; @Nullable private Handler playerHandler; @@ -77,16 +79,19 @@ public class FakeMediaPeriod implements MediaPeriod { * @param trackGroupArray The track group array. * @param singleSampleTimeUs The timestamp to use for the single sample in each track, in * microseconds. - * @param eventDispatcher A dispatcher for media source events. + * @param mediaSourceEventDispatcher A dispatcher for {@link MediaSourceEventListener} events. */ public FakeMediaPeriod( - TrackGroupArray trackGroupArray, long singleSampleTimeUs, EventDispatcher eventDispatcher) { + TrackGroupArray trackGroupArray, + long singleSampleTimeUs, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher) { this( trackGroupArray, TrackDataFactory.singleSampleWithTimeUs(singleSampleTimeUs), - eventDispatcher, + mediaSourceEventDispatcher, DrmSessionManager.DUMMY, - /* deferOnPrepared= */ false); + new DrmSessionEventListener.EventDispatcher(), + /* deferOnPrepared */ false); } /** @@ -95,22 +100,26 @@ public class FakeMediaPeriod implements MediaPeriod { * @param trackGroupArray The track group array. * @param singleSampleTimeUs The timestamp to use for the single sample in each track, in * microseconds. - * @param eventDispatcher A dispatcher for media source events. + * @param mediaSourceEventDispatcher A dispatcher for {@link MediaSourceEventListener} events. * @param drmSessionManager The {@link DrmSessionManager} used for DRM interactions. + * @param drmEventDispatcher A dispatcher for {@link DrmSessionEventListener} events. * @param deferOnPrepared Whether {@link Callback#onPrepared(MediaPeriod)} should be called only - * after {@link #setPreparationComplete()} has been called. If {@code false} + * after {@link #setPreparationComplete()} has been called. If {@code false} preparation + * completes immediately. */ public FakeMediaPeriod( TrackGroupArray trackGroupArray, long singleSampleTimeUs, - EventDispatcher eventDispatcher, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, boolean deferOnPrepared) { this( trackGroupArray, TrackDataFactory.singleSampleWithTimeUs(singleSampleTimeUs), - eventDispatcher, + mediaSourceEventDispatcher, drmSessionManager, + drmEventDispatcher, deferOnPrepared); } @@ -120,8 +129,9 @@ public class FakeMediaPeriod implements MediaPeriod { * @param trackGroupArray The track group array. * @param trackDataFactory A source for the underlying sample data for each track in {@code * trackGroupArray}. - * @param eventDispatcher A dispatcher for media source events. + * @param mediaSourceEventDispatcher A dispatcher for media source events. * @param drmSessionManager The DrmSessionManager used for DRM interactions. + * @param drmEventDispatcher A dispatcher for {@link DrmSessionEventListener} events. * @param deferOnPrepared Whether {@link Callback#onPrepared(MediaPeriod)} should be called only * after {@link #setPreparationComplete()} has been called. If {@code false} preparation * completes immediately. @@ -129,18 +139,20 @@ public class FakeMediaPeriod implements MediaPeriod { public FakeMediaPeriod( TrackGroupArray trackGroupArray, TrackDataFactory trackDataFactory, - EventDispatcher eventDispatcher, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, boolean deferOnPrepared) { this.trackGroupArray = trackGroupArray; + this.mediaSourceEventDispatcher = mediaSourceEventDispatcher; this.drmSessionManager = drmSessionManager; - this.eventDispatcher = eventDispatcher; + this.drmEventDispatcher = drmEventDispatcher; this.deferOnPrepared = deferOnPrepared; this.trackDataFactory = trackDataFactory; discontinuityPositionUs = C.TIME_UNSET; sampleStreams = new ArrayList<>(); fakePreparationLoadTaskId = LoadEventInfo.getNewId(); - eventDispatcher.mediaPeriodCreated(); + mediaSourceEventDispatcher.mediaPeriodCreated(); } /** @@ -177,12 +189,12 @@ public class FakeMediaPeriod implements MediaPeriod { for (int i = 0; i < sampleStreams.size(); i++) { releaseSampleStream(sampleStreams.get(i)); } - eventDispatcher.mediaPeriodReleased(); + mediaSourceEventDispatcher.mediaPeriodReleased(); } @Override public synchronized void prepare(Callback callback, long positionUs) { - eventDispatcher.loadStarted( + mediaSourceEventDispatcher.loadStarted( new LoadEventInfo(fakePreparationLoadTaskId, FAKE_DATA_SPEC, SystemClock.elapsedRealtime()), C.DATA_TYPE_MEDIA, C.TRACK_TYPE_UNKNOWN, @@ -232,7 +244,13 @@ public class FakeMediaPeriod implements MediaPeriod { int indexInTrackGroup = selection.getIndexInTrackGroup(selection.getSelectedIndex()); assertThat(indexInTrackGroup).isAtLeast(0); assertThat(indexInTrackGroup).isLessThan(trackGroup.length); - streams[i] = createSampleStream(positionUs, selection, drmSessionManager, eventDispatcher); + streams[i] = + createSampleStream( + positionUs, + selection, + mediaSourceEventDispatcher, + drmSessionManager, + drmEventDispatcher); sampleStreams.add(streams[i]); streamResetFlags[i] = true; } @@ -254,7 +272,7 @@ public class FakeMediaPeriod implements MediaPeriod { public long readDiscontinuity() { assertThat(prepared).isTrue(); if (!notifiedReadingStarted) { - eventDispatcher.readingStarted(); + mediaSourceEventDispatcher.readingStarted(); notifiedReadingStarted = true; } long positionDiscontinuityUs = this.discontinuityPositionUs; @@ -304,23 +322,28 @@ public class FakeMediaPeriod implements MediaPeriod { * * @param positionUs The position at which the tracks were selected, in microseconds. * @param selection A selection of tracks. + * @param mediaSourceEventDispatcher A dispatcher for {@link MediaSourceEventListener} events that + * should be used by the sample stream. * @param drmSessionManager The DRM session manager. - * @param eventDispatcher A dispatcher for events that should be used by the sample stream. + * @param drmEventDispatcher A dispatcher for {@link DrmSessionEventListener} events that should + * be used by the sample stream. * @return A {@link SampleStream} for this selection. */ protected SampleStream createSampleStream( long positionUs, TrackSelection selection, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher) { + DrmSessionEventListener.EventDispatcher drmEventDispatcher) { FakeSampleStream sampleStream = new FakeSampleStream( + mediaSourceEventDispatcher, drmSessionManager, - eventDispatcher, + drmEventDispatcher, selection.getSelectedFormat(), trackDataFactory.create( selection.getSelectedFormat(), - Assertions.checkNotNull(eventDispatcher.mediaPeriodId))); + Assertions.checkNotNull(mediaSourceEventDispatcher.mediaPeriodId))); sampleStream.seekTo(positionUs); return sampleStream; } @@ -329,7 +352,8 @@ public class FakeMediaPeriod implements MediaPeriod { * Seeks inside the given sample stream. * * @param sampleStream A sample stream that was created by a call to {@link - * #createSampleStream(long, TrackSelection, DrmSessionManager, EventDispatcher)}. + * #createSampleStream(long, TrackSelection, MediaSourceEventListener.EventDispatcher, + * DrmSessionManager, DrmSessionEventListener.EventDispatcher)}. * @param positionUs The position to seek to, in microseconds. */ protected void seekSampleStream(SampleStream sampleStream, long positionUs) { @@ -341,7 +365,8 @@ public class FakeMediaPeriod implements MediaPeriod { * Releases the given sample stream. * * @param sampleStream A sample stream that was created by a call to {@link - * #createSampleStream(long, TrackSelection, DrmSessionManager, EventDispatcher)}. + * #createSampleStream(long, TrackSelection, MediaSourceEventListener.EventDispatcher, + * DrmSessionManager, DrmSessionEventListener.EventDispatcher)}. */ protected void releaseSampleStream(SampleStream sampleStream) { ((FakeSampleStream) sampleStream).release(); @@ -350,7 +375,7 @@ public class FakeMediaPeriod implements MediaPeriod { private void finishPreparation() { prepared = true; Util.castNonNull(prepareCallback).onPrepared(this); - eventDispatcher.loadCompleted( + mediaSourceEventDispatcher.loadCompleted( new LoadEventInfo( fakePreparationLoadTaskId, FAKE_DATA_SPEC, @@ -373,7 +398,8 @@ public class FakeMediaPeriod implements MediaPeriod { /** * Returns the list of {@link FakeSampleStreamItem}s that will be passed to {@link - * FakeSampleStream#FakeSampleStream(DrmSessionManager, EventDispatcher, Format, List)}. + * FakeSampleStream#FakeSampleStream(MediaSourceEventListener.EventDispatcher, + * DrmSessionManager, DrmSessionEventListener.EventDispatcher, Format, List)}. * * @param format The format of the track to provide data for. * @param mediaPeriodId The {@link MediaPeriodId} to provide data for. diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java index 741594686a..0978547610 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java @@ -26,6 +26,7 @@ import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline.Period; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.source.BaseMediaSource; import com.google.android.exoplayer2.source.ForwardingTimeline; @@ -33,7 +34,7 @@ import com.google.android.exoplayer2.source.LoadEventInfo; import com.google.android.exoplayer2.source.MediaLoadData; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.testutil.FakeMediaPeriod.TrackDataFactory; @@ -215,11 +216,19 @@ public class FakeMediaSource extends BaseMediaSource { int periodIndex = castNonNull(timeline).getIndexOfPeriod(id.periodUid); Assertions.checkArgument(periodIndex != C.INDEX_UNSET); Period period = timeline.getPeriod(periodIndex, new Period()); - EventDispatcher eventDispatcher = + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher = createEventDispatcher(period.windowIndex, id, period.getPositionInWindowMs()); + DrmSessionEventListener.EventDispatcher drmEventDispatcher = + createDrmEventDispatcher(period.windowIndex, id); FakeMediaPeriod mediaPeriod = createFakeMediaPeriod( - id, trackGroupArray, allocator, drmSessionManager, eventDispatcher, transferListener); + id, + trackGroupArray, + allocator, + mediaSourceEventDispatcher, + drmSessionManager, + drmEventDispatcher, + transferListener); activeMediaPeriods.add(mediaPeriod); createdMediaPeriods.add(id); return mediaPeriod; @@ -308,7 +317,10 @@ public class FakeMediaSource extends BaseMediaSource { * @param id The identifier of the period. * @param trackGroupArray The {@link TrackGroupArray} supported by the media period. * @param allocator An {@link Allocator} from which to obtain media buffer allocations. - * @param eventDispatcher An {@link EventDispatcher} to dispatch media source events. + * @param mediaSourceEventDispatcher An {@link MediaSourceEventListener.EventDispatcher} to + * dispatch media source events. + * @param drmEventDispatcher An {@link MediaSourceEventListener.EventDispatcher} to dispatch DRM + * events. * @param transferListener The transfer listener which should be informed of any data transfers. * May be null if no listener is available. * @return A new {@link FakeMediaPeriod}. @@ -318,8 +330,9 @@ public class FakeMediaSource extends BaseMediaSource { MediaPeriodId id, TrackGroupArray trackGroupArray, Allocator allocator, + MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, @Nullable TransferListener transferListener) { long positionInWindowUs = timeline.getPeriodByUid(id.periodUid, new Period()).getPositionInWindowUs(); @@ -329,8 +342,9 @@ public class FakeMediaSource extends BaseMediaSource { trackDataFactory != null ? trackDataFactory : TrackDataFactory.singleSampleWithTimeUs(defaultFirstSampleTimeUs), - eventDispatcher, + mediaSourceEventDispatcher, drmSessionManager, + drmEventDispatcher, /* deferOnPrepared= */ false); } @@ -347,7 +361,8 @@ public class FakeMediaSource extends BaseMediaSource { /* mediaStartTimeMs= */ C.TIME_UNSET, /* mediaEndTimeMs = */ C.TIME_UNSET); long elapsedRealTimeMs = SystemClock.elapsedRealtime(); - EventDispatcher eventDispatcher = createEventDispatcher(/* mediaPeriodId= */ null); + MediaSourceEventListener.EventDispatcher eventDispatcher = + createEventDispatcher(/* mediaPeriodId= */ null); long loadTaskId = LoadEventInfo.getNewId(); eventDispatcher.loadStarted( new LoadEventInfo( diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java index 420b9b83ae..85bccf96e5 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java @@ -25,8 +25,9 @@ import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmSession; +import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; -import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; +import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MimeTypes; @@ -126,10 +127,11 @@ public class FakeSampleStream implements SampleStream { } } + @Nullable private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher; private final Format initialFormat; private final List fakeSampleStreamItems; private final DrmSessionManager drmSessionManager; - @Nullable private final EventDispatcher eventDispatcher; + private final DrmSessionEventListener.EventDispatcher drmEventDispatcher; private int sampleItemIndex; private @MonotonicNonNull Format downstreamFormat; @@ -140,8 +142,11 @@ public class FakeSampleStream implements SampleStream { * Creates a fake sample stream which outputs the given {@link Format} followed by the provided * {@link FakeSampleStreamItem items}. * + * @param mediaSourceEventDispatcher A {@link MediaSourceEventListener.EventDispatcher} to notify + * of media events. * @param drmSessionManager A {@link DrmSessionManager} for DRM interactions. - * @param eventDispatcher An {@link EventDispatcher} to notify of read events. + * @param drmEventDispatcher A {@link DrmSessionEventListener.EventDispatcher} to notify of DRM + * events. * @param initialFormat The first {@link Format} to output. * @param fakeSampleStreamItems The {@link FakeSampleStreamItem items} to customize the return * values of {@link #readData(FormatHolder, DecoderInputBuffer, boolean)}. This is assumed to @@ -150,12 +155,14 @@ public class FakeSampleStream implements SampleStream { * FakeSampleStreamItem#END_OF_STREAM_ITEM}. */ public FakeSampleStream( + @Nullable MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher, DrmSessionManager drmSessionManager, - @Nullable EventDispatcher eventDispatcher, + DrmSessionEventListener.EventDispatcher drmEventDispatcher, Format initialFormat, List fakeSampleStreamItems) { + this.mediaSourceEventDispatcher = mediaSourceEventDispatcher; this.drmSessionManager = drmSessionManager; - this.eventDispatcher = eventDispatcher; + this.drmEventDispatcher = drmEventDispatcher; this.initialFormat = initialFormat; this.fakeSampleStreamItems = new ArrayList<>(fakeSampleStreamItems); } @@ -268,13 +275,13 @@ public class FakeSampleStream implements SampleStream { Looper playbackLooper = Assertions.checkNotNull(Looper.myLooper()); currentDrmSession = newDrmInitData != null - ? drmSessionManager.acquireSession(playbackLooper, eventDispatcher, newDrmInitData) + ? drmSessionManager.acquireSession(playbackLooper, drmEventDispatcher, newDrmInitData) : drmSessionManager.acquirePlaceholderSession( playbackLooper, MimeTypes.getTrackType(newFormat.sampleMimeType)); outputFormatHolder.drmSession = currentDrmSession; if (previousSession != null) { - previousSession.release(eventDispatcher); + previousSession.release(drmEventDispatcher); } } @@ -308,13 +315,13 @@ public class FakeSampleStream implements SampleStream { /** Release this SampleStream and all underlying resources. */ public void release() { if (currentDrmSession != null) { - currentDrmSession.release(eventDispatcher); + currentDrmSession.release(drmEventDispatcher); currentDrmSession = null; } } private void notifyEventDispatcher(Format format) { - if (eventDispatcher != null) { + if (mediaSourceEventDispatcher != null) { @Nullable SampleInfo sampleInfo = null; for (int i = sampleItemIndex; i < fakeSampleStreamItems.size(); i++) { sampleInfo = fakeSampleStreamItems.get(i).sampleInfo; @@ -323,7 +330,7 @@ public class FakeSampleStream implements SampleStream { } } long nextSampleTimeUs = sampleInfo != null ? sampleInfo.timeUs : C.TIME_END_OF_SOURCE; - eventDispatcher.downstreamFormatChanged( + mediaSourceEventDispatcher.downstreamFormatChanged( C.TRACK_TYPE_UNKNOWN, format, C.SELECTION_REASON_UNKNOWN,