Commit cc520a67 by olly Committed by Oliver Woodman

Simplify playback of clear samples without keys

- Move property to DrmSession; it feels like a more natural place
  for it to go (and provides greater flexibility).
- Change flags to a boolean.

PiperOrigin-RevId: 281758729
parent 9be09b35
...@@ -106,6 +106,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -106,6 +106,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private final ProvisioningManager<T> provisioningManager; private final ProvisioningManager<T> provisioningManager;
private final ReleaseCallback<T> releaseCallback; private final ReleaseCallback<T> releaseCallback;
private final @DefaultDrmSessionManager.Mode int mode; private final @DefaultDrmSessionManager.Mode int mode;
private final boolean playClearSamplesWithoutKeys;
private final boolean isPlaceholderSession; private final boolean isPlaceholderSession;
private final HashMap<String, String> keyRequestParameters; private final HashMap<String, String> keyRequestParameters;
private final EventDispatcher<DefaultDrmSessionEventListener> eventDispatcher; private final EventDispatcher<DefaultDrmSessionEventListener> eventDispatcher;
...@@ -154,6 +155,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -154,6 +155,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
ReleaseCallback<T> releaseCallback, ReleaseCallback<T> releaseCallback,
@Nullable List<SchemeData> schemeDatas, @Nullable List<SchemeData> schemeDatas,
@DefaultDrmSessionManager.Mode int mode, @DefaultDrmSessionManager.Mode int mode,
boolean playClearSamplesWithoutKeys,
boolean isPlaceholderSession, boolean isPlaceholderSession,
@Nullable byte[] offlineLicenseKeySetId, @Nullable byte[] offlineLicenseKeySetId,
HashMap<String, String> keyRequestParameters, HashMap<String, String> keyRequestParameters,
...@@ -170,6 +172,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -170,6 +172,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
this.releaseCallback = releaseCallback; this.releaseCallback = releaseCallback;
this.mediaDrm = mediaDrm; this.mediaDrm = mediaDrm;
this.mode = mode; this.mode = mode;
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
this.isPlaceholderSession = isPlaceholderSession; this.isPlaceholderSession = isPlaceholderSession;
if (offlineLicenseKeySetId != null) { if (offlineLicenseKeySetId != null) {
this.offlineLicenseKeySetId = offlineLicenseKeySetId; this.offlineLicenseKeySetId = offlineLicenseKeySetId;
...@@ -229,6 +232,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -229,6 +232,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} }
@Override @Override
public boolean playClearSamplesWithoutKeys() {
return playClearSamplesWithoutKeys;
}
@Override
public final @Nullable DrmSessionException getError() { public final @Nullable DrmSessionException getError() {
return state == STATE_ERROR ? lastException : null; return state == STATE_ERROR ? lastException : null;
} }
......
...@@ -58,7 +58,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe ...@@ -58,7 +58,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
private ExoMediaDrm.Provider<ExoMediaCrypto> exoMediaDrmProvider; private ExoMediaDrm.Provider<ExoMediaCrypto> exoMediaDrmProvider;
private boolean multiSession; private boolean multiSession;
private int[] useDrmSessionsForClearContentTrackTypes; private int[] useDrmSessionsForClearContentTrackTypes;
@Flags private int flags; private boolean playClearSamplesWithoutKeys;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy; private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
/** /**
...@@ -164,11 +164,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe ...@@ -164,11 +164,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
* @return This builder. * @return This builder.
*/ */
public Builder setPlayClearSamplesWithoutKeys(boolean playClearSamplesWithoutKeys) { public Builder setPlayClearSamplesWithoutKeys(boolean playClearSamplesWithoutKeys) {
if (playClearSamplesWithoutKeys) { this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
this.flags |= FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS;
} else {
this.flags &= ~FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS;
}
return this; return this;
} }
...@@ -192,7 +188,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe ...@@ -192,7 +188,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
keyRequestParameters, keyRequestParameters,
multiSession, multiSession,
useDrmSessionsForClearContentTrackTypes, useDrmSessionsForClearContentTrackTypes,
flags, playClearSamplesWithoutKeys,
loadErrorHandlingPolicy); loadErrorHandlingPolicy);
} }
} }
...@@ -245,7 +241,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe ...@@ -245,7 +241,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
private final EventDispatcher<DefaultDrmSessionEventListener> eventDispatcher; private final EventDispatcher<DefaultDrmSessionEventListener> eventDispatcher;
private final boolean multiSession; private final boolean multiSession;
private final int[] useDrmSessionsForClearContentTrackTypes; private final int[] useDrmSessionsForClearContentTrackTypes;
@Flags private final int flags; private final boolean playClearSamplesWithoutKeys;
private final ProvisioningManagerImpl provisioningManagerImpl; private final ProvisioningManagerImpl provisioningManagerImpl;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy; private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
...@@ -339,7 +335,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe ...@@ -339,7 +335,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
keyRequestParameters == null ? new HashMap<>() : keyRequestParameters, keyRequestParameters == null ? new HashMap<>() : keyRequestParameters,
multiSession, multiSession,
/* useDrmSessionsForClearContentTrackTypes= */ new int[0], /* useDrmSessionsForClearContentTrackTypes= */ new int[0],
/* flags= */ 0, /* playClearSamplesWithoutKeys= */ false,
new DefaultLoadErrorHandlingPolicy(initialDrmRequestRetryCount)); new DefaultLoadErrorHandlingPolicy(initialDrmRequestRetryCount));
} }
...@@ -352,7 +348,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe ...@@ -352,7 +348,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
HashMap<String, String> keyRequestParameters, HashMap<String, String> keyRequestParameters,
boolean multiSession, boolean multiSession,
int[] useDrmSessionsForClearContentTrackTypes, int[] useDrmSessionsForClearContentTrackTypes,
@Flags int flags, boolean playClearSamplesWithoutKeys,
LoadErrorHandlingPolicy loadErrorHandlingPolicy) { LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
Assertions.checkNotNull(uuid); Assertions.checkNotNull(uuid);
Assertions.checkArgument(!C.COMMON_PSSH_UUID.equals(uuid), "Use C.CLEARKEY_UUID instead"); Assertions.checkArgument(!C.COMMON_PSSH_UUID.equals(uuid), "Use C.CLEARKEY_UUID instead");
...@@ -363,7 +359,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe ...@@ -363,7 +359,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
this.eventDispatcher = new EventDispatcher<>(); this.eventDispatcher = new EventDispatcher<>();
this.multiSession = multiSession; this.multiSession = multiSession;
this.useDrmSessionsForClearContentTrackTypes = useDrmSessionsForClearContentTrackTypes; this.useDrmSessionsForClearContentTrackTypes = useDrmSessionsForClearContentTrackTypes;
this.flags = flags; this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
provisioningManagerImpl = new ProvisioningManagerImpl(); provisioningManagerImpl = new ProvisioningManagerImpl();
mode = MODE_PLAYBACK; mode = MODE_PLAYBACK;
...@@ -542,12 +538,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe ...@@ -542,12 +538,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
} }
@Override @Override
@Flags
public final int getFlags() {
return flags;
}
@Override
@Nullable @Nullable
public Class<T> getExoMediaCryptoType(DrmInitData drmInitData) { public Class<T> getExoMediaCryptoType(DrmInitData drmInitData) {
return canAcquireSession(drmInitData) return canAcquireSession(drmInitData)
...@@ -571,6 +561,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe ...@@ -571,6 +561,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
private DefaultDrmSession<T> createNewDefaultSession( private DefaultDrmSession<T> createNewDefaultSession(
@Nullable List<SchemeData> schemeDatas, boolean isPlaceholderSession) { @Nullable List<SchemeData> schemeDatas, boolean isPlaceholderSession) {
Assertions.checkNotNull(exoMediaDrm); Assertions.checkNotNull(exoMediaDrm);
// Placeholder sessions should always play clear samples without keys.
boolean playClearSamplesWithoutKeys = this.playClearSamplesWithoutKeys | isPlaceholderSession;
return new DefaultDrmSession<>( return new DefaultDrmSession<>(
uuid, uuid,
exoMediaDrm, exoMediaDrm,
...@@ -578,6 +570,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe ...@@ -578,6 +570,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
/* releaseCallback= */ this::onSessionReleased, /* releaseCallback= */ this::onSessionReleased,
schemeDatas, schemeDatas,
mode, mode,
playClearSamplesWithoutKeys,
isPlaceholderSession, isPlaceholderSession,
offlineLicenseKeySetId, offlineLicenseKeySetId,
keyRequestParameters, keyRequestParameters,
......
...@@ -93,6 +93,11 @@ public interface DrmSession<T extends ExoMediaCrypto> { ...@@ -93,6 +93,11 @@ public interface DrmSession<T extends ExoMediaCrypto> {
*/ */
@State int getState(); @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 * Returns the cause of the error state, or null if {@link #getState()} is not {@link
* #STATE_ERROR}. * #STATE_ERROR}.
......
...@@ -16,13 +16,9 @@ ...@@ -16,13 +16,9 @@
package com.google.android.exoplayer2.drm; package com.google.android.exoplayer2.drm;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.IntDef;
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 java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** /**
* Manages a DRM session. * Manages a DRM session.
...@@ -59,26 +55,6 @@ public interface DrmSessionManager<T extends ExoMediaCrypto> { ...@@ -59,26 +55,6 @@ public interface DrmSessionManager<T extends ExoMediaCrypto> {
} }
}; };
/** 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.
*
* <p>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. * Acquires any required resources.
* *
...@@ -136,12 +112,6 @@ public interface DrmSessionManager<T extends ExoMediaCrypto> { ...@@ -136,12 +112,6 @@ public interface DrmSessionManager<T extends ExoMediaCrypto> {
*/ */
DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData); DrmSession<T> 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 * 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}. * DrmInitData}, or null if a session cannot be acquired with the given {@link DrmInitData}.
......
...@@ -34,6 +34,11 @@ public final class ErrorStateDrmSession<T extends ExoMediaCrypto> implements Drm ...@@ -34,6 +34,11 @@ public final class ErrorStateDrmSession<T extends ExoMediaCrypto> implements Drm
} }
@Override @Override
public boolean playClearSamplesWithoutKeys() {
return false;
}
@Override
@Nullable @Nullable
public DrmSessionException getError() { public DrmSessionException getError() {
return error; return error;
......
...@@ -49,7 +49,6 @@ import java.io.IOException; ...@@ -49,7 +49,6 @@ import java.io.IOException;
private static final int SAMPLE_CAPACITY_INCREMENT = 1000; private static final int SAMPLE_CAPACITY_INCREMENT = 1000;
private final DrmSessionManager<?> drmSessionManager; private final DrmSessionManager<?> drmSessionManager;
private final boolean playClearSamplesWithoutKeys;
@Nullable private Format downstreamFormat; @Nullable private Format downstreamFormat;
@Nullable private DrmSession<?> currentDrmSession; @Nullable private DrmSession<?> currentDrmSession;
...@@ -79,9 +78,6 @@ import java.io.IOException; ...@@ -79,9 +78,6 @@ import java.io.IOException;
public SampleMetadataQueue(DrmSessionManager<?> drmSessionManager) { public SampleMetadataQueue(DrmSessionManager<?> drmSessionManager) {
this.drmSessionManager = drmSessionManager; this.drmSessionManager = drmSessionManager;
playClearSamplesWithoutKeys =
(drmSessionManager.getFlags() & DrmSessionManager.FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS)
!= 0;
capacity = SAMPLE_CAPACITY_INCREMENT; capacity = SAMPLE_CAPACITY_INCREMENT;
sourceIds = new int[capacity]; sourceIds = new int[capacity];
offsets = new long[capacity]; offsets = new long[capacity];
...@@ -282,7 +278,7 @@ import java.io.IOException; ...@@ -282,7 +278,7 @@ import java.io.IOException;
} else { } else {
// A clear sample in an encrypted section may be read if playClearSamplesWithoutKeys is true. // A clear sample in an encrypted section may be read if playClearSamplesWithoutKeys is true.
return (flags[relativeReadIndex] & C.BUFFER_FLAG_ENCRYPTED) == 0 return (flags[relativeReadIndex] & C.BUFFER_FLAG_ENCRYPTED) == 0
&& playClearSamplesWithoutKeys; && Assertions.checkNotNull(currentDrmSession).playClearSamplesWithoutKeys();
} }
} }
...@@ -341,7 +337,8 @@ import java.io.IOException; ...@@ -341,7 +337,8 @@ import java.io.IOException;
boolean mayReadSample = boolean mayReadSample =
skipDrmChecks skipDrmChecks
|| Util.castNonNull(downstreamFormat).drmInitData == null || Util.castNonNull(downstreamFormat).drmInitData == null
|| (playClearSamplesWithoutKeys && !isNextSampleEncrypted) || (Assertions.checkNotNull(currentDrmSession).playClearSamplesWithoutKeys()
&& !isNextSampleEncrypted)
|| Assertions.checkNotNull(currentDrmSession).getState() || Assertions.checkNotNull(currentDrmSession).getState()
== DrmSession.STATE_OPENED_WITH_KEYS; == DrmSession.STATE_OPENED_WITH_KEYS;
if (!mayReadSample) { if (!mayReadSample) {
......
...@@ -331,8 +331,7 @@ public final class SampleQueueTest { ...@@ -331,8 +331,7 @@ public final class SampleQueueTest {
@Test @Test
public void testIsReadyReturnsTrueForClearSampleAndPlayClearSamplesWithoutKeysIsTrue() { public void testIsReadyReturnsTrueForClearSampleAndPlayClearSamplesWithoutKeysIsTrue() {
when(mockDrmSessionManager.getFlags()) when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true);
.thenReturn(DrmSessionManager.FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS);
// 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);
writeTestDataWithEncryptedSections(); writeTestDataWithEncryptedSections();
...@@ -458,8 +457,7 @@ public final class SampleQueueTest { ...@@ -458,8 +457,7 @@ public final class SampleQueueTest {
@Test @Test
public void testAllowPlayClearSamplesWithoutKeysReadsClearSamples() { public void testAllowPlayClearSamplesWithoutKeysReadsClearSamples() {
when(mockDrmSessionManager.getFlags()) when(mockDrmSession.playClearSamplesWithoutKeys()).thenReturn(true);
.thenReturn(DrmSessionManager.FLAG_PLAY_CLEAR_SAMPLES_WITHOUT_KEYS);
// 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);
when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED); when(mockDrmSession.getState()).thenReturn(DrmSession.STATE_OPENED);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment