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 d7324249e0b..7104e4bad7b 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 @@ -87,7 +87,7 @@ private MissingSchemeDataException(UUID uuid) { private static final String TAG = "DefaultDrmSessionMgr"; private final UUID uuid; - private final ExoMediaDrm mediaDrm; + private final ExoMediaDrm.Provider exoMediaDrmProvider; private final MediaDrmCallback callback; @Nullable private final HashMap optionalKeyRequestParameters; private final EventDispatcher eventDispatcher; @@ -98,6 +98,8 @@ private MissingSchemeDataException(UUID uuid) { private final List> sessions; private final List> provisioningSessions; + private int prepareCallsCount; + @Nullable private ExoMediaDrm exoMediaDrm; @Nullable private DefaultDrmSession placeholderDrmSession; @Nullable private Looper playbackLooper; private int mode; @@ -107,19 +109,19 @@ private MissingSchemeDataException(UUID uuid) { /** * @param uuid The UUID of the drm scheme. - * @param mediaDrm An underlying {@link ExoMediaDrm} for use by the manager. + * @param exoMediaDrm An underlying {@link ExoMediaDrm} for use by the manager. * @param callback Performs key and provisioning requests. * @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument * to {@link ExoMediaDrm#getKeyRequest(byte[], List, int, HashMap)}. May be null. */ public DefaultDrmSessionManager( UUID uuid, - ExoMediaDrm mediaDrm, + ExoMediaDrm exoMediaDrm, MediaDrmCallback callback, @Nullable HashMap optionalKeyRequestParameters) { this( uuid, - mediaDrm, + exoMediaDrm, callback, optionalKeyRequestParameters, /* multiSession= */ false, @@ -128,7 +130,7 @@ public DefaultDrmSessionManager( /** * @param uuid The UUID of the drm scheme. - * @param mediaDrm An underlying {@link ExoMediaDrm} for use by the manager. + * @param exoMediaDrm An underlying {@link ExoMediaDrm} for use by the manager. * @param callback Performs key and provisioning requests. * @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument * to {@link ExoMediaDrm#getKeyRequest(byte[], List, int, HashMap)}. May be null. @@ -137,13 +139,13 @@ public DefaultDrmSessionManager( */ public DefaultDrmSessionManager( UUID uuid, - ExoMediaDrm mediaDrm, + ExoMediaDrm exoMediaDrm, MediaDrmCallback callback, @Nullable HashMap optionalKeyRequestParameters, boolean multiSession) { this( uuid, - mediaDrm, + exoMediaDrm, callback, optionalKeyRequestParameters, multiSession, @@ -152,7 +154,7 @@ public DefaultDrmSessionManager( /** * @param uuid The UUID of the drm scheme. - * @param mediaDrm An underlying {@link ExoMediaDrm} for use by the manager. + * @param exoMediaDrm An underlying {@link ExoMediaDrm} for use by the manager. * @param callback Performs key and provisioning requests. * @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument * to {@link ExoMediaDrm#getKeyRequest(byte[], List, int, HashMap)}. May be null. @@ -163,14 +165,14 @@ public DefaultDrmSessionManager( */ public DefaultDrmSessionManager( UUID uuid, - ExoMediaDrm mediaDrm, + ExoMediaDrm exoMediaDrm, MediaDrmCallback callback, @Nullable HashMap optionalKeyRequestParameters, boolean multiSession, int initialDrmRequestRetryCount) { this( uuid, - mediaDrm, + new ExoMediaDrm.AppManagedProvider<>(exoMediaDrm), callback, optionalKeyRequestParameters, multiSession, @@ -180,38 +182,26 @@ public DefaultDrmSessionManager( private DefaultDrmSessionManager( UUID uuid, - ExoMediaDrm mediaDrm, + ExoMediaDrm.Provider exoMediaDrmProvider, MediaDrmCallback callback, @Nullable HashMap optionalKeyRequestParameters, boolean multiSession, boolean allowPlaceholderSessions, LoadErrorHandlingPolicy loadErrorHandlingPolicy) { Assertions.checkNotNull(uuid); - Assertions.checkNotNull(mediaDrm); Assertions.checkArgument(!C.COMMON_PSSH_UUID.equals(uuid), "Use C.CLEARKEY_UUID instead"); this.uuid = uuid; - this.mediaDrm = mediaDrm; + this.exoMediaDrmProvider = exoMediaDrmProvider; this.callback = callback; this.optionalKeyRequestParameters = optionalKeyRequestParameters; this.eventDispatcher = new EventDispatcher<>(); this.multiSession = multiSession; - boolean canAcquirePlaceholderSessions = - !FrameworkMediaCrypto.class.equals(mediaDrm.getExoMediaCryptoType()) - || !FrameworkMediaCrypto.WORKAROUND_DEVICE_NEEDS_KEYS_TO_CONFIGURE_CODEC; // TODO: Allow customization once this class has a Builder. - this.allowPlaceholderSessions = canAcquirePlaceholderSessions && allowPlaceholderSessions; + this.allowPlaceholderSessions = allowPlaceholderSessions; this.loadErrorHandlingPolicy = loadErrorHandlingPolicy; mode = MODE_PLAYBACK; sessions = new ArrayList<>(); provisioningSessions = new ArrayList<>(); - if (multiSession && C.WIDEVINE_UUID.equals(uuid) && Util.SDK_INT >= 19) { - // TODO: Enabling session sharing probably doesn't do anything useful here. It would only be - // useful if DefaultDrmSession instances were aware of one another's state, which is not - // implemented. Or if custom renderers are being used that allow playback to proceed before - // keys, which seems unlikely to be true in practice. - mediaDrm.setPropertyString("sessionSharing", "enable"); - } - mediaDrm.setOnEventListener(new MediaDrmEventListener()); } /** @@ -268,6 +258,30 @@ public void setMode(@Mode int mode, @Nullable byte[] offlineLicenseKeySetId) { // DrmSessionManager implementation. + @Override + public final void prepare() { + if (prepareCallsCount++ == 0) { + Assertions.checkState(exoMediaDrm == null); + exoMediaDrm = exoMediaDrmProvider.acquireExoMediaDrm(uuid); + if (multiSession && C.WIDEVINE_UUID.equals(uuid) && Util.SDK_INT >= 19) { + // TODO: Enabling session sharing probably doesn't do anything useful here. It would only be + // useful if DefaultDrmSession instances were aware of one another's state, which is not + // implemented. Or if custom renderers are being used that allow playback to proceed before + // keys, which seems unlikely to be true in practice. + exoMediaDrm.setPropertyString("sessionSharing", "enable"); + } + exoMediaDrm.setOnEventListener(new MediaDrmEventListener()); + } + } + + @Override + public final void release() { + if (--prepareCallsCount == 0) { + Assertions.checkNotNull(exoMediaDrm).release(); + exoMediaDrm = null; + } + } + @Override public boolean canAcquireSession(DrmInitData drmInitData) { if (offlineLicenseKeySetId != null) { @@ -304,7 +318,13 @@ public boolean canAcquireSession(DrmInitData drmInitData) { @Nullable public DrmSession acquirePlaceholderSession(Looper playbackLooper) { assertExpectedPlaybackLooper(playbackLooper); - if (!allowPlaceholderSessions || mediaDrm.getExoMediaCryptoType() == null) { + Assertions.checkNotNull(exoMediaDrm); + boolean avoidPlaceholderDrmSessions = + FrameworkMediaCrypto.class.equals(exoMediaDrm.getExoMediaCryptoType()) + && FrameworkMediaCrypto.WORKAROUND_DEVICE_NEEDS_KEYS_TO_CONFIGURE_CODEC; + if (avoidPlaceholderDrmSessions + || !allowPlaceholderSessions + || exoMediaDrm.getExoMediaCryptoType() == null) { return null; } maybeCreateMediaDrmHandler(playbackLooper); @@ -359,7 +379,9 @@ public DrmSession acquireSession(Looper playbackLooper, DrmInitData drmInitDa @Override @Nullable public Class getExoMediaCryptoType(DrmInitData drmInitData) { - return canAcquireSession(drmInitData) ? mediaDrm.getExoMediaCryptoType() : null; + return canAcquireSession(drmInitData) + ? Assertions.checkNotNull(exoMediaDrm).getExoMediaCryptoType() + : null; } // ProvisioningManager implementation. @@ -408,9 +430,10 @@ private void maybeCreateMediaDrmHandler(Looper playbackLooper) { private DefaultDrmSession createNewDefaultSession( @Nullable List schemeDatas, boolean isPlaceholderSession) { + Assertions.checkNotNull(exoMediaDrm); return new DefaultDrmSession<>( uuid, - mediaDrm, + exoMediaDrm, /* provisioningManager= */ this, /* releaseCallback= */ this::onSessionReleased, schemeDatas, 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 d30b782b30b..79dc743bc97 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 @@ -201,6 +201,7 @@ public synchronized void releaseLicense(byte[] offlineLicenseKeySetId) public synchronized Pair getLicenseDurationRemainingSec(byte[] offlineLicenseKeySetId) throws DrmSessionException { Assertions.checkNotNull(offlineLicenseKeySetId); + drmSessionManager.prepare(); DrmSession drmSession = openBlockingKeyRequest( DefaultDrmSessionManager.MODE_QUERY, offlineLicenseKeySetId, DUMMY_DRM_INIT_DATA); @@ -208,6 +209,7 @@ public synchronized Pair getLicenseDurationRemainingSec(byte[] offli Pair licenseDurationRemainingSec = WidevineUtil.getLicenseDurationRemainingSec(drmSession); drmSession.releaseReference(); + drmSessionManager.release(); if (error != null) { if (error.getCause() instanceof KeysExpiredException) { return Pair.create(0L, 0L); @@ -227,11 +229,13 @@ public void release() { private byte[] blockingKeyRequest( @Mode int licenseMode, @Nullable byte[] offlineLicenseKeySetId, DrmInitData drmInitData) throws DrmSessionException { + drmSessionManager.prepare(); DrmSession drmSession = openBlockingKeyRequest(licenseMode, offlineLicenseKeySetId, drmInitData); DrmSessionException error = drmSession.getError(); byte[] keySetId = drmSession.getOfflineLicenseKeySetId(); drmSession.releaseReference(); + drmSessionManager.release(); if (error != null) { throw error; }