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
This commit is contained in:
olly 2019-11-21 16:50:24 +00:00 committed by Oliver Woodman
parent a7d962bf27
commit ab8816214e
7 changed files with 33 additions and 57 deletions

View File

@ -106,6 +106,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private final ProvisioningManager<T> provisioningManager;
private final ReleaseCallback<T> releaseCallback;
private final @DefaultDrmSessionManager.Mode int mode;
private final boolean playClearSamplesWithoutKeys;
private final boolean isPlaceholderSession;
private final HashMap<String, String> keyRequestParameters;
private final EventDispatcher<DefaultDrmSessionEventListener> eventDispatcher;
@ -154,6 +155,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
ReleaseCallback<T> releaseCallback,
@Nullable List<SchemeData> schemeDatas,
@DefaultDrmSessionManager.Mode int mode,
boolean playClearSamplesWithoutKeys,
boolean isPlaceholderSession,
@Nullable byte[] offlineLicenseKeySetId,
HashMap<String, String> 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;

View File

@ -58,7 +58,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
private ExoMediaDrm.Provider<ExoMediaCrypto> exoMediaDrmProvider;
private boolean multiSession;
private int[] useDrmSessionsForClearContentTrackTypes;
@Flags private int flags;
private boolean playClearSamplesWithoutKeys;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
/**
@ -164,11 +164,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> 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<T extends ExoMediaCrypto> implements DrmSe
keyRequestParameters,
multiSession,
useDrmSessionsForClearContentTrackTypes,
flags,
playClearSamplesWithoutKeys,
loadErrorHandlingPolicy);
}
}
@ -245,7 +241,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
private final EventDispatcher<DefaultDrmSessionEventListener> 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<T extends ExoMediaCrypto> 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<T extends ExoMediaCrypto> implements DrmSe
HashMap<String, String> 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<T extends ExoMediaCrypto> 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<T extends ExoMediaCrypto> implements DrmSe
return session;
}
@Override
@Flags
public final int getFlags() {
return flags;
}
@Override
@Nullable
public Class<T> getExoMediaCryptoType(DrmInitData drmInitData) {
@ -571,6 +561,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
private DefaultDrmSession<T> createNewDefaultSession(
@Nullable List<SchemeData> 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<T extends ExoMediaCrypto> implements DrmSe
/* releaseCallback= */ this::onSessionReleased,
schemeDatas,
mode,
playClearSamplesWithoutKeys,
isPlaceholderSession,
offlineLicenseKeySetId,
keyRequestParameters,

View File

@ -93,6 +93,11 @@ public interface DrmSession<T extends ExoMediaCrypto> {
*/
@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}.

View File

@ -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<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.
*
@ -136,12 +112,6 @@ public interface DrmSessionManager<T extends ExoMediaCrypto> {
*/
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
* DrmInitData}, or null if a session cannot be acquired with the given {@link DrmInitData}.

View File

@ -33,6 +33,11 @@ public final class ErrorStateDrmSession<T extends ExoMediaCrypto> implements Drm
return STATE_ERROR;
}
@Override
public boolean playClearSamplesWithoutKeys() {
return false;
}
@Override
@Nullable
public DrmSessionException getError() {

View File

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

View File

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