DRM refactor / cleanup

PiperOrigin-RevId: 391403236
This commit is contained in:
olly 2021-08-18 00:28:53 +01:00 committed by bachinger
parent cd297b048a
commit 85142be9a4
40 changed files with 274 additions and 246 deletions

View File

@ -23,8 +23,8 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.util.Util;
@ -132,7 +132,7 @@ public class Libgav1VideoRenderer extends DecoderVideoRenderer {
|| !Gav1Library.isAvailable()) {
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE);
}
if (format.exoMediaCryptoType != null) {
if (format.cryptoType != C.CRYPTO_TYPE_NONE) {
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_DRM);
}
return RendererCapabilities.create(
@ -140,7 +140,7 @@ public class Libgav1VideoRenderer extends DecoderVideoRenderer {
}
@Override
protected Gav1Decoder createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto)
protected Gav1Decoder createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)
throws Gav1DecoderException {
TraceUtil.beginSection("createGav1Decoder");
int initialInputBufferSize =

View File

@ -29,7 +29,7 @@ import com.google.android.exoplayer2.audio.AudioSink;
import com.google.android.exoplayer2.audio.AudioSink.SinkFormatSupport;
import com.google.android.exoplayer2.audio.DecoderAudioRenderer;
import com.google.android.exoplayer2.audio.DefaultAudioSink;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TraceUtil;
@ -97,7 +97,7 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer<FfmpegAudioD
|| (!sinkSupportsFormat(format, C.ENCODING_PCM_16BIT)
&& !sinkSupportsFormat(format, C.ENCODING_PCM_FLOAT))) {
return C.FORMAT_UNSUPPORTED_SUBTYPE;
} else if (format.exoMediaCryptoType != null) {
} else if (format.cryptoType != C.CRYPTO_TYPE_NONE) {
return C.FORMAT_UNSUPPORTED_DRM;
} else {
return C.FORMAT_HANDLED;
@ -111,7 +111,7 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer<FfmpegAudioD
}
@Override
protected FfmpegAudioDecoder createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto)
protected FfmpegAudioDecoder createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)
throws FfmpegDecoderException {
TraceUtil.beginSection("createFfmpegAudioDecoder");
int initialInputBufferSize =

View File

@ -25,9 +25,9 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.decoder.Decoder;
import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.DecoderVideoRenderer;
@ -95,7 +95,7 @@ public final class FfmpegVideoRenderer extends DecoderVideoRenderer {
@SuppressWarnings("nullness:return")
@Override
protected Decoder<VideoDecoderInputBuffer, VideoDecoderOutputBuffer, FfmpegDecoderException>
createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto)
createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)
throws FfmpegDecoderException {
TraceUtil.beginSection("createFfmpegVideoDecoder");
// TODO: Implement, remove the SuppressWarnings annotation, and update the return type to use

View File

@ -23,7 +23,7 @@ import com.google.android.exoplayer2.audio.AudioProcessor;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.audio.AudioSink;
import com.google.android.exoplayer2.audio.DecoderAudioRenderer;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.extractor.FlacStreamMetadata;
import com.google.android.exoplayer2.util.FlacConstants;
import com.google.android.exoplayer2.util.MimeTypes;
@ -100,7 +100,7 @@ public final class LibflacAudioRenderer extends DecoderAudioRenderer<FlacDecoder
}
if (!sinkSupportsFormat(outputFormat)) {
return C.FORMAT_UNSUPPORTED_SUBTYPE;
} else if (format.exoMediaCryptoType != null) {
} else if (format.cryptoType != C.CRYPTO_TYPE_NONE) {
return C.FORMAT_UNSUPPORTED_DRM;
} else {
return C.FORMAT_HANDLED;
@ -108,7 +108,7 @@ public final class LibflacAudioRenderer extends DecoderAudioRenderer<FlacDecoder
}
@Override
protected FlacDecoder createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto)
protected FlacDecoder createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)
throws FlacDecoderException {
TraceUtil.beginSection("createFlacDecoder");
FlacDecoder decoder =

View File

@ -25,7 +25,7 @@ import com.google.android.exoplayer2.audio.AudioSink;
import com.google.android.exoplayer2.audio.AudioSink.SinkFormatSupport;
import com.google.android.exoplayer2.audio.DecoderAudioRenderer;
import com.google.android.exoplayer2.audio.OpusUtil;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.util.Util;
@ -81,9 +81,7 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer<OpusDecoder> {
@Override
@C.FormatSupport
protected int supportsFormatInternal(Format format) {
boolean drmIsSupported =
format.exoMediaCryptoType == null
|| OpusLibrary.matchesExpectedExoMediaCryptoType(format.exoMediaCryptoType);
boolean drmIsSupported = OpusLibrary.supportsCryptoType(format.cryptoType);
if (!OpusLibrary.isAvailable()
|| !MimeTypes.AUDIO_OPUS.equalsIgnoreCase(format.sampleMimeType)) {
return C.FORMAT_UNSUPPORTED_TYPE;
@ -98,7 +96,7 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer<OpusDecoder> {
}
@Override
protected OpusDecoder createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto)
protected OpusDecoder createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)
throws OpusDecoderException {
TraceUtil.beginSection("createOpusDecoder");
@SinkFormatSupport
@ -115,7 +113,7 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer<OpusDecoder> {
NUM_BUFFERS,
initialInputBufferSize,
format.initializationData,
mediaCrypto,
cryptoConfig,
outputFloat);
TraceUtil.endSection();

View File

@ -21,12 +21,12 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.audio.OpusUtil;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.decoder.CryptoException;
import com.google.android.exoplayer2.decoder.CryptoInfo;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.nio.ByteBuffer;
@ -44,7 +44,7 @@ public final class OpusDecoder
public final boolean outputFloat;
public final int channelCount;
@Nullable private final ExoMediaCrypto exoMediaCrypto;
@Nullable private final CryptoConfig cryptoConfig;
private final int preSkipSamples;
private final int seekPreRollSamples;
private final long nativeDecoderContext;
@ -60,8 +60,8 @@ public final class OpusDecoder
* @param initializationData Codec-specific initialization data. The first element must contain an
* opus header. Optionally, the list may contain two additional buffers, which must contain
* the encoder delay and seek pre roll values in nanoseconds, encoded as longs.
* @param exoMediaCrypto The {@link ExoMediaCrypto} object required for decoding encrypted
* content. Maybe null and can be ignored if decoder does not handle encrypted content.
* @param cryptoConfig The {@link CryptoConfig} object required for decoding encrypted content.
* May be null and can be ignored if decoder does not handle encrypted content.
* @param outputFloat Forces the decoder to output float PCM samples when set
* @throws OpusDecoderException Thrown if an exception occurs when initializing the decoder.
*/
@ -70,15 +70,15 @@ public final class OpusDecoder
int numOutputBuffers,
int initialInputBufferSize,
List<byte[]> initializationData,
@Nullable ExoMediaCrypto exoMediaCrypto,
@Nullable CryptoConfig cryptoConfig,
boolean outputFloat)
throws OpusDecoderException {
super(new DecoderInputBuffer[numInputBuffers], new SimpleOutputBuffer[numOutputBuffers]);
if (!OpusLibrary.isAvailable()) {
throw new OpusDecoderException("Failed to load decoder native libraries");
}
this.exoMediaCrypto = exoMediaCrypto;
if (exoMediaCrypto != null && !OpusLibrary.opusIsSecureDecodeSupported()) {
this.cryptoConfig = cryptoConfig;
if (cryptoConfig != null && !OpusLibrary.opusIsSecureDecodeSupported()) {
throw new OpusDecoderException("Opus decoder does not support secure decode");
}
int initializationDataSize = initializationData.size();
@ -177,7 +177,7 @@ public final class OpusDecoder
inputData.limit(),
outputBuffer,
OpusUtil.SAMPLE_RATE,
exoMediaCrypto,
cryptoConfig,
cryptoInfo.mode,
Assertions.checkNotNull(cryptoInfo.key),
Assertions.checkNotNull(cryptoInfo.iv),
@ -248,7 +248,7 @@ public final class OpusDecoder
int inputSize,
SimpleOutputBuffer outputBuffer,
int sampleRate,
@Nullable ExoMediaCrypto mediaCrypto,
@Nullable CryptoConfig mediaCrypto,
int inputMode,
byte[] key,
byte[] iv,

View File

@ -16,10 +16,9 @@
package com.google.android.exoplayer2.ext.opus;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.LibraryLoader;
import com.google.android.exoplayer2.util.Util;
/** Configures and queries the underlying native library. */
public final class OpusLibrary {
@ -29,7 +28,7 @@ public final class OpusLibrary {
}
private static final LibraryLoader LOADER = new LibraryLoader("opusV2JNI");
@Nullable private static Class<? extends ExoMediaCrypto> exoMediaCryptoType;
@C.CryptoType private static int cryptoType = C.CRYPTO_TYPE_UNSUPPORTED;
private OpusLibrary() {}
@ -38,14 +37,14 @@ public final class OpusLibrary {
* it must do so before calling any other method defined by this class, and before instantiating a
* {@link LibopusAudioRenderer} instance.
*
* @param exoMediaCryptoType The {@link ExoMediaCrypto} type expected for decoding protected
* content.
* @param cryptoType The {@link C.CryptoType} for which the decoder library supports decrypting
* protected content, or {@link C#CRYPTO_TYPE_UNSUPPORTED} if the library does not support
* decryption.
* @param libraries The names of the Opus native libraries.
*/
public static void setLibraries(
Class<? extends ExoMediaCrypto> exoMediaCryptoType, String... libraries) {
public static void setLibraries(@C.CryptoType int cryptoType, String... libraries) {
OpusLibrary.cryptoType = cryptoType;
LOADER.setLibraries(libraries);
OpusLibrary.exoMediaCryptoType = exoMediaCryptoType;
}
/** Returns whether the underlying library is available, loading it if necessary. */
@ -59,13 +58,10 @@ public final class OpusLibrary {
return isAvailable() ? opusGetVersion() : null;
}
/**
* Returns whether the given {@link ExoMediaCrypto} type matches the one required for decoding
* protected content.
*/
public static boolean matchesExpectedExoMediaCryptoType(
Class<? extends ExoMediaCrypto> exoMediaCryptoType) {
return Util.areEqual(OpusLibrary.exoMediaCryptoType, exoMediaCryptoType);
/** Returns whether the library supports the given {@link C.CryptoType}. */
public static boolean supportsCryptoType(@C.CryptoType int cryptoType) {
return cryptoType == C.CRYPTO_TYPE_NONE
|| (cryptoType != C.CRYPTO_TYPE_UNSUPPORTED && cryptoType == OpusLibrary.cryptoType);
}
public static native String opusGetVersion();

View File

@ -24,8 +24,8 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.video.DecoderVideoRenderer;
@ -129,9 +129,7 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer {
if (!VpxLibrary.isAvailable() || !MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType)) {
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE);
}
boolean drmIsSupported =
format.exoMediaCryptoType == null
|| VpxLibrary.matchesExpectedExoMediaCryptoType(format.exoMediaCryptoType);
boolean drmIsSupported = VpxLibrary.supportsCryptoType(format.cryptoType);
if (!drmIsSupported) {
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_DRM);
}
@ -140,14 +138,14 @@ public class LibvpxVideoRenderer extends DecoderVideoRenderer {
}
@Override
protected VpxDecoder createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto)
protected VpxDecoder createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)
throws VpxDecoderException {
TraceUtil.beginSection("createVpxDecoder");
int initialInputBufferSize =
format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE;
VpxDecoder decoder =
new VpxDecoder(
numInputBuffers, numOutputBuffers, initialInputBufferSize, mediaCrypto, threads);
numInputBuffers, numOutputBuffers, initialInputBufferSize, cryptoConfig, threads);
this.decoder = decoder;
TraceUtil.endSection();
return decoder;

View File

@ -21,11 +21,11 @@ import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.decoder.CryptoException;
import com.google.android.exoplayer2.decoder.CryptoInfo;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.VideoDecoderInputBuffer;
@ -43,7 +43,7 @@ public final class VpxDecoder
private static final int DECODE_ERROR = -1;
private static final int DRM_ERROR = -2;
@Nullable private final ExoMediaCrypto exoMediaCrypto;
@Nullable private final CryptoConfig cryptoConfig;
private final long vpxDecContext;
@Nullable private ByteBuffer lastSupplementalData;
@ -56,8 +56,8 @@ public final class VpxDecoder
* @param numInputBuffers The number of input buffers.
* @param numOutputBuffers The number of output buffers.
* @param initialInputBufferSize The initial size of each input buffer.
* @param exoMediaCrypto The {@link ExoMediaCrypto} object required for decoding encrypted
* content. Maybe null and can be ignored if decoder does not handle encrypted content.
* @param cryptoConfig The {@link CryptoConfig} object required for decoding encrypted content.
* May be null and can be ignored if decoder does not handle encrypted content.
* @param threads Number of threads libvpx will use to decode.
* @throws VpxDecoderException Thrown if an exception occurs when initializing the decoder.
*/
@ -65,7 +65,7 @@ public final class VpxDecoder
int numInputBuffers,
int numOutputBuffers,
int initialInputBufferSize,
@Nullable ExoMediaCrypto exoMediaCrypto,
@Nullable CryptoConfig cryptoConfig,
int threads)
throws VpxDecoderException {
super(
@ -74,8 +74,8 @@ public final class VpxDecoder
if (!VpxLibrary.isAvailable()) {
throw new VpxDecoderException("Failed to load decoder native libraries.");
}
this.exoMediaCrypto = exoMediaCrypto;
if (exoMediaCrypto != null && !VpxLibrary.vpxIsSecureDecodeSupported()) {
this.cryptoConfig = cryptoConfig;
if (cryptoConfig != null && !VpxLibrary.vpxIsSecureDecodeSupported()) {
throw new VpxDecoderException("Vpx decoder does not support secure decode.");
}
vpxDecContext =
@ -134,7 +134,7 @@ public final class VpxDecoder
vpxDecContext,
inputData,
inputSize,
exoMediaCrypto,
cryptoConfig,
cryptoInfo.mode,
Assertions.checkNotNull(cryptoInfo.key),
Assertions.checkNotNull(cryptoInfo.iv),
@ -215,7 +215,7 @@ public final class VpxDecoder
long context,
ByteBuffer encoded,
int length,
@Nullable ExoMediaCrypto mediaCrypto,
@Nullable CryptoConfig mediaCrypto,
int inputMode,
byte[] key,
byte[] iv,

View File

@ -16,10 +16,9 @@
package com.google.android.exoplayer2.ext.vp9;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.LibraryLoader;
import com.google.android.exoplayer2.util.Util;
/** Configures and queries the underlying native library. */
public final class VpxLibrary {
@ -29,7 +28,7 @@ public final class VpxLibrary {
}
private static final LibraryLoader LOADER = new LibraryLoader("vpx", "vpxV2JNI");
@Nullable private static Class<? extends ExoMediaCrypto> exoMediaCryptoType;
@C.CryptoType private static int cryptoType = C.CRYPTO_TYPE_UNSUPPORTED;
private VpxLibrary() {}
@ -38,14 +37,14 @@ public final class VpxLibrary {
* it must do so before calling any other method defined by this class, and before instantiating a
* {@link LibvpxVideoRenderer} instance.
*
* @param exoMediaCryptoType The {@link ExoMediaCrypto} type required for decoding protected
* content.
* @param cryptoType The {@link C.CryptoType} for which the decoder library supports decrypting
* protected content, or {@link C#CRYPTO_TYPE_UNSUPPORTED} if the library does not support
* decryption.
* @param libraries The names of the Vpx native libraries.
*/
public static void setLibraries(
Class<? extends ExoMediaCrypto> exoMediaCryptoType, String... libraries) {
public static void setLibraries(@C.CryptoType int cryptoType, String... libraries) {
VpxLibrary.cryptoType = cryptoType;
LOADER.setLibraries(libraries);
VpxLibrary.exoMediaCryptoType = exoMediaCryptoType;
}
/** Returns whether the underlying library is available, loading it if necessary. */
@ -75,13 +74,10 @@ public final class VpxLibrary {
return indexHbd >= 0;
}
/**
* Returns whether the given {@link ExoMediaCrypto} type matches the one required for decoding
* protected content.
*/
public static boolean matchesExpectedExoMediaCryptoType(
Class<? extends ExoMediaCrypto> exoMediaCryptoType) {
return Util.areEqual(VpxLibrary.exoMediaCryptoType, exoMediaCryptoType);
/** Returns whether the library supports the given {@link C.CryptoType}. */
public static boolean supportsCryptoType(@C.CryptoType int cryptoType) {
return cryptoType == C.CRYPTO_TYPE_NONE
|| (cryptoType != C.CRYPTO_TYPE_UNSUPPORTED && cryptoType == VpxLibrary.cryptoType);
}
private static native String vpxGetVersion();

View File

@ -20,6 +20,7 @@ import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.view.Surface;
import androidx.annotation.IntDef;
@ -116,6 +117,33 @@ public final class C {
/** The name of the sans-serif font family. */
public static final String SANS_SERIF_NAME = "sans-serif";
/**
* Types of crypto implementation. May be one of {@link #CRYPTO_TYPE_NONE}, {@link
* #CRYPTO_TYPE_UNSUPPORTED} or {@link #CRYPTO_TYPE_FRAMEWORK}. May also be an app-defined value
* (see {@link #CRYPTO_TYPE_CUSTOM_BASE}).
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef(
open = true,
value = {
CRYPTO_TYPE_UNSUPPORTED,
CRYPTO_TYPE_NONE,
CRYPTO_TYPE_FRAMEWORK,
})
public @interface CryptoType {}
/** No crypto. */
public static final int CRYPTO_TYPE_NONE = 0;
/** An unsupported crypto type. */
public static final int CRYPTO_TYPE_UNSUPPORTED = 1;
/** Framework crypto in which a {@link MediaCodec} is configured with a {@link MediaCrypto}. */
public static final int CRYPTO_TYPE_FRAMEWORK = 2;
/**
* Applications or extensions may define custom {@code CRYPTO_TYPE_*} constants greater than or
* equal to this value.
*/
public static final int CRYPTO_TYPE_CUSTOM_BASE = 10000;
/**
* Crypto modes for a codec. One of {@link #CRYPTO_MODE_UNENCRYPTED}, {@link #CRYPTO_MODE_AES_CTR}
* or {@link #CRYPTO_MODE_AES_CBC}.

View File

@ -19,8 +19,6 @@ import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.drm.UnsupportedMediaCrypto;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.util.BundleableUtils;
import com.google.android.exoplayer2.util.MimeTypes;
@ -174,7 +172,7 @@ public final class Format implements Bundleable {
// Provided by the source.
@Nullable private Class<? extends ExoMediaCrypto> exoMediaCryptoType;
@C.CryptoType private int cryptoType;
/** Creates a new instance with default values. */
public Builder() {
@ -195,6 +193,8 @@ public final class Format implements Bundleable {
pcmEncoding = NO_VALUE;
// Text specific.
accessibilityChannel = NO_VALUE;
// Provided by the source.
cryptoType = C.CRYPTO_TYPE_NONE;
}
/**
@ -238,7 +238,7 @@ public final class Format implements Bundleable {
// Text specific.
this.accessibilityChannel = format.accessibilityChannel;
// Provided by the source.
this.exoMediaCryptoType = format.exoMediaCryptoType;
this.cryptoType = format.cryptoType;
}
/**
@ -585,14 +585,13 @@ public final class Format implements Bundleable {
// Provided by source.
/**
* Sets {@link Format#exoMediaCryptoType}. The default value is {@code null}.
* Sets {@link Format#cryptoType}. The default value is {@link C#CRYPTO_TYPE_NONE}.
*
* @param exoMediaCryptoType The {@link Format#exoMediaCryptoType}.
* @param cryptoType The {@link C.CryptoType}.
* @return The builder.
*/
public Builder setExoMediaCryptoType(
@Nullable Class<? extends ExoMediaCrypto> exoMediaCryptoType) {
this.exoMediaCryptoType = exoMediaCryptoType;
public Builder setCryptoType(@C.CryptoType int cryptoType) {
this.cryptoType = cryptoType;
return this;
}
@ -756,11 +755,12 @@ public final class Format implements Bundleable {
// Provided by source.
/**
* The type of {@link ExoMediaCrypto} that will be associated with the content this format
* describes, or {@code null} if the content is not encrypted. Cannot be null if {@link
* #drmInitData} is non-null.
* The type of crypto that must be used to decode samples associated with this format, or {@link
* C#CRYPTO_TYPE_NONE} if the content is not encrypted. Cannot be {@link C#CRYPTO_TYPE_NONE} if
* {@link #drmInitData} is non-null, but may be {@link C#CRYPTO_TYPE_UNSUPPORTED} to indicate that
* the samples are encrypted using an unsupported crypto type.
*/
@Nullable public final Class<? extends ExoMediaCrypto> exoMediaCryptoType;
@C.CryptoType public final int cryptoType;
// Lazily initialized hashcode.
private int hashCode;
@ -964,11 +964,11 @@ public final class Format implements Bundleable {
// Text specific.
accessibilityChannel = builder.accessibilityChannel;
// Provided by source.
if (builder.exoMediaCryptoType == null && drmInitData != null) {
// Encrypted content must always have a non-null exoMediaCryptoType.
exoMediaCryptoType = UnsupportedMediaCrypto.class;
if (builder.cryptoType == C.CRYPTO_TYPE_NONE && drmInitData != null) {
// Encrypted content cannot use CRYPTO_TYPE_NONE.
cryptoType = C.CRYPTO_TYPE_UNSUPPORTED;
} else {
exoMediaCryptoType = builder.exoMediaCryptoType;
cryptoType = builder.cryptoType;
}
}
@ -1113,10 +1113,9 @@ public final class Format implements Bundleable {
return buildUpon().setWidth(width).setHeight(height).build();
}
/** Returns a copy of this format with the specified {@link #exoMediaCryptoType}. */
public Format copyWithExoMediaCryptoType(
@Nullable Class<? extends ExoMediaCrypto> exoMediaCryptoType) {
return buildUpon().setExoMediaCryptoType(exoMediaCryptoType).build();
/** Returns a copy of this format with the specified {@link #cryptoType}. */
public Format copyWithCryptoType(@C.CryptoType int cryptoType) {
return buildUpon().setCryptoType(cryptoType).build();
}
/**
@ -1197,7 +1196,7 @@ public final class Format implements Bundleable {
// Text specific.
result = 31 * result + accessibilityChannel;
// Provided by the source.
result = 31 * result + (exoMediaCryptoType == null ? 0 : exoMediaCryptoType.hashCode());
result = 31 * result + cryptoType;
hashCode = result;
}
return hashCode;
@ -1232,9 +1231,9 @@ public final class Format implements Bundleable {
&& encoderDelay == other.encoderDelay
&& encoderPadding == other.encoderPadding
&& accessibilityChannel == other.accessibilityChannel
&& cryptoType == other.cryptoType
&& Float.compare(frameRate, other.frameRate) == 0
&& Float.compare(pixelWidthHeightRatio, other.pixelWidthHeightRatio) == 0
&& Util.areEqual(exoMediaCryptoType, other.exoMediaCryptoType)
&& Util.areEqual(id, other.id)
&& Util.areEqual(label, other.label)
&& Util.areEqual(codecs, other.codecs)
@ -1360,6 +1359,7 @@ public final class Format implements Bundleable {
FIELD_ENCODER_DELAY,
FIELD_ENCODER_PADDING,
FIELD_ACCESSIBILITY_CHANNEL,
FIELD_CRYPTO_TYPE,
})
private @interface FieldNumber {}
@ -1392,13 +1392,8 @@ public final class Format implements Bundleable {
private static final int FIELD_ENCODER_DELAY = 26;
private static final int FIELD_ENCODER_PADDING = 27;
private static final int FIELD_ACCESSIBILITY_CHANNEL = 28;
private static final int FIELD_CRYPTO_TYPE = 29;
/**
* {@inheritDoc}
*
* <p>Omits the {@link #exoMediaCryptoType} field. The {@link #exoMediaCryptoType} of an instance
* restored by {@link #CREATOR} will always be {@link UnsupportedMediaCrypto}.
*/
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
@ -1443,6 +1438,8 @@ public final class Format implements Bundleable {
bundle.putInt(keyForField(FIELD_ENCODER_PADDING), encoderPadding);
// Text specific.
bundle.putInt(keyForField(FIELD_ACCESSIBILITY_CHANNEL), accessibilityChannel);
// Source specific.
bundle.putInt(keyForField(FIELD_CRYPTO_TYPE), cryptoType);
return bundle;
}
@ -1512,7 +1509,9 @@ public final class Format implements Bundleable {
bundle.getInt(keyForField(FIELD_ENCODER_PADDING), DEFAULT.encoderPadding))
// Text specific.
.setAccessibilityChannel(
bundle.getInt(keyForField(FIELD_ACCESSIBILITY_CHANNEL), DEFAULT.accessibilityChannel));
bundle.getInt(keyForField(FIELD_ACCESSIBILITY_CHANNEL), DEFAULT.accessibilityChannel))
// Source specific.
.setCryptoType(bundle.getInt(keyForField(FIELD_CRYPTO_TYPE), DEFAULT.cryptoType));
return builder.build();
}

View File

@ -13,7 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.drm;
package com.google.android.exoplayer2.decoder;
/** Enables decoding of encrypted data using keys in a DRM session. */
public interface ExoMediaCrypto {}
import com.google.android.exoplayer2.C;
/**
* Configuration for a decoder to allow it to decode encrypted media data. The configuration is
* {@link C.CryptoType} specific.
*/
public interface CryptoConfig {}

View File

@ -21,7 +21,11 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
/** Compatibility wrapper for {@link android.media.MediaCodec.CryptoInfo}. */
/**
* Metadata describing the structure of an encrypted input sample.
*
* <p>This class is a compatibility wrapper for {@link android.media.MediaCodec.CryptoInfo}.
*/
public final class CryptoInfo {
/**

View File

@ -22,8 +22,6 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.drm.UnsupportedMediaCrypto;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
import com.google.android.exoplayer2.util.MimeTypes;
@ -45,15 +43,11 @@ public final class FormatTest {
}
@Test
public void roundTripViaBundle_ofParameters_yieldsEqualInstanceExceptExoMediaCryptoType() {
public void roundTripViaBundle_ofParameters_yieldsEqualInstance() {
Format formatToBundle = createTestFormat();
Format formatFromBundle = Format.CREATOR.fromBundle(formatToBundle.toBundle());
assertThat(formatFromBundle.exoMediaCryptoType).isEqualTo(UnsupportedMediaCrypto.class);
assertThat(formatFromBundle)
.isEqualTo(
formatToBundle.buildUpon().setExoMediaCryptoType(UnsupportedMediaCrypto.class).build());
assertThat(formatFromBundle).isEqualTo(formatToBundle);
}
private static Format createTestFormat() {
@ -93,7 +87,7 @@ public final class FormatTest {
.setPeakBitrate(2048)
.setCodecs("codec")
.setMetadata(metadata)
.setContainerMimeType(MimeTypes.VIDEO_MP4)
.setContainerMimeType(VIDEO_MP4)
.setSampleMimeType(MimeTypes.VIDEO_H264)
.setMaxInputSize(5000)
.setInitializationData(initializationData)
@ -113,7 +107,7 @@ public final class FormatTest {
.setEncoderDelay(1001)
.setEncoderPadding(1002)
.setAccessibilityChannel(2)
.setExoMediaCryptoType(ExoMediaCrypto.class)
.setCryptoType(C.CRYPTO_TYPE_CUSTOM_BASE)
.build();
}

View File

@ -38,6 +38,7 @@ import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher;
import com.google.android.exoplayer2.audio.AudioSink.SinkFormatSupport;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.decoder.Decoder;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.decoder.DecoderException;
@ -46,7 +47,6 @@ import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation;
import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
@ -330,12 +330,12 @@ public abstract class DecoderAudioRenderer<
* Creates a decoder for the given format.
*
* @param format The format for which a decoder is required.
* @param mediaCrypto The {@link ExoMediaCrypto} object required for decoding encrypted content.
* Maybe null and can be ignored if decoder does not handle encrypted content.
* @param cryptoConfig The {@link CryptoConfig} object required for decoding encrypted content.
* May be null and can be ignored if decoder does not handle encrypted content.
* @return The decoder.
* @throws DecoderException If an error occurred creating a suitable decoder.
*/
protected abstract T createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto)
protected abstract T createDecoder(Format format, @Nullable CryptoConfig cryptoConfig)
throws DecoderException;
/**
@ -603,10 +603,10 @@ public abstract class DecoderAudioRenderer<
setDecoderDrmSession(sourceDrmSession);
ExoMediaCrypto mediaCrypto = null;
CryptoConfig cryptoConfig = null;
if (decoderDrmSession != null) {
mediaCrypto = decoderDrmSession.getMediaCrypto();
if (mediaCrypto == null) {
cryptoConfig = decoderDrmSession.getCryptoConfig();
if (cryptoConfig == null) {
DrmSessionException drmError = decoderDrmSession.getError();
if (drmError != null) {
// Continue for now. We may be able to avoid failure if a new input format causes the
@ -621,7 +621,7 @@ public abstract class DecoderAudioRenderer<
try {
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
TraceUtil.beginSection("createAudioDecoder");
decoder = createDecoder(inputFormat, mediaCrypto);
decoder = createDecoder(inputFormat, cryptoConfig);
TraceUtil.endSection();
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
eventDispatcher.decoderInitialized(

View File

@ -272,7 +272,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
}
@TunnelingSupport
int tunnelingSupport = Util.SDK_INT >= 21 ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED;
boolean formatHasDrm = format.exoMediaCryptoType != null;
boolean formatHasDrm = format.cryptoType != C.CRYPTO_TYPE_NONE;
boolean supportsFormatDrm = supportsFormatDrm(format);
// In direct mode, if the format has DRM then we need to use a decoder that only decrypts.
// Else we don't don't need a decoder at all.

View File

@ -31,6 +31,7 @@ import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
@ -141,7 +142,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private int referenceCount;
@Nullable private HandlerThread requestHandlerThread;
@Nullable private RequestHandler requestHandler;
@Nullable private ExoMediaCrypto mediaCrypto;
@Nullable private CryptoConfig cryptoConfig;
@Nullable private DrmSessionException lastException;
@Nullable private byte[] sessionId;
private byte @MonotonicNonNull [] offlineLicenseKeySetId;
@ -260,7 +261,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
}
@Override
public final @Nullable DrmSessionException getError() {
@Nullable
public final DrmSessionException getError() {
return state == STATE_ERROR ? lastException : null;
}
@ -270,8 +272,9 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
}
@Override
public final @Nullable ExoMediaCrypto getMediaCrypto() {
return mediaCrypto;
@Nullable
public final CryptoConfig getCryptoConfig() {
return cryptoConfig;
}
@Override
@ -326,7 +329,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
requestHandler = null;
Util.castNonNull(requestHandlerThread).quit();
requestHandlerThread = null;
mediaCrypto = null;
cryptoConfig = null;
lastException = null;
currentKeyRequest = null;
currentProvisionRequest = null;
@ -361,7 +364,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
try {
sessionId = mediaDrm.openSession();
mediaCrypto = mediaDrm.createMediaCrypto(sessionId);
cryptoConfig = mediaDrm.createCryptoConfig(sessionId);
state = STATE_OPENED;
// Capture state into a local so a consistent value is seen by the lambda.
int localState = state;

View File

@ -579,19 +579,16 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
}
@Override
@Nullable
public Class<? extends ExoMediaCrypto> getExoMediaCryptoType(Format format) {
Class<? extends ExoMediaCrypto> exoMediaCryptoType =
checkNotNull(exoMediaDrm).getExoMediaCryptoType();
@C.CryptoType
public int getCryptoType(Format format) {
@C.CryptoType int cryptoType = checkNotNull(exoMediaDrm).getCryptoType();
if (format.drmInitData == null) {
int trackType = MimeTypes.getTrackType(format.sampleMimeType);
return Util.linearSearch(useDrmSessionsForClearContentTrackTypes, trackType) != C.INDEX_UNSET
? exoMediaCryptoType
: null;
? cryptoType
: C.CRYPTO_TYPE_NONE;
} else {
return canAcquireSession(format.drmInitData)
? exoMediaCryptoType
: UnsupportedMediaCrypto.class;
return canAcquireSession(format.drmInitData) ? cryptoType : C.CRYPTO_TYPE_UNSUPPORTED;
}
}
@ -602,12 +599,12 @@ public class DefaultDrmSessionManager implements DrmSessionManager {
int trackType, boolean shouldReleasePreacquiredSessionsBeforeRetrying) {
ExoMediaDrm exoMediaDrm = checkNotNull(this.exoMediaDrm);
boolean avoidPlaceholderDrmSessions =
FrameworkMediaCrypto.class.equals(exoMediaDrm.getExoMediaCryptoType())
&& FrameworkMediaCrypto.WORKAROUND_DEVICE_NEEDS_KEYS_TO_CONFIGURE_CODEC;
exoMediaDrm.getCryptoType() == C.CRYPTO_TYPE_FRAMEWORK
&& FrameworkCryptoConfig.WORKAROUND_DEVICE_NEEDS_KEYS_TO_CONFIGURE_CODEC;
// Avoid attaching a session to sparse formats.
if (avoidPlaceholderDrmSessions
|| Util.linearSearch(useDrmSessionsForClearContentTrackTypes, trackType) == C.INDEX_UNSET
|| UnsupportedMediaCrypto.class.equals(exoMediaDrm.getExoMediaCryptoType())) {
|| exoMediaDrm.getCryptoType() == C.CRYPTO_TYPE_UNSUPPORTED) {
return null;
}
if (placeholderDrmSession == null) {

View File

@ -19,6 +19,7 @@ import android.media.MediaDrm;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import java.io.IOException;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@ -109,11 +110,11 @@ public interface DrmSession {
UUID getSchemeUuid();
/**
* Returns an {@link ExoMediaCrypto} for the open session, or null if called before the session
* has been opened or after it's been released.
* Returns a {@link CryptoConfig} for the open session, or null if called before the session has
* been opened or after it's been released.
*/
@Nullable
ExoMediaCrypto getMediaCrypto();
CryptoConfig getCryptoConfig();
/**
* Returns a map describing the key status for the session, or null if called before the session

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm;
import android.os.Looper;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackException;
@ -61,9 +62,9 @@ public interface DrmSessionManager {
}
@Override
@Nullable
public Class<UnsupportedMediaCrypto> getExoMediaCryptoType(Format format) {
return format.drmInitData != null ? UnsupportedMediaCrypto.class : null;
@C.CryptoType
public int getCryptoType(Format format) {
return format.drmInitData != null ? C.CRYPTO_TYPE_UNSUPPORTED : C.CRYPTO_TYPE_NONE;
}
};
@ -171,19 +172,18 @@ public interface DrmSessionManager {
Format format);
/**
* Returns the {@link ExoMediaCrypto} type associated to sessions acquired for the given {@link
* Format}. Returns the {@link UnsupportedMediaCrypto} type if this DRM session manager does not
* support any of the DRM schemes defined in the given {@link Format}. Returns null if {@link
* Format#drmInitData} is null and {@link #acquireSession} would return null for the given {@link
* Format}.
* Returns the {@link C.CryptoType} that the DRM session manager will use for a given {@link
* Format}. Returns {@link C#CRYPTO_TYPE_UNSUPPORTED} if the manager does not support any of the
* DRM schemes defined in the {@link Format}. Returns {@link C#CRYPTO_TYPE_NONE} if {@link
* Format#drmInitData} is null and {@link #acquireSession} will return {@code null} for the given
* {@link Format}.
*
* @param format The {@link Format} for which to return the {@link ExoMediaCrypto} type.
* @return The {@link ExoMediaCrypto} type associated to sessions acquired using the given {@link
* Format}, or {@link UnsupportedMediaCrypto} if this DRM session manager does not support any
* of the DRM schemes defined in the given {@link Format}. May be null if {@link
* Format#drmInitData} is null and {@link #acquireSession} would return null for the given
* {@link Format}.
* @param format The {@link Format}.
* @return The {@link C.CryptoType} that the manager will use, or @link C#CRYPTO_TYPE_UNSUPPORTED}
* if the manager does not support any of the DRM schemes defined in the {@link Format}. Will
* be {@link C#CRYPTO_TYPE_NONE} if {@link Format#drmInitData} is null and {@link
* #acquireSession} will return null for the given {@link Format}.
*/
@Nullable
Class<? extends ExoMediaCrypto> getExoMediaCryptoType(Format format);
@C.CryptoType
int getCryptoType(Format format);
}

View File

@ -19,6 +19,8 @@ import android.media.MediaDrmException;
import android.os.PersistableBundle;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.util.Util;
import java.util.HashMap;
import java.util.List;
@ -142,13 +144,14 @@ public final class DummyExoMediaDrm implements ExoMediaDrm {
}
@Override
public ExoMediaCrypto createMediaCrypto(byte[] sessionId) {
public CryptoConfig createCryptoConfig(byte[] sessionId) {
// Should not be invoked. No session should exist.
throw new IllegalStateException();
}
@Override
public Class<UnsupportedMediaCrypto> getExoMediaCryptoType() {
return UnsupportedMediaCrypto.class;
@C.CryptoType
public int getCryptoType() {
return C.CRYPTO_TYPE_UNSUPPORTED;
}
}

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.util.Assertions;
import java.util.Map;
import java.util.UUID;
@ -53,7 +54,7 @@ public final class ErrorStateDrmSession implements DrmSession {
@Override
@Nullable
public ExoMediaCrypto getMediaCrypto() {
public CryptoConfig getCryptoConfig() {
return null;
}

View File

@ -25,6 +25,8 @@ import android.os.Handler;
import android.os.PersistableBundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@ -538,14 +540,19 @@ public interface ExoMediaDrm {
void setPropertyByteArray(String propertyName, byte[] value);
/**
* Creates an {@link ExoMediaCrypto} for a given session.
* Creates a {@link CryptoConfig} that can be passed to a compatible decoder to allow decryption
* of protected content using the specified session.
*
* @param sessionId The ID of the session.
* @return An {@link ExoMediaCrypto} for the given session.
* @throws MediaCryptoException If an {@link ExoMediaCrypto} could not be created.
* @return A {@link CryptoConfig} for the given session.
* @throws MediaCryptoException If a {@link CryptoConfig} could not be created.
*/
ExoMediaCrypto createMediaCrypto(byte[] sessionId) throws MediaCryptoException;
CryptoConfig createCryptoConfig(byte[] sessionId) throws MediaCryptoException;
/** Returns the {@link ExoMediaCrypto} type created by {@link #createMediaCrypto(byte[])}. */
Class<? extends ExoMediaCrypto> getExoMediaCryptoType();
/**
* Returns the {@link C.CryptoType type} of {@link CryptoConfig} instances returned by {@link
* #createCryptoConfig}.
*/
@C.CryptoType
int getCryptoType();
}

View File

@ -15,15 +15,19 @@
*/
package com.google.android.exoplayer2.drm;
import android.media.MediaCodec;
import android.media.MediaCrypto;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.util.Util;
import java.util.UUID;
/**
* An {@link ExoMediaCrypto} implementation that contains the necessary information to build or
* update a framework {@link MediaCrypto}.
* A {@link CryptoConfig} for {@link C#CRYPTO_TYPE_FRAMEWORK}. Contains the necessary information to
* build or update a framework {@link MediaCrypto} that can be used to configure a {@link
* MediaCodec}.
*/
public final class FrameworkMediaCrypto implements ExoMediaCrypto {
public final class FrameworkCryptoConfig implements CryptoConfig {
/**
* Whether the device needs keys to have been loaded into the {@link DrmSession} before codec
@ -50,7 +54,7 @@ public final class FrameworkMediaCrypto implements ExoMediaCrypto {
* @param forceAllowInsecureDecoderComponents Whether to allow use of insecure decoder components
* even if the underlying platform says otherwise.
*/
public FrameworkMediaCrypto(
public FrameworkCryptoConfig(
UUID uuid, byte[] sessionId, boolean forceAllowInsecureDecoderComponents) {
this.uuid = uuid;
this.sessionId = sessionId;

View File

@ -314,20 +314,21 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
}
@Override
public FrameworkMediaCrypto createMediaCrypto(byte[] sessionId) throws MediaCryptoException {
public FrameworkCryptoConfig createCryptoConfig(byte[] sessionId) throws MediaCryptoException {
// Work around a bug prior to Lollipop where L1 Widevine forced into L3 mode would still
// indicate that it required secure video decoders [Internal ref: b/11428937].
boolean forceAllowInsecureDecoderComponents =
Util.SDK_INT < 21
&& C.WIDEVINE_UUID.equals(uuid)
&& "L3".equals(getPropertyString("securityLevel"));
return new FrameworkMediaCrypto(
return new FrameworkCryptoConfig(
adjustUuid(uuid), sessionId, forceAllowInsecureDecoderComponents);
}
@Override
public Class<FrameworkMediaCrypto> getExoMediaCryptoType() {
return FrameworkMediaCrypto.class;
@C.CryptoType
public int getCryptoType() {
return C.CRYPTO_TYPE_FRAMEWORK;
}
private static SchemeData getSchemeData(UUID uuid, List<SchemeData> schemeDatas) {

View File

@ -50,6 +50,7 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer.InsufficientCapacityException;
@ -57,8 +58,7 @@ import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation;
import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation.DecoderDiscardReasons;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.drm.FrameworkCryptoConfig;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.SampleStream;
@ -545,8 +545,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
if (codecDrmSession != null) {
if (mediaCrypto == null) {
@Nullable
FrameworkMediaCrypto sessionMediaCrypto = getFrameworkMediaCrypto(codecDrmSession);
if (sessionMediaCrypto == null) {
FrameworkCryptoConfig sessionCryptoConfig = getFrameworkCryptoConfig(codecDrmSession);
if (sessionCryptoConfig == null) {
@Nullable DrmSessionException drmError = codecDrmSession.getError();
if (drmError != null) {
// Continue for now. We may be able to avoid failure if a new input format causes the
@ -557,17 +557,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
} else {
try {
mediaCrypto = new MediaCrypto(sessionMediaCrypto.uuid, sessionMediaCrypto.sessionId);
mediaCrypto = new MediaCrypto(sessionCryptoConfig.uuid, sessionCryptoConfig.sessionId);
} catch (MediaCryptoException e) {
throw createRendererException(
e, inputFormat, PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR);
}
mediaCryptoRequiresSecureDecoder =
!sessionMediaCrypto.forceAllowInsecureDecoderComponents
!sessionCryptoConfig.forceAllowInsecureDecoderComponents
&& mediaCrypto.requiresSecureDecoderComponent(mimeType);
}
}
if (FrameworkMediaCrypto.WORKAROUND_DEVICE_NEEDS_KEYS_TO_CONFIGURE_CODEC) {
if (FrameworkCryptoConfig.WORKAROUND_DEVICE_NEEDS_KEYS_TO_CONFIGURE_CODEC) {
@DrmSession.State int drmSessionState = codecDrmSession.getState();
if (drmSessionState == DrmSession.STATE_ERROR) {
DrmSessionException drmSessionException =
@ -1074,11 +1074,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
return codecInfos;
}
/**
* Configures rendering where no codec is used. Called instead of {@link
* #configureCodec(MediaCodecInfo, MediaCodecAdapter, Format, MediaCrypto, float)} when no codec
* is used to render.
*/
/** Configures rendering where no codec is used. */
private void initBypass(Format format) {
disableBypass(); // In case of transition between 2 bypass formats.
@ -2047,8 +2043,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
/** Returns whether this renderer supports the given {@link Format Format's} DRM scheme. */
protected static boolean supportsFormatDrm(Format format) {
return format.exoMediaCryptoType == null
|| FrameworkMediaCrypto.class.equals(format.exoMediaCryptoType);
return format.cryptoType == C.CRYPTO_TYPE_NONE || format.cryptoType == C.CRYPTO_TYPE_FRAMEWORK;
}
/**
@ -2088,8 +2083,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
// TODO: Add an API check once [Internal ref: b/128835874] is fixed.
return true;
}
@Nullable FrameworkMediaCrypto newMediaCrypto = getFrameworkMediaCrypto(newSession);
if (newMediaCrypto == null) {
@Nullable FrameworkCryptoConfig newCryptoConfig = getFrameworkCryptoConfig(newSession);
if (newCryptoConfig == null) {
// We'd only expect this to happen if the CDM from which newSession is obtained needs
// provisioning. This is unlikely to happen (it probably requires a switch from one DRM scheme
// to another, where the new CDM hasn't been used before and needs provisioning). It would be
@ -2101,7 +2096,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
boolean requiresSecureDecoder;
if (newMediaCrypto.forceAllowInsecureDecoderComponents) {
if (newCryptoConfig.forceAllowInsecureDecoderComponents) {
requiresSecureDecoder = false;
} else {
requiresSecureDecoder = newSession.requiresSecureDecoder(newFormat.sampleMimeType);
@ -2136,7 +2131,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
@RequiresApi(23)
private void updateDrmSessionV23() throws ExoPlaybackException {
try {
mediaCrypto.setMediaDrmSession(getFrameworkMediaCrypto(sourceDrmSession).sessionId);
mediaCrypto.setMediaDrmSession(getFrameworkCryptoConfig(sourceDrmSession).sessionId);
} catch (MediaCryptoException e) {
throw createRendererException(e, inputFormat, PlaybackException.ERROR_CODE_DRM_SYSTEM_ERROR);
}
@ -2146,18 +2141,19 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
@Nullable
private FrameworkMediaCrypto getFrameworkMediaCrypto(DrmSession drmSession)
private FrameworkCryptoConfig getFrameworkCryptoConfig(DrmSession drmSession)
throws ExoPlaybackException {
@Nullable ExoMediaCrypto mediaCrypto = drmSession.getMediaCrypto();
if (mediaCrypto != null && !(mediaCrypto instanceof FrameworkMediaCrypto)) {
@Nullable CryptoConfig cryptoConfig = drmSession.getCryptoConfig();
if (cryptoConfig != null && !(cryptoConfig instanceof FrameworkCryptoConfig)) {
// This should not happen if the track went through a supportsFormatDrm() check, during track
// selection.
throw createRendererException(
new IllegalArgumentException("Expecting FrameworkMediaCrypto but found: " + mediaCrypto),
new IllegalArgumentException(
"Expecting FrameworkCryptoConfig but found: " + cryptoConfig),
inputFormat,
PlaybackException.ERROR_CODE_DRM_SCHEME_UNSUPPORTED);
}
return (FrameworkMediaCrypto) mediaCrypto;
return (FrameworkCryptoConfig) cryptoConfig;
}
/**

View File

@ -93,7 +93,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
public int supportsFormat(Format format) {
if (decoderFactory.supportsFormat(format)) {
return RendererCapabilities.create(
format.exoMediaCryptoType == null ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM);
format.cryptoType == C.CRYPTO_TYPE_NONE ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM);
} else {
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE);
}

View File

@ -783,9 +783,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
trackFormat = trackFormat.buildUpon().setAverageBitrate(icyHeaders.bitrate).build();
}
}
trackFormat =
trackFormat.copyWithExoMediaCryptoType(
drmSessionManager.getExoMediaCryptoType(trackFormat));
trackFormat = trackFormat.copyWithCryptoType(drmSessionManager.getCryptoType(trackFormat));
trackArray[i] = new TrackGroup(trackFormat);
}
trackState = new TrackState(new TrackGroupArray(trackArray), trackIsAudioVideoFlags);

View File

@ -899,8 +899,7 @@ public class SampleQueue implements TrackOutput {
outputFormatHolder.format =
drmSessionManager != null
? newFormat.copyWithExoMediaCryptoType(
drmSessionManager.getExoMediaCryptoType(newFormat))
? newFormat.copyWithCryptoType(drmSessionManager.getCryptoType(newFormat))
: newFormat;
outputFormatHolder.drmSession = currentDrmSession;
if (drmSessionManager == null) {

View File

@ -134,7 +134,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
public int supportsFormat(Format format) {
if (decoderFactory.supportsFormat(format)) {
return RendererCapabilities.create(
format.exoMediaCryptoType == null ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM);
format.cryptoType == C.CRYPTO_TYPE_NONE ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM);
} else if (MimeTypes.isText(format.sampleMimeType)) {
return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE);
} else {

View File

@ -36,6 +36,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.decoder.Decoder;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.decoder.DecoderException;
@ -43,7 +44,6 @@ import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.source.SampleStream.ReadDataResult;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
@ -529,14 +529,14 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
* Creates a decoder for the given format.
*
* @param format The format for which a decoder is required.
* @param mediaCrypto The {@link ExoMediaCrypto} object required for decoding encrypted content.
* @param cryptoConfig The {@link CryptoConfig} object required for decoding encrypted content.
* May be null and can be ignored if decoder does not handle encrypted content.
* @return The decoder.
* @throws DecoderException If an error occurred creating a suitable decoder.
*/
protected abstract Decoder<
VideoDecoderInputBuffer, ? extends VideoDecoderOutputBuffer, ? extends DecoderException>
createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) throws DecoderException;
createDecoder(Format format, @Nullable CryptoConfig cryptoConfig) throws DecoderException;
/**
* Renders the specified output buffer.
@ -664,10 +664,10 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
setDecoderDrmSession(sourceDrmSession);
ExoMediaCrypto mediaCrypto = null;
CryptoConfig cryptoConfig = null;
if (decoderDrmSession != null) {
mediaCrypto = decoderDrmSession.getMediaCrypto();
if (mediaCrypto == null) {
cryptoConfig = decoderDrmSession.getCryptoConfig();
if (cryptoConfig == null) {
DrmSessionException drmError = decoderDrmSession.getError();
if (drmError != null) {
// Continue for now. We may be able to avoid failure if a new input format causes the
@ -681,7 +681,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
try {
long decoderInitializingTimestamp = SystemClock.elapsedRealtime();
decoder = createDecoder(inputFormat, mediaCrypto);
decoder = createDecoder(inputFormat, cryptoConfig);
setDecoderOutputMode(outputMode);
long decoderInitializedTimestamp = SystemClock.elapsedRealtime();
eventDispatcher.decoderInitialized(

View File

@ -30,13 +30,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererConfiguration;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.decoder.DecoderException;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.testutil.FakeSampleStream;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.MimeTypes;
@ -75,7 +75,7 @@ public class DecoderAudioRendererTest {
}
@Override
protected FakeDecoder createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) {
protected FakeDecoder createDecoder(Format format, @Nullable CryptoConfig cryptoConfig) {
return new FakeDecoder();
}

View File

@ -41,8 +41,8 @@ import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.testutil.FakeCryptoConfig;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
@ -73,7 +73,7 @@ public final class SampleQueueTest {
private static final Format FORMAT_ENCRYPTED =
new Format.Builder().setId(/* id= */ "encrypted").setDrmInitData(new DrmInitData()).build();
private static final Format FORMAT_ENCRYPTED_WITH_EXO_MEDIA_CRYPTO_TYPE =
FORMAT_ENCRYPTED.copyWithExoMediaCryptoType(MockExoMediaCrypto.class);
FORMAT_ENCRYPTED.copyWithCryptoType(FakeCryptoConfig.TYPE);
private static final byte[] DATA = TestUtil.buildTestData(ALLOCATION_SIZE * 10);
/*
@ -1761,8 +1761,6 @@ public final class SampleQueueTest {
return format.buildUpon().setLabel(label).build();
}
private static final class MockExoMediaCrypto implements ExoMediaCrypto {}
private static final class MockDrmSessionManager implements DrmSessionManager {
private final DrmSession mockDrmSession;
@ -1772,8 +1770,8 @@ public final class SampleQueueTest {
this.mockDrmSession = mockDrmSession;
}
@Nullable
@Override
@Nullable
public DrmSession acquireSession(
Looper playbackLooper,
@Nullable DrmSessionEventListener.EventDispatcher eventDispatcher,
@ -1781,12 +1779,12 @@ public final class SampleQueueTest {
return format.drmInitData != null ? mockDrmSession : mockPlaceholderDrmSession;
}
@Nullable
@Override
public Class<? extends ExoMediaCrypto> getExoMediaCryptoType(Format format) {
@C.CryptoType
public int getCryptoType(Format format) {
return mockPlaceholderDrmSession != null || format.drmInitData != null
? MockExoMediaCrypto.class
: null;
? FakeCryptoConfig.TYPE
: C.CRYPTO_TYPE_NONE;
}
}
}

View File

@ -34,12 +34,12 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.RendererConfiguration;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.decoder.DecoderException;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.testutil.FakeSampleStream;
import com.google.android.exoplayer2.upstream.DefaultAllocator;
import com.google.android.exoplayer2.util.MimeTypes;
@ -128,7 +128,7 @@ public final class DecoderVideoRendererTest {
VideoDecoderInputBuffer,
? extends VideoDecoderOutputBuffer,
? extends DecoderException>
createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) {
createDecoder(Format format, @Nullable CryptoConfig cryptoConfig) {
return new SimpleDecoder<
VideoDecoderInputBuffer, VideoDecoderOutputBuffer, DecoderException>(
new VideoDecoderInputBuffer[10], new VideoDecoderOutputBuffer[10]) {

View File

@ -667,8 +667,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
Format[] formats = new Format[representations.size()];
for (int j = 0; j < formats.length; j++) {
Format format = representations.get(j).format;
formats[j] =
format.copyWithExoMediaCryptoType(drmSessionManager.getExoMediaCryptoType(format));
formats[j] = format.copyWithCryptoType(drmSessionManager.getCryptoType(format));
}
AdaptationSet firstAdaptationSet = adaptationSets.get(adaptationSetIndices[0]);

View File

@ -1411,8 +1411,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
Format[] exposedFormats = new Format[trackGroup.length];
for (int j = 0; j < trackGroup.length; j++) {
Format format = trackGroup.getFormat(j);
exposedFormats[j] =
format.copyWithExoMediaCryptoType(drmSessionManager.getExoMediaCryptoType(format));
exposedFormats[j] = format.copyWithCryptoType(drmSessionManager.getCryptoType(format));
}
trackGroups[i] = new TrackGroup(exposedFormats);
}

View File

@ -261,8 +261,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
for (int j = 0; j < manifestFormats.length; j++) {
Format manifestFormat = manifestFormats[j];
exposedFormats[j] =
manifestFormat.copyWithExoMediaCryptoType(
drmSessionManager.getExoMediaCryptoType(manifestFormat));
manifestFormat.copyWithCryptoType(drmSessionManager.getCryptoType(manifestFormat));
}
trackGroups[i] = new TrackGroup(exposedFormats);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2020 The Android Open Source Project
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,7 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.drm;
package com.google.android.exoplayer2.testutil;
/** {@link ExoMediaCrypto} type that cannot be used to handle any type of protected content. */
public final class UnsupportedMediaCrypto implements ExoMediaCrypto {}
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.decoder.CryptoConfig;
/** Fake {@link CryptoConfig}. */
public final class FakeCryptoConfig implements CryptoConfig {
public static final int TYPE = C.CRYPTO_TYPE_CUSTOM_BASE;
}

View File

@ -26,8 +26,9 @@ import android.os.Parcelable;
import android.os.PersistableBundle;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.decoder.CryptoConfig;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.drm.ExoMediaDrm;
import com.google.android.exoplayer2.drm.MediaDrmCallback;
import com.google.android.exoplayer2.drm.MediaDrmCallbackException;
@ -334,15 +335,16 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
}
@Override
public ExoMediaCrypto createMediaCrypto(byte[] sessionId) throws MediaCryptoException {
public CryptoConfig createCryptoConfig(byte[] sessionId) throws MediaCryptoException {
Assertions.checkState(referenceCount > 0);
Assertions.checkState(openSessionIds.contains(toByteList(sessionId)));
return new FakeExoMediaCrypto();
return new FakeCryptoConfig();
}
@Override
public Class<FakeExoMediaCrypto> getExoMediaCryptoType() {
return FakeExoMediaCrypto.class;
@C.CryptoType
public int getCryptoType() {
return FakeCryptoConfig.TYPE;
}
// Methods to facilitate testing
@ -389,8 +391,6 @@ public final class FakeExoMediaDrm implements ExoMediaDrm {
return ImmutableList.copyOf(Bytes.asList(byteArray));
}
private static class FakeExoMediaCrypto implements ExoMediaCrypto {}
/** An license server implementation to interact with {@link FakeExoMediaDrm}. */
public static class LicenseServer implements MediaDrmCallback {