diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java index 6140acdff6..a619674fa0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java @@ -106,6 +106,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; private final ProvisioningManager provisioningManager; private final ReleaseCallback releaseCallback; private final @DefaultDrmSessionManager.Mode int mode; + private final boolean playClearSamplesWithoutKeys; private final boolean isPlaceholderSession; private final HashMap keyRequestParameters; private final EventDispatcher eventDispatcher; @@ -154,6 +155,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ReleaseCallback releaseCallback, @Nullable List schemeDatas, @DefaultDrmSessionManager.Mode int mode, + boolean playClearSamplesWithoutKeys, boolean isPlaceholderSession, @Nullable byte[] offlineLicenseKeySetId, HashMap keyRequestParameters, @@ -170,6 +172,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; this.releaseCallback = releaseCallback; this.mediaDrm = mediaDrm; this.mode = mode; + this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; this.isPlaceholderSession = isPlaceholderSession; if (offlineLicenseKeySetId != null) { this.offlineLicenseKeySetId = offlineLicenseKeySetId; @@ -228,6 +231,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; return state; } + @Override + public boolean playClearSamplesWithoutKeys() { + return playClearSamplesWithoutKeys; + } + @Override public final @Nullable DrmSessionException getError() { return state == STATE_ERROR ? lastException : null; 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 f8e854e51d..1c27d745de 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 @@ -58,7 +58,7 @@ public class DefaultDrmSessionManager implements DrmSe private ExoMediaDrm.Provider exoMediaDrmProvider; private boolean multiSession; private int[] useDrmSessionsForClearContentTrackTypes; - @Flags private int flags; + private boolean playClearSamplesWithoutKeys; private LoadErrorHandlingPolicy loadErrorHandlingPolicy; /** @@ -164,11 +164,7 @@ public class DefaultDrmSessionManager implements DrmSe * @return This builder. */ public Builder setPlayClearSamplesWithoutKeys(boolean playClearSamplesWithoutKeys) { - if (playClearSamplesWithoutKeys) { - this.flags |= FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS; - } else { - this.flags &= ~FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS; - } + this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; return this; } @@ -192,7 +188,7 @@ public class DefaultDrmSessionManager implements DrmSe keyRequestParameters, multiSession, useDrmSessionsForClearContentTrackTypes, - flags, + playClearSamplesWithoutKeys, loadErrorHandlingPolicy); } } @@ -245,7 +241,7 @@ public class DefaultDrmSessionManager implements DrmSe private final EventDispatcher eventDispatcher; private final boolean multiSession; private final int[] useDrmSessionsForClearContentTrackTypes; - @Flags private final int flags; + private final boolean playClearSamplesWithoutKeys; private final ProvisioningManagerImpl provisioningManagerImpl; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; @@ -339,7 +335,7 @@ public class DefaultDrmSessionManager implements DrmSe keyRequestParameters == null ? new HashMap<>() : keyRequestParameters, multiSession, /* useDrmSessionsForClearContentTrackTypes= */ new int[0], - /* flags= */ 0, + /* playClearSamplesWithoutKeys= */ false, new DefaultLoadErrorHandlingPolicy(initialDrmRequestRetryCount)); } @@ -352,7 +348,7 @@ public class DefaultDrmSessionManager implements DrmSe HashMap keyRequestParameters, boolean multiSession, int[] useDrmSessionsForClearContentTrackTypes, - @Flags int flags, + boolean playClearSamplesWithoutKeys, LoadErrorHandlingPolicy loadErrorHandlingPolicy) { Assertions.checkNotNull(uuid); Assertions.checkArgument(!C.COMMON_PSSH_UUID.equals(uuid), "Use C.CLEARKEY_UUID instead"); @@ -363,7 +359,7 @@ public class DefaultDrmSessionManager implements DrmSe this.eventDispatcher = new EventDispatcher<>(); this.multiSession = multiSession; this.useDrmSessionsForClearContentTrackTypes = useDrmSessionsForClearContentTrackTypes; - this.flags = flags; + this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; provisioningManagerImpl = new ProvisioningManagerImpl(); mode = MODE_PLAYBACK; @@ -541,12 +537,6 @@ public class DefaultDrmSessionManager implements DrmSe return session; } - @Override - @Flags - public final int getFlags() { - return flags; - } - @Override @Nullable public Class getExoMediaCryptoType(DrmInitData drmInitData) { @@ -571,6 +561,8 @@ public class DefaultDrmSessionManager implements DrmSe private DefaultDrmSession createNewDefaultSession( @Nullable List schemeDatas, boolean isPlaceholderSession) { Assertions.checkNotNull(exoMediaDrm); + // Placeholder sessions should always play clear samples without keys. + boolean playClearSamplesWithoutKeys = this.playClearSamplesWithoutKeys | isPlaceholderSession; return new DefaultDrmSession<>( uuid, exoMediaDrm, @@ -578,6 +570,7 @@ public class DefaultDrmSessionManager implements DrmSe /* releaseCallback= */ this::onSessionReleased, schemeDatas, mode, + playClearSamplesWithoutKeys, isPlaceholderSession, offlineLicenseKeySetId, keyRequestParameters, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java index 13e29e141a..6c2fdecd01 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSession.java @@ -93,6 +93,11 @@ public interface DrmSession { */ @State int getState(); + /** Returns whether this session allows playback of clear samples prior to keys being loaded. */ + default boolean playClearSamplesWithoutKeys() { + return false; + } + /** * Returns the cause of the error state, or null if {@link #getState()} is not {@link * #STATE_ERROR}. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java index c92d68ed17..4aef731558 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DrmSessionManager.java @@ -16,13 +16,9 @@ package com.google.android.exoplayer2.drm; import android.os.Looper; -import androidx.annotation.IntDef; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; /** * Manages a DRM session. @@ -59,26 +55,6 @@ public interface DrmSessionManager { } }; - /** Flags that control the handling of DRM protected content. */ - @Documented - @Retention(RetentionPolicy.SOURCE) - @IntDef( - flag = true, - value = {FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS}) - @interface Flags {} - - /** - * When this flag is set, clear samples of an encrypted region may be rendered when no keys are - * available. - * - *

Encrypted media may contain clear (un-encrypted) regions. For example a media file may start - * with a short clear region so as to allow playback to begin in parallel with key acquisition. - * When this flag is set, consumers of sample data are permitted to access the clear regions of - * encrypted media files when the associated {@link DrmSession} has not yet obtained the keys - * necessary for the encrypted regions of the media. - */ - int FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS = 1; - /** * Acquires any required resources. * @@ -136,12 +112,6 @@ public interface DrmSessionManager { */ DrmSession acquireSession(Looper playbackLooper, DrmInitData drmInitData); - /** Returns flags that control the handling of DRM protected content. */ - @Flags - default int getFlags() { - return 0; - } - /** * Returns the {@link ExoMediaCrypto} type returned by sessions acquired using the given {@link * DrmInitData}, or null if a session cannot be acquired with the given {@link DrmInitData}. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/ErrorStateDrmSession.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/ErrorStateDrmSession.java index aa15c82900..0028e47987 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/drm/ErrorStateDrmSession.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/ErrorStateDrmSession.java @@ -33,6 +33,11 @@ public final class ErrorStateDrmSession implements Drm return STATE_ERROR; } + @Override + public boolean playClearSamplesWithoutKeys() { + return false; + } + @Override @Nullable public DrmSessionException getError() { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleMetadataQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleMetadataQueue.java index 0cc576a145..22061f58ef 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/SampleMetadataQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/SampleMetadataQueue.java @@ -49,7 +49,6 @@ import java.io.IOException; private static final int SAMPLE_CAPACITY_INCREMENT = 1000; private final DrmSessionManager drmSessionManager; - private final boolean playClearSamplesWithoutKeys; @Nullable private Format downstreamFormat; @Nullable private DrmSession currentDrmSession; @@ -79,9 +78,6 @@ import java.io.IOException; public SampleMetadataQueue(DrmSessionManager drmSessionManager) { this.drmSessionManager = drmSessionManager; - playClearSamplesWithoutKeys = - (drmSessionManager.getFlags() & DrmSessionManager.FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS) - != 0; capacity = SAMPLE_CAPACITY_INCREMENT; sourceIds = new int[capacity]; offsets = new long[capacity]; @@ -282,7 +278,7 @@ import java.io.IOException; } else { // A clear sample in an encrypted section may be read if playClearSamplesWithoutKeys is true. return (flags[relativeReadIndex] & C.BUFFER_FLAG_ENCRYPTED) == 0 - && playClearSamplesWithoutKeys; + && Assertions.checkNotNull(currentDrmSession).playClearSamplesWithoutKeys(); } } @@ -341,7 +337,8 @@ import java.io.IOException; boolean mayReadSample = skipDrmChecks || Util.castNonNull(downstreamFormat).drmInitData == null - || (playClearSamplesWithoutKeys && !isNextSampleEncrypted) + || (Assertions.checkNotNull(currentDrmSession).playClearSamplesWithoutKeys() + && !isNextSampleEncrypted) || Assertions.checkNotNull(currentDrmSession).getState() == DrmSession.STATE_OPENED_WITH_KEYS; if (!mayReadSample) { 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 df6ccc1f02..441ac9e05a 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 @@ -331,8 +331,7 @@ public final class SampleQueueTest { @Test public void testIsReadyReturnsTrueForClearSampleAndPlayClearSamplesWithoutKeysIsTrue() { - when(mockDrmSessionManager.getFlags()) - .thenReturn(DrmSessionManager.FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS); + when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true); // We recreate the queue to ensure the mock DRM session manager flags are taken into account. sampleQueue = new SampleQueue(allocator, mockDrmSessionManager); writeTestDataWithEncryptedSections(); @@ -458,8 +457,7 @@ public final class SampleQueueTest { @Test public void testAllowPlayClearSamplesWithoutKeysReadsClearSamples() { - when(mockDrmSessionManager.getFlags()) - .thenReturn(DrmSessionManager.FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS); + when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true); // We recreate the queue to ensure the mock DRM session manager flags are taken into account. sampleQueue = new SampleQueue(allocator, mockDrmSessionManager); when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED);