Fix parsing of version 1 pssh boxes, and ignore version 2+.
Issue: #1195 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=117155301
This commit is contained in:
parent
64d7f2f846
commit
2b4dcbef3f
@ -32,6 +32,7 @@ import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
@ -56,6 +57,8 @@ import java.util.regex.Pattern;
|
||||
public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
implements UriLoadable.Parser<MediaPresentationDescription> {
|
||||
|
||||
private static final String TAG = "MediaPresentationDescriptionParser";
|
||||
|
||||
private static final Pattern FRAME_RATE_PATTERN = Pattern.compile("(\\d+)(?:/(\\d+))?");
|
||||
|
||||
private final String contentId;
|
||||
@ -244,7 +247,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
seenFirstBaseUrl = true;
|
||||
}
|
||||
} else if (ParserUtil.isStartTag(xpp, "ContentProtection")) {
|
||||
contentProtectionsBuilder.addAdaptationSetProtection(parseContentProtection(xpp));
|
||||
ContentProtection contentProtection = parseContentProtection(xpp);
|
||||
if (contentProtection != null) {
|
||||
contentProtectionsBuilder.addAdaptationSetProtection(contentProtection);
|
||||
}
|
||||
} else if (ParserUtil.isStartTag(xpp, "ContentComponent")) {
|
||||
language = checkLanguageConsistency(language, xpp.getAttributeValue(null, "lang"));
|
||||
contentType = checkContentTypeConsistency(contentType, parseContentType(xpp));
|
||||
@ -300,10 +306,11 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a ContentProtection element.
|
||||
* Parses a {@link ContentProtection} element.
|
||||
*
|
||||
* @throws XmlPullParserException If an error occurs parsing the element.
|
||||
* @throws IOException If an error occurs reading the element.
|
||||
* @return The parsed {@link ContentProtection} element, or null if the element is unsupported.
|
||||
**/
|
||||
protected ContentProtection parseContentProtection(XmlPullParser xpp)
|
||||
throws XmlPullParserException, IOException {
|
||||
@ -312,16 +319,17 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
SchemeInitData data = null;
|
||||
do {
|
||||
xpp.next();
|
||||
// The cenc:pssh element is defined in 23001-7:2015
|
||||
// The cenc:pssh element is defined in 23001-7:2015.
|
||||
if (ParserUtil.isStartTag(xpp, "cenc:pssh") && xpp.next() == XmlPullParser.TEXT) {
|
||||
data = new SchemeInitData(MimeTypes.VIDEO_MP4,
|
||||
Base64.decode(xpp.getText(), Base64.DEFAULT));
|
||||
uuid = PsshAtomUtil.parseUuid(data.data);
|
||||
if (uuid == null) {
|
||||
throw new ParserException("Invalid pssh atom in cenc:pssh element");
|
||||
}
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "ContentProtection"));
|
||||
if (uuid == null) {
|
||||
Log.w(TAG, "Skipped unsupported ContentProtection element");
|
||||
return null;
|
||||
}
|
||||
return buildContentProtection(schemeIdUri, uuid, data);
|
||||
}
|
||||
|
||||
@ -379,7 +387,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
} else if (ParserUtil.isStartTag(xpp, "SegmentTemplate")) {
|
||||
segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase);
|
||||
} else if (ParserUtil.isStartTag(xpp, "ContentProtection")) {
|
||||
contentProtectionsBuilder.addRepresentationProtection(parseContentProtection(xpp));
|
||||
ContentProtection contentProtection = parseContentProtection(xpp);
|
||||
if (contentProtection != null) {
|
||||
contentProtectionsBuilder.addAdaptationSetProtection(contentProtection);
|
||||
}
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "Representation"));
|
||||
|
||||
|
@ -33,10 +33,13 @@ import com.google.android.exoplayer.util.NalUnitUtil;
|
||||
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Facilitates the extraction of data from the fragmented mp4 container format.
|
||||
@ -45,6 +48,8 @@ import java.util.Stack;
|
||||
*/
|
||||
public final class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
private static final String TAG = "FragmentedMp4Extractor";
|
||||
|
||||
/**
|
||||
* Flag to work around an issue in some video streams where every frame is marked as a sync frame.
|
||||
* The workaround overrides the sync frame flags in the stream, forcing them to false except for
|
||||
@ -299,8 +304,13 @@ public final class FragmentedMp4Extractor implements Extractor {
|
||||
drmInitData = new DrmInitData.Mapped();
|
||||
}
|
||||
byte[] psshData = child.data.data;
|
||||
drmInitData.put(PsshAtomUtil.parseUuid(psshData),
|
||||
new SchemeInitData(MimeTypes.VIDEO_MP4, psshData));
|
||||
UUID uuid = PsshAtomUtil.parseUuid(psshData);
|
||||
if (uuid == null) {
|
||||
Log.w(TAG, "Skipped pssh atom (failed to extract uuid)");
|
||||
} else {
|
||||
drmInitData.put(PsshAtomUtil.parseUuid(psshData),
|
||||
new SchemeInitData(MimeTypes.VIDEO_MP4, psshData));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (drmInitData != null) {
|
||||
|
@ -17,6 +17,9 @@ package com.google.android.exoplayer.extractor.mp4;
|
||||
|
||||
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -25,6 +28,8 @@ import java.util.UUID;
|
||||
*/
|
||||
public final class PsshAtomUtil {
|
||||
|
||||
private static final String TAG = "PsshAtomUtil";
|
||||
|
||||
private PsshAtomUtil() {}
|
||||
|
||||
/**
|
||||
@ -48,75 +53,88 @@ public final class PsshAtomUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the UUID from a PSSH atom.
|
||||
* Parses the UUID from a PSSH atom. Version 0 and 1 PSSH atoms are supported.
|
||||
* <p>
|
||||
* The UUID is only parsed if the data is a valid PSSH atom.
|
||||
*
|
||||
* @param atom The atom to parse.
|
||||
* @return The parsed UUID. Null if the data is not a valid PSSH atom.
|
||||
* @return The parsed UUID. Null if the input is not a valid PSSH atom, or if the PSSH atom has
|
||||
* an unsupported version.
|
||||
*/
|
||||
public static UUID parseUuid(byte[] atom) {
|
||||
ParsableByteArray atomData = new ParsableByteArray(atom);
|
||||
if (!isPsshAtom(atomData, null)) {
|
||||
Pair<UUID, byte[]> parsedAtom = parsePsshAtom(atom);
|
||||
if (parsedAtom == null) {
|
||||
return null;
|
||||
}
|
||||
atomData.setPosition(Atom.FULL_HEADER_SIZE);
|
||||
return new UUID(atomData.readLong(), atomData.readLong());
|
||||
return parsedAtom.first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the scheme specific data from a PSSH atom.
|
||||
* Parses the scheme specific data from a PSSH atom. Version 0 and 1 PSSH atoms are supported.
|
||||
* <p>
|
||||
* The scheme specific data is only parsed if the data is a valid PSSH atom matching the given
|
||||
* UUID, or if the data is a valid PSSH atom of any type in the case that the passed UUID is null.
|
||||
*
|
||||
* @param atom The atom to parse.
|
||||
* @param uuid The required UUID of the PSSH atom, or null to accept any UUID.
|
||||
* @return The parsed scheme specific data. Null if the data is not a valid PSSH atom or if its
|
||||
* UUID does not match the one provided.
|
||||
* @return The parsed scheme specific data. Null if the input is not a valid PSSH atom, or if the
|
||||
* PSSH atom has an unsupported version, or if the PSSH atom does not match the passed UUID.
|
||||
*/
|
||||
public static byte[] parseSchemeSpecificData(byte[] atom, UUID uuid) {
|
||||
ParsableByteArray atomData = new ParsableByteArray(atom);
|
||||
if (!isPsshAtom(atomData, uuid)) {
|
||||
Pair<UUID, byte[]> parsedAtom = parsePsshAtom(atom);
|
||||
if (parsedAtom == null) {
|
||||
return null;
|
||||
}
|
||||
atomData.setPosition(Atom.FULL_HEADER_SIZE + 16 /* UUID */);
|
||||
int dataSize = atomData.readInt();
|
||||
byte[] data = new byte[dataSize];
|
||||
atomData.readBytes(data, 0, dataSize);
|
||||
return data;
|
||||
if (uuid != null && !uuid.equals(parsedAtom.first)) {
|
||||
Log.w(TAG, "UUID mismatch. Expected: " + uuid + ", got: " + parsedAtom.first + ".");
|
||||
return null;
|
||||
}
|
||||
return parsedAtom.second;
|
||||
}
|
||||
|
||||
private static boolean isPsshAtom(ParsableByteArray atomData, UUID uuid) {
|
||||
/**
|
||||
* Parses the UUID and scheme specific data from a PSSH atom. Version 0 and 1 PSSH atoms are
|
||||
* supported.
|
||||
*
|
||||
* @param atom The atom to parse.
|
||||
* @return A pair consisting of the parsed UUID and scheme specific data. Null if the input is
|
||||
* not a valid PSSH atom, or if the PSSH atom has an unsupported version.
|
||||
*/
|
||||
private static Pair<UUID, byte[]> parsePsshAtom(byte[] atom) {
|
||||
ParsableByteArray atomData = new ParsableByteArray(atom);
|
||||
if (atomData.limit() < Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) {
|
||||
// Data too short.
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
atomData.setPosition(0);
|
||||
int atomSize = atomData.readInt();
|
||||
if (atomSize != atomData.bytesLeft() + 4) {
|
||||
// Not an atom, or incorrect atom size.
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
int atomType = atomData.readInt();
|
||||
if (atomType != Atom.TYPE_pssh) {
|
||||
// Not an atom, or incorrect atom type.
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
atomData.setPosition(Atom.FULL_HEADER_SIZE);
|
||||
if (uuid == null) {
|
||||
atomData.skipBytes(16);
|
||||
} else if (atomData.readLong() != uuid.getMostSignificantBits()
|
||||
|| atomData.readLong() != uuid.getLeastSignificantBits()) {
|
||||
// UUID doesn't match.
|
||||
return false;
|
||||
int atomVersion = Atom.parseFullAtomVersion(atomData.readInt());
|
||||
if (atomVersion > 1) {
|
||||
Log.w(TAG, "Unsupported pssh version: " + atomVersion);
|
||||
return null;
|
||||
}
|
||||
int dataSize = atomData.readInt();
|
||||
UUID uuid = new UUID(atomData.readLong(), atomData.readLong());
|
||||
if (atomVersion == 1) {
|
||||
int keyIdCount = atomData.readUnsignedIntToInt();
|
||||
atomData.skipBytes(16 * keyIdCount);
|
||||
}
|
||||
int dataSize = atomData.readUnsignedIntToInt();
|
||||
if (dataSize != atomData.bytesLeft()) {
|
||||
// Incorrect dataSize.
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
return true;
|
||||
byte[] data = new byte[dataSize];
|
||||
atomData.readBytes(data, 0, dataSize);
|
||||
return Pair.create(uuid, data);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user