Commit 317a9944 by aquilescanta Committed by Oliver Woodman

Add type to DrmInitData.SchemeData

At the moment, only CENC-defined scheme types are known values.
This will allow having more information about the encryption
scheme through the format, which in turn will allow more informed
decisions on format support.

Issue:#1661
Issue:#1989
Issue:#2089

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=159538907
parent 2c09f8e0
...@@ -53,9 +53,9 @@ public final class FormatTest extends TestCase { ...@@ -53,9 +53,9 @@ public final class FormatTest extends TestCase {
} }
public void testParcelable() { public void testParcelable() {
DrmInitData.SchemeData DRM_DATA_1 = new DrmInitData.SchemeData(WIDEVINE_UUID, VIDEO_MP4, DrmInitData.SchemeData DRM_DATA_1 = new DrmInitData.SchemeData(WIDEVINE_UUID, "cenc", VIDEO_MP4,
TestUtil.buildTestData(128, 1 /* data seed */)); TestUtil.buildTestData(128, 1 /* data seed */));
DrmInitData.SchemeData DRM_DATA_2 = new DrmInitData.SchemeData(C.UUID_NIL, VIDEO_WEBM, DrmInitData.SchemeData DRM_DATA_2 = new DrmInitData.SchemeData(C.UUID_NIL, null, VIDEO_WEBM,
TestUtil.buildTestData(128, 1 /* data seed */)); TestUtil.buildTestData(128, 1 /* data seed */));
DrmInitData drmInitData = new DrmInitData(DRM_DATA_1, DRM_DATA_2); DrmInitData drmInitData = new DrmInitData(DRM_DATA_1, DRM_DATA_2);
byte[] projectionData = new byte[] {1, 2, 3}; byte[] projectionData = new byte[] {1, 2, 3};
......
...@@ -31,16 +31,16 @@ import junit.framework.TestCase; ...@@ -31,16 +31,16 @@ import junit.framework.TestCase;
*/ */
public class DrmInitDataTest extends TestCase { public class DrmInitDataTest extends TestCase {
private static final SchemeData DATA_1 = private static final SchemeData DATA_1 = new SchemeData(WIDEVINE_UUID, "cbc1", VIDEO_MP4,
new SchemeData(WIDEVINE_UUID, VIDEO_MP4, TestUtil.buildTestData(128, 1 /* data seed */)); TestUtil.buildTestData(128, 1 /* data seed */));
private static final SchemeData DATA_2 = private static final SchemeData DATA_2 = new SchemeData(PLAYREADY_UUID, null, VIDEO_MP4,
new SchemeData(PLAYREADY_UUID, VIDEO_MP4, TestUtil.buildTestData(128, 2 /* data seed */)); TestUtil.buildTestData(128, 2 /* data seed */));
private static final SchemeData DATA_1B = private static final SchemeData DATA_1B = new SchemeData(WIDEVINE_UUID, "cens", VIDEO_MP4,
new SchemeData(WIDEVINE_UUID, VIDEO_MP4, TestUtil.buildTestData(128, 1 /* data seed */)); TestUtil.buildTestData(128, 1 /* data seed */));
private static final SchemeData DATA_2B = private static final SchemeData DATA_2B = new SchemeData(PLAYREADY_UUID, null, VIDEO_MP4,
new SchemeData(PLAYREADY_UUID, VIDEO_MP4, TestUtil.buildTestData(128, 2 /* data seed */)); TestUtil.buildTestData(128, 2 /* data seed */));
private static final SchemeData DATA_UNIVERSAL = private static final SchemeData DATA_UNIVERSAL = new SchemeData(C.UUID_NIL, null, VIDEO_MP4,
new SchemeData(C.UUID_NIL, VIDEO_MP4, TestUtil.buildTestData(128, 3 /* data seed */)); TestUtil.buildTestData(128, 3 /* data seed */));
public void testParcelable() { public void testParcelable() {
DrmInitData drmInitDataToParcel = new DrmInitData(DATA_1, DATA_2); DrmInitData drmInitDataToParcel = new DrmInitData(DATA_1, DATA_2);
......
...@@ -154,7 +154,7 @@ public class OfflineLicenseHelperTest extends InstrumentationTestCase { ...@@ -154,7 +154,7 @@ public class OfflineLicenseHelperTest extends InstrumentationTestCase {
} }
private static DrmInitData newDrmInitData() { private static DrmInitData newDrmInitData() {
return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, "mimeType", return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, "cenc", "mimeType",
new byte[] {1, 4, 7, 0, 3, 6})); new byte[] {1, 4, 7, 0, 3, 6}));
} }
......
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData; import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
...@@ -102,6 +103,33 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable { ...@@ -102,6 +103,33 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
return schemeDatas[index]; return schemeDatas[index];
} }
/**
* Returns a copy of the {@link DrmInitData} instance whose {@link SchemeData}s have been updated
* to have the specified scheme type.
*
* @param schemeType A protection scheme type. May be null.
* @return A copy of the {@link DrmInitData} instance whose {@link SchemeData}s have been updated
* to have the specified scheme type.
*/
public DrmInitData copyWithSchemeType(@Nullable String schemeType) {
boolean isCopyRequired = false;
for (SchemeData schemeData : schemeDatas) {
if (!Util.areEqual(schemeData.type, schemeType)) {
isCopyRequired = true;
break;
}
}
if (isCopyRequired) {
SchemeData[] schemeDatas = new SchemeData[this.schemeDatas.length];
for (int i = 0; i < schemeDatas.length; i++) {
schemeDatas[i] = this.schemeDatas[i].copyWithSchemeType(schemeType);
}
return new DrmInitData(schemeDatas);
} else {
return this;
}
}
@Override @Override
public int hashCode() { public int hashCode() {
if (hashCode == 0) { if (hashCode == 0) {
...@@ -168,6 +196,10 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable { ...@@ -168,6 +196,10 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
*/ */
private final UUID uuid; private final UUID uuid;
/** /**
* The protection scheme type, or null if not applicable or unknown.
*/
@Nullable public final String type;
/**
* The mimeType of {@link #data}. * The mimeType of {@link #data}.
*/ */
public final String mimeType; public final String mimeType;
...@@ -183,22 +215,26 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable { ...@@ -183,22 +215,26 @@ 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 * @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). * universal (i.e. applies to all schemes).
* @param type The type of the protection scheme, or null if not applicable or unknown.
* @param mimeType The mimeType of the initialization data. * @param mimeType The mimeType of the initialization data.
* @param data The initialization data. * @param data The initialization data.
*/ */
public SchemeData(UUID uuid, String mimeType, byte[] data) { public SchemeData(UUID uuid, @Nullable String type, String mimeType, byte[] data) {
this(uuid, mimeType, data, false); this(uuid, type, mimeType, data, false);
} }
/** /**
* @param uuid The {@link UUID} of the DRM scheme, or {@link C#UUID_NIL} if the data is * @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). * universal (i.e. applies to all schemes).
* @param type The type of the protection scheme, or null if not applicable or unknown.
* @param mimeType The mimeType of the initialization data. * @param mimeType The mimeType of the initialization data.
* @param data The initialization data. * @param data The initialization data.
* @param requiresSecureDecryption Whether secure decryption is required. * @param requiresSecureDecryption Whether secure decryption is required.
*/ */
public SchemeData(UUID uuid, String mimeType, byte[] data, boolean requiresSecureDecryption) { public SchemeData(UUID uuid, @Nullable String type, String mimeType, byte[] data,
boolean requiresSecureDecryption) {
this.uuid = Assertions.checkNotNull(uuid); this.uuid = Assertions.checkNotNull(uuid);
this.type = type;
this.mimeType = Assertions.checkNotNull(mimeType); this.mimeType = Assertions.checkNotNull(mimeType);
this.data = Assertions.checkNotNull(data); this.data = Assertions.checkNotNull(data);
this.requiresSecureDecryption = requiresSecureDecryption; this.requiresSecureDecryption = requiresSecureDecryption;
...@@ -206,6 +242,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable { ...@@ -206,6 +242,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
/* package */ SchemeData(Parcel in) { /* package */ SchemeData(Parcel in) {
uuid = new UUID(in.readLong(), in.readLong()); uuid = new UUID(in.readLong(), in.readLong());
type = in.readString();
mimeType = in.readString(); mimeType = in.readString();
data = in.createByteArray(); data = in.createByteArray();
requiresSecureDecryption = in.readByte() != 0; requiresSecureDecryption = in.readByte() != 0;
...@@ -221,6 +258,19 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable { ...@@ -221,6 +258,19 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
return C.UUID_NIL.equals(uuid) || schemeUuid.equals(uuid); return C.UUID_NIL.equals(uuid) || schemeUuid.equals(uuid);
} }
/**
* Returns a copy of the {@link SchemeData} instance with the given scheme type.
*
* @param type A protection scheme type.
* @return A copy of the {@link SchemeData} instance with the given scheme type.
*/
public SchemeData copyWithSchemeType(String type) {
if (Util.areEqual(this.type, type)) {
return this;
}
return new SchemeData(uuid, type, mimeType, data, requiresSecureDecryption);
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (!(obj instanceof SchemeData)) { if (!(obj instanceof SchemeData)) {
...@@ -231,13 +281,14 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable { ...@@ -231,13 +281,14 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
} }
SchemeData other = (SchemeData) obj; SchemeData other = (SchemeData) obj;
return mimeType.equals(other.mimeType) && Util.areEqual(uuid, other.uuid) return mimeType.equals(other.mimeType) && Util.areEqual(uuid, other.uuid)
&& Arrays.equals(data, other.data); && Util.areEqual(type, other.type) && Arrays.equals(data, other.data);
} }
@Override @Override
public int hashCode() { public int hashCode() {
if (hashCode == 0) { if (hashCode == 0) {
int result = uuid.hashCode(); int result = uuid.hashCode();
result = 31 * result + (type == null ? 0 : type.hashCode());
result = 31 * result + mimeType.hashCode(); result = 31 * result + mimeType.hashCode();
result = 31 * result + Arrays.hashCode(data); result = 31 * result + Arrays.hashCode(data);
hashCode = result; hashCode = result;
...@@ -256,6 +307,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable { ...@@ -256,6 +307,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(uuid.getMostSignificantBits()); dest.writeLong(uuid.getMostSignificantBits());
dest.writeLong(uuid.getLeastSignificantBits()); dest.writeLong(uuid.getLeastSignificantBits());
dest.writeString(type);
dest.writeString(mimeType); dest.writeString(mimeType);
dest.writeByteArray(data); dest.writeByteArray(data);
dest.writeByte((byte) (requiresSecureDecryption ? 1 : 0)); dest.writeByte((byte) (requiresSecureDecryption ? 1 : 0));
......
...@@ -586,7 +586,7 @@ public final class MatroskaExtractor implements Extractor { ...@@ -586,7 +586,7 @@ public final class MatroskaExtractor implements Extractor {
if (currentTrack.cryptoData == null) { if (currentTrack.cryptoData == null) {
throw new ParserException("Encrypted Track found but ContentEncKeyID was not found"); throw new ParserException("Encrypted Track found but ContentEncKeyID was not found");
} }
currentTrack.drmInitData = new DrmInitData(new SchemeData(C.UUID_NIL, currentTrack.drmInitData = new DrmInitData(new SchemeData(C.UUID_NIL, null,
MimeTypes.VIDEO_WEBM, currentTrack.cryptoData.encryptionKey)); MimeTypes.VIDEO_WEBM, currentTrack.cryptoData.encryptionKey));
} }
break; break;
......
...@@ -615,10 +615,10 @@ import java.util.List; ...@@ -615,10 +615,10 @@ import java.util.List;
|| childAtomType == Atom.TYPE_wvtt || childAtomType == Atom.TYPE_stpp || childAtomType == Atom.TYPE_wvtt || childAtomType == Atom.TYPE_stpp
|| childAtomType == Atom.TYPE_c608) { || childAtomType == Atom.TYPE_c608) {
parseTextSampleEntry(stsd, childAtomType, childStartPosition, childAtomSize, trackId, parseTextSampleEntry(stsd, childAtomType, childStartPosition, childAtomSize, trackId,
language, drmInitData, out); language, out);
} else if (childAtomType == Atom.TYPE_camm) { } else if (childAtomType == Atom.TYPE_camm) {
out.format = Format.createSampleFormat(Integer.toString(trackId), out.format = Format.createSampleFormat(Integer.toString(trackId),
MimeTypes.APPLICATION_CAMERA_MOTION, null, Format.NO_VALUE, drmInitData); MimeTypes.APPLICATION_CAMERA_MOTION, null, Format.NO_VALUE, null);
} }
stsd.setPosition(childStartPosition + childAtomSize); stsd.setPosition(childStartPosition + childAtomSize);
} }
...@@ -626,8 +626,7 @@ import java.util.List; ...@@ -626,8 +626,7 @@ import java.util.List;
} }
private static void parseTextSampleEntry(ParsableByteArray parent, int atomType, int position, private static void parseTextSampleEntry(ParsableByteArray parent, int atomType, int position,
int atomSize, int trackId, String language, DrmInitData drmInitData, StsdData out) int atomSize, int trackId, String language, StsdData out) throws ParserException {
throws ParserException {
parent.setPosition(position + Atom.HEADER_SIZE + StsdData.STSD_HEADER_SIZE); parent.setPosition(position + Atom.HEADER_SIZE + StsdData.STSD_HEADER_SIZE);
// Default values. // Default values.
...@@ -658,8 +657,7 @@ import java.util.List; ...@@ -658,8 +657,7 @@ import java.util.List;
} }
out.format = Format.createTextSampleFormat(Integer.toString(trackId), mimeType, null, out.format = Format.createTextSampleFormat(Integer.toString(trackId), mimeType, null,
Format.NO_VALUE, 0, language, Format.NO_VALUE, drmInitData, subsampleOffsetUs, Format.NO_VALUE, 0, language, Format.NO_VALUE, null, subsampleOffsetUs, initializationData);
initializationData);
} }
private static void parseVideoSampleEntry(ParsableByteArray parent, int atomType, int position, private static void parseVideoSampleEntry(ParsableByteArray parent, int atomType, int position,
...@@ -677,7 +675,14 @@ import java.util.List; ...@@ -677,7 +675,14 @@ import java.util.List;
int childPosition = parent.getPosition(); int childPosition = parent.getPosition();
if (atomType == Atom.TYPE_encv) { if (atomType == Atom.TYPE_encv) {
atomType = parseSampleEntryEncryptionData(parent, position, size, out, entryIndex); atomType = parseSampleEntryEncryptionData(parent, position, size, out, entryIndex);
TrackEncryptionBox encryptionBox = out.trackEncryptionBoxes[entryIndex];
String schemeType = encryptionBox != null ? encryptionBox.schemeType : null;
if (schemeType != null) {
drmInitData = drmInitData.copyWithSchemeType(schemeType);
}
parent.setPosition(childPosition); parent.setPosition(childPosition);
} else {
drmInitData = null;
} }
List<byte[]> initializationData = null; List<byte[]> initializationData = null;
...@@ -846,7 +851,14 @@ import java.util.List; ...@@ -846,7 +851,14 @@ import java.util.List;
int childPosition = parent.getPosition(); int childPosition = parent.getPosition();
if (atomType == Atom.TYPE_enca) { if (atomType == Atom.TYPE_enca) {
atomType = parseSampleEntryEncryptionData(parent, position, size, out, entryIndex); atomType = parseSampleEntryEncryptionData(parent, position, size, out, entryIndex);
TrackEncryptionBox encryptionBox = out.trackEncryptionBoxes[entryIndex];
String schemeType = encryptionBox != null ? encryptionBox.schemeType : null;
if (schemeType != null) {
drmInitData = drmInitData.copyWithSchemeType(schemeType);
}
parent.setPosition(childPosition); parent.setPosition(childPosition);
} else {
drmInitData = null;
} }
// If the atom type determines a MIME type, set it immediately. // If the atom type determines a MIME type, set it immediately.
...@@ -1051,9 +1063,9 @@ import java.util.List; ...@@ -1051,9 +1063,9 @@ import java.util.List;
private static Pair<Integer, TrackEncryptionBox> parseSinfFromParent(ParsableByteArray parent, private static Pair<Integer, TrackEncryptionBox> parseSinfFromParent(ParsableByteArray parent,
int position, int size) { int position, int size) {
int childPosition = position + Atom.HEADER_SIZE; int childPosition = position + Atom.HEADER_SIZE;
int schemeInformationBoxPosition = C.POSITION_UNSET;
boolean isCencScheme = false; int schemeInformationBoxSize = 0;
TrackEncryptionBox trackEncryptionBox = null; String schemeType = null;
Integer dataFormat = null; Integer dataFormat = null;
while (childPosition - position < size) { while (childPosition - position < size) {
parent.setPosition(childPosition); parent.setPosition(childPosition);
...@@ -1063,24 +1075,30 @@ import java.util.List; ...@@ -1063,24 +1075,30 @@ import java.util.List;
dataFormat = parent.readInt(); dataFormat = parent.readInt();
} else if (childAtomType == Atom.TYPE_schm) { } else if (childAtomType == Atom.TYPE_schm) {
parent.skipBytes(4); parent.skipBytes(4);
isCencScheme = parent.readInt() == TYPE_cenc; // scheme_type field. Defined in ISO/IEC 23001-7:2016, section 4.1.
schemeType = parent.readString(4);
} else if (childAtomType == Atom.TYPE_schi) { } else if (childAtomType == Atom.TYPE_schi) {
trackEncryptionBox = parseSchiFromParent(parent, childPosition, childAtomSize); schemeInformationBoxPosition = childPosition;
schemeInformationBoxSize = childAtomSize;
} }
childPosition += childAtomSize; childPosition += childAtomSize;
} }
if (isCencScheme) { if (schemeType != null) {
Assertions.checkArgument(dataFormat != null, "frma atom is mandatory"); Assertions.checkArgument(dataFormat != null, "frma atom is mandatory");
Assertions.checkArgument(trackEncryptionBox != null, "schi->tenc atom is mandatory"); Assertions.checkArgument(schemeInformationBoxPosition != C.POSITION_UNSET,
return Pair.create(dataFormat, trackEncryptionBox); "schi atom is mandatory");
TrackEncryptionBox encryptionBox = parseSchiFromParent(parent, schemeInformationBoxPosition,
schemeInformationBoxSize, schemeType);
Assertions.checkArgument(encryptionBox != null, "tenc atom is mandatory");
return Pair.create(dataFormat, encryptionBox);
} else { } else {
return null; return null;
} }
} }
private static TrackEncryptionBox parseSchiFromParent(ParsableByteArray parent, int position, private static TrackEncryptionBox parseSchiFromParent(ParsableByteArray parent, int position,
int size) { int size, String schemeType) {
int childPosition = position + Atom.HEADER_SIZE; int childPosition = position + Atom.HEADER_SIZE;
while (childPosition - position < size) { while (childPosition - position < size) {
parent.setPosition(childPosition); parent.setPosition(childPosition);
...@@ -1092,7 +1110,8 @@ import java.util.List; ...@@ -1092,7 +1110,8 @@ import java.util.List;
int defaultInitVectorSize = parent.readUnsignedByte(); int defaultInitVectorSize = parent.readUnsignedByte();
byte[] defaultKeyId = new byte[16]; byte[] defaultKeyId = new byte[16];
parent.readBytes(defaultKeyId, 0, defaultKeyId.length); parent.readBytes(defaultKeyId, 0, defaultKeyId.length);
return new TrackEncryptionBox(defaultIsEncrypted, defaultInitVectorSize, defaultKeyId); return new TrackEncryptionBox(defaultIsEncrypted, schemeType, defaultInitVectorSize,
defaultKeyId);
} }
childPosition += childAtomSize; childPosition += childAtomSize;
} }
......
...@@ -559,11 +559,12 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -559,11 +559,12 @@ public final class FragmentedMp4Extractor implements Extractor {
parseTruns(traf, trackBundle, decodeTime, flags); parseTruns(traf, trackBundle, decodeTime, flags);
TrackEncryptionBox encryptionBox = trackBundle.track
.getSampleDescriptionEncryptionBox(fragment.header.sampleDescriptionIndex);
LeafAtom saiz = traf.getLeafAtomOfType(Atom.TYPE_saiz); LeafAtom saiz = traf.getLeafAtomOfType(Atom.TYPE_saiz);
if (saiz != null) { if (saiz != null) {
TrackEncryptionBox trackEncryptionBox = trackBundle.track parseSaiz(encryptionBox, saiz.data, fragment);
.sampleDescriptionEncryptionBoxes[fragment.header.sampleDescriptionIndex];
parseSaiz(trackEncryptionBox, saiz.data, fragment);
} }
LeafAtom saio = traf.getLeafAtomOfType(Atom.TYPE_saio); LeafAtom saio = traf.getLeafAtomOfType(Atom.TYPE_saio);
...@@ -579,7 +580,8 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -579,7 +580,8 @@ public final class FragmentedMp4Extractor implements Extractor {
LeafAtom sbgp = traf.getLeafAtomOfType(Atom.TYPE_sbgp); LeafAtom sbgp = traf.getLeafAtomOfType(Atom.TYPE_sbgp);
LeafAtom sgpd = traf.getLeafAtomOfType(Atom.TYPE_sgpd); LeafAtom sgpd = traf.getLeafAtomOfType(Atom.TYPE_sgpd);
if (sbgp != null && sgpd != null) { if (sbgp != null && sgpd != null) {
parseSgpd(sbgp.data, sgpd.data, fragment); parseSgpd(sbgp.data, sgpd.data, encryptionBox != null ? encryptionBox.schemeType : null,
fragment);
} }
int leafChildrenSize = traf.leafChildren.size(); int leafChildrenSize = traf.leafChildren.size();
...@@ -868,8 +870,8 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -868,8 +870,8 @@ public final class FragmentedMp4Extractor implements Extractor {
out.fillEncryptionData(senc); out.fillEncryptionData(senc);
} }
private static void parseSgpd(ParsableByteArray sbgp, ParsableByteArray sgpd, TrackFragment out) private static void parseSgpd(ParsableByteArray sbgp, ParsableByteArray sgpd, String schemeType,
throws ParserException { TrackFragment out) throws ParserException {
sbgp.setPosition(Atom.HEADER_SIZE); sbgp.setPosition(Atom.HEADER_SIZE);
int sbgpFullAtom = sbgp.readInt(); int sbgpFullAtom = sbgp.readInt();
if (sbgp.readInt() != SAMPLE_GROUP_TYPE_seig) { if (sbgp.readInt() != SAMPLE_GROUP_TYPE_seig) {
...@@ -910,7 +912,7 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -910,7 +912,7 @@ public final class FragmentedMp4Extractor implements Extractor {
byte[] keyId = new byte[16]; byte[] keyId = new byte[16];
sgpd.readBytes(keyId, 0, keyId.length); sgpd.readBytes(keyId, 0, keyId.length);
out.definesEncryptionData = true; out.definesEncryptionData = true;
out.trackEncryptionBox = new TrackEncryptionBox(isProtected, initVectorSize, keyId); out.trackEncryptionBox = new TrackEncryptionBox(isProtected, schemeType, initVectorSize, keyId);
} }
/** /**
...@@ -1135,7 +1137,7 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -1135,7 +1137,7 @@ public final class FragmentedMp4Extractor implements Extractor {
if (fragment.definesEncryptionData) { if (fragment.definesEncryptionData) {
encryptionBox = fragment.trackEncryptionBox != null encryptionBox = fragment.trackEncryptionBox != null
? fragment.trackEncryptionBox ? fragment.trackEncryptionBox
: track.sampleDescriptionEncryptionBoxes[fragment.header.sampleDescriptionIndex]; : track.getSampleDescriptionEncryptionBox(fragment.header.sampleDescriptionIndex);
if (encryptionBox != currentTrackBundle.cachedEncryptionBox) { if (encryptionBox != currentTrackBundle.cachedEncryptionBox) {
cryptoData = new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, encryptionBox.keyId); cryptoData = new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, encryptionBox.keyId);
} else { } else {
...@@ -1205,7 +1207,7 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -1205,7 +1207,7 @@ public final class FragmentedMp4Extractor implements Extractor {
int sampleDescriptionIndex = trackFragment.header.sampleDescriptionIndex; int sampleDescriptionIndex = trackFragment.header.sampleDescriptionIndex;
TrackEncryptionBox encryptionBox = trackFragment.trackEncryptionBox != null TrackEncryptionBox encryptionBox = trackFragment.trackEncryptionBox != null
? trackFragment.trackEncryptionBox ? trackFragment.trackEncryptionBox
: trackBundle.track.sampleDescriptionEncryptionBoxes[sampleDescriptionIndex]; : trackBundle.track.getSampleDescriptionEncryptionBox(sampleDescriptionIndex);
int vectorSize = encryptionBox.initializationVectorSize; int vectorSize = encryptionBox.initializationVectorSize;
boolean subsampleEncryption = trackFragment boolean subsampleEncryption = trackFragment
.sampleHasSubsampleEncryptionTable[trackBundle.currentSampleIndex]; .sampleHasSubsampleEncryptionTable[trackBundle.currentSampleIndex];
...@@ -1245,7 +1247,7 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -1245,7 +1247,7 @@ public final class FragmentedMp4Extractor implements Extractor {
if (uuid == null) { if (uuid == null) {
Log.w(TAG, "Skipped pssh atom (failed to extract uuid)"); Log.w(TAG, "Skipped pssh atom (failed to extract uuid)");
} else { } else {
schemeDatas.add(new SchemeData(uuid, MimeTypes.VIDEO_MP4, psshData)); schemeDatas.add(new SchemeData(uuid, null, MimeTypes.VIDEO_MP4, psshData));
} }
} }
} }
...@@ -1325,8 +1327,12 @@ public final class FragmentedMp4Extractor implements Extractor { ...@@ -1325,8 +1327,12 @@ public final class FragmentedMp4Extractor implements Extractor {
} }
public void updateDrmInitData(DrmInitData drmInitData) { public void updateDrmInitData(DrmInitData drmInitData) {
output.format(track.format.copyWithDrmInitData(drmInitData)); TrackEncryptionBox encryptionBox =
track.getSampleDescriptionEncryptionBox(fragment.header.sampleDescriptionIndex);
String schemeType = encryptionBox != null ? encryptionBox.schemeType : null;
output.format(track.format.copyWithDrmInitData(drmInitData.copyWithSchemeType(schemeType)));
} }
} }
} }
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.extractor.mp4; package com.google.android.exoplayer2.extractor.mp4;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
...@@ -78,11 +79,6 @@ public final class Track { ...@@ -78,11 +79,6 @@ public final class Track {
@Transformation public final int sampleTransformation; @Transformation public final int sampleTransformation;
/** /**
* Track encryption boxes for the different track sample descriptions. Entries may be null.
*/
public final TrackEncryptionBox[] sampleDescriptionEncryptionBoxes;
/**
* Durations of edit list segments in the movie timescale. Null if there is no edit list. * Durations of edit list segments in the movie timescale. Null if there is no edit list.
*/ */
public final long[] editListDurations; public final long[] editListDurations;
...@@ -98,9 +94,11 @@ public final class Track { ...@@ -98,9 +94,11 @@ public final class Track {
*/ */
public final int nalUnitLengthFieldLength; public final int nalUnitLengthFieldLength;
@Nullable private final TrackEncryptionBox[] sampleDescriptionEncryptionBoxes;
public Track(int id, int type, long timescale, long movieTimescale, long durationUs, public Track(int id, int type, long timescale, long movieTimescale, long durationUs,
Format format, @Transformation int sampleTransformation, Format format, @Transformation int sampleTransformation,
TrackEncryptionBox[] sampleDescriptionEncryptionBoxes, int nalUnitLengthFieldLength, @Nullable TrackEncryptionBox[] sampleDescriptionEncryptionBoxes, int nalUnitLengthFieldLength,
long[] editListDurations, long[] editListMediaTimes) { long[] editListDurations, long[] editListMediaTimes) {
this.id = id; this.id = id;
this.type = type; this.type = type;
...@@ -115,4 +113,16 @@ public final class Track { ...@@ -115,4 +113,16 @@ public final class Track {
this.editListMediaTimes = editListMediaTimes; this.editListMediaTimes = editListMediaTimes;
} }
/**
* Returns the {@link TrackEncryptionBox} for the given sample description index.
*
* @param sampleDescriptionIndex The given sample description index
* @return The {@link TrackEncryptionBox} for the given sample description index. Maybe null if no
* such entry exists.
*/
public TrackEncryptionBox getSampleDescriptionEncryptionBox(int sampleDescriptionIndex) {
return sampleDescriptionEncryptionBoxes == null ? null
: sampleDescriptionEncryptionBoxes[sampleDescriptionIndex];
}
} }
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2.extractor.mp4; package com.google.android.exoplayer2.extractor.mp4;
import android.support.annotation.Nullable;
/** /**
* Encapsulates information parsed from a track encryption (tenc) box or sample group description * Encapsulates information parsed from a track encryption (tenc) box or sample group description
* (sgpd) box in an MP4 stream. * (sgpd) box in an MP4 stream.
...@@ -27,6 +29,11 @@ public final class TrackEncryptionBox { ...@@ -27,6 +29,11 @@ public final class TrackEncryptionBox {
public final boolean isEncrypted; public final boolean isEncrypted;
/** /**
* The protection scheme type, as defined by the 'schm' box, or null if unknown.
*/
@Nullable public final String schemeType;
/**
* The initialization vector size in bytes for the samples in the corresponding sample group. * The initialization vector size in bytes for the samples in the corresponding sample group.
*/ */
public final int initializationVectorSize; public final int initializationVectorSize;
...@@ -37,13 +44,15 @@ public final class TrackEncryptionBox { ...@@ -37,13 +44,15 @@ public final class TrackEncryptionBox {
public final byte[] keyId; public final byte[] keyId;
/** /**
* @param isEncrypted Indicates the encryption state of the samples in the sample group. * @param isEncrypted See {@link #isEncrypted}.
* @param initializationVectorSize The initialization vector size in bytes for the samples in the * @param schemeType See {@link #schemeType}.
* corresponding sample group. * @param initializationVectorSize See {@link #initializationVectorSize}.
* @param keyId The key identifier for the samples in the corresponding sample group. * @param keyId See {@link #keyId}.
*/ */
public TrackEncryptionBox(boolean isEncrypted, int initializationVectorSize, byte[] keyId) { public TrackEncryptionBox(boolean isEncrypted, @Nullable String schemeType,
int initializationVectorSize, byte[] keyId) {
this.isEncrypted = isEncrypted; this.isEncrypted = isEncrypted;
this.schemeType = schemeType;
this.initializationVectorSize = initializationVectorSize; this.initializationVectorSize = initializationVectorSize;
this.keyId = keyId; this.keyId = keyId;
} }
......
...@@ -75,7 +75,7 @@ public final class DashUtilTest extends TestCase { ...@@ -75,7 +75,7 @@ public final class DashUtilTest extends TestCase {
} }
private static DrmInitData newDrmInitData() { private static DrmInitData newDrmInitData() {
return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, "mimeType", return new DrmInitData(new SchemeData(C.WIDEVINE_UUID, null, "mimeType",
new byte[]{1, 4, 7, 0, 3, 6})); new byte[]{1, 4, 7, 0, 3, 6}));
} }
......
...@@ -343,6 +343,7 @@ public class DashManifestParser extends DefaultHandler ...@@ -343,6 +343,7 @@ public class DashManifestParser extends DefaultHandler
IOException { IOException {
String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri"); String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri");
boolean isPlayReady = "urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95".equals(schemeIdUri); boolean isPlayReady = "urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95".equals(schemeIdUri);
String schemeType = xpp.getAttributeValue(null, "value");
byte[] data = null; byte[] data = null;
UUID uuid = null; UUID uuid = null;
boolean requiresSecureDecoder = false; boolean requiresSecureDecoder = false;
...@@ -368,8 +369,8 @@ public class DashManifestParser extends DefaultHandler ...@@ -368,8 +369,8 @@ public class DashManifestParser extends DefaultHandler
requiresSecureDecoder = robustnessLevel != null && robustnessLevel.startsWith("HW"); requiresSecureDecoder = robustnessLevel != null && robustnessLevel.startsWith("HW");
} }
} while (!XmlPullParserUtil.isEndTag(xpp, "ContentProtection")); } while (!XmlPullParserUtil.isEndTag(xpp, "ContentProtection"));
return data != null ? new SchemeData(uuid, MimeTypes.VIDEO_MP4, data, requiresSecureDecoder) return data != null
: null; ? new SchemeData(uuid, schemeType, MimeTypes.VIDEO_MP4, data, requiresSecureDecoder) : null;
} }
/** /**
......
...@@ -69,7 +69,7 @@ import java.util.ArrayList; ...@@ -69,7 +69,7 @@ import java.util.ArrayList;
if (protectionElement != null) { if (protectionElement != null) {
byte[] keyId = getProtectionElementKeyId(protectionElement.data); byte[] keyId = getProtectionElementKeyId(protectionElement.data);
trackEncryptionBoxes = new TrackEncryptionBox[] { trackEncryptionBoxes = new TrackEncryptionBox[] {
new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId)}; new TrackEncryptionBox(true, null, INITIALIZATION_VECTOR_SIZE, keyId)};
} else { } else {
trackEncryptionBoxes = null; trackEncryptionBoxes = null;
} }
......
...@@ -375,7 +375,7 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> { ...@@ -375,7 +375,7 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
StreamElement[] streamElementArray = new StreamElement[streamElements.size()]; StreamElement[] streamElementArray = new StreamElement[streamElements.size()];
streamElements.toArray(streamElementArray); streamElements.toArray(streamElementArray);
if (protectionElement != null) { if (protectionElement != null) {
DrmInitData drmInitData = new DrmInitData(new SchemeData(protectionElement.uuid, DrmInitData drmInitData = new DrmInitData(new SchemeData(protectionElement.uuid, null,
MimeTypes.VIDEO_MP4, protectionElement.data)); MimeTypes.VIDEO_MP4, protectionElement.data));
for (StreamElement streamElement : streamElementArray) { for (StreamElement streamElement : streamElementArray) {
for (int i = 0; i < streamElement.formats.length; i++) { for (int i = 0; i < streamElement.formats.length; i++) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment