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 ca7a657a0f..47483a027d 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
@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.drm;
import static com.google.android.exoplayer2.util.Assertions.checkState;
+import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
import static java.lang.Math.min;
import android.annotation.SuppressLint;
@@ -285,6 +286,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
return offlineLicenseKeySetId;
}
+ @Override
+ public boolean requiresSecureDecoder(String mimeType) {
+ return mediaDrm.requiresSecureDecoder(checkStateNotNull(sessionId), mimeType);
+ }
+
@Override
public void acquire(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) {
checkState(referenceCount >= 0);
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 b993345613..7f07fd9c66 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
@@ -137,6 +137,15 @@ public interface DrmSession {
@Nullable
byte[] getOfflineLicenseKeySetId();
+ /**
+ * Returns whether this session requires use of a secure decoder for the given MIME type. Assumes
+ * a license policy that requires the highest level of security supported by the session.
+ *
+ *
The session must be in {@link #getState() state} {@link #STATE_OPENED} or {@link
+ * #STATE_OPENED_WITH_KEYS}.
+ */
+ boolean requiresSecureDecoder(String mimeType);
+
/**
* Increments the reference count. When the caller no longer needs to use the instance, it must
* call {@link #release(DrmSessionEventListener.EventDispatcher)} to decrement the reference
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/DummyExoMediaDrm.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/DummyExoMediaDrm.java
index 9631b76491..b5c7692ab8 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/drm/DummyExoMediaDrm.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/DummyExoMediaDrm.java
@@ -93,6 +93,12 @@ public final class DummyExoMediaDrm implements ExoMediaDrm {
throw new IllegalStateException();
}
+ @Override
+ public boolean requiresSecureDecoder(byte[] sessionId, String mimeType) {
+ // Should not be invoked. No session should exist.
+ throw new IllegalStateException();
+ }
+
@Override
public void acquire() {
// Do nothing.
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 068f1b3782..51cb019432 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
@@ -69,6 +69,11 @@ public final class ErrorStateDrmSession implements DrmSession {
return null;
}
+ @Override
+ public boolean requiresSecureDecoder(String mimeType) {
+ return false;
+ }
+
@Override
public void acquire(@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher) {
// Do nothing.
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/ExoMediaDrm.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/ExoMediaDrm.java
index b20fc916c4..583fcf979a 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/drm/ExoMediaDrm.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/ExoMediaDrm.java
@@ -460,6 +460,15 @@ public interface ExoMediaDrm {
*/
Map queryKeyStatus(byte[] sessionId);
+ /**
+ * Returns whether the given session requires use of a secure decoder for the given MIME type.
+ * Assumes a license policy that requires the highest level of security supported by the session.
+ *
+ * @param sessionId The ID of the session.
+ * @param mimeType The content MIME type to query.
+ */
+ boolean requiresSecureDecoder(byte[] sessionId, String mimeType);
+
/**
* Increments the reference count. When the caller no longer needs to use the instance, it must
* call {@link #release()} to decrement the reference count.
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaDrm.java b/library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaDrm.java
index 48255714bd..8c2b1c3a62 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaDrm.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaDrm.java
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm;
import android.annotation.SuppressLint;
import android.media.DeniedByServerException;
+import android.media.MediaCrypto;
import android.media.MediaCryptoException;
import android.media.MediaDrm;
import android.media.MediaDrmException;
@@ -24,6 +25,7 @@ import android.media.NotProvisionedException;
import android.media.UnsupportedSchemeException;
import android.os.PersistableBundle;
import android.text.TextUtils;
+import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C;
@@ -244,6 +246,26 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
return mediaDrm.queryKeyStatus(sessionId);
}
+ @Override
+ public boolean requiresSecureDecoder(byte[] sessionId, String mimeType) {
+ if (Util.SDK_INT >= 31) {
+ return Api31.requiresSecureDecoder(mediaDrm, mimeType);
+ }
+
+ MediaCrypto mediaCrypto;
+ try {
+ mediaCrypto = new MediaCrypto(uuid, sessionId);
+ } catch (MediaCryptoException e) {
+ // This shouldn't happen, but if it does then assume that a secure decoder may be required.
+ return true;
+ }
+ try {
+ return mediaCrypto.requiresSecureDecoderComponent(mimeType);
+ } finally {
+ mediaCrypto.release();
+ }
+ }
+
@Override
public synchronized void acquire() {
Assertions.checkState(referenceCount > 0);
@@ -476,4 +498,12 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
newData.put(xmlWithMockLaUrl.getBytes(Charsets.UTF_16LE));
return newData.array();
}
+
+ @RequiresApi(31)
+ private static class Api31 {
+ @DoNotInline
+ public static boolean requiresSecureDecoder(MediaDrm mediaDrm, String mimeType) {
+ return mediaDrm.requiresSecureDecoder(mimeType);
+ }
+ }
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
index 9d689faa03..2100847c7b 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
@@ -2099,7 +2099,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
// the case is to occur, so we re-initialize in this case.
return true;
}
- if (!codecInfo.secure && maybeRequiresSecureDecoder(newMediaCrypto, newFormat)) {
+
+ boolean requiresSecureDecoder;
+ if (newMediaCrypto.forceAllowInsecureDecoderComponents) {
+ requiresSecureDecoder = false;
+ } else {
+ requiresSecureDecoder = newSession.requiresSecureDecoder(newFormat.sampleMimeType);
+ }
+ if (!codecInfo.secure && requiresSecureDecoder) {
// Re-initialization is required because newSession might require switching to the secure
// output path.
return true;
@@ -2108,32 +2115,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
return false;
}
- /**
- * Returns whether a {@link DrmSession} may require a secure decoder for a given {@link Format}.
- *
- * @param sessionMediaCrypto The {@link DrmSession}'s {@link FrameworkMediaCrypto}.
- * @param format The {@link Format}.
- * @return Whether a secure decoder may be required.
- */
- private boolean maybeRequiresSecureDecoder(
- FrameworkMediaCrypto sessionMediaCrypto, Format format) {
- if (sessionMediaCrypto.forceAllowInsecureDecoderComponents) {
- return false;
- }
- MediaCrypto mediaCrypto;
- try {
- mediaCrypto = new MediaCrypto(sessionMediaCrypto.uuid, sessionMediaCrypto.sessionId);
- } catch (MediaCryptoException e) {
- // This shouldn't happen, but if it does then assume that a secure decoder may be required.
- return true;
- }
- try {
- return mediaCrypto.requiresSecureDecoderComponent(format.sampleMimeType);
- } finally {
- mediaCrypto.release();
- }
- }
-
private void reinitializeCodec() throws ExoPlaybackException {
releaseCodec();
maybeInitCodecOrBypass();
diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExoMediaDrm.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExoMediaDrm.java
index e48307d7ee..0f8d3dc348 100644
--- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExoMediaDrm.java
+++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeExoMediaDrm.java
@@ -272,6 +272,11 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
: KEY_STATUS_UNAVAILABLE);
}
+ @Override
+ public boolean requiresSecureDecoder(byte[] sessionId, String mimeType) {
+ return false;
+ }
+
@Override
public void acquire() {
Assertions.checkState(referenceCount > 0);