mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
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:
parent
9be09b35e2
commit
cc520a670e
7 changed files with 33 additions and 57 deletions
|
|
@ -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;
|
||||||
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;
|
||||||
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;
|
||||||
|
|
@ -228,6 +231,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean playClearSamplesWithoutKeys() {
|
||||||
|
return playClearSamplesWithoutKeys;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
||||||
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
|
||||||
* @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
|
||||||
keyRequestParameters,
|
keyRequestParameters,
|
||||||
multiSession,
|
multiSession,
|
||||||
useDrmSessionsForClearContentTrackTypes,
|
useDrmSessionsForClearContentTrackTypes,
|
||||||
flags,
|
playClearSamplesWithoutKeys,
|
||||||
loadErrorHandlingPolicy);
|
loadErrorHandlingPolicy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
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
|
||||||
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
|
||||||
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;
|
||||||
|
|
@ -541,12 +537,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Flags
|
|
||||||
public final int getFlags() {
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Class<T> getExoMediaCryptoType(DrmInitData drmInitData) {
|
public Class<T> getExoMediaCryptoType(DrmInitData drmInitData) {
|
||||||
|
|
@ -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
|
||||||
/* 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> {
|
||||||
*/
|
*/
|
||||||
@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 @@
|
||||||
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> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 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> {
|
||||||
*/
|
*/
|
||||||
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}.
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,11 @@ public final class ErrorStateDrmSession<T extends ExoMediaCrypto> implements Drm
|
||||||
return STATE_ERROR;
|
return STATE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean playClearSamplesWithoutKeys() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public DrmSessionException getError() {
|
public DrmSessionException getError() {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
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;
|
||||||
} 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;
|
||||||
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 {
|
||||||
|
|
||||||
@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 {
|
||||||
|
|
||||||
@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);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue