/** * Parses a ContentProtection element. * * @param xpp The parser from which to read. * @throws XmlPullParserException If an error occurs parsing the element. * @throws IOException If an error occurs reading the element. * @return {@link SchemeData} parsed from the ContentProtection element, or null if the element is * unsupported. */ protected SchemeData parseContentProtection(XmlPullParser xpp) throws XmlPullParserException, IOException { String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri"); boolean isPlayReady = "urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95".equals(schemeIdUri); byte[] data = null; UUID uuid = null; boolean requiresSecureDecoder = false; 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")) { String robustnessLevel = xpp.getAttributeValue(null, "robustness_level"); requiresSecureDecoder = robustnessLevel != null && robustnessLevel.startsWith("HW"); } } while (!XmlPullParserUtil.isEndTag(xpp, "ContentProtection")); return data != null ? new SchemeData(uuid, MimeTypes.VIDEO_MP4, data, requiresSecureDecoder) : null; }
/** * Parses a ContentProtection element. * * @param xpp The parser from which to read. * @throws XmlPullParserException If an error occurs parsing the element. * @throws IOException If an error occurs reading the element. * @return {@link SchemeData} parsed from the ContentProtection element, or null if the element is * unsupported. */ protected SchemeData parseContentProtection(XmlPullParser xpp) throws XmlPullParserException, IOException { byte[] data = null; UUID uuid = null; boolean seenPsshElement = false; boolean requiresSecureDecoder = false; do { xpp.next(); // The cenc:pssh element is defined in 23001-7:2015. if (XmlPullParserUtil.isStartTag(xpp, "cenc:pssh") && xpp.next() == XmlPullParser.TEXT) { seenPsshElement = true; data = Base64.decode(xpp.getText(), Base64.DEFAULT); uuid = PsshAtomUtil.parseUuid(data); } else if (XmlPullParserUtil.isStartTag(xpp, "widevine:license")) { String robustnessLevel = xpp.getAttributeValue(null, "robustness_level"); requiresSecureDecoder = robustnessLevel != null && robustnessLevel.startsWith("HW"); } } while (!XmlPullParserUtil.isEndTag(xpp, "ContentProtection")); if (!seenPsshElement) { return null; } else if (uuid != null) { return new SchemeData(uuid, MimeTypes.VIDEO_MP4, data, requiresSecureDecoder); } else { Log.w(TAG, "Skipped unsupported ContentProtection element"); return null; } }
/** * Parses a ContentProtection element. * * @param xpp The parser from which to read. * @throws XmlPullParserException If an error occurs parsing the element. * @throws IOException If an error occurs reading the element. * @return The scheme type and/or {@link SchemeData} parsed from the ContentProtection element. * Either or both may be null, depending on the ContentProtection element being parsed. */ protected Pair<String, SchemeData> parseContentProtection(XmlPullParser xpp) throws XmlPullParserException, IOException { String schemeType = null; byte[] data = null; UUID uuid = null; boolean requiresSecureDecoder = false; String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri"); if (schemeIdUri != null) { switch (Util.toLowerInvariant(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 (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 = uuid != null ? new SchemeData(uuid, MimeTypes.VIDEO_MP4, data, requiresSecureDecoder) : null; return Pair.create(schemeType, schemeData); }
/** * Extracts {@link SchemeData} suitable for the given DRM scheme {@link UUID}. * * @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, 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); 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); } } if (matchingSchemeDatas.isEmpty()) { return null; } // For Widevine PSSH boxes, prefer V1 boxes from API 23 and V0 before. if (C.WIDEVINE_UUID.equals(uuid)) { for (int i = 0; i < matchingSchemeDatas.size(); i++) { SchemeData matchingSchemeData = matchingSchemeDatas.get(i); 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) { return matchingSchemeData; } } } // If we don't have any special handling, prefer the first matching scheme data. return matchingSchemeDatas.get(0); }
private static byte[] getSchemeInitData(SchemeData data, UUID uuid) { byte[] schemeInitData = data.data; if (Util.SDK_INT < 21) { // Prior to L the Widevine CDM required data to be extracted from the PSSH atom. byte[] psshData = PsshAtomUtil.parseSchemeSpecificData(schemeInitData, uuid); if (psshData == null) { // Extraction failed. schemeData isn't a Widevine PSSH atom, so leave it unchanged. } else { schemeInitData = psshData; } } return schemeInitData; }
@Override public DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData) { Assertions.checkState(this.playbackLooper == null || this.playbackLooper == playbackLooper); if (++openCount != 1) { return this; } if (this.playbackLooper == null) { this.playbackLooper = playbackLooper; mediaDrmHandler = new MediaDrmHandler(playbackLooper); postResponseHandler = new PostResponseHandler(playbackLooper); } requestHandlerThread = new HandlerThread("DrmRequestHandler"); requestHandlerThread.start(); postRequestHandler = new PostRequestHandler(requestHandlerThread.getLooper()); if (offlineLicenseKeySetId == null) { SchemeData schemeData = drmInitData.get(uuid); if (schemeData == null) { onError(new IllegalStateException("Media does not support uuid: " + uuid)); return this; } schemeInitData = schemeData.data; schemeMimeType = schemeData.mimeType; if (Util.SDK_INT < 21) { // Prior to L the Widevine CDM required data to be extracted from the PSSH atom. byte[] psshData = PsshAtomUtil.parseSchemeSpecificData(schemeInitData, C.WIDEVINE_UUID); if (psshData == null) { // Extraction failed. schemeData isn't a Widevine PSSH atom, so leave it unchanged. } else { schemeInitData = psshData; } } if (Util.SDK_INT < 26 && C.CLEARKEY_UUID.equals(uuid) && (MimeTypes.VIDEO_MP4.equals(schemeMimeType) || MimeTypes.AUDIO_MP4.equals(schemeMimeType))) { // Prior to API level 26 the ClearKey CDM only accepted "cenc" as the scheme for MP4. schemeMimeType = CENC_SCHEME_MIME_TYPE; } } state = STATE_OPENING; openInternal(true); return this; }
@Override public Object build() { return new ProtectionElement(uuid, PsshAtomUtil.buildPsshAtom(uuid, initData)); }