提交 8c424798 编写于 作者: A aquilescanta 提交者: Oliver Woodman

Fill manifest drm info with media files' pssh when needed

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=174185407
上级 46172ffd
......@@ -21,6 +21,7 @@ import android.media.MediaFormat;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
......@@ -464,8 +465,8 @@ public final class Format implements Parcelable {
float frameRate = this.frameRate == NO_VALUE ? manifestFormat.frameRate : this.frameRate;
@C.SelectionFlags int selectionFlags = this.selectionFlags | manifestFormat.selectionFlags;
String language = this.language == null ? manifestFormat.language : this.language;
DrmInitData drmInitData = manifestFormat.drmInitData != null ? manifestFormat.drmInitData
: this.drmInitData;
DrmInitData drmInitData = manifestFormat.drmInitData != null
? getFilledManifestDrmData(manifestFormat.drmInitData) : this.drmInitData;
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width,
height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
......@@ -731,4 +732,42 @@ public final class Format implements Parcelable {
};
private DrmInitData getFilledManifestDrmData(DrmInitData manifestDrmData) {
// All exposed SchemeDatas must include key request information.
ArrayList<SchemeData> exposedSchemeDatas = new ArrayList<>();
ArrayList<SchemeData> emptySchemeDatas = new ArrayList<>();
for (int i = 0; i < manifestDrmData.schemeDataCount; i++) {
SchemeData schemeData = manifestDrmData.get(i);
if (schemeData.hasData()) {
exposedSchemeDatas.add(schemeData);
} else /* needs initialization data filling */ {
emptySchemeDatas.add(schemeData);
}
}
if (emptySchemeDatas.isEmpty()) {
// Manifest DRM information is complete.
return manifestDrmData;
} else if (drmInitData == null) {
// The manifest DRM data needs filling but this format does not include enough information to
// do it. A subset of the manifest's scheme datas should not be exposed because a
// DrmSessionManager could decide it does not support the format, while the missing
// information comes in a format feed immediately after.
return null;
}
int needFillingCount = emptySchemeDatas.size();
for (int i = 0; i < drmInitData.schemeDataCount; i++) {
SchemeData mediaSchemeData = drmInitData.get(i);
for (int j = 0; j < needFillingCount; j++) {
if (mediaSchemeData.canReplace(emptySchemeDatas.get(j))) {
exposedSchemeDatas.add(mediaSchemeData);
break;
}
}
}
return exposedSchemeDatas.isEmpty() ? null : new DrmInitData(manifestDrmData.schemeType,
exposedSchemeDatas.toArray(new SchemeData[exposedSchemeDatas.size()]));
}
}
......@@ -340,7 +340,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
@Override
public boolean canAcquireSession(@NonNull DrmInitData drmInitData) {
SchemeData schemeData = getSchemeData(drmInitData, uuid);
SchemeData schemeData = getSchemeData(drmInitData, uuid, true);
if (schemeData == null) {
// No data for this manager's scheme.
return false;
......@@ -371,7 +371,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
byte[] initData = null;
String mimeType = null;
if (offlineLicenseKeySetId == null) {
SchemeData data = getSchemeData(drmInitData, uuid);
SchemeData data = getSchemeData(drmInitData, uuid, false);
if (data == null) {
final IllegalStateException error = new IllegalStateException(
"Media does not support uuid: " + uuid);
......@@ -467,15 +467,19 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
*
* @param drmInitData The {@link DrmInitData} from which to extract the {@link SchemeData}.
* @param uuid The UUID.
* @param allowMissingData Whether a {@link SchemeData} with null {@link SchemeData#data} may be
* returned.
* @return The extracted {@link SchemeData}, or null if no suitable data is present.
*/
private static SchemeData getSchemeData(DrmInitData drmInitData, UUID uuid) {
private static SchemeData getSchemeData(DrmInitData drmInitData, UUID uuid,
boolean allowMissingData) {
// Look for matching scheme data (matching the Common PSSH box for ClearKey).
List<SchemeData> matchingSchemeDatas = new ArrayList<>(drmInitData.schemeDataCount);
for (int i = 0; i < drmInitData.schemeDataCount; i++) {
SchemeData schemeData = drmInitData.get(i);
if (schemeData.matches(uuid)
|| (C.CLEARKEY_UUID.equals(uuid) && schemeData.matches(C.COMMON_PSSH_UUID))) {
boolean uuidMatches = schemeData.matches(uuid)
|| (C.CLEARKEY_UUID.equals(uuid) && schemeData.matches(C.COMMON_PSSH_UUID));
if (uuidMatches && (schemeData.data != null || allowMissingData)) {
matchingSchemeDatas.add(schemeData);
}
}
......@@ -488,7 +492,8 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
if (C.WIDEVINE_UUID.equals(uuid)) {
for (int i = 0; i < matchingSchemeDatas.size(); i++) {
SchemeData matchingSchemeData = matchingSchemeDatas.get(i);
int version = PsshAtomUtil.parseVersion(matchingSchemeData.data);
int version = matchingSchemeData.hasData()
? PsshAtomUtil.parseVersion(matchingSchemeData.data) : -1;
if (Util.SDK_INT < 23 && version == 0) {
return matchingSchemeData;
} else if (Util.SDK_INT >= 23 && version == 1) {
......
......@@ -54,6 +54,14 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
this(null, false, schemeDatas.toArray(new SchemeData[schemeDatas.size()]));
}
/**
* @param schemeType See {@link #schemeType}.
* @param schemeDatas Scheme initialization data for possibly multiple DRM schemes.
*/
public DrmInitData(String schemeType, List<SchemeData> schemeDatas) {
this(schemeType, false, schemeDatas.toArray(new SchemeData[schemeDatas.size()]));
}
/**
* @param schemeDatas Scheme initialization data for possibly multiple DRM schemes.
*/
......@@ -62,7 +70,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
}
/**
* @param schemeType The protection scheme type, or null if not applicable or unknown.
* @param schemeType See {@link #schemeType}.
* @param schemeDatas Scheme initialization data for possibly multiple DRM schemes.
*/
public DrmInitData(@Nullable String schemeType, SchemeData... schemeDatas) {
......@@ -203,7 +211,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
*/
public final String mimeType;
/**
* The initialization data.
* The initialization data. May be null for scheme support checks only.
*/
public final byte[] data;
/**
......@@ -214,8 +222,8 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
/**
* @param uuid The {@link UUID} of the DRM scheme, or {@link C#UUID_NIL} if the data is
* universal (i.e. applies to all schemes).
* @param mimeType The mimeType of the initialization data.
* @param data The initialization data.
* @param mimeType See {@link #mimeType}.
* @param data See {@link #data}.
*/
public SchemeData(UUID uuid, String mimeType, byte[] data) {
this(uuid, mimeType, data, false);
......@@ -224,14 +232,14 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
/**
* @param uuid The {@link UUID} of the DRM scheme, or {@link C#UUID_NIL} if the data is
* universal (i.e. applies to all schemes).
* @param mimeType The mimeType of the initialization data.
* @param data The initialization data.
* @param requiresSecureDecryption Whether secure decryption is required.
* @param mimeType See {@link #mimeType}.
* @param data See {@link #data}.
* @param requiresSecureDecryption See {@link #requiresSecureDecryption}.
*/
public SchemeData(UUID uuid, String mimeType, byte[] data, boolean requiresSecureDecryption) {
this.uuid = Assertions.checkNotNull(uuid);
this.mimeType = Assertions.checkNotNull(mimeType);
this.data = Assertions.checkNotNull(data);
this.data = data;
this.requiresSecureDecryption = requiresSecureDecryption;
}
......@@ -252,6 +260,23 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
return C.UUID_NIL.equals(uuid) || schemeUuid.equals(uuid);
}
/**
* Returns whether this {@link SchemeData} can be used to replace {@code other}.
*
* @param other A {@link SchemeData}.
* @return Whether this {@link SchemeData} can be used to replace {@code other}.
*/
public boolean canReplace(SchemeData other) {
return hasData() && !other.hasData() && matches(other.uuid);
}
/**
* Returns whether {@link #data} is non-null.
*/
public boolean hasData() {
return data != null;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof SchemeData)) {
......
......@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm;
import android.annotation.TargetApi;
import android.os.Looper;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
/**
* Manages a DRM session.
......@@ -39,7 +40,8 @@ public interface DrmSessionManager<T extends ExoMediaCrypto> {
* must be returned to {@link #releaseSession(DrmSession)} when it is no longer required.
*
* @param playbackLooper The looper associated with the media playback thread.
* @param drmInitData DRM initialization data.
* @param drmInitData DRM initialization data. All contained {@link SchemeData}s must contain
* non-null {@link SchemeData#data}.
* @return The DRM session.
*/
DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData);
......
......@@ -346,45 +346,54 @@ public class DashManifestParser extends DefaultHandler
protected Pair<String, SchemeData> parseContentProtection(XmlPullParser xpp)
throws XmlPullParserException, IOException {
String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri");
boolean isPlayReady = "urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95".equals(schemeIdUri);
String schemeType = null;
byte[] data = null;
UUID uuid = null;
boolean requiresSecureDecoder = false;
if ("urn:mpeg:dash:mp4protection:2011".equals(schemeIdUri)) {
schemeType = xpp.getAttributeValue(null, "value");
String defaultKid = xpp.getAttributeValue(null, "cenc:default_KID");
if (defaultKid != null && !"00000000-0000-0000-0000-000000000000".equals(defaultKid)) {
UUID keyId = UUID.fromString(defaultKid);
data = PsshAtomUtil.buildPsshAtom(C.COMMON_PSSH_UUID, new UUID[] {keyId}, null);
uuid = C.COMMON_PSSH_UUID;
}
switch (schemeIdUri) {
case "urn:mpeg:dash:mp4protection:2011":
schemeType = xpp.getAttributeValue(null, "value");
String defaultKid = xpp.getAttributeValue(null, "cenc:default_KID");
if (defaultKid != null && !"00000000-0000-0000-0000-000000000000".equals(defaultKid)) {
UUID keyId = UUID.fromString(defaultKid);
data = PsshAtomUtil.buildPsshAtom(C.COMMON_PSSH_UUID, new UUID[] {keyId}, null);
uuid = C.COMMON_PSSH_UUID;
}
break;
case "urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95":
uuid = C.PLAYREADY_UUID;
break;
case "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed":
uuid = C.WIDEVINE_UUID;
break;
default:
break;
}
do {
xpp.next();
if (data == null && XmlPullParserUtil.isStartTag(xpp, "cenc:pssh")
&& xpp.next() == XmlPullParser.TEXT) {
// The cenc:pssh element is defined in 23001-7:2015.
data = Base64.decode(xpp.getText(), Base64.DEFAULT);
uuid = PsshAtomUtil.parseUuid(data);
if (uuid == null) {
Log.w(TAG, "Skipping malformed cenc:pssh data");
data = null;
}
} else if (data == null && isPlayReady && XmlPullParserUtil.isStartTag(xpp, "mspr:pro")
&& xpp.next() == XmlPullParser.TEXT) {
// The mspr:pro element is defined in DASH Content Protection using Microsoft PlayReady.
data = PsshAtomUtil.buildPsshAtom(C.PLAYREADY_UUID,
Base64.decode(xpp.getText(), Base64.DEFAULT));
uuid = C.PLAYREADY_UUID;
} else if (XmlPullParserUtil.isStartTag(xpp, "widevine:license")) {
if (XmlPullParserUtil.isStartTag(xpp, "widevine:license")) {
String robustnessLevel = xpp.getAttributeValue(null, "robustness_level");
requiresSecureDecoder = robustnessLevel != null && robustnessLevel.startsWith("HW");
} else if (data == null) {
if (XmlPullParserUtil.isStartTag(xpp, "cenc:pssh") && xpp.next() == XmlPullParser.TEXT) {
// The cenc:pssh element is defined in 23001-7:2015.
data = Base64.decode(xpp.getText(), Base64.DEFAULT);
uuid = PsshAtomUtil.parseUuid(data);
if (uuid == null) {
Log.w(TAG, "Skipping malformed cenc:pssh data");
data = null;
}
} else if (uuid == C.PLAYREADY_UUID && XmlPullParserUtil.isStartTag(xpp, "mspr:pro")
&& xpp.next() == XmlPullParser.TEXT) {
// The mspr:pro element is defined in DASH Content Protection using Microsoft PlayReady.
data = PsshAtomUtil.buildPsshAtom(C.PLAYREADY_UUID,
Base64.decode(xpp.getText(), Base64.DEFAULT));
}
}
} while (!XmlPullParserUtil.isEndTag(xpp, "ContentProtection"));
SchemeData schemeData = data != null
SchemeData schemeData = uuid != null
? new SchemeData(uuid, MimeTypes.VIDEO_MP4, data, requiresSecureDecoder) : null;
return Pair.create(schemeType, schemeData);
}
......@@ -518,10 +527,8 @@ public class DashManifestParser extends DefaultHandler
ArrayList<SchemeData> drmSchemeDatas = representationInfo.drmSchemeDatas;
drmSchemeDatas.addAll(extraDrmSchemeDatas);
if (!drmSchemeDatas.isEmpty()) {
DrmInitData drmInitData = new DrmInitData(drmSchemeDatas);
if (drmSchemeType != null) {
drmInitData = drmInitData.copyWithSchemeType(drmSchemeType);
}
filterRedundantIncompleteSchemeDatas(drmSchemeDatas);
DrmInitData drmInitData = new DrmInitData(drmSchemeType, drmSchemeDatas);
format = format.copyWithDrmInitData(drmInitData);
}
ArrayList<Descriptor> inbandEventStreams = representationInfo.inbandEventStreams;
......@@ -728,6 +735,25 @@ public class DashManifestParser extends DefaultHandler
// Utility methods.
/**
* Removes unnecessary {@link SchemeData}s with null {@link SchemeData#data}.
*/
private static void filterRedundantIncompleteSchemeDatas(ArrayList<SchemeData> schemeDatas) {
for (int i = schemeDatas.size() - 1; i >= 0; i--) {
SchemeData schemeData = schemeDatas.get(i);
if (!schemeData.hasData()) {
for (int j = 0; j < schemeDatas.size(); j++) {
if (schemeDatas.get(j).canReplace(schemeData)) {
// schemeData is incomplete, but there is another matching SchemeData which does contain
// data, so we remove the incomplete one.
schemeDatas.remove(i);
break;
}
}
}
}
}
/**
* Derives a sample mimeType from a container mimeType and codecs attribute.
*
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册