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..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 @@ -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; + /** + * {@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. */ public final long durationUs; /** @@ -81,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, 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 +105,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 +116,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 +191,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 +214,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 +230,6 @@ public HlsMediaPlaylist( boolean hasIndependentSegmentsTag, boolean hasEndTag, boolean hasProgramDateTime, - DrmInitData drmInitData, List segments) { super(baseUri, tags); this.playlistType = playlistType; @@ -241,7 +242,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 +309,6 @@ public HlsMediaPlaylist copyWith(long startTimeUs, int discontinuitySequence) { hasIndependentSegmentsTag, hasEndTag, hasProgramDateTime, - drmInitData, segments); } @@ -337,7 +336,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..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 @@ -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); @@ -475,6 +477,7 @@ private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String new Segment( line, initializationSegment, + drmInitData, segmentDurationUs, relativeDiscontinuitySequence, segmentStartTimeUs, @@ -506,7 +509,6 @@ private static HlsMediaPlaylist parseMediaPlaylist(LineIterator iterator, String hasIndependentSegmentsTag, hasEndTag, /* hasProgramDateTime= */ playlistStartTimeUs != 0, - drmInitData, segments); } 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