DRM: Pass sessionId rather than MediaCrypto

ExoMediaCrypto.requiresSecureDecoderComponent() is removed, and
FrameworkMediaCrypto.forceAllowInsecureDecoderComponents is made
public to allow determining whether a secure decoder is required
to be implemented in MediaCodecRenderer.

PiperOrigin-RevId: 228909771
This commit is contained in:
olly 2019-01-11 19:02:00 +00:00 committed by Oliver Woodman
parent 2d30d66746
commit 1f03093dc5
5 changed files with 58 additions and 67 deletions

View File

@ -15,14 +15,5 @@
*/ */
package com.google.android.exoplayer2.drm; package com.google.android.exoplayer2.drm;
/** /** An opaque {@link android.media.MediaCrypto} equivalent. */
* An opaque {@link android.media.MediaCrypto} equivalent. public interface ExoMediaCrypto {}
*/
public interface ExoMediaCrypto {
/**
* @see android.media.MediaCrypto#requiresSecureDecoderComponent(String)
*/
boolean requiresSecureDecoderComponent(String mimeType);
}

View File

@ -265,11 +265,9 @@ public interface ExoMediaDrm<T extends ExoMediaCrypto> {
/** /**
* @see android.media.MediaCrypto#MediaCrypto(UUID, byte[]) * @see android.media.MediaCrypto#MediaCrypto(UUID, byte[])
* * @param sessionId The DRM session ID.
* @param initData Opaque initialization data specific to the crypto scheme.
* @return An object extends {@link ExoMediaCrypto}, using opaque crypto scheme specific data. * @return An object extends {@link ExoMediaCrypto}, using opaque crypto scheme specific data.
* @throws MediaCryptoException If the instance can't be created. * @throws MediaCryptoException If the instance can't be created.
*/ */
T createMediaCrypto(byte[] initData) throws MediaCryptoException; T createMediaCrypto(byte[] sessionId) throws MediaCryptoException;
} }

View File

@ -17,48 +17,35 @@ package com.google.android.exoplayer2.drm;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.media.MediaCrypto; import android.media.MediaCrypto;
import com.google.android.exoplayer2.util.Assertions; import java.util.UUID;
/** /**
* An {@link ExoMediaCrypto} implementation that wraps the framework {@link MediaCrypto}. * An {@link ExoMediaCrypto} implementation that contains the necessary information to build or
* update a framework {@link MediaCrypto}.
*/ */
@TargetApi(16) @TargetApi(16)
public final class FrameworkMediaCrypto implements ExoMediaCrypto { public final class FrameworkMediaCrypto implements ExoMediaCrypto {
private final MediaCrypto mediaCrypto; /** The DRM scheme UUID. */
private final boolean forceAllowInsecureDecoderComponents; public final UUID uuid;
/** The DRM session id. */
public final byte[] sessionId;
/**
* Whether to allow use of insecure decoder components even if the underlying platform says
* otherwise.
*/
public final boolean forceAllowInsecureDecoderComponents;
/** /**
* @param mediaCrypto The {@link MediaCrypto} to wrap. * @param uuid The DRM scheme UUID.
* @param sessionId The DRM session id.
* @param forceAllowInsecureDecoderComponents Whether to allow use of insecure decoder components
* even if the underlying platform says otherwise.
*/ */
public FrameworkMediaCrypto(MediaCrypto mediaCrypto) { public FrameworkMediaCrypto(
this(mediaCrypto, false); UUID uuid, byte[] sessionId, boolean forceAllowInsecureDecoderComponents) {
} this.uuid = uuid;
this.sessionId = sessionId;
/**
* @param mediaCrypto The {@link MediaCrypto} to wrap.
* @param forceAllowInsecureDecoderComponents Whether to force
* {@link #requiresSecureDecoderComponent(String)} to return {@code false}, rather than
* {@link MediaCrypto#requiresSecureDecoderComponent(String)} of the wrapped
* {@link MediaCrypto}.
*/
public FrameworkMediaCrypto(MediaCrypto mediaCrypto,
boolean forceAllowInsecureDecoderComponents) {
this.mediaCrypto = Assertions.checkNotNull(mediaCrypto);
this.forceAllowInsecureDecoderComponents = forceAllowInsecureDecoderComponents; this.forceAllowInsecureDecoderComponents = forceAllowInsecureDecoderComponents;
} }
/**
* Returns the wrapped {@link MediaCrypto}.
*/
public MediaCrypto getWrappedMediaCrypto() {
return mediaCrypto;
}
@Override
public boolean requiresSecureDecoderComponent(String mimeType) {
return !forceAllowInsecureDecoderComponents
&& mediaCrypto.requiresSecureDecoderComponent(mimeType);
}
} }

View File

@ -18,7 +18,6 @@ package com.google.android.exoplayer2.drm;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.media.DeniedByServerException; import android.media.DeniedByServerException;
import android.media.MediaCrypto;
import android.media.MediaCryptoException; import android.media.MediaCryptoException;
import android.media.MediaDrm; import android.media.MediaDrm;
import android.media.MediaDrmException; import android.media.MediaDrmException;
@ -210,7 +209,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
boolean forceAllowInsecureDecoderComponents = Util.SDK_INT < 21 boolean forceAllowInsecureDecoderComponents = Util.SDK_INT < 21
&& C.WIDEVINE_UUID.equals(uuid) && "L3".equals(getPropertyString("securityLevel")); && C.WIDEVINE_UUID.equals(uuid) && "L3".equals(getPropertyString("securityLevel"));
return new FrameworkMediaCrypto( return new FrameworkMediaCrypto(
new MediaCrypto(adjustUuid(uuid), initData), forceAllowInsecureDecoderComponents); adjustUuid(uuid), initData, forceAllowInsecureDecoderComponents);
} }
private static SchemeData getSchemeData(UUID uuid, List<SchemeData> schemeDatas) { private static SchemeData getSchemeData(UUID uuid, List<SchemeData> schemeDatas) {

View File

@ -20,6 +20,7 @@ import android.media.MediaCodec;
import android.media.MediaCodec.CodecException; import android.media.MediaCodec.CodecException;
import android.media.MediaCodec.CryptoException; import android.media.MediaCodec.CryptoException;
import android.media.MediaCrypto; import android.media.MediaCrypto;
import android.media.MediaCryptoException;
import android.media.MediaFormat; import android.media.MediaFormat;
import android.os.Bundle; import android.os.Bundle;
import android.os.Looper; import android.os.Looper;
@ -294,6 +295,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private Format outputFormat; private Format outputFormat;
@Nullable private DrmSession<FrameworkMediaCrypto> codecDrmSession; @Nullable private DrmSession<FrameworkMediaCrypto> codecDrmSession;
@Nullable private DrmSession<FrameworkMediaCrypto> sourceDrmSession; @Nullable private DrmSession<FrameworkMediaCrypto> sourceDrmSession;
@Nullable private MediaCrypto mediaCrypto;
private boolean drmSessionRequiresSecureDecoder;
private long renderTimeLimitMs; private long renderTimeLimitMs;
private float rendererOperatingRate; private float rendererOperatingRate;
@Nullable private MediaCodec codec; @Nullable private MediaCodec codec;
@ -460,22 +463,28 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
setCodecDrmSession(sourceDrmSession); setCodecDrmSession(sourceDrmSession);
String mimeType = inputFormat.sampleMimeType; String mimeType = inputFormat.sampleMimeType;
MediaCrypto wrappedMediaCrypto = null;
boolean drmSessionRequiresSecureDecoder = false;
if (codecDrmSession != null) { if (codecDrmSession != null) {
FrameworkMediaCrypto mediaCrypto = codecDrmSession.getMediaCrypto();
if (mediaCrypto == null) { if (mediaCrypto == null) {
DrmSessionException drmError = codecDrmSession.getError(); FrameworkMediaCrypto sessionMediaCrypto = codecDrmSession.getMediaCrypto();
if (drmError != null) { if (sessionMediaCrypto == null) {
// Continue for now. We may be able to avoid failure if the session recovers, or if a new DrmSessionException drmError = codecDrmSession.getError();
// input format causes the session to be replaced before it's used. if (drmError != null) {
// Continue for now. We may be able to avoid failure if the session recovers, or if a
// new input format causes the session to be replaced before it's used.
} else {
// The drm session isn't open yet.
return;
}
} else { } else {
// The drm session isn't open yet. try {
return; mediaCrypto = new MediaCrypto(sessionMediaCrypto.uuid, sessionMediaCrypto.sessionId);
} catch (MediaCryptoException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex());
}
drmSessionRequiresSecureDecoder =
!sessionMediaCrypto.forceAllowInsecureDecoderComponents
&& mediaCrypto.requiresSecureDecoderComponent(mimeType);
} }
} else {
wrappedMediaCrypto = mediaCrypto.getWrappedMediaCrypto();
drmSessionRequiresSecureDecoder = mediaCrypto.requiresSecureDecoderComponent(mimeType);
} }
if (deviceNeedsDrmKeysToConfigureCodecWorkaround()) { if (deviceNeedsDrmKeysToConfigureCodecWorkaround()) {
@DrmSession.State int drmSessionState = codecDrmSession.getState(); @DrmSession.State int drmSessionState = codecDrmSession.getState();
@ -489,7 +498,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
try { try {
maybeInitCodecWithFallback(wrappedMediaCrypto, drmSessionRequiresSecureDecoder); maybeInitCodecWithFallback(mediaCrypto, drmSessionRequiresSecureDecoder);
} catch (DecoderInitializationException e) { } catch (DecoderInitializationException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex()); throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
@ -553,7 +562,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
@Override @Override
protected void onDisabled() { protected void onDisabled() {
inputFormat = null; inputFormat = null;
if (codecDrmSession != null || sourceDrmSession != null) { if (sourceDrmSession != null || codecDrmSession != null) {
// TODO: Do something better with this case. // TODO: Do something better with this case.
onReset(); onReset();
} else { } else {
@ -591,7 +600,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
} finally { } finally {
codec = null; codec = null;
setCodecDrmSession(null); try {
if (mediaCrypto != null) {
mediaCrypto.release();
}
} finally {
mediaCrypto = null;
setCodecDrmSession(null);
}
} }
} }
@ -923,7 +939,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
private void releaseDrmSessionIfUnused(@Nullable DrmSession<FrameworkMediaCrypto> session) { private void releaseDrmSessionIfUnused(@Nullable DrmSession<FrameworkMediaCrypto> session) {
if (session != null && session != codecDrmSession && session != sourceDrmSession) { if (session != null && session != sourceDrmSession && session != codecDrmSession) {
drmSessionManager.releaseSession(session); drmSessionManager.releaseSession(session);
} }
} }
@ -1128,7 +1144,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
DrmSession<FrameworkMediaCrypto> session = DrmSession<FrameworkMediaCrypto> session =
drmSessionManager.acquireSession(Looper.myLooper(), newFormat.drmInitData); drmSessionManager.acquireSession(Looper.myLooper(), newFormat.drmInitData);
if (session == codecDrmSession || session == sourceDrmSession) { if (session == sourceDrmSession || session == codecDrmSession) {
// We already had this session. The manager must be reference counting, so release it once // We already had this session. The manager must be reference counting, so release it once
// to get the count attributed to this renderer back down to 1. // to get the count attributed to this renderer back down to 1.
drmSessionManager.releaseSession(session); drmSessionManager.releaseSession(session);