From ed6e8c30fa269bbf34a4e8906dca8ddaf77f04bf Mon Sep 17 00:00:00 2001 From: takusemba Date: Tue, 8 May 2018 16:48:53 +0900 Subject: [PATCH 1/4] support multiple drm init data --- .../exoplayer2/source/hls/HlsChunkSource.java | 2 +- .../source/hls/playlist/HlsMediaPlaylist.java | 19 ++++++++----------- .../hls/playlist/HlsPlaylistParser.java | 6 +++--- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java index 9a02bd785a0..77798afc9e6 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java @@ -360,7 +360,7 @@ public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, long l isTimestampMaster, timestampAdjuster, previous, - mediaPlaylist.drmInitData, + segment.drmInitData, encryptionKey, encryptionIv); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java index f905def54bd..24d5e68ed8e 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java @@ -42,6 +42,11 @@ public static final class Segment implements Comparable { * used for all segments that share an EXT-X-MAP tag. */ @Nullable public final Segment initializationSegment; + /** + * DRM initialization data for sample decryption, or null if none of the segment uses sample + * encryption. + */ + @Nullable public final DrmInitData drmInitData; /** The duration of the segment in microseconds, as defined by #EXTINF. */ public final long durationUs; /** @@ -81,7 +86,7 @@ public static final class Segment implements Comparable { * @param byterangeLength See {@link #byterangeLength}. */ public Segment(String uri, long byterangeOffset, long byterangeLength) { - this(uri, null, 0, -1, C.TIME_UNSET, null, null, byterangeOffset, byterangeLength, false); + this(uri, null, null, 0, -1, C.TIME_UNSET, null, null, byterangeOffset, byterangeLength, false); } /** @@ -99,6 +104,7 @@ public Segment(String uri, long byterangeOffset, long byterangeLength) { public Segment( String url, Segment initializationSegment, + @Nullable DrmInitData drmInitData, long durationUs, int relativeDiscontinuitySequence, long relativeStartTimeUs, @@ -109,6 +115,7 @@ public Segment( boolean hasGapTag) { this.url = url; this.initializationSegment = initializationSegment; + this.drmInitData = drmInitData; this.durationUs = durationUs; this.relativeDiscontinuitySequence = relativeDiscontinuitySequence; this.relativeStartTimeUs = relativeStartTimeUs; @@ -183,11 +190,6 @@ public int compareTo(@NonNull Long relativeStartTimeUs) { * Whether the playlist contains a #EXT-X-PROGRAM-DATE-TIME tag. */ public final boolean hasProgramDateTime; - /** - * DRM initialization data for sample decryption, or null if none of the segment uses sample - * encryption. - */ - public final DrmInitData drmInitData; /** * The list of segments in the playlist. */ @@ -211,7 +213,6 @@ public int compareTo(@NonNull Long relativeStartTimeUs) { * @param hasIndependentSegmentsTag See {@link #hasIndependentSegmentsTag}. * @param hasEndTag See {@link #hasEndTag}. * @param hasProgramDateTime See {@link #hasProgramDateTime}. - * @param drmInitData See {@link #drmInitData}. * @param segments See {@link #segments}. */ public HlsMediaPlaylist( @@ -228,7 +229,6 @@ public HlsMediaPlaylist( boolean hasIndependentSegmentsTag, boolean hasEndTag, boolean hasProgramDateTime, - DrmInitData drmInitData, List segments) { super(baseUri, tags); this.playlistType = playlistType; @@ -241,7 +241,6 @@ public HlsMediaPlaylist( this.hasIndependentSegmentsTag = hasIndependentSegmentsTag; this.hasEndTag = hasEndTag; this.hasProgramDateTime = hasProgramDateTime; - this.drmInitData = drmInitData; this.segments = Collections.unmodifiableList(segments); if (!segments.isEmpty()) { Segment last = segments.get(segments.size() - 1); @@ -309,7 +308,6 @@ public HlsMediaPlaylist copyWith(long startTimeUs, int discontinuitySequence) { hasIndependentSegmentsTag, hasEndTag, hasProgramDateTime, - drmInitData, segments); } @@ -337,7 +335,6 @@ public HlsMediaPlaylist copyWithEndTag() { hasIndependentSegmentsTag, /* hasEndTag= */ true, hasProgramDateTime, - drmInitData, segments); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index 7187bdb0ca1..824fa489f1a 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -17,6 +17,7 @@ import android.net.Uri; import android.util.Base64; +import android.util.SparseArray; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.ParserException; @@ -424,8 +425,7 @@ private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String } else if (method != null) { SchemeData schemeData = parseWidevineSchemeData(line, keyFormat); if (schemeData != null) { - drmInitData = - new DrmInitData( + drmInitData = new DrmInitData( (METHOD_SAMPLE_AES_CENC.equals(method) || METHOD_SAMPLE_AES_CTR.equals(method)) ? C.CENC_TYPE_cenc @@ -475,6 +475,7 @@ private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String new Segment( line, initializationSegment, + drmInitData, segmentDurationUs, relativeDiscontinuitySequence, segmentStartTimeUs, @@ -506,7 +507,6 @@ private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String hasIndependentSegmentsTag, hasEndTag, /* hasProgramDateTime= */ playlistStartTimeUs != 0, - drmInitData, segments); } From ccb559086a633e1a6cc6b344e627f0d5a004fd1b Mon Sep 17 00:00:00 2001 From: takusemba Date: Tue, 8 May 2018 17:29:32 +0900 Subject: [PATCH 2/4] put null to drmInitData if EXT-X-KEY is NONE --- .../exoplayer2/source/hls/playlist/HlsPlaylistParser.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index 824fa489f1a..4773ea4c44e 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -433,6 +433,8 @@ private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String schemeData); } } + } else { + drmInitData = null; } } else if (line.startsWith(TAG_BYTERANGE)) { String byteRange = parseStringAttr(line, REGEX_BYTERANGE); From 658960fc6c10bb033fb28dc35d85a62256defde2 Mon Sep 17 00:00:00 2001 From: takusemba Date: Tue, 8 May 2018 17:31:49 +0900 Subject: [PATCH 3/4] add test --- .../playlist/HlsMediaPlaylistParserTest.java | 52 ++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java index 7a8a4d79259..1e48e93da12 100644 --- a/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java +++ b/library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java @@ -160,8 +160,9 @@ public void testParseSampleAesMethod() throws Exception { new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME))); HlsMediaPlaylist playlist = (HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream); - assertThat(playlist.drmInitData.schemeType).isEqualTo(C.CENC_TYPE_cbcs); - assertThat(playlist.drmInitData.get(0).matches(C.WIDEVINE_UUID)).isTrue(); + assertThat(playlist.segments.get(0).drmInitData).isNull(); + assertThat(playlist.segments.get(1).drmInitData.schemeType).isEqualTo(C.CENC_TYPE_cbcs); + assertThat(playlist.segments.get(1).drmInitData.get(0).matches(C.WIDEVINE_UUID)).isTrue(); } @Test @@ -184,8 +185,9 @@ public void testParseSampleAesCencMethod() throws Exception { new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME))); HlsMediaPlaylist playlist = (HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream); - assertThat(playlist.drmInitData.schemeType).isEqualTo(C.CENC_TYPE_cenc); - assertThat(playlist.drmInitData.get(0).matches(C.WIDEVINE_UUID)).isTrue(); + assertThat(playlist.segments.get(0).drmInitData).isNull(); + assertThat(playlist.segments.get(1).drmInitData.schemeType).isEqualTo(C.CENC_TYPE_cenc); + assertThat(playlist.segments.get(1).drmInitData.get(0).matches(C.WIDEVINE_UUID)).isTrue(); } @Test @@ -208,8 +210,46 @@ public void testParseSampleAesCtrMethod() throws Exception { new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME))); HlsMediaPlaylist playlist = (HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream); - assertThat(playlist.drmInitData.schemeType).isEqualTo(C.CENC_TYPE_cenc); - assertThat(playlist.drmInitData.get(0).matches(C.WIDEVINE_UUID)).isTrue(); + assertThat(playlist.segments.get(0).drmInitData).isNull(); + assertThat(playlist.segments.get(1).drmInitData.schemeType).isEqualTo(C.CENC_TYPE_cenc); + assertThat(playlist.segments.get(1).drmInitData.get(0).matches(C.WIDEVINE_UUID)).isTrue(); + } + + @Test + public void testParseMultipleEncryptionsMethod() throws Exception { + Uri playlistUri = Uri.parse("https://example.com/test.m3u8"); + String playlistString = + "#EXTM3U\n" + + "#EXT-X-MEDIA-SEQUENCE:0\n" + + "#EXTINF:8,\n" + + "https://priv.example.com/1.ts\n" + + "\n" + + "#EXT-X-KEY:URI=\"data:text/plain;base64,VGhpcyBpcyBhbiBlYXN0ZXIgZWdn\"," + + "IV=0x9358382AEB449EE23C3D809DA0B9CCD3,KEYFORMATVERSIONS=\"1\"," + + "KEYFORMAT=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\"," + + "IV=0x1566B,METHOD=SAMPLE-AES-CENC \n" + + "#EXTINF:8,\n" + + "https://priv.example.com/2.ts\n" + + "#EXT-X-KEY:METHOD=NONE\n" + + "#EXTINF:8,\n" + + "https://priv.example.com/3.ts\n" + + "#EXT-X-KEY:URI=\"data:text/plain;base64,VGhpcyBpcyBhbiBlYXN0ZXIgZWdn\"," + + "IV=0x9358382AEB449EE23C3D809DA0B9CCD3,KEYFORMATVERSIONS=\"1\"," + + "KEYFORMAT=\"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed\"," + + "IV=0x1566B,METHOD=SAMPLE-AES-CENC \n" + + "#EXTINF:8,\n" + + "https://priv.example.com/4.ts\n" + + "#EXT-X-ENDLIST\n"; + InputStream inputStream = + new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME))); + HlsMediaPlaylist playlist = + (HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream); + assertThat(playlist.segments.get(0).drmInitData).isNull(); + assertThat(playlist.segments.get(1).drmInitData.schemeType).isEqualTo(C.CENC_TYPE_cenc); + assertThat(playlist.segments.get(1).drmInitData.get(0).matches(C.WIDEVINE_UUID)).isTrue(); + assertThat(playlist.segments.get(2).drmInitData).isNull(); + assertThat(playlist.segments.get(3).drmInitData.schemeType).isEqualTo(C.CENC_TYPE_cenc); + assertThat(playlist.segments.get(3).drmInitData.get(0).matches(C.WIDEVINE_UUID)).isTrue(); } @Test From 826b29ecab106e8e90a7b9057bd79802f2df207f Mon Sep 17 00:00:00 2001 From: takusemba Date: Tue, 22 May 2018 13:04:21 +0900 Subject: [PATCH 4/4] fix review points --- .../exoplayer2/source/hls/playlist/HlsMediaPlaylist.java | 7 ++++--- .../exoplayer2/source/hls/playlist/HlsPlaylistParser.java | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java index 24d5e68ed8e..d7cba71e8f8 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java @@ -43,8 +43,8 @@ public static final class Segment implements Comparable { */ @Nullable public final Segment initializationSegment; /** - * DRM initialization data for sample decryption, or null if none of the segment uses sample - * encryption. + * {@link DrmInitData} for sample decryption, or null if the segment's samples are not + * DRM-protected. */ @Nullable public final DrmInitData drmInitData; /** The duration of the segment in microseconds, as defined by #EXTINF. */ @@ -86,7 +86,8 @@ public static final class Segment implements Comparable { * @param byterangeLength See {@link #byterangeLength}. */ public Segment(String uri, long byterangeOffset, long byterangeLength) { - this(uri, null, null, 0, -1, C.TIME_UNSET, null, null, byterangeOffset, byterangeLength, false); + this(uri, null, null, 0, -1, C.TIME_UNSET, null, null, byterangeOffset, byterangeLength, + false); } /** diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index 4773ea4c44e..3c45efc81f3 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -17,7 +17,6 @@ import android.net.Uri; import android.util.Base64; -import android.util.SparseArray; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.ParserException; @@ -425,7 +424,8 @@ private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String } else if (method != null) { SchemeData schemeData = parseWidevineSchemeData(line, keyFormat); if (schemeData != null) { - drmInitData = new DrmInitData( + drmInitData = + new DrmInitData( (METHOD_SAMPLE_AES_CENC.equals(method) || METHOD_SAMPLE_AES_CTR.equals(method)) ? C.CENC_TYPE_cenc