Completely separate MediaSource & DrmSession EventDispatchers

PiperOrigin-RevId: 319989989
This commit is contained in:
ibaker 2020-07-07 16:40:27 +01:00 committed by kim-vde
parent 4c75339ee8
commit fa594489d9
46 changed files with 878 additions and 868 deletions

View file

@ -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<T> {
/** Performs this operation on the given argument. */
void accept(T t);
}

View file

@ -70,7 +70,8 @@ import java.util.Set;
private final IdentityHashMap<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod;
private final Map<Object, MediaSourceHolder> mediaSourceByUid;
private final MediaSourceListInfoRefreshListener mediaSourceListInfoListener;
private final MediaSourceEventListener.EventDispatcher eventDispatcher;
private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher;
private final DrmSessionEventListener.EventDispatcher drmEventDispatcher;
private final HashMap<MediaSourceList.MediaSourceHolder, MediaSourceAndListener> childSources;
private final Set<MediaSourceHolder> 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;
}
}

View file

@ -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<String, String> keyRequestParameters;
private final CopyOnWriteMultiset<MediaSourceEventDispatcher> eventDispatchers;
private final CopyOnWriteMultiset<DrmSessionEventListener.EventDispatcher> 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<DrmSessionEventListener> event) {
for (MediaSourceEventDispatcher eventDispatcher : eventDispatchers.elementSet()) {
eventDispatcher.dispatch(event, DrmSessionEventListener.class);
private void dispatchEvent(Consumer<DrmSessionEventListener.EventDispatcher> event) {
for (DrmSessionEventListener.EventDispatcher eventDispatcher : eventDispatchers.elementSet()) {
event.accept(eventDispatcher);
}
}

View file

@ -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.
*
* <p>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<SchemeData> 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<SchemeData> 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;

View file

@ -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}.
*
* <p>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.
* <p>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);
}

View file

@ -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<EventDispatcher.ListenerAndHandler> listenerAndHandlers;
/** Creates an event dispatcher. */
public EventDispatcher() {
this(
/* listenerAndHandlers= */ new CopyOnWriteArrayList<>(),
/* windowIndex= */ 0,
/* mediaPeriodId= */ null);
}
private EventDispatcher(
CopyOnWriteArrayList<EventDispatcher.ListenerAndHandler> 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;
}
}
}
}

View file

@ -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.
*
* <p>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);
/**

View file

@ -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.
}
}

View file

@ -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<String, String> 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<String, String> 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);
}
/**

View file

@ -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<MediaSourceCaller> mediaSourceCallers;
private final HashSet<MediaSourceCaller> 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.
*
* <p>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 <T> void addEventListenerInternal(
Handler handler, T eventListener, Class<T> listenerClass) {
eventDispatcher.addEventListener(handler, eventListener, listenerClass);
}
/**
* Removes a listener from the internal {@link MediaSourceEventDispatcher}.
*
* @see MediaSourceEventDispatcher#removeEventListener(Object, Class)
*/
protected final <T> void removeEventListenerInternal(T eventListener, Class<T> listenerClass) {
eventDispatcher.removeEventListener(eventListener, listenerClass);
drmEventDispatcher.removeEventListener(eventListener);
}
@Override

View file

@ -222,10 +222,12 @@ public abstract class CompositeMediaSource<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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<T> 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;
}

View file

@ -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<ListenerAndHandler> 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<ListenerInfo> listeners,
CopyOnWriteArrayList<ListenerAndHandler> 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;
}
}
}
}

View file

@ -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,

View file

@ -283,6 +283,7 @@ public final class ProgressiveMediaSource extends BaseMediaSource
dataSource,
extractorsFactory,
drmSessionManager,
createDrmEventDispatcher(id),
loadableLoadErrorHandlingPolicy,
createEventDispatcher(id),
this,

View file

@ -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);
}
}

View file

@ -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<T extends ChunkSource> implements SampleStream, S
private final boolean[] embeddedTracksSelected;
private final T chunkSource;
private final SequenceableLoader.Callback<ChunkSampleStream<T>> 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<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> implements SampleStream, S
}
boolean canceled = !loadErrorAction.isRetry();
eventDispatcher.loadError(
mediaSourceEventDispatcher.loadError(
loadEventInfo,
loadable.type,
primaryTrackType,
@ -593,7 +597,7 @@ public class ChunkSampleStream<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> 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<T extends ChunkSource> implements SampleStream, S
private void maybeNotifyDownstreamFormat() {
if (!notifiedDownstreamFormat) {
eventDispatcher.downstreamFormatChanged(
mediaSourceEventDispatcher.downstreamFormatChanged(
embeddedTrackTypes[index],
embeddedTrackFormats[index],
C.SELECTION_REASON_UNKNOWN,

View file

@ -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.
*
* <p>Adds the correct {@code windowIndex} and {@code mediaPeriodId} values (and {@code
* mediaTimeOffsetMs} if needed).
*
* <p>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<T> {
/** 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<ListenerInfo> 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<ListenerInfo> 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.
*
* <p>Calls to {@link #dispatch(EventWithPeriodId, Class)} will propagate to {@code eventListener}
* if the {@code listenerClass} types are equal.
*
* <p>The same listener instance can be added multiple times with different {@code listenerClass}
* values (i.e. if the instance implements multiple listener interfaces).
*
* <p>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.
*
* <p><b>NOTE</b>: 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 <T> void addEventListener(Handler handler, T eventListener, Class<T> listenerClass) {
Assertions.checkNotNull(handler);
Assertions.checkNotNull(eventListener);
listenerInfos.add(new ListenerInfo(handler, eventListener, listenerClass));
}
/**
* Removes a listener from the event dispatcher.
*
* <p>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 <T> void removeEventListener(T eventListener, Class<T> 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 <T> void dispatch(EventWithPeriodId<T> event, Class<T> 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();
}
}
}

View file

@ -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<Allocation> 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),

View file

@ -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,

View file

@ -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));

View file

@ -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

View file

@ -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),

View file

@ -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);
}
};

View file

@ -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;
}

View file

@ -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,

View file

@ -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,

View file

@ -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);
}
}

View file

@ -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));

View file

@ -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),

View file

@ -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<ChunkSampleStream<DashChunkSource>, PlayerTrackEmsgHandler>
trackEmsgHandlerBySampleStream;
private final EventDispatcher eventDispatcher;
private final MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher;
private final DrmSessionEventListener.EventDispatcher drmEventDispatcher;
@Nullable private Callback callback;
private ChunkSampleStream<DashChunkSource>[] 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);

View file

@ -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,

View file

@ -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();
}

View file

@ -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),

View file

@ -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);

View file

@ -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,

View file

@ -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<HlsMediaChunk> 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<String, DrmInitData> overridingDrmInitData) {
super(allocator, playbackLooper, drmSessionManager, eventDispatcher);
this.overridingDrmInitData = overridingDrmInitData;

View file

@ -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,

View file

@ -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(

View file

@ -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);

View file

@ -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<SsManifest> 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);

View file

@ -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());
}
}

View file

@ -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

View file

@ -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,

View file

@ -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<SampleStream> 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.

View file

@ -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(

View file

@ -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<FakeSampleStreamItem> 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<FakeSampleStreamItem> 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,