diff --git a/library/src/androidTest/java/com/google/android/exoplayer/extractor/webm/WebmExtractorTest.java b/library/src/androidTest/java/com/google/android/exoplayer/extractor/webm/WebmExtractorTest.java index e463a17e82..4011a53e9d 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer/extractor/webm/WebmExtractorTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer/extractor/webm/WebmExtractorTest.java @@ -21,6 +21,7 @@ import com.google.android.exoplayer.C; import com.google.android.exoplayer.MediaFormat; 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.extractor.ChunkIndex; import com.google.android.exoplayer.extractor.SeekMap; import com.google.android.exoplayer.extractor.webm.StreamBuilder.ContentEncodingSettings; @@ -237,8 +238,12 @@ public final class WebmExtractorTest extends InstrumentationTestCase { assertIndex(DEFAULT_TIMECODE_SCALE, 1); DrmInitData drmInitData = extractorOutput.drmInitData; assertNotNull(drmInitData); - android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, drmInitData.get(WIDEVINE_UUID)); - android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, drmInitData.get(ZERO_UUID)); + SchemeInitData 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); + assertEquals(MimeTypes.VIDEO_WEBM, zeroInitData.mimeType); + android.test.MoreAsserts.assertEquals(TEST_ENCRYPTION_KEY_ID, zeroInitData.data); } public void testPrepareThreeCuePoints() throws IOException, InterruptedException { diff --git a/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java b/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java index 9fb4d8c216..e5ea8f7863 100644 --- a/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java +++ b/library/src/main/java/com/google/android/exoplayer/FrameworkSampleSource.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer; import com.google.android.exoplayer.SampleSource.SampleSourceReader; import com.google.android.exoplayer.drm.DrmInitData; +import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData; import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil; import com.google.android.exoplayer.util.Assertions; @@ -273,10 +274,10 @@ public final class FrameworkSampleSource implements SampleSource, SampleSourceRe if (psshInfo == null || psshInfo.isEmpty()) { return null; } - DrmInitData.Mapped drmInitData = new DrmInitData.Mapped(MimeTypes.VIDEO_MP4); + DrmInitData.Mapped drmInitData = new DrmInitData.Mapped(); for (UUID uuid : psshInfo.keySet()) { byte[] psshAtom = PsshAtomUtil.buildPsshAtom(uuid, psshInfo.get(uuid)); - drmInitData.put(uuid, psshAtom); + drmInitData.put(uuid, new SchemeInitData(MimeTypes.VIDEO_MP4, psshAtom)); } return drmInitData; } diff --git a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java index f28bde2899..cd388b312a 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/DashChunkSource.java @@ -1054,8 +1054,6 @@ public class DashChunkSource implements ChunkSource, Output { } private static DrmInitData getDrmInitData(AdaptationSet adaptationSet) { - String drmInitMimeType = mimeTypeIsWebm(adaptationSet.representations.get(0).format.mimeType) - ? MimeTypes.VIDEO_WEBM : MimeTypes.VIDEO_MP4; if (adaptationSet.contentProtections.isEmpty()) { return null; } else { @@ -1064,7 +1062,7 @@ public class DashChunkSource implements ChunkSource, Output { ContentProtection contentProtection = adaptationSet.contentProtections.get(i); if (contentProtection.uuid != null && contentProtection.data != null) { if (drmInitData == null) { - drmInitData = new DrmInitData.Mapped(drmInitMimeType); + drmInitData = new DrmInitData.Mapped(); } drmInitData.put(contentProtection.uuid, contentProtection.data); } diff --git a/library/src/main/java/com/google/android/exoplayer/dash/mpd/ContentProtection.java b/library/src/main/java/com/google/android/exoplayer/dash/mpd/ContentProtection.java index 8f02fdc6f4..1ebba25f4e 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/mpd/ContentProtection.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/mpd/ContentProtection.java @@ -15,10 +15,10 @@ */ package com.google.android.exoplayer.dash.mpd; +import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData; import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Util; -import java.util.Arrays; import java.util.UUID; /** @@ -37,16 +37,16 @@ public class ContentProtection { public final UUID uuid; /** - * Protection scheme specific data. May be null. + * Protection scheme specific initialization data. May be null. */ - public final byte[] data; + public final SchemeInitData data; /** * @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. */ - public ContentProtection(String schemeUriId, UUID uuid, byte[] data) { + public ContentProtection(String schemeUriId, UUID uuid, SchemeInitData data) { this.schemeUriId = Assertions.checkNotNull(schemeUriId); this.uuid = uuid; this.data = data; @@ -64,20 +64,14 @@ public class ContentProtection { ContentProtection other = (ContentProtection) obj; return schemeUriId.equals(other.schemeUriId) && Util.areEqual(uuid, other.uuid) - && Arrays.equals(data, other.data); + && Util.areEqual(data, other.data); } @Override public int hashCode() { - int hashCode = 1; - - hashCode = hashCode * 37 + schemeUriId.hashCode(); - if (uuid != null) { - hashCode = hashCode * 37 + uuid.hashCode(); - } - if (data != null) { - hashCode = hashCode * 37 + Arrays.hashCode(data); - } + int hashCode = schemeUriId.hashCode(); + hashCode = (37 * hashCode) + (uuid != null ? uuid.hashCode() : 0); + hashCode = (37 * hashCode) + (data != null ? data.hashCode() : 0); return hashCode; } diff --git a/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java b/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java index 19c64067c6..a205599e45 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java @@ -21,6 +21,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.extractor.mp4.PsshAtomUtil; import com.google.android.exoplayer.upstream.UriLoadable; import com.google.android.exoplayer.util.Assertions; @@ -317,23 +318,24 @@ public class MediaPresentationDescriptionParser extends DefaultHandler throws XmlPullParserException, IOException { String schemeIdUri = xpp.getAttributeValue(null, "schemeIdUri"); UUID uuid = null; - byte[] psshAtom = null; + SchemeInitData data = null; do { xpp.next(); // The cenc:pssh element is defined in 23001-7:2015 if (ParserUtil.isStartTag(xpp, "cenc:pssh") && xpp.next() == XmlPullParser.TEXT) { - psshAtom = Base64.decode(xpp.getText(), Base64.DEFAULT); - uuid = PsshAtomUtil.parseUuid(psshAtom); + 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")); - - return buildContentProtection(schemeIdUri, uuid, psshAtom); + return buildContentProtection(schemeIdUri, uuid, data); } - protected ContentProtection buildContentProtection(String schemeIdUri, UUID uuid, byte[] data) { + protected ContentProtection buildContentProtection(String schemeIdUri, UUID uuid, + SchemeInitData data) { return new ContentProtection(schemeIdUri, uuid, data); } diff --git a/library/src/main/java/com/google/android/exoplayer/drm/DrmInitData.java b/library/src/main/java/com/google/android/exoplayer/drm/DrmInitData.java index bdb4565fc3..c01fc59ca7 100644 --- a/library/src/main/java/com/google/android/exoplayer/drm/DrmInitData.java +++ b/library/src/main/java/com/google/android/exoplayer/drm/DrmInitData.java @@ -15,25 +15,19 @@ */ package com.google.android.exoplayer.drm; +import com.google.android.exoplayer.util.Assertions; + import android.media.MediaDrm; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** - * Encapsulates initialization data required by a {@link MediaDrm} instance. + * Encapsulates initialization data required by a {@link MediaDrm} instances. */ -public abstract class DrmInitData { - - /** - * The container mime type. - */ - public final String mimeType; - - public DrmInitData(String mimeType) { - this.mimeType = mimeType; - } +public interface DrmInitData { /** * Retrieves initialization data for a given DRM scheme, specified by its UUID. @@ -41,22 +35,21 @@ public abstract class DrmInitData { * @param schemeUuid The DRM scheme's UUID. * @return The initialization data for the scheme, or null if the scheme is not supported. */ - public abstract byte[] get(UUID schemeUuid); + public abstract SchemeInitData get(UUID schemeUuid); /** * A {@link DrmInitData} implementation that maps UUID onto scheme specific data. */ - public static final class Mapped extends DrmInitData { + public static final class Mapped implements DrmInitData { - private final Map schemeData; + private final Map schemeData; - public Mapped(String mimeType) { - super(mimeType); + public Mapped() { schemeData = new HashMap<>(); } @Override - public byte[] get(UUID schemeUuid) { + public SchemeInitData get(UUID schemeUuid) { return schemeData.get(schemeUuid); } @@ -64,10 +57,10 @@ public abstract class DrmInitData { * Inserts scheme specific initialization data. * * @param schemeUuid The scheme UUID. - * @param data The corresponding initialization data. + * @param schemeInitData The corresponding initialization data. */ - public void put(UUID schemeUuid, byte[] data) { - schemeData.put(schemeUuid, data); + public void put(UUID schemeUuid, SchemeInitData schemeInitData) { + schemeData.put(schemeUuid, schemeInitData); } } @@ -75,20 +68,62 @@ public abstract class DrmInitData { /** * A {@link DrmInitData} implementation that returns the same initialization data for all schemes. */ - public static final class Universal extends DrmInitData { + public static final class Universal implements DrmInitData { - private byte[] data; + private SchemeInitData data; - public Universal(String mimeType, byte[] data) { - super(mimeType); + public Universal(SchemeInitData data) { this.data = data; } @Override - public byte[] get(UUID schemeUuid) { + public SchemeInitData get(UUID schemeUuid) { return data; } } + /** + * Scheme initialization data. + */ + public static final class SchemeInitData { + + /** + * The mimeType of {@link #data}. + */ + public final String mimeType; + /** + * The initialization data. + */ + public final byte[] data; + + /** + * @param mimeType The mimeType of the initialization data. + * @param data The initialization data. + */ + public SchemeInitData(String mimeType, byte[] data) { + this.mimeType = Assertions.checkNotNull(mimeType); + this.data = Assertions.checkNotNull(data); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SchemeInitData)) { + return false; + } + if (obj == this) { + return true; + } + + SchemeInitData other = (SchemeInitData) obj; + return mimeType.equals(other.mimeType) && Arrays.equals(data, other.data); + } + + @Override + public int hashCode() { + return mimeType.hashCode() + 31 * Arrays.hashCode(data); + } + + } + } diff --git a/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java b/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java index a0a3ab0710..be22d4c6c8 100644 --- a/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java +++ b/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer.drm; +import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData; import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil; import com.google.android.exoplayer.util.Util; @@ -103,8 +104,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager { private int state; private MediaCrypto mediaCrypto; private Exception lastException; - private String mimeType; - private byte[] schemeData; + private SchemeInitData schemeInitData; private byte[] sessionId; /** @@ -273,20 +273,19 @@ public class StreamingDrmSessionManager implements DrmSessionManager { requestHandlerThread.start(); postRequestHandler = new PostRequestHandler(requestHandlerThread.getLooper()); } - if (schemeData == null) { - mimeType = drmInitData.mimeType; - schemeData = drmInitData.get(uuid); - if (schemeData == null) { + if (schemeInitData == null) { + schemeInitData = drmInitData.get(uuid); + if (schemeInitData == 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(schemeData, WIDEVINE_UUID); + byte[] psshData = PsshAtomUtil.parseSchemeSpecificData(schemeInitData.data, WIDEVINE_UUID); if (psshData == null) { // Extraction failed. schemeData isn't a Widevine PSSH atom, so leave it unchanged. } else { - schemeData = psshData; + schemeInitData = new SchemeInitData(schemeInitData.mimeType, psshData); } } } @@ -307,7 +306,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager { postRequestHandler = null; requestHandlerThread.quit(); requestHandlerThread = null; - schemeData = null; + schemeInitData = null; mediaCrypto = null; lastException = null; if (sessionId != null) { @@ -369,7 +368,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager { private void postKeyRequest() { KeyRequest keyRequest; try { - keyRequest = mediaDrm.getKeyRequest(sessionId, schemeData, mimeType, + keyRequest = mediaDrm.getKeyRequest(sessionId, schemeInitData.data, schemeInitData.mimeType, MediaDrm.KEY_TYPE_STREAMING, optionalKeyRequestParameters); postRequestHandler.obtainMessage(MSG_KEYS, keyRequest).sendToTarget(); } catch (NotProvisionedException e) { diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/FragmentedMp4Extractor.java b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/FragmentedMp4Extractor.java index 8a00dd1872..56b20274e0 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/FragmentedMp4Extractor.java @@ -18,6 +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.extractor.ChunkIndex; import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.ExtractorInput; @@ -292,10 +293,11 @@ public final class FragmentedMp4Extractor implements Extractor { LeafAtom child = moovChildren.get(i); if (child.type == Atom.TYPE_pssh) { if (drmInitData == null) { - drmInitData = new DrmInitData.Mapped(MimeTypes.VIDEO_MP4); + drmInitData = new DrmInitData.Mapped(); } byte[] psshData = child.data.data; - drmInitData.put(PsshAtomUtil.parseUuid(psshData), psshData); + drmInitData.put(PsshAtomUtil.parseUuid(psshData), + new SchemeInitData(MimeTypes.VIDEO_MP4, psshData)); } } if (drmInitData != null) { diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/webm/WebmExtractor.java b/library/src/main/java/com/google/android/exoplayer/extractor/webm/WebmExtractor.java index d805eec7ba..96ce99a638 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/webm/WebmExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/webm/WebmExtractor.java @@ -19,6 +19,7 @@ import com.google.android.exoplayer.C; import com.google.android.exoplayer.MediaFormat; 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.extractor.ChunkIndex; import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.ExtractorInput; @@ -450,8 +451,8 @@ public final class WebmExtractor implements Extractor { throw new ParserException("Encrypted Track found but ContentEncKeyID was not found"); } if (!sentDrmInitData) { - extractorOutput.drmInitData( - new DrmInitData.Universal(MimeTypes.VIDEO_WEBM, currentTrack.encryptionKeyId)); + extractorOutput.drmInitData(new DrmInitData.Universal( + new SchemeInitData(MimeTypes.VIDEO_WEBM, currentTrack.encryptionKeyId))); sentDrmInitData = true; } } diff --git a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java index 1482f83e3e..8f83a817ac 100644 --- a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java @@ -29,6 +29,7 @@ import com.google.android.exoplayer.chunk.FormatEvaluator; import com.google.android.exoplayer.chunk.FormatEvaluator.Evaluation; import com.google.android.exoplayer.chunk.MediaChunk; import com.google.android.exoplayer.drm.DrmInitData; +import com.google.android.exoplayer.drm.DrmInitData.SchemeInitData; import com.google.android.exoplayer.extractor.mp4.FragmentedMp4Extractor; import com.google.android.exoplayer.extractor.mp4.Track; import com.google.android.exoplayer.extractor.mp4.TrackEncryptionBox; @@ -144,8 +145,9 @@ public class SmoothStreamingChunkSource implements ChunkSource, byte[] keyId = getProtectionElementKeyId(protectionElement.data); trackEncryptionBoxes = new TrackEncryptionBox[1]; trackEncryptionBoxes[0] = new TrackEncryptionBox(true, INITIALIZATION_VECTOR_SIZE, keyId); - drmInitData = new DrmInitData.Mapped(MimeTypes.VIDEO_MP4); - drmInitData.put(protectionElement.uuid, protectionElement.data); + drmInitData = new DrmInitData.Mapped(); + drmInitData.put(protectionElement.uuid, + new SchemeInitData(MimeTypes.VIDEO_MP4, protectionElement.data)); } else { trackEncryptionBoxes = null; drmInitData = null;