Add DRM format support checks for MediaSource provided DRM

PiperOrigin-RevId: 256161522
This commit is contained in:
aquilescanta 2019-07-02 15:33:50 +01:00 committed by Oliver Woodman
parent 16bf7f9106
commit 7408b4355a
12 changed files with 184 additions and 30 deletions

View file

@ -77,12 +77,17 @@ public final class LibopusAudioRenderer extends SimpleDecoderAudioRenderer {
@Override
protected int supportsFormatInternal(DrmSessionManager<ExoMediaCrypto> drmSessionManager,
Format format) {
boolean drmIsSupported =
format.drmInitData == null
|| OpusLibrary.matchesExpectedExoMediaCryptoType(format.exoMediaCryptoType)
|| (format.exoMediaCryptoType == null
&& supportsFormatDrm(drmSessionManager, format.drmInitData));
if (!OpusLibrary.isAvailable()
|| !MimeTypes.AUDIO_OPUS.equalsIgnoreCase(format.sampleMimeType)) {
return FORMAT_UNSUPPORTED_TYPE;
} else if (!supportsOutput(format.channelCount, C.ENCODING_PCM_16BIT)) {
return FORMAT_UNSUPPORTED_SUBTYPE;
} else if (!supportsFormatDrm(drmSessionManager, format.drmInitData)) {
} else if (!drmIsSupported) {
return FORMAT_UNSUPPORTED_DRM;
} else {
return FORMAT_HANDLED;
@ -110,5 +115,4 @@ public final class LibopusAudioRenderer extends SimpleDecoderAudioRenderer {
Format.NO_VALUE, decoder.getChannelCount(), decoder.getSampleRate(), C.ENCODING_PCM_16BIT,
null, null, 0, null);
}
}

View file

@ -16,7 +16,9 @@
package com.google.android.exoplayer2.ext.opus;
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.
@ -28,6 +30,7 @@ public final class OpusLibrary {
}
private static final LibraryLoader LOADER = new LibraryLoader("opusV2JNI");
private static Class<? extends ExoMediaCrypto> exoMediaCryptoType;
private OpusLibrary() {}
@ -36,10 +39,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 libraries The names of the Opus native libraries.
*/
public static void setLibraries(String... libraries) {
public static void setLibraries(
Class<? extends ExoMediaCrypto> exoMediaCryptoType, String... libraries) {
LOADER.setLibraries(libraries);
OpusLibrary.exoMediaCryptoType = exoMediaCryptoType;
}
/**
@ -56,6 +63,15 @@ 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);
}
public static native String opusGetVersion();
public static native boolean opusIsSecureDecodeSupported();
}

View file

@ -284,7 +284,13 @@ public class LibvpxVideoRenderer extends BaseRenderer {
public int supportsFormat(Format format) {
if (!VpxLibrary.isAvailable() || !MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType)) {
return FORMAT_UNSUPPORTED_TYPE;
} else if (!supportsFormatDrm(drmSessionManager, format.drmInitData)) {
}
boolean drmIsSupported =
format.drmInitData == null
|| VpxLibrary.matchesExpectedExoMediaCryptoType(format.exoMediaCryptoType)
|| (format.exoMediaCryptoType == null
&& supportsFormatDrm(drmSessionManager, format.drmInitData));
if (!drmIsSupported) {
return FORMAT_UNSUPPORTED_DRM;
}
return FORMAT_HANDLED | ADAPTIVE_SEAMLESS;

View file

@ -16,7 +16,9 @@
package com.google.android.exoplayer2.ext.vp9;
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.
@ -28,6 +30,7 @@ public final class VpxLibrary {
}
private static final LibraryLoader LOADER = new LibraryLoader("vpx", "vpxV2JNI");
private static Class<? extends ExoMediaCrypto> exoMediaCryptoType;
private VpxLibrary() {}
@ -36,10 +39,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 libraries The names of the Vpx native libraries.
*/
public static void setLibraries(String... libraries) {
public static void setLibraries(
Class<? extends ExoMediaCrypto> exoMediaCryptoType, String... libraries) {
LOADER.setLibraries(libraries);
VpxLibrary.exoMediaCryptoType = exoMediaCryptoType;
}
/**
@ -74,6 +81,15 @@ 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);
}
private static native String vpxGetVersion();
private static native String vpxGetBuildConfig();
public static native boolean vpxIsSecureDecodeSupported();

View file

@ -19,6 +19,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
@ -163,6 +165,15 @@ public final class Format implements Parcelable {
*/
public final int accessibilityChannel;
// Provided by source.
/**
* The type of the {@link ExoMediaCrypto} provided by the media source, if the media source can
* acquire a {@link DrmSession} for {@link #drmInitData}. Null if the media source cannot acquire
* a session for {@link #drmInitData}, or if not applicable.
*/
@Nullable public final Class<? extends ExoMediaCrypto> exoMediaCryptoType;
// Lazily initialized hashcode.
private int hashCode;
@ -236,7 +247,8 @@ public final class Format implements Parcelable {
/* encoderDelay= */ NO_VALUE,
/* encoderPadding= */ NO_VALUE,
/* language= */ null,
/* accessibilityChannel= */ NO_VALUE);
/* accessibilityChannel= */ NO_VALUE,
/* exoMediaCryptoType= */ null);
}
public static Format createVideoSampleFormat(
@ -340,7 +352,8 @@ public final class Format implements Parcelable {
/* encoderDelay= */ NO_VALUE,
/* encoderPadding= */ NO_VALUE,
/* language= */ null,
/* accessibilityChannel= */ NO_VALUE);
/* accessibilityChannel= */ NO_VALUE,
/* exoMediaCryptoType= */ null);
}
// Audio.
@ -413,7 +426,8 @@ public final class Format implements Parcelable {
/* encoderDelay= */ NO_VALUE,
/* encoderPadding= */ NO_VALUE,
language,
/* accessibilityChannel= */ NO_VALUE);
/* accessibilityChannel= */ NO_VALUE,
/* exoMediaCryptoType= */ null);
}
public static Format createAudioSampleFormat(
@ -518,7 +532,8 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
/* accessibilityChannel= */ NO_VALUE);
/* accessibilityChannel= */ NO_VALUE,
/* exoMediaCryptoType= */ null);
}
// Text.
@ -585,7 +600,8 @@ public final class Format implements Parcelable {
/* encoderDelay= */ NO_VALUE,
/* encoderPadding= */ NO_VALUE,
language,
accessibilityChannel);
accessibilityChannel,
/* exoMediaCryptoType= */ null);
}
public static Format createTextSampleFormat(
@ -698,7 +714,8 @@ public final class Format implements Parcelable {
/* encoderDelay= */ NO_VALUE,
/* encoderPadding= */ NO_VALUE,
language,
accessibilityChannel);
accessibilityChannel,
/* exoMediaCryptoType= */ null);
}
// Image.
@ -740,7 +757,8 @@ public final class Format implements Parcelable {
/* encoderDelay= */ NO_VALUE,
/* encoderPadding= */ NO_VALUE,
language,
/* accessibilityChannel= */ NO_VALUE);
/* accessibilityChannel= */ NO_VALUE,
/* exoMediaCryptoType= */ null);
}
// Generic.
@ -804,7 +822,8 @@ public final class Format implements Parcelable {
/* encoderDelay= */ NO_VALUE,
/* encoderPadding= */ NO_VALUE,
language,
/* accessibilityChannel= */ NO_VALUE);
/* accessibilityChannel= */ NO_VALUE,
/* exoMediaCryptoType= */ null);
}
public static Format createSampleFormat(
@ -837,7 +856,8 @@ public final class Format implements Parcelable {
/* encoderDelay= */ NO_VALUE,
/* encoderPadding= */ NO_VALUE,
/* language= */ null,
/* accessibilityChannel= */ NO_VALUE);
/* accessibilityChannel= */ NO_VALUE,
/* exoMediaCryptoType= */ null);
}
public static Format createSampleFormat(
@ -874,7 +894,8 @@ public final class Format implements Parcelable {
/* encoderDelay= */ NO_VALUE,
/* encoderPadding= */ NO_VALUE,
/* language= */ null,
/* accessibilityChannel= */ NO_VALUE);
/* accessibilityChannel= */ NO_VALUE,
/* exoMediaCryptoType= */ null);
}
/* package */ Format(
@ -910,7 +931,9 @@ public final class Format implements Parcelable {
int encoderPadding,
// Audio and text specific.
@Nullable String language,
int accessibilityChannel) {
int accessibilityChannel,
// Provided by source.
@Nullable Class<? extends ExoMediaCrypto> exoMediaCryptoType) {
this.id = id;
this.label = label;
this.selectionFlags = selectionFlags;
@ -946,6 +969,8 @@ public final class Format implements Parcelable {
// Audio and text specific.
this.language = Util.normalizeLanguageCode(language);
this.accessibilityChannel = accessibilityChannel;
// Provided by source.
this.exoMediaCryptoType = exoMediaCryptoType;
}
@SuppressWarnings("ResourceType")
@ -988,6 +1013,8 @@ public final class Format implements Parcelable {
// Audio and text specific.
language = in.readString();
accessibilityChannel = in.readInt();
// Provided by source.
exoMediaCryptoType = null;
}
public Format copyWithMaxInputSize(int maxInputSize) {
@ -1019,7 +1046,8 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
accessibilityChannel);
accessibilityChannel,
exoMediaCryptoType);
}
public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) {
@ -1051,7 +1079,8 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
accessibilityChannel);
accessibilityChannel,
exoMediaCryptoType);
}
public Format copyWithContainerInfo(
@ -1099,7 +1128,8 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
accessibilityChannel);
accessibilityChannel,
exoMediaCryptoType);
}
@SuppressWarnings("ReferenceEquality")
@ -1178,7 +1208,8 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
accessibilityChannel);
accessibilityChannel,
exoMediaCryptoType);
}
public Format copyWithGaplessInfo(int encoderDelay, int encoderPadding) {
@ -1210,7 +1241,8 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
accessibilityChannel);
accessibilityChannel,
exoMediaCryptoType);
}
public Format copyWithFrameRate(float frameRate) {
@ -1242,7 +1274,8 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
accessibilityChannel);
accessibilityChannel,
exoMediaCryptoType);
}
public Format copyWithDrmInitData(@Nullable DrmInitData drmInitData) {
@ -1274,7 +1307,8 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
accessibilityChannel);
accessibilityChannel,
exoMediaCryptoType);
}
public Format copyWithMetadata(@Nullable Metadata metadata) {
@ -1306,7 +1340,8 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
accessibilityChannel);
accessibilityChannel,
exoMediaCryptoType);
}
public Format copyWithRotationDegrees(int rotationDegrees) {
@ -1338,7 +1373,8 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
accessibilityChannel);
accessibilityChannel,
exoMediaCryptoType);
}
public Format copyWithBitrate(int bitrate) {
@ -1370,7 +1406,8 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
accessibilityChannel);
accessibilityChannel,
exoMediaCryptoType);
}
public Format copyWithVideoSize(int width, int height) {
@ -1402,7 +1439,41 @@ public final class Format implements Parcelable {
encoderDelay,
encoderPadding,
language,
accessibilityChannel);
accessibilityChannel,
exoMediaCryptoType);
}
public Format copyWithExoMediaCryptoType(Class<? extends ExoMediaCrypto> exoMediaCryptoType) {
return new Format(
id,
label,
selectionFlags,
roleFlags,
bitrate,
codecs,
metadata,
containerMimeType,
sampleMimeType,
maxInputSize,
initializationData,
drmInitData,
subsampleOffsetUs,
width,
height,
frameRate,
rotationDegrees,
pixelWidthHeightRatio,
projectionData,
stereoMode,
colorInfo,
channelCount,
sampleRate,
pcmEncoding,
encoderDelay,
encoderPadding,
language,
accessibilityChannel,
exoMediaCryptoType);
}
/**
@ -1481,6 +1552,8 @@ public final class Format implements Parcelable {
// Audio and text specific.
result = 31 * result + (language == null ? 0 : language.hashCode());
result = 31 * result + accessibilityChannel;
// Provided by source.
result = 31 * result + (exoMediaCryptoType == null ? 0 : exoMediaCryptoType.hashCode());
hashCode = result;
}
return hashCode;
@ -1516,6 +1589,7 @@ public final class Format implements Parcelable {
&& accessibilityChannel == other.accessibilityChannel
&& 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)

View file

@ -308,7 +308,11 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
return FORMAT_UNSUPPORTED_TYPE;
}
int tunnelingSupport = Util.SDK_INT >= 21 ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED;
boolean supportsFormatDrm = supportsFormatDrm(drmSessionManager, format.drmInitData);
boolean supportsFormatDrm =
format.drmInitData == null
|| FrameworkMediaCrypto.class.equals(format.exoMediaCryptoType)
|| (format.exoMediaCryptoType == null
&& supportsFormatDrm(drmSessionManager, format.drmInitData));
if (supportsFormatDrm
&& allowPassthrough(format.channelCount, mimeType)
&& mediaCodecSelector.getPassthroughDecoderInfo() != null) {

View file

@ -436,6 +436,12 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto>
return session;
}
@Override
@Nullable
public Class<T> getExoMediaCryptoType(DrmInitData drmInitData) {
return canAcquireSession(drmInitData) ? mediaDrm.getExoMediaCryptoType() : null;
}
// ProvisioningManager implementation.
@Override

View file

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm;
import android.os.Looper;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@ -49,6 +50,12 @@ public interface DrmSessionManager<T extends ExoMediaCrypto> {
new DrmSession.DrmSessionException(
new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME)));
}
@Override
@Nullable
public Class<ExoMediaCrypto> getExoMediaCryptoType(DrmInitData drmInitData) {
return null;
}
};
/** Flags that control the handling of DRM protected content. */
@ -99,4 +106,11 @@ public interface DrmSessionManager<T extends ExoMediaCrypto> {
default int getFlags() {
return 0;
}
/**
* Returns the {@link ExoMediaCrypto} type returned by sessions acquired using the given {@link
* DrmInitData}, or null if a session cannot be acquired with the given {@link DrmInitData}.
*/
@Nullable
Class<? extends ExoMediaCrypto> getExoMediaCryptoType(DrmInitData drmInitData);
}

View file

@ -271,4 +271,7 @@ public interface ExoMediaDrm<T extends ExoMediaCrypto> {
* @throws MediaCryptoException If the instance can't be created.
*/
T createMediaCrypto(byte[] sessionId) throws MediaCryptoException;
/** Returns the {@link ExoMediaCrypto} type created by {@link #createMediaCrypto(byte[])}. */
Class<T> getExoMediaCryptoType();
}

View file

@ -225,6 +225,11 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
adjustUuid(uuid), initData, forceAllowInsecureDecoderComponents);
}
@Override
public Class<FrameworkMediaCrypto> getExoMediaCryptoType() {
return FrameworkMediaCrypto.class;
}
private static SchemeData getSchemeData(UUID uuid, List<SchemeData> schemeDatas) {
if (!C.WIDEVINE_UUID.equals(uuid)) {
// For non-Widevine CDMs always use the first scheme data.

View file

@ -336,7 +336,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
if (decoderInfos.isEmpty()) {
return FORMAT_UNSUPPORTED_SUBTYPE;
}
if (!supportsFormatDrm(drmSessionManager, drmInitData)) {
boolean supportsFormatDrm =
format.drmInitData == null
|| FrameworkMediaCrypto.class.equals(format.exoMediaCryptoType)
|| (format.exoMediaCryptoType == null
&& supportsFormatDrm(drmSessionManager, format.drmInitData));
if (!supportsFormatDrm) {
return FORMAT_UNSUPPORTED_DRM;
}
// Check capabilities for the first decoder in the list, which takes priority.

View file

@ -91,7 +91,8 @@ public final class FormatTest {
/* encoderDelay= */ 1001,
/* encoderPadding= */ 1002,
"language",
/* accessibilityChannel= */ Format.NO_VALUE);
/* accessibilityChannel= */ Format.NO_VALUE,
/* exoMediaCryptoType= */ null);
Parcel parcel = Parcel.obtain();
formatToParcel.writeToParcel(parcel, 0);