mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add encryption information to initialization segments
PiperOrigin-RevId: 233953493
This commit is contained in:
parent
1c38b226ea
commit
12ed18c74c
@ -88,8 +88,15 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||||||
* @param uri See {@link #url}.
|
* @param uri See {@link #url}.
|
||||||
* @param byterangeOffset See {@link #byterangeOffset}.
|
* @param byterangeOffset See {@link #byterangeOffset}.
|
||||||
* @param byterangeLength See {@link #byterangeLength}.
|
* @param byterangeLength See {@link #byterangeLength}.
|
||||||
|
* @param fullSegmentEncryptionKeyUri See {@link #fullSegmentEncryptionKeyUri}.
|
||||||
|
* @param encryptionIV See {@link #encryptionIV}.
|
||||||
*/
|
*/
|
||||||
public Segment(String uri, long byterangeOffset, long byterangeLength) {
|
public Segment(
|
||||||
|
String uri,
|
||||||
|
long byterangeOffset,
|
||||||
|
long byterangeLength,
|
||||||
|
String fullSegmentEncryptionKeyUri,
|
||||||
|
String encryptionIV) {
|
||||||
this(
|
this(
|
||||||
uri,
|
uri,
|
||||||
/* initializationSegment= */ null,
|
/* initializationSegment= */ null,
|
||||||
@ -98,8 +105,8 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||||||
/* relativeDiscontinuitySequence= */ -1,
|
/* relativeDiscontinuitySequence= */ -1,
|
||||||
/* relativeStartTimeUs= */ C.TIME_UNSET,
|
/* relativeStartTimeUs= */ C.TIME_UNSET,
|
||||||
/* drmInitData= */ null,
|
/* drmInitData= */ null,
|
||||||
/* fullSegmentEncryptionKeyUri= */ null,
|
fullSegmentEncryptionKeyUri,
|
||||||
/* encryptionIV= */ null,
|
encryptionIV,
|
||||||
byterangeOffset,
|
byterangeOffset,
|
||||||
byterangeLength,
|
byterangeLength,
|
||||||
/* hasGapTag= */ false);
|
/* hasGapTag= */ false);
|
||||||
|
@ -455,8 +455,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
boolean hasGapTag = false;
|
boolean hasGapTag = false;
|
||||||
|
|
||||||
DrmInitData playlistProtectionSchemes = null;
|
DrmInitData playlistProtectionSchemes = null;
|
||||||
String encryptionKeyUri = null;
|
String fullSegmentEncryptionKeyUri = null;
|
||||||
String encryptionIV = null;
|
String fullSegmentEncryptionIV = null;
|
||||||
TreeMap<String, SchemeData> currentSchemeDatas = new TreeMap<>();
|
TreeMap<String, SchemeData> currentSchemeDatas = new TreeMap<>();
|
||||||
String encryptionScheme = null;
|
String encryptionScheme = null;
|
||||||
DrmInitData cachedDrmInitData = null;
|
DrmInitData cachedDrmInitData = null;
|
||||||
@ -489,7 +489,19 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
segmentByteRangeOffset = Long.parseLong(splitByteRange[1]);
|
segmentByteRangeOffset = Long.parseLong(splitByteRange[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initializationSegment = new Segment(uri, segmentByteRangeOffset, segmentByteRangeLength);
|
if (fullSegmentEncryptionKeyUri != null && fullSegmentEncryptionIV == null) {
|
||||||
|
// See RFC 8216, Section 4.3.2.5.
|
||||||
|
throw new ParserException(
|
||||||
|
"The encryption IV attribute must be present when an initialization segment is "
|
||||||
|
+ "encrypted with METHOD=AES-128.");
|
||||||
|
}
|
||||||
|
initializationSegment =
|
||||||
|
new Segment(
|
||||||
|
uri,
|
||||||
|
segmentByteRangeOffset,
|
||||||
|
segmentByteRangeLength,
|
||||||
|
fullSegmentEncryptionKeyUri,
|
||||||
|
fullSegmentEncryptionIV);
|
||||||
segmentByteRangeOffset = 0;
|
segmentByteRangeOffset = 0;
|
||||||
segmentByteRangeLength = C.LENGTH_UNSET;
|
segmentByteRangeLength = C.LENGTH_UNSET;
|
||||||
} else if (line.startsWith(TAG_TARGET_DURATION)) {
|
} else if (line.startsWith(TAG_TARGET_DURATION)) {
|
||||||
@ -521,17 +533,17 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
String method = parseStringAttr(line, REGEX_METHOD, variableDefinitions);
|
String method = parseStringAttr(line, REGEX_METHOD, variableDefinitions);
|
||||||
String keyFormat =
|
String keyFormat =
|
||||||
parseOptionalStringAttr(line, REGEX_KEYFORMAT, KEYFORMAT_IDENTITY, variableDefinitions);
|
parseOptionalStringAttr(line, REGEX_KEYFORMAT, KEYFORMAT_IDENTITY, variableDefinitions);
|
||||||
encryptionKeyUri = null;
|
fullSegmentEncryptionKeyUri = null;
|
||||||
encryptionIV = null;
|
fullSegmentEncryptionIV = null;
|
||||||
if (METHOD_NONE.equals(method)) {
|
if (METHOD_NONE.equals(method)) {
|
||||||
currentSchemeDatas.clear();
|
currentSchemeDatas.clear();
|
||||||
cachedDrmInitData = null;
|
cachedDrmInitData = null;
|
||||||
} else /* !METHOD_NONE.equals(method) */ {
|
} else /* !METHOD_NONE.equals(method) */ {
|
||||||
encryptionIV = parseOptionalStringAttr(line, REGEX_IV, variableDefinitions);
|
fullSegmentEncryptionIV = parseOptionalStringAttr(line, REGEX_IV, variableDefinitions);
|
||||||
if (KEYFORMAT_IDENTITY.equals(keyFormat)) {
|
if (KEYFORMAT_IDENTITY.equals(keyFormat)) {
|
||||||
if (METHOD_AES_128.equals(method)) {
|
if (METHOD_AES_128.equals(method)) {
|
||||||
// The segment is fully encrypted using an identity key.
|
// The segment is fully encrypted using an identity key.
|
||||||
encryptionKeyUri = parseStringAttr(line, REGEX_URI, variableDefinitions);
|
fullSegmentEncryptionKeyUri = parseStringAttr(line, REGEX_URI, variableDefinitions);
|
||||||
} else {
|
} else {
|
||||||
// Do nothing. Samples are encrypted using an identity key, but this is not supported.
|
// Do nothing. Samples are encrypted using an identity key, but this is not supported.
|
||||||
// Hopefully, a traditional DRM alternative is also provided.
|
// Hopefully, a traditional DRM alternative is also provided.
|
||||||
@ -581,10 +593,10 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
hasEndTag = true;
|
hasEndTag = true;
|
||||||
} else if (!line.startsWith("#")) {
|
} else if (!line.startsWith("#")) {
|
||||||
String segmentEncryptionIV;
|
String segmentEncryptionIV;
|
||||||
if (encryptionKeyUri == null) {
|
if (fullSegmentEncryptionKeyUri == null) {
|
||||||
segmentEncryptionIV = null;
|
segmentEncryptionIV = null;
|
||||||
} else if (encryptionIV != null) {
|
} else if (fullSegmentEncryptionIV != null) {
|
||||||
segmentEncryptionIV = encryptionIV;
|
segmentEncryptionIV = fullSegmentEncryptionIV;
|
||||||
} else {
|
} else {
|
||||||
segmentEncryptionIV = Long.toHexString(segmentMediaSequence);
|
segmentEncryptionIV = Long.toHexString(segmentMediaSequence);
|
||||||
}
|
}
|
||||||
@ -615,7 +627,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
relativeDiscontinuitySequence,
|
relativeDiscontinuitySequence,
|
||||||
segmentStartTimeUs,
|
segmentStartTimeUs,
|
||||||
cachedDrmInitData,
|
cachedDrmInitData,
|
||||||
encryptionKeyUri,
|
fullSegmentEncryptionKeyUri,
|
||||||
segmentEncryptionIV,
|
segmentEncryptionIV,
|
||||||
segmentByteRangeOffset,
|
segmentByteRangeOffset,
|
||||||
segmentByteRangeLength,
|
segmentByteRangeLength,
|
||||||
|
@ -16,9 +16,11 @@
|
|||||||
package com.google.android.exoplayer2.source.hls.playlist;
|
package com.google.android.exoplayer2.source.hls.playlist;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.ParserException;
|
||||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
|
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@ -365,6 +367,61 @@ public class HlsMediaPlaylistParserTest {
|
|||||||
assertThat(segments.get(3).initializationSegment.url).isEqualTo("init2.ts");
|
assertThat(segments.get(3).initializationSegment.url).isEqualTo("init2.ts");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncryptedMapTag() throws IOException {
|
||||||
|
Uri playlistUri = Uri.parse("https://example.com/test3.m3u8");
|
||||||
|
String playlistString =
|
||||||
|
"#EXTM3U\n"
|
||||||
|
+ "#EXT-X-VERSION:3\n"
|
||||||
|
+ "#EXT-X-TARGETDURATION:5\n"
|
||||||
|
+ "#EXT-X-MEDIA-SEQUENCE:10\n"
|
||||||
|
+ "#EXT-X-KEY:METHOD=AES-128,"
|
||||||
|
+ "URI=\"https://priv.example.com/key.php?r=2680\",IV=0x1566B\n"
|
||||||
|
+ "#EXT-X-MAP:URI=\"init1.ts\""
|
||||||
|
+ "#EXTINF:5.005,\n"
|
||||||
|
+ "02/00/32.ts\n"
|
||||||
|
+ "#EXT-X-KEY:METHOD=NONE\n"
|
||||||
|
+ "#EXT-X-MAP:URI=\"init2.ts\""
|
||||||
|
+ "#EXTINF:5.005,\n"
|
||||||
|
+ "02/00/47.ts\n";
|
||||||
|
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
|
||||||
|
HlsMediaPlaylist playlist =
|
||||||
|
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
|
||||||
|
|
||||||
|
List<Segment> segments = playlist.segments;
|
||||||
|
Segment initSegment1 = segments.get(0).initializationSegment;
|
||||||
|
assertThat(initSegment1.fullSegmentEncryptionKeyUri)
|
||||||
|
.isEqualTo("https://priv.example.com/key.php?r=2680");
|
||||||
|
assertThat(initSegment1.encryptionIV).isEqualTo("0x1566B");
|
||||||
|
Segment initSegment2 = segments.get(1).initializationSegment;
|
||||||
|
assertThat(initSegment2.fullSegmentEncryptionKeyUri).isNull();
|
||||||
|
assertThat(initSegment2.encryptionIV).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncryptedMapTagWithNoIvFails() throws IOException {
|
||||||
|
Uri playlistUri = Uri.parse("https://example.com/test3.m3u8");
|
||||||
|
String playlistString =
|
||||||
|
"#EXTM3U\n"
|
||||||
|
+ "#EXT-X-VERSION:3\n"
|
||||||
|
+ "#EXT-X-TARGETDURATION:5\n"
|
||||||
|
+ "#EXT-X-MEDIA-SEQUENCE:10\n"
|
||||||
|
+ "#EXT-X-KEY:METHOD=AES-128,"
|
||||||
|
+ "URI=\"https://priv.example.com/key.php?r=2680\"\n"
|
||||||
|
+ "#EXT-X-MAP:URI=\"init1.ts\""
|
||||||
|
+ "#EXTINF:5.005,\n"
|
||||||
|
+ "02/00/32.ts\n";
|
||||||
|
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
|
||||||
|
|
||||||
|
try {
|
||||||
|
new HlsPlaylistParser().parse(playlistUri, inputStream);
|
||||||
|
fail();
|
||||||
|
} catch (ParserException e) {
|
||||||
|
// Expected because the initialization segment does not have a defined initialization vector,
|
||||||
|
// although it is affected by an EXT-X-KEY tag.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMasterPlaylistAttributeInheritance() throws IOException {
|
public void testMasterPlaylistAttributeInheritance() throws IOException {
|
||||||
Uri playlistUri = Uri.parse("https://example.com/test3.m3u8");
|
Uri playlistUri = Uri.parse("https://example.com/test3.m3u8");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user