diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java index 3f75500a2b..04756c57ed 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java @@ -409,7 +409,7 @@ 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, DrmSessionEventListener.EventDispatcher, Format)} - * or {@link #acquirePlaceholderSession} is called. + * is called. * *
By default, the mode is {@link #MODE_PLAYBACK} and a streaming license is requested when
* required.
@@ -469,34 +469,6 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
@Override
@Nullable
- public DrmSession acquirePlaceholderSession(Looper playbackLooper, int trackType) {
- initPlaybackLooper(playbackLooper);
- ExoMediaDrm exoMediaDrm = Assertions.checkNotNull(this.exoMediaDrm);
- boolean avoidPlaceholderDrmSessions =
- FrameworkMediaCrypto.class.equals(exoMediaDrm.getExoMediaCryptoType())
- && FrameworkMediaCrypto.WORKAROUND_DEVICE_NEEDS_KEYS_TO_CONFIGURE_CODEC;
- // Avoid attaching a session to sparse formats.
- if (avoidPlaceholderDrmSessions
- || Util.linearSearch(useDrmSessionsForClearContentTrackTypes, trackType) == C.INDEX_UNSET
- || exoMediaDrm.getExoMediaCryptoType() == null) {
- return null;
- }
- maybeCreateMediaDrmHandler(playbackLooper);
- if (placeholderDrmSession == null) {
- DefaultDrmSession placeholderDrmSession =
- createAndAcquireSessionWithRetry(
- /* schemeDatas= */ ImmutableList.of(),
- /* isPlaceholderSession= */ true,
- /* eventDispatcher= */ null);
- sessions.add(placeholderDrmSession);
- this.placeholderDrmSession = placeholderDrmSession;
- } else {
- placeholderDrmSession.acquire(/* eventDispatcher= */ null);
- }
- return placeholderDrmSession;
- }
-
- @Override
public DrmSession acquireSession(
Looper playbackLooper,
@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher,
@@ -504,6 +476,11 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
initPlaybackLooper(playbackLooper);
maybeCreateMediaDrmHandler(playbackLooper);
+ if (format.drmInitData == null) {
+ // Content is not encrypted.
+ return maybeAcquirePlaceholderSession(MimeTypes.getTrackType(format.sampleMimeType));
+ }
+
@Nullable List 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
- * encrypted content periods.
- *
- * @param playbackLooper The looper associated with the media playback thread.
- * @param trackType The type of the track to acquire a placeholder session for. Must be one of the
- * {@link C}{@code .TRACK_TYPE_*} constants.
- * @return The placeholder DRM session, or null if this DRM session manager does not support
- * placeholder sessions.
- */
- @Nullable
- default DrmSession acquirePlaceholderSession(Looper playbackLooper, int trackType) {
- return null;
- }
-
/**
* Returns a {@link DrmSession} for the specified {@link Format}, with an incremented reference
- * count. When the caller no longer needs to use the instance, it must call {@link
+ * count. May return null if the {@link Format#drmInitData} is null and the DRM session manager is
+ * not configured to attach a {@link DrmSession} to clear content. When the caller no longer needs
+ * to use a returned {@link DrmSession}, it must call {@link
* DrmSession#release(DrmSessionEventListener.EventDispatcher)} to decrement the reference count.
*
+ * If the provided {@link Format} contains a null {@link Format#drmInitData}, the returned
+ * {@link DrmSession} (if not null) will be a placeholder session which does not execute key
+ * requests, and cannot be used to handle encrypted content. However, a placeholder session may be
+ * used to configure secure decoders for playback of clear content periods, which can reduce the
+ * cost of transitioning between clear and encrypted content.
+ *
* @param playbackLooper The looper associated with the media playback thread.
* @param eventDispatcher The {@link DrmSessionEventListener.EventDispatcher} used to distribute
* events, and passed on to {@link
* DrmSession#acquire(DrmSessionEventListener.EventDispatcher)}.
- * @param format The {@link Format} for which to acquire a {@link DrmSession}. Must contain a
- * non-null {@link Format#drmInitData}.
- * @return The DRM session.
+ * @param format The {@link Format} for which to acquire a {@link DrmSession}.
+ * @return The DRM session. May be null if the given {@link Format#drmInitData} is null.
*/
+ @Nullable
DrmSession acquireSession(
Looper playbackLooper,
@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher,
@@ -105,16 +98,16 @@ public interface DrmSessionManager {
/**
* Returns the {@link ExoMediaCrypto} type associated to sessions acquired for the given {@link
* Format}. Returns the {@link UnsupportedMediaCrypto} type if this DRM session manager does not
- * support any of the DRM schemes defined in the given {@link Format}. If the {@link Format}
- * describes unencrypted content, returns an {@link ExoMediaCrypto} type if this DRM session
- * manager would associate a {@link #acquirePlaceholderSession placeholder session} to the given
- * {@link Format}, or null otherwise.
+ * support any of the DRM schemes defined in the given {@link Format}. Returns null if {@link
+ * Format#drmInitData} is null and {@link #acquireSession} would return null for the given {@link
+ * Format}.
*
* @param format The {@link Format} for which to return the {@link ExoMediaCrypto} type.
- * @return The {@link ExoMediaCrypto} type associated to sessions acquired using the given
- * parameters, or the {@link UnsupportedMediaCrypto} type if the provided {@code drmInitData}
- * is not supported, or {@code null} if {@code drmInitData} is null and no DRM session will be
- * associated to the given {@code trackType}.
+ * @return The {@link ExoMediaCrypto} type associated to sessions acquired using the given {@link
+ * Format}, or {@link UnsupportedMediaCrypto} if this DRM session manager does not support any
+ * of the DRM schemes defined in the given {@link Format}. May be null if {@link
+ * Format#drmInitData} is null and {@link #acquireSession} would return null for the given
+ * {@link Format}.
*/
@Nullable
Class extends ExoMediaCrypto> getExoMediaCryptoType(Format format);
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java
index 71091c878d..b218d0cadb 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/OfflineLicenseHelper.java
@@ -184,7 +184,8 @@ public final class OfflineLicenseHelper {
/**
* Downloads an offline license.
*
- * @param format The {@link Format} of the content whose license is to be downloaded.
+ * @param format The {@link Format} of the content whose license is to be downloaded. Must contain
+ * a non-null {@link Format#drmInitData}.
* @return The key set id for the downloaded license.
* @throws DrmSessionException Thrown when a DRM session error occurs.
*/
@@ -278,13 +279,14 @@ public final class OfflineLicenseHelper {
private DrmSession openBlockingKeyRequest(
@Mode int licenseMode, @Nullable byte[] offlineLicenseKeySetId, Format format) {
+ Assertions.checkNotNull(format.drmInitData);
drmSessionManager.setMode(licenseMode, offlineLicenseKeySetId);
conditionVariable.close();
DrmSession drmSession =
drmSessionManager.acquireSession(handlerThread.getLooper(), eventDispatcher, format);
// Block current thread until key loading is finished
conditionVariable.block();
- return drmSession;
+ return Assertions.checkNotNull(drmSession);
}
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java
index 515f805845..28d25feb03 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java
@@ -327,13 +327,7 @@ public class SampleQueue implements TrackOutput {
* Attempts to read from the queue.
*
* {@link Format Formats} read from this method may be associated to a {@link DrmSession}
- * through {@link FormatHolder#drmSession}, which is populated in two scenarios:
- *
- *
- *
+ * through {@link FormatHolder#drmSession}.
*
* @param formatHolder A {@link FormatHolder} to populate in the case of reading a format.
* @param buffer A {@link DecoderInputBuffer} to populate in the case of reading a sample or the
@@ -842,10 +836,7 @@ public class SampleQueue implements TrackOutput {
// is being used for both DrmInitData.
@Nullable DrmSession previousSession = currentDrmSession;
currentDrmSession =
- newDrmInitData != null
- ? drmSessionManager.acquireSession(playbackLooper, drmEventDispatcher, newFormat)
- : drmSessionManager.acquirePlaceholderSession(
- playbackLooper, MimeTypes.getTrackType(newFormat.sampleMimeType));
+ drmSessionManager.acquireSession(playbackLooper, drmEventDispatcher, newFormat);
outputFormatHolder.drmSession = currentDrmSession;
if (previousSession != null) {
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java b/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java
index 4583c542b3..54e2dd902d 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/source/SampleQueueTest.java
@@ -38,6 +38,7 @@ 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.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.upstream.Allocator;
@@ -54,7 +55,6 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
/** Test for {@link SampleQueue}. */
@@ -69,6 +69,8 @@ public final class SampleQueueTest {
private static final Format FORMAT_SPLICED = buildFormat(/* id= */ "spliced");
private static final Format FORMAT_ENCRYPTED =
new Format.Builder().setId(/* id= */ "encrypted").setDrmInitData(new DrmInitData()).build();
+ private static final Format FORMAT_ENCRYPTED_WITH_EXO_MEDIA_CRYPTO_TYPE =
+ FORMAT_ENCRYPTED.copyWithExoMediaCryptoType(MockExoMediaCrypto.class);
private static final byte[] DATA = TestUtil.buildTestData(ALLOCATION_SIZE * 10);
/*
@@ -128,7 +130,7 @@ public final class SampleQueueTest {
new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, new byte[16], 0, 0);
private Allocator allocator;
- private DrmSessionManager mockDrmSessionManager;
+ private MockDrmSessionManager mockDrmSessionManager;
private DrmSession mockDrmSession;
private DrmSessionEventListener.EventDispatcher eventDispatcher;
private SampleQueue sampleQueue;
@@ -138,11 +140,8 @@ public final class SampleQueueTest {
@Before
public void setUp() {
allocator = new DefaultAllocator(false, ALLOCATION_SIZE);
- mockDrmSessionManager = Mockito.mock(DrmSessionManager.class);
mockDrmSession = Mockito.mock(DrmSession.class);
- when(mockDrmSessionManager.acquireSession(
- ArgumentMatchers.any(), ArgumentMatchers.any(), ArgumentMatchers.any()))
- .thenReturn(mockDrmSession);
+ mockDrmSessionManager = new MockDrmSessionManager(mockDrmSession);
eventDispatcher = new DrmSessionEventListener.EventDispatcher();
sampleQueue =
new SampleQueue(
@@ -399,7 +398,7 @@ public final class SampleQueueTest {
@Test
public void isReadyReturnsTrueForValidDrmSession() {
writeTestDataWithEncryptedSections();
- assertReadFormat(/* formatRequired= */ false, FORMAT_ENCRYPTED);
+ assertReadFormat(/* formatRequired= */ false, FORMAT_ENCRYPTED_WITH_EXO_MEDIA_CRYPTO_TYPE);
assertThat(sampleQueue.isReady(/* loadingFinished= */ false)).isFalse();
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED_WITH_KEYS);
assertThat(sampleQueue.isReady(/* loadingFinished= */ false)).isTrue();
@@ -424,7 +423,7 @@ public final class SampleQueueTest {
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED);
writeTestDataWithEncryptedSections();
- assertReadFormat(/* formatRequired= */ false, FORMAT_ENCRYPTED);
+ assertReadFormat(/* formatRequired= */ false, FORMAT_ENCRYPTED_WITH_EXO_MEDIA_CRYPTO_TYPE);
assertReadNothing(/* formatRequired= */ false);
assertThat(inputBuffer.waitingForKeys).isTrue();
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED_WITH_KEYS);
@@ -464,9 +463,7 @@ public final class SampleQueueTest {
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED_WITH_KEYS);
DrmSession mockPlaceholderDrmSession = Mockito.mock(DrmSession.class);
when(mockPlaceholderDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED_WITH_KEYS);
- when(mockDrmSessionManager.acquirePlaceholderSession(
- ArgumentMatchers.any(), ArgumentMatchers.anyInt()))
- .thenReturn(mockPlaceholderDrmSession);
+ mockDrmSessionManager.mockPlaceholderDrmSession = mockPlaceholderDrmSession;
writeTestDataWithEncryptedSections();
int result =
@@ -497,9 +494,7 @@ public final class SampleQueueTest {
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED_WITH_KEYS);
DrmSession mockPlaceholderDrmSession = Mockito.mock(DrmSession.class);
when(mockPlaceholderDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED_WITH_KEYS);
- when(mockDrmSessionManager.acquirePlaceholderSession(
- ArgumentMatchers.any(), ArgumentMatchers.anyInt()))
- .thenReturn(mockPlaceholderDrmSession);
+ mockDrmSessionManager.mockPlaceholderDrmSession = mockPlaceholderDrmSession;
writeFormat(ENCRYPTED_SAMPLE_FORMATS[0]);
byte[] sampleData = new byte[] {0, 1, 2};
@@ -540,7 +535,7 @@ public final class SampleQueueTest {
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED);
writeTestDataWithEncryptedSections();
- assertReadFormat(/* formatRequired= */ false, FORMAT_ENCRYPTED);
+ assertReadFormat(/* formatRequired= */ false, FORMAT_ENCRYPTED_WITH_EXO_MEDIA_CRYPTO_TYPE);
assertReadNothing(/* formatRequired= */ false);
sampleQueue.maybeThrowError();
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_ERROR);
@@ -569,7 +564,7 @@ public final class SampleQueueTest {
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED);
writeTestDataWithEncryptedSections();
- assertReadFormat(/* formatRequired= */ false, FORMAT_ENCRYPTED);
+ assertReadFormat(/* formatRequired= */ false, FORMAT_ENCRYPTED_WITH_EXO_MEDIA_CRYPTO_TYPE);
assertReadEncryptedSample(/* sampleIndex= */ 0);
}
@@ -1497,4 +1492,33 @@ public final class SampleQueueTest {
private static Format copyWithLabel(Format format, String label) {
return format.buildUpon().setLabel(label).build();
}
+
+ private static final class MockExoMediaCrypto implements ExoMediaCrypto {}
+
+ private static final class MockDrmSessionManager implements DrmSessionManager {
+
+ private final DrmSession mockDrmSession;
+ @Nullable private DrmSession mockPlaceholderDrmSession;
+
+ private MockDrmSessionManager(DrmSession mockDrmSession) {
+ this.mockDrmSession = mockDrmSession;
+ }
+
+ @Nullable
+ @Override
+ public DrmSession acquireSession(
+ Looper playbackLooper,
+ @Nullable DrmSessionEventListener.EventDispatcher eventDispatcher,
+ Format format) {
+ return format.drmInitData != null ? mockDrmSession : mockPlaceholderDrmSession;
+ }
+
+ @Nullable
+ @Override
+ public Class extends ExoMediaCrypto> getExoMediaCryptoType(Format format) {
+ return mockPlaceholderDrmSession != null || format.drmInitData != null
+ ? MockExoMediaCrypto.class
+ : null;
+ }
+ }
}
diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java
index 824d3c02e3..7d63e129db 100644
--- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java
+++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeSampleStream.java
@@ -30,7 +30,6 @@ import com.google.android.exoplayer2.drm.DrmSessionManager;
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;
import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.Iterables;
import java.io.IOException;
@@ -274,10 +273,7 @@ public class FakeSampleStream implements SampleStream {
@Nullable DrmSession previousSession = currentDrmSession;
Looper playbackLooper = Assertions.checkNotNull(Looper.myLooper());
currentDrmSession =
- newDrmInitData != null
- ? drmSessionManager.acquireSession(playbackLooper, drmEventDispatcher, newFormat)
- : drmSessionManager.acquirePlaceholderSession(
- playbackLooper, MimeTypes.getTrackType(newFormat.sampleMimeType));
+ drmSessionManager.acquireSession(playbackLooper, drmEventDispatcher, newFormat);
outputFormatHolder.drmSession = currentDrmSession;
if (previousSession != null) {