Add EventDispatcher to DrmSession(Manager) acquire & release calls

This is passed in but not currently used. I'll use it in follow-up
changes.

Pre-work for issue:#6765

PiperOrigin-RevId: 299314341
This commit is contained in:
ibaker 2020-03-06 10:55:01 +00:00 committed by Oliver Woodman
parent 1f4156ce6d
commit eeab811301
14 changed files with 127 additions and 55 deletions

View File

@ -33,6 +33,7 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.EventDispatcher; import com.google.android.exoplayer2.util.EventDispatcher;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -258,7 +259,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} }
@Override @Override
public void acquire() { public void acquire(@Nullable MediaSourceEventDispatcher eventDispatcher) {
Assertions.checkState(referenceCount >= 0); Assertions.checkState(referenceCount >= 0);
if (++referenceCount == 1) { if (++referenceCount == 1) {
Assertions.checkState(state == STATE_OPENING); Assertions.checkState(state == STATE_OPENING);
@ -272,7 +273,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} }
@Override @Override
public void release() { public void release(@Nullable MediaSourceEventDispatcher eventDispatcher) {
if (--referenceCount == 0) { if (--referenceCount == 0) {
// Assigning null to various non-null variables for clean-up. // Assigning null to various non-null variables for clean-up.
state = STATE_RELEASED; state = STATE_RELEASED;
@ -288,7 +289,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
if (sessionId != null) { if (sessionId != null) {
mediaDrm.closeSession(sessionId); mediaDrm.closeSession(sessionId);
sessionId = null; sessionId = null;
eventDispatcher.dispatch(DefaultDrmSessionEventListener::onDrmSessionReleased); this.eventDispatcher.dispatch(DefaultDrmSessionEventListener::onDrmSessionReleased);
} }
releaseCallback.onSessionReleased(this); releaseCallback.onSessionReleased(this);
} }

View File

@ -31,6 +31,7 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.EventDispatcher; import com.google.android.exoplayer2.util.EventDispatcher;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -386,8 +387,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
/** /**
* Sets the mode, which determines the role of sessions acquired from the instance. This must be * Sets the mode, which determines the role of sessions acquired from the instance. This must be
* called before {@link #acquireSession(Looper, DrmInitData)} or {@link * called before {@link #acquireSession(Looper, MediaSourceEventDispatcher, DrmInitData)} or
* #acquirePlaceholderSession} is called. * {@link #acquirePlaceholderSession} is called.
* *
* <p>By default, the mode is {@link #MODE_PLAYBACK} and a streaming license is requested when * <p>By default, the mode is {@link #MODE_PLAYBACK} and a streaming license is requested when
* required. * required.
@ -490,12 +491,15 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
sessions.add(placeholderDrmSession); sessions.add(placeholderDrmSession);
this.placeholderDrmSession = placeholderDrmSession; this.placeholderDrmSession = placeholderDrmSession;
} }
placeholderDrmSession.acquire(); placeholderDrmSession.acquire(/* eventDispatcher= */ null);
return placeholderDrmSession; return placeholderDrmSession;
} }
@Override @Override
public DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData) { public DrmSession<T> acquireSession(
Looper playbackLooper,
@Nullable MediaSourceEventDispatcher eventDispatcher,
DrmInitData drmInitData) {
assertExpectedPlaybackLooper(playbackLooper); assertExpectedPlaybackLooper(playbackLooper);
maybeCreateMediaDrmHandler(playbackLooper); maybeCreateMediaDrmHandler(playbackLooper);
@ -504,7 +508,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
schemeDatas = getSchemeDatas(drmInitData, uuid, false); schemeDatas = getSchemeDatas(drmInitData, uuid, false);
if (schemeDatas.isEmpty()) { if (schemeDatas.isEmpty()) {
final MissingSchemeDataException error = new MissingSchemeDataException(uuid); final MissingSchemeDataException error = new MissingSchemeDataException(uuid);
eventDispatcher.dispatch(listener -> listener.onDrmSessionManagerError(error)); this.eventDispatcher.dispatch(listener -> listener.onDrmSessionManagerError(error));
return new ErrorStateDrmSession<>(new DrmSessionException(error)); return new ErrorStateDrmSession<>(new DrmSessionException(error));
} }
} }
@ -531,7 +535,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
} }
sessions.add(session); sessions.add(session);
} }
session.acquire(); session.acquire(eventDispatcher);
return session; return session;
} }

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer2.drm;
import android.media.MediaDrm; import android.media.MediaDrm;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -30,8 +31,11 @@ import java.util.Map;
public interface DrmSession<T extends ExoMediaCrypto> { public interface DrmSession<T extends ExoMediaCrypto> {
/** /**
* Invokes {@code newSession's} {@link #acquire()} and {@code previousSession's} {@link * Acquires {@code newSession} then releases {@code previousSession}.
* #release()} in that order. Null arguments are ignored. Does nothing if {@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. * and {@code newSession} are the same session.
*/ */
static <T extends ExoMediaCrypto> void replaceSession( static <T extends ExoMediaCrypto> void replaceSession(
@ -41,10 +45,10 @@ public interface DrmSession<T extends ExoMediaCrypto> {
return; return;
} }
if (newSession != null) { if (newSession != null) {
newSession.acquire(); newSession.acquire(/* eventDispatcher= */ null);
} }
if (previousSession != null) { if (previousSession != null) {
previousSession.release(); previousSession.release(/* eventDispatcher= */ null);
} }
} }
@ -132,13 +136,20 @@ public interface DrmSession<T extends ExoMediaCrypto> {
/** /**
* Increments the reference count. When the caller no longer needs to use the instance, it must * Increments the reference count. When the caller no longer needs to use the instance, it must
* call {@link #release()} to decrement the reference count. * call {@link #release(MediaSourceEventDispatcher)} 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.
*/ */
void acquire(); void acquire(@Nullable MediaSourceEventDispatcher eventDispatcher);
/** /**
* Decrements the reference count. If the reference count drops to 0 underlying resources are * Decrements the reference count. If the reference count drops to 0 underlying resources are
* released, and the instance cannot be re-used. * 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)}).
*/ */
void release(); void release(@Nullable MediaSourceEventDispatcher eventDispatcher);
} }

View File

@ -19,6 +19,7 @@ import android.os.Looper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
/** /**
* Manages a DRM session. * Manages a DRM session.
@ -42,7 +43,9 @@ public interface DrmSessionManager<T extends ExoMediaCrypto> {
@Override @Override
public DrmSession<ExoMediaCrypto> acquireSession( public DrmSession<ExoMediaCrypto> acquireSession(
Looper playbackLooper, DrmInitData drmInitData) { Looper playbackLooper,
@Nullable MediaSourceEventDispatcher eventDispatcher,
DrmInitData drmInitData) {
return new ErrorStateDrmSession<>( return new ErrorStateDrmSession<>(
new DrmSession.DrmSessionException( new DrmSession.DrmSessionException(
new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME))); new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME)));
@ -83,7 +86,7 @@ public interface DrmSessionManager<T extends ExoMediaCrypto> {
/** /**
* Returns a {@link DrmSession} that does not execute key requests, with an incremented reference * 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 * count. When the caller no longer needs to use the instance, it must call {@link
* DrmSession#release()} to decrement the reference count. * DrmSession#release(MediaSourceEventDispatcher)} to decrement the reference count.
* *
* <p>Placeholder {@link DrmSession DrmSessions} may be used to configure secure decoders for * <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 * playback of clear content periods. This can reduce the cost of transitioning between clear and
@ -103,14 +106,19 @@ public interface DrmSessionManager<T extends ExoMediaCrypto> {
/** /**
* Returns a {@link DrmSession} for the specified {@link DrmInitData}, with an incremented * 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 * reference count. When the caller no longer needs to use the instance, it must call {@link
* DrmSession#release()} to decrement the reference count. * DrmSession#release(MediaSourceEventDispatcher)} to decrement the reference count.
* *
* @param playbackLooper The looper associated with the media playback thread. * @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 drmInitData DRM initialization data. All contained {@link SchemeData}s must contain * @param drmInitData DRM initialization data. All contained {@link SchemeData}s must contain
* non-null {@link SchemeData#data}. * non-null {@link SchemeData#data}.
* @return The DRM session. * @return The DRM session.
*/ */
DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData); DrmSession<T> acquireSession(
Looper playbackLooper,
@Nullable MediaSourceEventDispatcher eventDispatcher,
DrmInitData drmInitData);
/** /**
* Returns the {@link ExoMediaCrypto} type returned by sessions acquired using the given {@link * Returns the {@link ExoMediaCrypto} type returned by sessions acquired using the given {@link

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
import java.util.Map; import java.util.Map;
/** A {@link DrmSession} that's in a terminal error state. */ /** A {@link DrmSession} that's in a terminal error state. */
@ -63,12 +64,12 @@ public final class ErrorStateDrmSession<T extends ExoMediaCrypto> implements Drm
} }
@Override @Override
public void acquire() { public void acquire(@Nullable MediaSourceEventDispatcher eventDispatcher) {
// Do nothing. // Do nothing.
} }
@Override @Override
public void release() { public void release(@Nullable MediaSourceEventDispatcher eventDispatcher) {
// Do nothing. // Do nothing.
} }
} }

View File

@ -26,8 +26,8 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager.Mode; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager.Mode;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException; import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource.Factory;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -41,6 +41,7 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
private final ConditionVariable conditionVariable; private final ConditionVariable conditionVariable;
private final DefaultDrmSessionManager<T> drmSessionManager; private final DefaultDrmSessionManager<T> drmSessionManager;
private final HandlerThread handlerThread; private final HandlerThread handlerThread;
private final MediaSourceEventDispatcher eventDispatcher;
/** /**
* Instantiates a new instance which uses Widevine CDM. Call {@link #release()} when the instance * Instantiates a new instance which uses Widevine CDM. Call {@link #release()} when the instance
@ -49,14 +50,18 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
* @param defaultLicenseUrl The default license URL. Used for key requests that do not specify * @param defaultLicenseUrl The default license URL. Used for key requests that do not specify
* their own license URL. * their own license URL.
* @param httpDataSourceFactory A factory from which to obtain {@link HttpDataSource} instances. * @param httpDataSourceFactory A factory from which to obtain {@link HttpDataSource} instances.
* @param eventDispatcher A {@link MediaSourceEventDispatcher} used to distribute DRM-related
* events.
* @return A new instance which uses Widevine CDM. * @return A new instance which uses Widevine CDM.
* @throws UnsupportedDrmException If the Widevine DRM scheme is unsupported or cannot be * @throws UnsupportedDrmException If the Widevine DRM scheme is unsupported or cannot be
* instantiated. * instantiated.
*/ */
public static OfflineLicenseHelper<FrameworkMediaCrypto> newWidevineInstance( public static OfflineLicenseHelper<FrameworkMediaCrypto> newWidevineInstance(
String defaultLicenseUrl, Factory httpDataSourceFactory) String defaultLicenseUrl,
HttpDataSource.Factory httpDataSourceFactory,
MediaSourceEventDispatcher eventDispatcher)
throws UnsupportedDrmException { throws UnsupportedDrmException {
return newWidevineInstance(defaultLicenseUrl, false, httpDataSourceFactory, null); return newWidevineInstance(defaultLicenseUrl, false, httpDataSourceFactory, eventDispatcher);
} }
/** /**
@ -68,15 +73,24 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
* @param forceDefaultLicenseUrl Whether to use {@code defaultLicenseUrl} for key requests that * @param forceDefaultLicenseUrl Whether to use {@code defaultLicenseUrl} for key requests that
* include their own license URL. * include their own license URL.
* @param httpDataSourceFactory A factory from which to obtain {@link HttpDataSource} instances. * @param httpDataSourceFactory A factory from which to obtain {@link HttpDataSource} instances.
* @param eventDispatcher A {@link MediaSourceEventDispatcher} used to distribute DRM-related
* events.
* @return A new instance which uses Widevine CDM. * @return A new instance which uses Widevine CDM.
* @throws UnsupportedDrmException If the Widevine DRM scheme is unsupported or cannot be * @throws UnsupportedDrmException If the Widevine DRM scheme is unsupported or cannot be
* instantiated. * instantiated.
*/ */
public static OfflineLicenseHelper<FrameworkMediaCrypto> newWidevineInstance( public static OfflineLicenseHelper<FrameworkMediaCrypto> newWidevineInstance(
String defaultLicenseUrl, boolean forceDefaultLicenseUrl, Factory httpDataSourceFactory) String defaultLicenseUrl,
boolean forceDefaultLicenseUrl,
HttpDataSource.Factory httpDataSourceFactory,
MediaSourceEventDispatcher eventDispatcher)
throws UnsupportedDrmException { throws UnsupportedDrmException {
return newWidevineInstance(defaultLicenseUrl, forceDefaultLicenseUrl, httpDataSourceFactory, return newWidevineInstance(
null); defaultLicenseUrl,
forceDefaultLicenseUrl,
httpDataSourceFactory,
/* optionalKeyRequestParameters= */ null,
eventDispatcher);
} }
/** /**
@ -89,6 +103,8 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
* include their own license URL. * include their own license URL.
* @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument * @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument
* to {@link MediaDrm#getKeyRequest}. May be null. * to {@link MediaDrm#getKeyRequest}. May be null.
* @param eventDispatcher A {@link MediaSourceEventDispatcher} used to distribute DRM-related
* events.
* @return A new instance which uses Widevine CDM. * @return A new instance which uses Widevine CDM.
* @throws UnsupportedDrmException If the Widevine DRM scheme is unsupported or cannot be * @throws UnsupportedDrmException If the Widevine DRM scheme is unsupported or cannot be
* instantiated. * instantiated.
@ -97,14 +113,16 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
public static OfflineLicenseHelper<FrameworkMediaCrypto> newWidevineInstance( public static OfflineLicenseHelper<FrameworkMediaCrypto> newWidevineInstance(
String defaultLicenseUrl, String defaultLicenseUrl,
boolean forceDefaultLicenseUrl, boolean forceDefaultLicenseUrl,
Factory httpDataSourceFactory, HttpDataSource.Factory httpDataSourceFactory,
@Nullable Map<String, String> optionalKeyRequestParameters) @Nullable Map<String, String> optionalKeyRequestParameters,
MediaSourceEventDispatcher eventDispatcher)
throws UnsupportedDrmException { throws UnsupportedDrmException {
return new OfflineLicenseHelper<>( return new OfflineLicenseHelper<>(
C.WIDEVINE_UUID, C.WIDEVINE_UUID,
FrameworkMediaDrm.DEFAULT_PROVIDER, FrameworkMediaDrm.DEFAULT_PROVIDER,
new HttpMediaDrmCallback(defaultLicenseUrl, forceDefaultLicenseUrl, httpDataSourceFactory), new HttpMediaDrmCallback(defaultLicenseUrl, forceDefaultLicenseUrl, httpDataSourceFactory),
optionalKeyRequestParameters); optionalKeyRequestParameters,
eventDispatcher);
} }
/** /**
@ -115,6 +133,8 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
* @param callback Performs key and provisioning requests. * @param callback Performs key and provisioning requests.
* @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument * @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument
* to {@link MediaDrm#getKeyRequest}. May be null. * to {@link MediaDrm#getKeyRequest}. May be null.
* @param eventDispatcher A {@link MediaSourceEventDispatcher} used to distribute DRM-related
* events.
* @see DefaultDrmSessionManager.Builder * @see DefaultDrmSessionManager.Builder
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -122,7 +142,8 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
UUID uuid, UUID uuid,
ExoMediaDrm.Provider<T> mediaDrmProvider, ExoMediaDrm.Provider<T> mediaDrmProvider,
MediaDrmCallback callback, MediaDrmCallback callback,
@Nullable Map<String, String> optionalKeyRequestParameters) { @Nullable Map<String, String> optionalKeyRequestParameters,
MediaSourceEventDispatcher eventDispatcher) {
handlerThread = new HandlerThread("OfflineLicenseHelper"); handlerThread = new HandlerThread("OfflineLicenseHelper");
handlerThread.start(); handlerThread.start();
conditionVariable = new ConditionVariable(); conditionVariable = new ConditionVariable();
@ -158,6 +179,7 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
.setKeyRequestParameters(optionalKeyRequestParameters) .setKeyRequestParameters(optionalKeyRequestParameters)
.build(callback); .build(callback);
drmSessionManager.addListener(new Handler(handlerThread.getLooper()), eventListener); drmSessionManager.addListener(new Handler(handlerThread.getLooper()), eventListener);
this.eventDispatcher = eventDispatcher;
} }
/** /**
@ -216,7 +238,7 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
DrmSessionException error = drmSession.getError(); DrmSessionException error = drmSession.getError();
Pair<Long, Long> licenseDurationRemainingSec = Pair<Long, Long> licenseDurationRemainingSec =
WidevineUtil.getLicenseDurationRemainingSec(drmSession); WidevineUtil.getLicenseDurationRemainingSec(drmSession);
drmSession.release(); drmSession.release(eventDispatcher);
drmSessionManager.release(); drmSessionManager.release();
if (error != null) { if (error != null) {
if (error.getCause() instanceof KeysExpiredException) { if (error.getCause() instanceof KeysExpiredException) {
@ -242,7 +264,7 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
drmInitData); drmInitData);
DrmSessionException error = drmSession.getError(); DrmSessionException error = drmSession.getError();
byte[] keySetId = drmSession.getOfflineLicenseKeySetId(); byte[] keySetId = drmSession.getOfflineLicenseKeySetId();
drmSession.release(); drmSession.release(eventDispatcher);
drmSessionManager.release(); drmSessionManager.release();
if (error != null) { if (error != null) {
throw error; throw error;
@ -254,8 +276,8 @@ public final class OfflineLicenseHelper<T extends ExoMediaCrypto> {
@Mode int licenseMode, @Nullable byte[] offlineLicenseKeySetId, DrmInitData drmInitData) { @Mode int licenseMode, @Nullable byte[] offlineLicenseKeySetId, DrmInitData drmInitData) {
drmSessionManager.setMode(licenseMode, offlineLicenseKeySetId); drmSessionManager.setMode(licenseMode, offlineLicenseKeySetId);
conditionVariable.close(); conditionVariable.close();
DrmSession<T> drmSession = drmSessionManager.acquireSession(handlerThread.getLooper(), DrmSession<T> drmSession =
drmInitData); drmSessionManager.acquireSession(handlerThread.getLooper(), eventDispatcher, drmInitData);
// Block current thread until key loading is finished // Block current thread until key loading is finished
conditionVariable.block(); conditionVariable.block();
return drmSession; return drmSession;

View File

@ -675,7 +675,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return sampleQueues[i]; return sampleQueues[i];
} }
} }
SampleQueue trackOutput = new SampleQueue(allocator, drmSessionManager); SampleQueue trackOutput = new SampleQueue(allocator, drmSessionManager, eventDispatcher);
trackOutput.setUpstreamFormatChangeListener(this); trackOutput.setUpstreamFormatChangeListener(this);
@NullableType @NullableType
TrackId[] sampleQueueTrackIds = Arrays.copyOf(this.sampleQueueTrackIds, trackCount + 1); TrackId[] sampleQueueTrackIds = Arrays.copyOf(this.sampleQueueTrackIds, trackCount + 1);

View File

@ -30,6 +30,7 @@ import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataReader; import com.google.android.exoplayer2.upstream.DataReader;
import com.google.android.exoplayer2.util.Assertions; 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.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
@ -55,6 +56,7 @@ public class SampleQueue implements TrackOutput {
private final SampleDataQueue sampleDataQueue; private final SampleDataQueue sampleDataQueue;
private final SampleExtrasHolder extrasHolder; private final SampleExtrasHolder extrasHolder;
private final DrmSessionManager<?> drmSessionManager; private final DrmSessionManager<?> drmSessionManager;
private final MediaSourceEventDispatcher eventDispatcher;
@Nullable private UpstreamFormatChangedListener upstreamFormatChangeListener; @Nullable private UpstreamFormatChangedListener upstreamFormatChangeListener;
@Nullable private Format downstreamFormat; @Nullable private Format downstreamFormat;
@ -94,10 +96,16 @@ public class SampleQueue implements TrackOutput {
* @param allocator An {@link Allocator} from which allocations for sample data can be obtained. * @param allocator An {@link Allocator} from which allocations for sample data can be obtained.
* @param drmSessionManager The {@link DrmSessionManager} to obtain {@link DrmSession DrmSessions} * @param drmSessionManager The {@link DrmSessionManager} to obtain {@link DrmSession DrmSessions}
* from. The created instance does not take ownership of this {@link DrmSessionManager}. * 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.
*/ */
public SampleQueue(Allocator allocator, DrmSessionManager<?> drmSessionManager) { public SampleQueue(
Allocator allocator,
DrmSessionManager<?> drmSessionManager,
MediaSourceEventDispatcher eventDispatcher) {
sampleDataQueue = new SampleDataQueue(allocator); sampleDataQueue = new SampleDataQueue(allocator);
this.drmSessionManager = drmSessionManager; this.drmSessionManager = drmSessionManager;
this.eventDispatcher = eventDispatcher;
extrasHolder = new SampleExtrasHolder(); extrasHolder = new SampleExtrasHolder();
capacity = SAMPLE_CAPACITY_INCREMENT; capacity = SAMPLE_CAPACITY_INCREMENT;
sourceIds = new int[capacity]; sourceIds = new int[capacity];
@ -647,7 +655,7 @@ public class SampleQueue implements TrackOutput {
private void releaseDrmSessionReferences() { private void releaseDrmSessionReferences() {
if (currentDrmSession != null) { if (currentDrmSession != null) {
currentDrmSession.release(); currentDrmSession.release(eventDispatcher);
currentDrmSession = null; currentDrmSession = null;
// Clear downstream format to avoid violating the assumption that downstreamFormat.drmInitData // Clear downstream format to avoid violating the assumption that downstreamFormat.drmInitData
// != null implies currentSession != null // != null implies currentSession != null
@ -791,13 +799,13 @@ public class SampleQueue implements TrackOutput {
Looper playbackLooper = Assertions.checkNotNull(Looper.myLooper()); Looper playbackLooper = Assertions.checkNotNull(Looper.myLooper());
currentDrmSession = currentDrmSession =
newDrmInitData != null newDrmInitData != null
? drmSessionManager.acquireSession(playbackLooper, newDrmInitData) ? drmSessionManager.acquireSession(playbackLooper, eventDispatcher, newDrmInitData)
: drmSessionManager.acquirePlaceholderSession( : drmSessionManager.acquirePlaceholderSession(
playbackLooper, MimeTypes.getTrackType(newFormat.sampleMimeType)); playbackLooper, MimeTypes.getTrackType(newFormat.sampleMimeType));
outputFormatHolder.drmSession = currentDrmSession; outputFormatHolder.drmSession = currentDrmSession;
if (previousSession != null) { if (previousSession != null) {
previousSession.release(); previousSession.release(eventDispatcher);
} }
} }

View File

@ -131,13 +131,14 @@ public class ChunkSampleStream<T extends ChunkSource> implements SampleStream, S
int[] trackTypes = new int[1 + embeddedTrackCount]; int[] trackTypes = new int[1 + embeddedTrackCount];
SampleQueue[] sampleQueues = new SampleQueue[1 + embeddedTrackCount]; SampleQueue[] sampleQueues = new SampleQueue[1 + embeddedTrackCount];
primarySampleQueue = new SampleQueue(allocator, drmSessionManager); primarySampleQueue = new SampleQueue(allocator, drmSessionManager, eventDispatcher);
trackTypes[0] = primaryTrackType; trackTypes[0] = primaryTrackType;
sampleQueues[0] = primarySampleQueue; sampleQueues[0] = primarySampleQueue;
for (int i = 0; i < embeddedTrackCount; i++) { for (int i = 0; i < embeddedTrackCount; i++) {
SampleQueue sampleQueue = SampleQueue sampleQueue =
new SampleQueue(allocator, DrmSessionManager.getDummyDrmSessionManager()); new SampleQueue(
allocator, DrmSessionManager.getDummyDrmSessionManager(), eventDispatcher);
embeddedSampleQueues[i] = sampleQueue; embeddedSampleQueues[i] = sampleQueue;
sampleQueues[i + 1] = sampleQueue; sampleQueues[i + 1] = sampleQueue;
trackTypes[i + 1] = this.embeddedTrackTypes[i]; trackTypes[i + 1] = this.embeddedTrackTypes[i];

View File

@ -25,6 +25,7 @@ import android.util.Pair;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
import java.util.HashMap; import java.util.HashMap;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@ -55,7 +56,8 @@ public class OfflineLicenseHelperTest {
C.WIDEVINE_UUID, C.WIDEVINE_UUID,
new ExoMediaDrm.AppManagedProvider<>(mediaDrm), new ExoMediaDrm.AppManagedProvider<>(mediaDrm),
mediaDrmCallback, mediaDrmCallback,
null); /* optionalKeyRequestParameters= */ null,
new MediaSourceEventDispatcher());
} }
@After @After

View File

@ -40,6 +40,7 @@ import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.MediaSourceEventDispatcher;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -125,6 +126,7 @@ public final class SampleQueueTest {
private Allocator allocator; private Allocator allocator;
private DrmSessionManager<ExoMediaCrypto> mockDrmSessionManager; private DrmSessionManager<ExoMediaCrypto> mockDrmSessionManager;
private DrmSession<ExoMediaCrypto> mockDrmSession; private DrmSession<ExoMediaCrypto> mockDrmSession;
private MediaSourceEventDispatcher eventDispatcher;
private SampleQueue sampleQueue; private SampleQueue sampleQueue;
private FormatHolder formatHolder; private FormatHolder formatHolder;
private DecoderInputBuffer inputBuffer; private DecoderInputBuffer inputBuffer;
@ -136,9 +138,11 @@ public final class SampleQueueTest {
mockDrmSessionManager = mockDrmSessionManager =
(DrmSessionManager<ExoMediaCrypto>) Mockito.mock(DrmSessionManager.class); (DrmSessionManager<ExoMediaCrypto>) Mockito.mock(DrmSessionManager.class);
mockDrmSession = (DrmSession<ExoMediaCrypto>) Mockito.mock(DrmSession.class); mockDrmSession = (DrmSession<ExoMediaCrypto>) Mockito.mock(DrmSession.class);
when(mockDrmSessionManager.acquireSession(ArgumentMatchers.any(), ArgumentMatchers.any())) when(mockDrmSessionManager.acquireSession(
ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()))
.thenReturn(mockDrmSession); .thenReturn(mockDrmSession);
sampleQueue = new SampleQueue(allocator, mockDrmSessionManager); eventDispatcher = new MediaSourceEventDispatcher();
sampleQueue = new SampleQueue(allocator, mockDrmSessionManager, eventDispatcher);
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
inputBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL); inputBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
} }
@ -355,7 +359,7 @@ public final class SampleQueueTest {
public void isReadyReturnsTrueForClearSampleAndPlayClearSamplesWithoutKeysIsTrue() { public void isReadyReturnsTrueForClearSampleAndPlayClearSamplesWithoutKeysIsTrue() {
when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true); when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true);
// We recreate the queue to ensure the mock DRM session manager flags are taken into account. // We recreate the queue to ensure the mock DRM session manager flags are taken into account.
sampleQueue = new SampleQueue(allocator, mockDrmSessionManager); sampleQueue = new SampleQueue(allocator, mockDrmSessionManager, eventDispatcher);
writeTestDataWithEncryptedSections(); writeTestDataWithEncryptedSections();
assertThat(sampleQueue.isReady(/* loadingFinished= */ false)).isTrue(); assertThat(sampleQueue.isReady(/* loadingFinished= */ false)).isTrue();
} }
@ -535,7 +539,7 @@ public final class SampleQueueTest {
public void allowPlayClearSamplesWithoutKeysReadsClearSamples() { public void allowPlayClearSamplesWithoutKeysReadsClearSamples() {
when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true); when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true);
// We recreate the queue to ensure the mock DRM session manager flags are taken into account. // We recreate the queue to ensure the mock DRM session manager flags are taken into account.
sampleQueue = new SampleQueue(allocator, mockDrmSessionManager); sampleQueue = new SampleQueue(allocator, mockDrmSessionManager, eventDispatcher);
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED); when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED);
writeTestDataWithEncryptedSections(); writeTestDataWithEncryptedSections();
@ -925,7 +929,7 @@ public final class SampleQueueTest {
public void adjustUpstreamFormat() { public void adjustUpstreamFormat() {
String label = "label"; String label = "label";
sampleQueue = sampleQueue =
new SampleQueue(allocator, mockDrmSessionManager) { new SampleQueue(allocator, mockDrmSessionManager, eventDispatcher) {
@Override @Override
public Format getAdjustedUpstreamFormat(Format format) { public Format getAdjustedUpstreamFormat(Format format) {
return super.getAdjustedUpstreamFormat(copyWithLabel(format, label)); return super.getAdjustedUpstreamFormat(copyWithLabel(format, label));
@ -941,7 +945,7 @@ public final class SampleQueueTest {
public void invalidateUpstreamFormatAdjustment() { public void invalidateUpstreamFormatAdjustment() {
AtomicReference<String> label = new AtomicReference<>("label1"); AtomicReference<String> label = new AtomicReference<>("label1");
sampleQueue = sampleQueue =
new SampleQueue(allocator, mockDrmSessionManager) { new SampleQueue(allocator, mockDrmSessionManager, eventDispatcher) {
@Override @Override
public Format getAdjustedUpstreamFormat(Format format) { public Format getAdjustedUpstreamFormat(Format format) {
return super.getAdjustedUpstreamFormat(copyWithLabel(format, label.get())); return super.getAdjustedUpstreamFormat(copyWithLabel(format, label.get()));

View File

@ -35,6 +35,7 @@ import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.dash.manifest.DashManifest; import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataReader; 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.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
@ -284,7 +285,11 @@ public final class PlayerEmsgHandler implements Handler.Callback {
private final MetadataInputBuffer buffer; private final MetadataInputBuffer buffer;
/* package */ PlayerTrackEmsgHandler(Allocator allocator) { /* package */ PlayerTrackEmsgHandler(Allocator allocator) {
this.sampleQueue = new SampleQueue(allocator, DrmSessionManager.getDummyDrmSessionManager()); this.sampleQueue =
new SampleQueue(
allocator,
DrmSessionManager.getDummyDrmSessionManager(),
new MediaSourceEventDispatcher());
formatHolder = new FormatHolder(); formatHolder = new FormatHolder();
buffer = new MetadataInputBuffer(); buffer = new MetadataInputBuffer();
} }

View File

@ -53,6 +53,7 @@ import com.google.android.exoplayer2.upstream.Loader;
import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction; import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log; 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.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
@ -907,7 +908,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
boolean isAudioVideo = type == C.TRACK_TYPE_AUDIO || type == C.TRACK_TYPE_VIDEO; boolean isAudioVideo = type == C.TRACK_TYPE_AUDIO || type == C.TRACK_TYPE_VIDEO;
FormatAdjustingSampleQueue trackOutput = FormatAdjustingSampleQueue trackOutput =
new FormatAdjustingSampleQueue(allocator, drmSessionManager, overridingDrmInitData); new FormatAdjustingSampleQueue(
allocator, drmSessionManager, eventDispatcher, overridingDrmInitData);
if (isAudioVideo) { if (isAudioVideo) {
trackOutput.setDrmInitData(drmInitData); trackOutput.setDrmInitData(drmInitData);
} }
@ -1348,8 +1350,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
public FormatAdjustingSampleQueue( public FormatAdjustingSampleQueue(
Allocator allocator, Allocator allocator,
DrmSessionManager<?> drmSessionManager, DrmSessionManager<?> drmSessionManager,
MediaSourceEventDispatcher eventDispatcher,
Map<String, DrmInitData> overridingDrmInitData) { Map<String, DrmInitData> overridingDrmInitData) {
super(allocator, drmSessionManager); super(allocator, drmSessionManager, eventDispatcher);
this.overridingDrmInitData = overridingDrmInitData; this.overridingDrmInitData = overridingDrmInitData;
} }

View File

@ -35,6 +35,7 @@ import com.google.android.exoplayer2.testutil.ActionSchedule;
import com.google.android.exoplayer2.testutil.HostActivity; import com.google.android.exoplayer2.testutil.HostActivity;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; 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.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
@ -75,8 +76,9 @@ public final class DashWidevineOfflineTest {
String widevineLicenseUrl = DashTestData.getWidevineLicenseUrl(true, useL1Widevine); String widevineLicenseUrl = DashTestData.getWidevineLicenseUrl(true, useL1Widevine);
httpDataSourceFactory = new DefaultHttpDataSourceFactory(USER_AGENT); httpDataSourceFactory = new DefaultHttpDataSourceFactory(USER_AGENT);
if (Util.SDK_INT >= 18) { if (Util.SDK_INT >= 18) {
offlineLicenseHelper = OfflineLicenseHelper.newWidevineInstance(widevineLicenseUrl, offlineLicenseHelper =
httpDataSourceFactory); OfflineLicenseHelper.newWidevineInstance(
widevineLicenseUrl, httpDataSourceFactory, new MediaSourceEventDispatcher());
} }
} }