Simplify DrmInitData.
- This moves to a single DrmInitData implementation that supports both specific and universal UUID matching. You can also do a combination of the two, which was not previously possible. - The object model is simplified as a result. This is a precursor to a change where DrmInitData will be included directly in the Format to enable key rotation. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=121472592
This commit is contained in:
parent
686816a610
commit
6e5ae4eddc
@ -19,8 +19,8 @@ import static com.google.android.exoplayer.drm.StreamingDrmSessionManager.PLAYRE
|
||||
import static com.google.android.exoplayer.drm.StreamingDrmSessionManager.WIDEVINE_UUID;
|
||||
import static com.google.android.exoplayer.util.MimeTypes.VIDEO_MP4;
|
||||
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.UuidSchemeInitDataTuple;
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer.testutil.TestUtil;
|
||||
|
||||
import android.test.MoreAsserts;
|
||||
@ -32,53 +32,110 @@ import junit.framework.TestCase;
|
||||
*/
|
||||
public class DrmInitDataTest extends TestCase {
|
||||
|
||||
private static final SchemeInitData DATA_1 =
|
||||
new SchemeInitData(VIDEO_MP4, TestUtil.buildTestData(128, 1 /* data seed */));
|
||||
private static final SchemeInitData DATA_2 =
|
||||
new SchemeInitData(VIDEO_MP4, TestUtil.buildTestData(128, 2 /* data seed */));
|
||||
private static final SchemeData DATA_1 =
|
||||
new SchemeData(WIDEVINE_UUID, VIDEO_MP4, TestUtil.buildTestData(128, 1 /* data seed */));
|
||||
private static final SchemeData DATA_2 =
|
||||
new SchemeData(PLAYREADY_UUID, VIDEO_MP4, TestUtil.buildTestData(128, 2 /* data seed */));
|
||||
private static final SchemeData DATA_1B =
|
||||
new SchemeData(WIDEVINE_UUID, VIDEO_MP4, TestUtil.buildTestData(128, 1 /* data seed */));
|
||||
private static final SchemeData DATA_2B =
|
||||
new SchemeData(PLAYREADY_UUID, VIDEO_MP4, TestUtil.buildTestData(128, 2 /* data seed */));
|
||||
private static final SchemeData DATA_UNIVERSAL =
|
||||
new SchemeData(C.UUID_NIL, VIDEO_MP4, TestUtil.buildTestData(128, 3 /* data seed */));
|
||||
|
||||
public void testMappedEquals() {
|
||||
DrmInitData.Mapped drmInitData = new DrmInitData.Mapped(
|
||||
new UuidSchemeInitDataTuple(WIDEVINE_UUID, DATA_1),
|
||||
new UuidSchemeInitDataTuple(PLAYREADY_UUID, DATA_2));
|
||||
public void testEquals() {
|
||||
DrmInitData drmInitData = new DrmInitData(DATA_1, DATA_2);
|
||||
|
||||
// Basic non-referential equality test.
|
||||
DrmInitData.Mapped testInitData = new DrmInitData.Mapped(
|
||||
new UuidSchemeInitDataTuple(WIDEVINE_UUID, DATA_1),
|
||||
new UuidSchemeInitDataTuple(PLAYREADY_UUID, DATA_2));
|
||||
DrmInitData testInitData = new DrmInitData(DATA_1, DATA_2);
|
||||
assertEquals(drmInitData, testInitData);
|
||||
assertEquals(drmInitData.hashCode(), testInitData.hashCode());
|
||||
|
||||
// Passing the tuples in reverse order shouldn't affect equality.
|
||||
testInitData = new DrmInitData.Mapped(
|
||||
new UuidSchemeInitDataTuple(PLAYREADY_UUID, DATA_2),
|
||||
new UuidSchemeInitDataTuple(WIDEVINE_UUID, DATA_1));
|
||||
// Basic non-referential equality test with non-referential scheme data.
|
||||
testInitData = new DrmInitData(DATA_1B, DATA_2B);
|
||||
assertEquals(drmInitData, testInitData);
|
||||
assertEquals(drmInitData.hashCode(), testInitData.hashCode());
|
||||
|
||||
// Passing the scheme data in reverse order shouldn't affect equality.
|
||||
testInitData = new DrmInitData(DATA_2, DATA_1);
|
||||
assertEquals(drmInitData, testInitData);
|
||||
assertEquals(drmInitData.hashCode(), testInitData.hashCode());
|
||||
|
||||
// Ditto.
|
||||
testInitData = new DrmInitData(DATA_2B, DATA_1B);
|
||||
assertEquals(drmInitData, testInitData);
|
||||
assertEquals(drmInitData.hashCode(), testInitData.hashCode());
|
||||
|
||||
// Different number of tuples should affect equality.
|
||||
testInitData = new DrmInitData.Mapped(
|
||||
new UuidSchemeInitDataTuple(WIDEVINE_UUID, DATA_1));
|
||||
testInitData = new DrmInitData(DATA_1);
|
||||
MoreAsserts.assertNotEqual(drmInitData, testInitData);
|
||||
|
||||
// Different data in one of the tuples should affect equality.
|
||||
testInitData = new DrmInitData.Mapped(
|
||||
new UuidSchemeInitDataTuple(WIDEVINE_UUID, DATA_1),
|
||||
new UuidSchemeInitDataTuple(PLAYREADY_UUID, DATA_1));
|
||||
testInitData = new DrmInitData(DATA_1, DATA_1);
|
||||
MoreAsserts.assertNotEqual(drmInitData, testInitData);
|
||||
}
|
||||
|
||||
public void testUniversalEquals() {
|
||||
DrmInitData.Universal drmInitData = new DrmInitData.Universal(DATA_1);
|
||||
public void testGet() {
|
||||
// Basic matching.
|
||||
DrmInitData testInitData = new DrmInitData(DATA_1, DATA_2);
|
||||
assertEquals(DATA_1, testInitData.get(WIDEVINE_UUID));
|
||||
assertEquals(DATA_2, testInitData.get(PLAYREADY_UUID));
|
||||
assertNull(testInitData.get(C.UUID_NIL));
|
||||
|
||||
// Basic non-referential equality test.
|
||||
DrmInitData.Universal testInitData = new DrmInitData.Universal(DATA_1);
|
||||
assertEquals(drmInitData, testInitData);
|
||||
assertEquals(drmInitData.hashCode(), testInitData.hashCode());
|
||||
// Basic matching including universal data.
|
||||
testInitData = new DrmInitData(DATA_1, DATA_2, DATA_UNIVERSAL);
|
||||
assertEquals(DATA_1, testInitData.get(WIDEVINE_UUID));
|
||||
assertEquals(DATA_2, testInitData.get(PLAYREADY_UUID));
|
||||
assertEquals(DATA_UNIVERSAL, testInitData.get(C.UUID_NIL));
|
||||
|
||||
// Different data should affect equality.
|
||||
testInitData = new DrmInitData.Universal(DATA_2);
|
||||
MoreAsserts.assertNotEqual(drmInitData, testInitData);
|
||||
// Passing the scheme data in reverse order shouldn't affect equality.
|
||||
testInitData = new DrmInitData(DATA_UNIVERSAL, DATA_2, DATA_1);
|
||||
assertEquals(DATA_1, testInitData.get(WIDEVINE_UUID));
|
||||
assertEquals(DATA_2, testInitData.get(PLAYREADY_UUID));
|
||||
assertEquals(DATA_UNIVERSAL, testInitData.get(C.UUID_NIL));
|
||||
|
||||
// Universal data should be returned in the absence of a specific match.
|
||||
testInitData = new DrmInitData(DATA_1, DATA_UNIVERSAL);
|
||||
assertEquals(DATA_1, testInitData.get(WIDEVINE_UUID));
|
||||
assertEquals(DATA_UNIVERSAL, testInitData.get(PLAYREADY_UUID));
|
||||
assertEquals(DATA_UNIVERSAL, testInitData.get(C.UUID_NIL));
|
||||
}
|
||||
|
||||
public void testDuplicateSchemeDataRejected() {
|
||||
try {
|
||||
new DrmInitData(DATA_1, DATA_1);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected.
|
||||
}
|
||||
|
||||
try {
|
||||
new DrmInitData(DATA_1, DATA_1B);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected.
|
||||
}
|
||||
|
||||
try {
|
||||
new DrmInitData(DATA_1, DATA_2, DATA_1B);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
public void testSchemeDataMatches() {
|
||||
assertTrue(DATA_1.matches(WIDEVINE_UUID));
|
||||
assertFalse(DATA_1.matches(PLAYREADY_UUID));
|
||||
assertFalse(DATA_2.matches(C.UUID_NIL));
|
||||
|
||||
assertFalse(DATA_2.matches(WIDEVINE_UUID));
|
||||
assertTrue(DATA_2.matches(PLAYREADY_UUID));
|
||||
assertFalse(DATA_2.matches(C.UUID_NIL));
|
||||
|
||||
assertTrue(DATA_UNIVERSAL.matches(WIDEVINE_UUID));
|
||||
assertTrue(DATA_UNIVERSAL.matches(PLAYREADY_UUID));
|
||||
assertTrue(DATA_UNIVERSAL.matches(C.UUID_NIL));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.Format;
|
||||
import com.google.android.exoplayer.ParserException;
|
||||
import com.google.android.exoplayer.drm.DrmInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer.extractor.ChunkIndex;
|
||||
import com.google.android.exoplayer.extractor.mkv.StreamBuilder.ContentEncodingSettings;
|
||||
import com.google.android.exoplayer.testutil.FakeExtractorOutput;
|
||||
@ -237,10 +237,10 @@ public final class MatroskaExtractorTest extends InstrumentationTestCase {
|
||||
assertSeekMap(DEFAULT_TIMECODE_SCALE, 1);
|
||||
DrmInitData drmInitData = extractorOutput.drmInitData;
|
||||
assertNotNull(drmInitData);
|
||||
SchemeInitData widevineInitData = drmInitData.get(WIDEVINE_UUID);
|
||||
SchemeData widevineInitData = drmInitData.get(WIDEVINE_UUID);
|
||||
assertEquals(MimeTypes.VIDEO_WEBM, widevineInitData.mimeType);
|
||||
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, widevineInitData.data);
|
||||
SchemeInitData zeroInitData = drmInitData.get(ZERO_UUID);
|
||||
SchemeData zeroInitData = drmInitData.get(ZERO_UUID);
|
||||
assertEquals(MimeTypes.VIDEO_WEBM, zeroInitData.mimeType);
|
||||
android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, zeroInitData.data);
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ import com.google.android.exoplayer.util.Util;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.MediaCodec;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Defines constants that are generally useful throughout the library.
|
||||
*/
|
||||
@ -175,6 +177,12 @@ public final class C {
|
||||
public static final int DEFAULT_MUXED_BUFFER_SIZE = DEFAULT_VIDEO_BUFFER_SIZE
|
||||
+ DEFAULT_AUDIO_BUFFER_SIZE + DEFAULT_TEXT_BUFFER_SIZE;
|
||||
|
||||
/**
|
||||
* The Nil UUID as defined by
|
||||
* <a href="https://tools.ietf.org/html/rfc4122#section-4.1.7">RFC4122</a>.
|
||||
*/
|
||||
public static final UUID UUID_NIL = new UUID(0L, 0L);
|
||||
|
||||
private C() {}
|
||||
|
||||
}
|
||||
|
@ -16,8 +16,7 @@
|
||||
package com.google.android.exoplayer;
|
||||
|
||||
import com.google.android.exoplayer.drm.DrmInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.UuidSchemeInitDataTuple;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
|
||||
import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
@ -287,15 +286,13 @@ public final class FrameworkSampleSource implements SampleSource {
|
||||
if (psshInfo == null || psshInfo.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
UuidSchemeInitDataTuple[] uuidSchemeInitDataTuples =
|
||||
new UuidSchemeInitDataTuple[psshInfo.size()];
|
||||
SchemeData[] schemeDatas = new SchemeData[psshInfo.size()];
|
||||
int count = 0;
|
||||
for (UUID uuid : psshInfo.keySet()) {
|
||||
byte[] psshAtom = PsshAtomUtil.buildPsshAtom(uuid, psshInfo.get(uuid));
|
||||
SchemeInitData schemeData = new SchemeInitData(MimeTypes.VIDEO_MP4, psshAtom);
|
||||
uuidSchemeInitDataTuples[count++] = new UuidSchemeInitDataTuple(uuid, schemeData);
|
||||
schemeDatas[count++] = new SchemeData(uuid, MimeTypes.VIDEO_MP4, psshAtom);
|
||||
}
|
||||
return new DrmInitData.Mapped(uuidSchemeInitDataTuples);
|
||||
return new DrmInitData(schemeDatas);
|
||||
}
|
||||
|
||||
private void seekToUsInternal(long positionUs, boolean force) {
|
||||
|
@ -37,7 +37,7 @@ import com.google.android.exoplayer.dash.mpd.Period;
|
||||
import com.google.android.exoplayer.dash.mpd.RangedUri;
|
||||
import com.google.android.exoplayer.dash.mpd.Representation;
|
||||
import com.google.android.exoplayer.drm.DrmInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.UuidSchemeInitDataTuple;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer.extractor.ChunkIndex;
|
||||
import com.google.android.exoplayer.extractor.SeekMap;
|
||||
import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor;
|
||||
@ -344,19 +344,17 @@ public class DashChunkSource implements ChunkSource {
|
||||
}
|
||||
|
||||
private static DrmInitData getDrmInitData(AdaptationSet adaptationSet) {
|
||||
ArrayList<UuidSchemeInitDataTuple> uuidSchemeInitDataTuples = null;
|
||||
ArrayList<SchemeData> schemeDatas = null;
|
||||
for (int i = 0; i < adaptationSet.contentProtections.size(); i++) {
|
||||
ContentProtection contentProtection = adaptationSet.contentProtections.get(i);
|
||||
if (contentProtection.uuid != null && contentProtection.data != null) {
|
||||
if (uuidSchemeInitDataTuples == null) {
|
||||
uuidSchemeInitDataTuples = new ArrayList<UuidSchemeInitDataTuple>();
|
||||
if (contentProtection.schemeData != null) {
|
||||
if (schemeDatas == null) {
|
||||
schemeDatas = new ArrayList<SchemeData>();
|
||||
}
|
||||
uuidSchemeInitDataTuples.add(
|
||||
new UuidSchemeInitDataTuple(contentProtection.uuid, contentProtection.data));
|
||||
schemeDatas.add(contentProtection.schemeData);
|
||||
}
|
||||
}
|
||||
return uuidSchemeInitDataTuples == null ? null
|
||||
: new DrmInitData.Mapped(uuidSchemeInitDataTuples);
|
||||
return schemeDatas == null ? null : new DrmInitData(schemeDatas);
|
||||
}
|
||||
|
||||
private static long getPeriodDurationUs(MediaPresentationDescription manifest, int index) {
|
||||
|
@ -15,12 +15,10 @@
|
||||
*/
|
||||
package com.google.android.exoplayer.dash.mpd;
|
||||
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a ContentProtection tag in an AdaptationSet.
|
||||
*/
|
||||
@ -31,25 +29,18 @@ public class ContentProtection {
|
||||
*/
|
||||
public final String schemeUriId;
|
||||
|
||||
/**
|
||||
* The UUID of the protection scheme. May be null.
|
||||
*/
|
||||
public final UUID uuid;
|
||||
|
||||
/**
|
||||
* Protection scheme specific initialization data. May be null.
|
||||
*/
|
||||
public final SchemeInitData data;
|
||||
public final SchemeData schemeData;
|
||||
|
||||
/**
|
||||
* @param schemeUriId Identifies the content protection scheme.
|
||||
* @param uuid The UUID of the protection scheme, if known. May be null.
|
||||
* @param data Protection scheme specific initialization data. May be null.
|
||||
* @param schemeData Protection scheme specific initialization data. May be null.
|
||||
*/
|
||||
public ContentProtection(String schemeUriId, UUID uuid, SchemeInitData data) {
|
||||
public ContentProtection(String schemeUriId, SchemeData schemeData) {
|
||||
this.schemeUriId = Assertions.checkNotNull(schemeUriId);
|
||||
this.uuid = uuid;
|
||||
this.data = data;
|
||||
this.schemeData = schemeData;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -62,17 +53,12 @@ public class ContentProtection {
|
||||
}
|
||||
|
||||
ContentProtection other = (ContentProtection) obj;
|
||||
return schemeUriId.equals(other.schemeUriId)
|
||||
&& Util.areEqual(uuid, other.uuid)
|
||||
&& Util.areEqual(data, other.data);
|
||||
return schemeUriId.equals(other.schemeUriId) && Util.areEqual(schemeData, other.schemeData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = schemeUriId.hashCode();
|
||||
hashCode = (31 * hashCode) + (uuid != null ? uuid.hashCode() : 0);
|
||||
hashCode = (31 * hashCode) + (data != null ? data.hashCode() : 0);
|
||||
return hashCode;
|
||||
return (31 * schemeUriId.hashCode()) + (schemeData != null ? schemeData.hashCode() : 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentList;
|
||||
import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTemplate;
|
||||
import com.google.android.exoplayer.dash.mpd.SegmentBase.SegmentTimelineElement;
|
||||
import com.google.android.exoplayer.dash.mpd.SegmentBase.SingleSegmentBase;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
|
||||
import com.google.android.exoplayer.upstream.UriLoadable;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
@ -314,29 +314,29 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
protected ContentProtection parseContentProtection(XmlPullParser xpp)
|
||||
throws XmlPullParserException, IOException {
|
||||
String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri");
|
||||
UUID uuid = null;
|
||||
SchemeInitData data = null;
|
||||
SchemeData schemeData = null;
|
||||
boolean seenPsshElement = false;
|
||||
do {
|
||||
xpp.next();
|
||||
// The cenc:pssh element is defined in 23001-7:2015.
|
||||
if (ParserUtil.isStartTag(xpp, "cenc:pssh") && xpp.next() == XmlPullParser.TEXT) {
|
||||
seenPsshElement = true;
|
||||
data = new SchemeInitData(MimeTypes.VIDEO_MP4,
|
||||
Base64.decode(xpp.getText(), Base64.DEFAULT));
|
||||
uuid = PsshAtomUtil.parseUuid(data.data);
|
||||
byte[] data = Base64.decode(xpp.getText(), Base64.DEFAULT);
|
||||
UUID uuid = PsshAtomUtil.parseUuid(data);
|
||||
if (uuid != null) {
|
||||
schemeData = new SchemeData(uuid, MimeTypes.VIDEO_MP4, data);
|
||||
}
|
||||
}
|
||||
} while (!ParserUtil.isEndTag(xpp, "ContentProtection"));
|
||||
if (seenPsshElement && uuid == null) {
|
||||
if (seenPsshElement && schemeData == null) {
|
||||
Log.w(TAG, "Skipped unsupported ContentProtection element");
|
||||
return null;
|
||||
}
|
||||
return buildContentProtection(schemeIdUri, uuid, data);
|
||||
return buildContentProtection(schemeIdUri, schemeData);
|
||||
}
|
||||
|
||||
protected ContentProtection buildContentProtection(String schemeIdUri, UUID uuid,
|
||||
SchemeInitData data) {
|
||||
return new ContentProtection(schemeIdUri, uuid, data);
|
||||
protected ContentProtection buildContentProtection(String schemeIdUri, SchemeData schemeData) {
|
||||
return new ContentProtection(schemeIdUri, schemeData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,119 +15,103 @@
|
||||
*/
|
||||
package com.google.android.exoplayer.drm;
|
||||
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Encapsulates DRM initialization data for possibly multiple DRM schemes.
|
||||
*/
|
||||
public interface DrmInitData {
|
||||
public final class DrmInitData implements Comparator<SchemeData> {
|
||||
|
||||
/**
|
||||
* Retrieves initialization data for a given DRM scheme, specified by its UUID.
|
||||
*
|
||||
* @param schemeUuid The DRM scheme's UUID.
|
||||
* @return The initialization data for the scheme, or null if the scheme is not supported.
|
||||
*/
|
||||
SchemeInitData get(UUID schemeUuid);
|
||||
private final SchemeData[] schemeDatas;
|
||||
|
||||
/**
|
||||
* A {@link DrmInitData} implementation that maps UUID onto scheme specific data.
|
||||
*/
|
||||
final class Mapped implements DrmInitData {
|
||||
// Lazily initialized hashcode.
|
||||
private int hashCode;
|
||||
|
||||
private final UuidSchemeInitDataTuple[] schemeDatas;
|
||||
public DrmInitData(List<SchemeData> schemeDatas) {
|
||||
this(false, schemeDatas.toArray(new SchemeData[schemeDatas.size()]));
|
||||
}
|
||||
|
||||
// Lazily initialized hashcode.
|
||||
private int hashCode;
|
||||
public DrmInitData(SchemeData... schemeDatas) {
|
||||
this(true, schemeDatas);
|
||||
}
|
||||
|
||||
public Mapped(UuidSchemeInitDataTuple... schemeDatas) {
|
||||
this.schemeDatas = schemeDatas.clone();
|
||||
Arrays.sort(this.schemeDatas); // Required for correct equals and hashcode implementations.
|
||||
private DrmInitData(boolean cloneSchemeDatas, SchemeData... schemeDatas) {
|
||||
if (cloneSchemeDatas) {
|
||||
schemeDatas = schemeDatas.clone();
|
||||
}
|
||||
|
||||
public Mapped(List<UuidSchemeInitDataTuple> schemeDatas) {
|
||||
this.schemeDatas = schemeDatas.toArray(new UuidSchemeInitDataTuple[schemeDatas.size()]);
|
||||
Arrays.sort(this.schemeDatas); // Required for correct equals and hashcode implementations.
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchemeInitData get(UUID schemeUuid) {
|
||||
for (UuidSchemeInitDataTuple schemeData : schemeDatas) {
|
||||
if (schemeUuid.equals(schemeData.uuid)) {
|
||||
return schemeData.data;
|
||||
}
|
||||
// Sorting ensures that universal scheme data(i.e. data that applies to all schemes) is matched
|
||||
// last. It's also required by the equals and hashcode implementations.
|
||||
Arrays.sort(schemeDatas, this);
|
||||
// Check for no duplicates.
|
||||
for (int i = 1; i < schemeDatas.length; i++) {
|
||||
if (schemeDatas[i - 1].uuid.equals(schemeDatas[i].uuid)) {
|
||||
throw new IllegalArgumentException("Duplicate data for uuid: " + schemeDatas[i].uuid);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
hashCode = Arrays.hashCode(schemeDatas);
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(schemeDatas, ((Mapped) obj).schemeDatas);
|
||||
}
|
||||
|
||||
this.schemeDatas = schemeDatas;
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link DrmInitData} implementation that returns the same data for all schemes.
|
||||
* Retrieves data for a given DRM scheme, specified by its UUID.
|
||||
*
|
||||
* @param uuid The DRM scheme's UUID.
|
||||
* @return The initialization data for the scheme, or null if the scheme is not supported.
|
||||
*/
|
||||
final class Universal implements DrmInitData {
|
||||
|
||||
private final SchemeInitData data;
|
||||
|
||||
public Universal(SchemeInitData data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchemeInitData get(UUID schemeUuid) {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return data == null ? 0 : data.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
public SchemeData get(UUID uuid) {
|
||||
for (SchemeData schemeData : schemeDatas) {
|
||||
if (schemeData.matches(uuid)) {
|
||||
return schemeData;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return Util.areEqual(data, ((Universal) obj).data);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
hashCode = Arrays.hashCode(schemeDatas);
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(schemeDatas, ((DrmInitData) obj).schemeDatas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(SchemeData first, SchemeData second) {
|
||||
return C.UUID_NIL.equals(first.uuid) ? (C.UUID_NIL.equals(second.uuid) ? 0 : 1)
|
||||
: first.uuid.compareTo(second.uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheme initialization data.
|
||||
*/
|
||||
final class SchemeInitData {
|
||||
public static final class SchemeData {
|
||||
|
||||
// Lazily initialized hashcode.
|
||||
private int hashCode;
|
||||
|
||||
/**
|
||||
* The {@link UUID} of the DRM scheme, or {@link C#UUID_NIL} if the data is universal (i.e.
|
||||
* applies to all schemes).
|
||||
*/
|
||||
private final UUID uuid;
|
||||
/**
|
||||
* The mimeType of {@link #data}.
|
||||
*/
|
||||
@ -138,73 +122,51 @@ public interface DrmInitData {
|
||||
public final byte[] data;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
public SchemeInitData(String mimeType, byte[] data) {
|
||||
public SchemeData(UUID uuid, String mimeType, byte[] data) {
|
||||
this.uuid = Assertions.checkNotNull(uuid);
|
||||
this.mimeType = Assertions.checkNotNull(mimeType);
|
||||
this.data = Assertions.checkNotNull(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this initialization data applies to the specified scheme.
|
||||
*
|
||||
* @param schemeUuid The scheme {@link UUID}.
|
||||
* @return True if this initialization data applies to the specified scheme. False otherwise.
|
||||
*/
|
||||
public boolean matches(UUID schemeUuid) {
|
||||
return C.UUID_NIL.equals(uuid) || schemeUuid.equals(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof SchemeInitData)) {
|
||||
if (!(obj instanceof SchemeData)) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
SchemeInitData other = (SchemeInitData) obj;
|
||||
return mimeType.equals(other.mimeType) && Arrays.equals(data, other.data);
|
||||
SchemeData other = (SchemeData) obj;
|
||||
return mimeType.equals(other.mimeType) && Util.areEqual(uuid, other.uuid)
|
||||
&& Arrays.equals(data, other.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
hashCode = mimeType.hashCode() + 31 * Arrays.hashCode(data);
|
||||
int result = ((uuid == null) ? 0 : uuid.hashCode());
|
||||
result = 31 * result + mimeType.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(data);
|
||||
hashCode = result;
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A tuple consisting of a {@link UUID} and a {@link SchemeInitData}.
|
||||
* <p>
|
||||
* Implements {@link Comparable} based on {@link UUID} ordering.
|
||||
*/
|
||||
final class UuidSchemeInitDataTuple implements Comparable<UuidSchemeInitDataTuple> {
|
||||
|
||||
public final UUID uuid;
|
||||
public final SchemeInitData data;
|
||||
|
||||
public UuidSchemeInitDataTuple(UUID uuid, SchemeInitData data) {
|
||||
this.uuid = Assertions.checkNotNull(uuid);
|
||||
this.data = Assertions.checkNotNull(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(UuidSchemeInitDataTuple another) {
|
||||
return uuid.compareTo(another.uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return uuid.hashCode() + 31 * data.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
UuidSchemeInitDataTuple other = (UuidSchemeInitDataTuple) obj;
|
||||
return Util.areEqual(uuid, other.uuid) && Util.areEqual(data, other.data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.google.android.exoplayer.drm;
|
||||
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
@ -104,7 +104,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
|
||||
private int state;
|
||||
private MediaCrypto mediaCrypto;
|
||||
private Exception lastException;
|
||||
private SchemeInitData schemeInitData;
|
||||
private SchemeData schemeData;
|
||||
private byte[] sessionId;
|
||||
|
||||
/**
|
||||
@ -273,19 +273,19 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
|
||||
requestHandlerThread.start();
|
||||
postRequestHandler = new PostRequestHandler(requestHandlerThread.getLooper());
|
||||
}
|
||||
if (schemeInitData == null) {
|
||||
schemeInitData = drmInitData.get(uuid);
|
||||
if (schemeInitData == null) {
|
||||
if (schemeData == null) {
|
||||
schemeData = drmInitData.get(uuid);
|
||||
if (schemeData == null) {
|
||||
onError(new IllegalStateException("Media does not support uuid: " + uuid));
|
||||
return;
|
||||
}
|
||||
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.data, WIDEVINE_UUID);
|
||||
byte[] psshData = PsshAtomUtil.parseSchemeSpecificData(schemeData.data, WIDEVINE_UUID);
|
||||
if (psshData == null) {
|
||||
// Extraction failed. schemeData isn't a Widevine PSSH atom, so leave it unchanged.
|
||||
} else {
|
||||
schemeInitData = new SchemeInitData(schemeInitData.mimeType, psshData);
|
||||
schemeData = new SchemeData(WIDEVINE_UUID, schemeData.mimeType, psshData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -306,7 +306,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
|
||||
postRequestHandler = null;
|
||||
requestHandlerThread.quit();
|
||||
requestHandlerThread = null;
|
||||
schemeInitData = null;
|
||||
schemeData = null;
|
||||
mediaCrypto = null;
|
||||
lastException = null;
|
||||
if (sessionId != null) {
|
||||
@ -368,7 +368,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
|
||||
private void postKeyRequest() {
|
||||
KeyRequest keyRequest;
|
||||
try {
|
||||
keyRequest = mediaDrm.getKeyRequest(sessionId, schemeInitData.data, schemeInitData.mimeType,
|
||||
keyRequest = mediaDrm.getKeyRequest(sessionId, schemeData.data, schemeData.mimeType,
|
||||
MediaDrm.KEY_TYPE_STREAMING, optionalKeyRequestParameters);
|
||||
postRequestHandler.obtainMessage(MSG_KEYS, keyRequest).sendToTarget();
|
||||
} catch (NotProvisionedException e) {
|
||||
|
@ -19,7 +19,7 @@ import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.Format;
|
||||
import com.google.android.exoplayer.ParserException;
|
||||
import com.google.android.exoplayer.drm.DrmInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer.extractor.ChunkIndex;
|
||||
import com.google.android.exoplayer.extractor.Extractor;
|
||||
import com.google.android.exoplayer.extractor.ExtractorInput;
|
||||
@ -484,8 +484,8 @@ public final class MatroskaExtractor implements Extractor {
|
||||
throw new ParserException("Encrypted Track found but ContentEncKeyID was not found");
|
||||
}
|
||||
if (!sentDrmInitData) {
|
||||
extractorOutput.drmInitData(new DrmInitData.Universal(
|
||||
new SchemeInitData(MimeTypes.VIDEO_WEBM, currentTrack.encryptionKeyId)));
|
||||
extractorOutput.drmInitData(new DrmInitData(
|
||||
new SchemeData(C.UUID_NIL, MimeTypes.VIDEO_WEBM, currentTrack.encryptionKeyId)));
|
||||
sentDrmInitData = true;
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,7 @@ package com.google.android.exoplayer.extractor.mp4;
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.ParserException;
|
||||
import com.google.android.exoplayer.drm.DrmInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.UuidSchemeInitDataTuple;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer.extractor.ChunkIndex;
|
||||
import com.google.android.exoplayer.extractor.Extractor;
|
||||
import com.google.android.exoplayer.extractor.ExtractorInput;
|
||||
@ -320,25 +319,24 @@ public final class FragmentedMp4Extractor implements Extractor {
|
||||
List<Atom.LeafAtom> moovLeafChildren = moov.leafChildren;
|
||||
int moovLeafChildrenSize = moovLeafChildren.size();
|
||||
|
||||
ArrayList<UuidSchemeInitDataTuple> uuidSchemeInitDataTuples = null;
|
||||
ArrayList<SchemeData> schemeDatas = null;
|
||||
for (int i = 0; i < moovLeafChildrenSize; i++) {
|
||||
LeafAtom child = moovLeafChildren.get(i);
|
||||
if (child.type == Atom.TYPE_pssh) {
|
||||
if (uuidSchemeInitDataTuples == null) {
|
||||
uuidSchemeInitDataTuples = new ArrayList<UuidSchemeInitDataTuple>();
|
||||
if (schemeDatas == null) {
|
||||
schemeDatas = new ArrayList<SchemeData>();
|
||||
}
|
||||
byte[] psshData = child.data.data;
|
||||
UUID uuid = PsshAtomUtil.parseUuid(psshData);
|
||||
if (uuid == null) {
|
||||
Log.w(TAG, "Skipped pssh atom (failed to extract uuid)");
|
||||
} else {
|
||||
uuidSchemeInitDataTuples.add(new UuidSchemeInitDataTuple(uuid,
|
||||
new SchemeInitData(MimeTypes.VIDEO_MP4, psshData)));
|
||||
schemeDatas.add(new SchemeData(uuid, MimeTypes.VIDEO_MP4, psshData));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uuidSchemeInitDataTuples != null) {
|
||||
extractorOutput.drmInitData(new DrmInitData.Mapped(uuidSchemeInitDataTuples));
|
||||
if (schemeDatas != null) {
|
||||
extractorOutput.drmInitData(new DrmInitData(schemeDatas));
|
||||
}
|
||||
|
||||
// Read declaration of track fragments in the Moov box.
|
||||
|
@ -29,8 +29,7 @@ import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener;
|
||||
import com.google.android.exoplayer.chunk.FormatEvaluator;
|
||||
import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator;
|
||||
import com.google.android.exoplayer.drm.DrmInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.UuidSchemeInitDataTuple;
|
||||
import com.google.android.exoplayer.drm.DrmInitData.SchemeData;
|
||||
import com.google.android.exoplayer.extractor.mp4.TrackEncryptionBox;
|
||||
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.ProtectionElement;
|
||||
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement;
|
||||
@ -70,7 +69,7 @@ public final class SmoothStreamingSampleSource implements SampleSource {
|
||||
private long durationUs;
|
||||
private SmoothStreamingManifest currentManifest;
|
||||
private TrackEncryptionBox[] trackEncryptionBoxes;
|
||||
private DrmInitData.Mapped drmInitData;
|
||||
private DrmInitData drmInitData;
|
||||
private TrackGroupArray trackGroups;
|
||||
private int[] trackGroupElementIndices;
|
||||
private boolean pendingReset;
|
||||
@ -120,8 +119,8 @@ public final class SmoothStreamingSampleSource implements SampleSource {
|
||||
byte[] keyId = getProtectionElementKeyId(protectionElement.data);
|
||||
trackEncryptionBoxes = new TrackEncryptionBox[1];
|
||||
trackEncryptionBoxes[0] = new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId);
|
||||
drmInitData = new DrmInitData.Mapped(new UuidSchemeInitDataTuple(protectionElement.uuid,
|
||||
new SchemeInitData(MimeTypes.VIDEO_MP4, protectionElement.data)));
|
||||
drmInitData = new DrmInitData(
|
||||
new SchemeData(protectionElement.uuid, MimeTypes.VIDEO_MP4, protectionElement.data));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user