mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add initial support for EXT-X-GAP
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=185676652
This commit is contained in:
parent
c8e950537d
commit
1d4bc7dda7
6 changed files with 165 additions and 82 deletions
|
|
@ -69,6 +69,7 @@
|
||||||
([#3622](https://github.com/google/ExoPlayer/issues/3622)).
|
([#3622](https://github.com/google/ExoPlayer/issues/3622)).
|
||||||
* Use long for media sequence numbers
|
* Use long for media sequence numbers
|
||||||
([#3747](https://github.com/google/ExoPlayer/issues/3747))
|
([#3747](https://github.com/google/ExoPlayer/issues/3747))
|
||||||
|
* Add initial support for the EXT-X-GAP tag.
|
||||||
* New Cast extension: Simplifies toggling between local and Cast playbacks.
|
* New Cast extension: Simplifies toggling between local and Cast playbacks.
|
||||||
* Audio:
|
* Audio:
|
||||||
* Support TrueHD passthrough for rechunked samples in Matroska files
|
* Support TrueHD passthrough for rechunked samples in Matroska files
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import junit.framework.TestCase;
|
||||||
*/
|
*/
|
||||||
public class HlsMediaPlaylistParserTest extends TestCase {
|
public class HlsMediaPlaylistParserTest extends TestCase {
|
||||||
|
|
||||||
public void testParseMediaPlaylist() {
|
public void testParseMediaPlaylist() throws IOException {
|
||||||
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
|
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
|
||||||
String playlistString = "#EXTM3U\n"
|
String playlistString = "#EXTM3U\n"
|
||||||
+ "#EXT-X-VERSION:3\n"
|
+ "#EXT-X-VERSION:3\n"
|
||||||
|
|
@ -69,76 +69,106 @@ public class HlsMediaPlaylistParserTest extends TestCase {
|
||||||
+ "#EXT-X-ENDLIST";
|
+ "#EXT-X-ENDLIST";
|
||||||
InputStream inputStream = new ByteArrayInputStream(
|
InputStream inputStream = new ByteArrayInputStream(
|
||||||
playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
|
playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
|
||||||
try {
|
HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUri, inputStream);
|
||||||
HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUri, inputStream);
|
|
||||||
assertThat(playlist).isNotNull();
|
|
||||||
|
|
||||||
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
|
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
|
||||||
assertThat(mediaPlaylist.playlistType).isEqualTo(HlsMediaPlaylist.PLAYLIST_TYPE_VOD);
|
assertThat(mediaPlaylist.playlistType).isEqualTo(HlsMediaPlaylist.PLAYLIST_TYPE_VOD);
|
||||||
assertThat(mediaPlaylist.startOffsetUs).isEqualTo(mediaPlaylist.durationUs - 25000000);
|
assertThat(mediaPlaylist.startOffsetUs).isEqualTo(mediaPlaylist.durationUs - 25000000);
|
||||||
|
|
||||||
assertThat(mediaPlaylist.mediaSequence).isEqualTo(2679);
|
assertThat(mediaPlaylist.mediaSequence).isEqualTo(2679);
|
||||||
assertThat(mediaPlaylist.version).isEqualTo(3);
|
assertThat(mediaPlaylist.version).isEqualTo(3);
|
||||||
assertThat(mediaPlaylist.hasEndTag).isTrue();
|
assertThat(mediaPlaylist.hasEndTag).isTrue();
|
||||||
List<Segment> segments = mediaPlaylist.segments;
|
List<Segment> segments = mediaPlaylist.segments;
|
||||||
assertThat(segments).isNotNull();
|
assertThat(segments).isNotNull();
|
||||||
assertThat(segments).hasSize(5);
|
assertThat(segments).hasSize(5);
|
||||||
|
|
||||||
Segment segment = segments.get(0);
|
Segment segment = segments.get(0);
|
||||||
assertThat(mediaPlaylist.discontinuitySequence + segment.relativeDiscontinuitySequence)
|
assertThat(mediaPlaylist.discontinuitySequence + segment.relativeDiscontinuitySequence)
|
||||||
.isEqualTo(4);
|
.isEqualTo(4);
|
||||||
assertThat(segment.durationUs).isEqualTo(7975000);
|
assertThat(segment.durationUs).isEqualTo(7975000);
|
||||||
assertThat(segment.fullSegmentEncryptionKeyUri).isNull();
|
assertThat(segment.fullSegmentEncryptionKeyUri).isNull();
|
||||||
assertThat(segment.encryptionIV).isNull();
|
assertThat(segment.encryptionIV).isNull();
|
||||||
assertThat(segment.byterangeLength).isEqualTo(51370);
|
assertThat(segment.byterangeLength).isEqualTo(51370);
|
||||||
assertThat(segment.byterangeOffset).isEqualTo(0);
|
assertThat(segment.byterangeOffset).isEqualTo(0);
|
||||||
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2679.ts");
|
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2679.ts");
|
||||||
|
|
||||||
segment = segments.get(1);
|
segment = segments.get(1);
|
||||||
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(0);
|
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(0);
|
||||||
assertThat(segment.durationUs).isEqualTo(7975000);
|
assertThat(segment.durationUs).isEqualTo(7975000);
|
||||||
assertThat(segment.fullSegmentEncryptionKeyUri)
|
assertThat(segment.fullSegmentEncryptionKeyUri)
|
||||||
.isEqualTo("https://priv.example.com/key.php?r=2680");
|
.isEqualTo("https://priv.example.com/key.php?r=2680");
|
||||||
assertThat(segment.encryptionIV).isEqualTo("0x1566B");
|
assertThat(segment.encryptionIV).isEqualTo("0x1566B");
|
||||||
assertThat(segment.byterangeLength).isEqualTo(51501);
|
assertThat(segment.byterangeLength).isEqualTo(51501);
|
||||||
assertThat(segment.byterangeOffset).isEqualTo(2147483648L);
|
assertThat(segment.byterangeOffset).isEqualTo(2147483648L);
|
||||||
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2680.ts");
|
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2680.ts");
|
||||||
|
|
||||||
segment = segments.get(2);
|
segment = segments.get(2);
|
||||||
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(0);
|
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(0);
|
||||||
assertThat(segment.durationUs).isEqualTo(7941000);
|
assertThat(segment.durationUs).isEqualTo(7941000);
|
||||||
assertThat(segment.fullSegmentEncryptionKeyUri).isNull();
|
assertThat(segment.fullSegmentEncryptionKeyUri).isNull();
|
||||||
assertThat(segment.encryptionIV).isEqualTo(null);
|
assertThat(segment.encryptionIV).isEqualTo(null);
|
||||||
assertThat(segment.byterangeLength).isEqualTo(51501);
|
assertThat(segment.byterangeLength).isEqualTo(51501);
|
||||||
assertThat(segment.byterangeOffset).isEqualTo(2147535149L);
|
assertThat(segment.byterangeOffset).isEqualTo(2147535149L);
|
||||||
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2681.ts");
|
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2681.ts");
|
||||||
|
|
||||||
segment = segments.get(3);
|
segment = segments.get(3);
|
||||||
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(1);
|
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(1);
|
||||||
assertThat(segment.durationUs).isEqualTo(7975000);
|
assertThat(segment.durationUs).isEqualTo(7975000);
|
||||||
assertThat(segment.fullSegmentEncryptionKeyUri)
|
assertThat(segment.fullSegmentEncryptionKeyUri)
|
||||||
.isEqualTo("https://priv.example.com/key.php?r=2682");
|
.isEqualTo("https://priv.example.com/key.php?r=2682");
|
||||||
// 0xA7A == 2682.
|
// 0xA7A == 2682.
|
||||||
assertThat(segment.encryptionIV).isNotNull();
|
assertThat(segment.encryptionIV).isNotNull();
|
||||||
assertThat(segment.encryptionIV.toUpperCase(Locale.getDefault())).isEqualTo("A7A");
|
assertThat(segment.encryptionIV.toUpperCase(Locale.getDefault())).isEqualTo("A7A");
|
||||||
assertThat(segment.byterangeLength).isEqualTo(51740);
|
assertThat(segment.byterangeLength).isEqualTo(51740);
|
||||||
assertThat(segment.byterangeOffset).isEqualTo(2147586650L);
|
assertThat(segment.byterangeOffset).isEqualTo(2147586650L);
|
||||||
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2682.ts");
|
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2682.ts");
|
||||||
|
|
||||||
segment = segments.get(4);
|
segment = segments.get(4);
|
||||||
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(1);
|
assertThat(segment.relativeDiscontinuitySequence).isEqualTo(1);
|
||||||
assertThat(segment.durationUs).isEqualTo(7975000);
|
assertThat(segment.durationUs).isEqualTo(7975000);
|
||||||
assertThat(segment.fullSegmentEncryptionKeyUri)
|
assertThat(segment.fullSegmentEncryptionKeyUri)
|
||||||
.isEqualTo("https://priv.example.com/key.php?r=2682");
|
.isEqualTo("https://priv.example.com/key.php?r=2682");
|
||||||
// 0xA7B == 2683.
|
// 0xA7B == 2683.
|
||||||
assertThat(segment.encryptionIV).isNotNull();
|
assertThat(segment.encryptionIV).isNotNull();
|
||||||
assertThat(segment.encryptionIV.toUpperCase(Locale.getDefault())).isEqualTo("A7B");
|
assertThat(segment.encryptionIV.toUpperCase(Locale.getDefault())).isEqualTo("A7B");
|
||||||
assertThat(segment.byterangeLength).isEqualTo(C.LENGTH_UNSET);
|
assertThat(segment.byterangeLength).isEqualTo(C.LENGTH_UNSET);
|
||||||
assertThat(segment.byterangeOffset).isEqualTo(0);
|
assertThat(segment.byterangeOffset).isEqualTo(0);
|
||||||
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts");
|
assertThat(segment.url).isEqualTo("https://priv.example.com/fileSequence2683.ts");
|
||||||
} catch (IOException exception) {
|
}
|
||||||
fail(exception.getMessage());
|
|
||||||
}
|
public void testGapTag() throws IOException {
|
||||||
|
Uri playlistUri = Uri.parse("https://example.com/test2.m3u8");
|
||||||
|
String playlistString =
|
||||||
|
"#EXTM3U\n"
|
||||||
|
+ "#EXT-X-VERSION:3\n"
|
||||||
|
+ "#EXT-X-TARGETDURATION:5\n"
|
||||||
|
+ "#EXT-X-PLAYLIST-TYPE:VOD\n"
|
||||||
|
+ "#EXT-X-MEDIA-SEQUENCE:0\n"
|
||||||
|
+ "#EXT-X-PROGRAM-DATE-TIME:2016-09-22T02:00:01+00:00\n"
|
||||||
|
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://example.com/key?value=something\"\n"
|
||||||
|
+ "#EXTINF:5.005,\n"
|
||||||
|
+ "02/00/27.ts\n"
|
||||||
|
+ "#EXTINF:5.005,\n"
|
||||||
|
+ "02/00/32.ts\n"
|
||||||
|
+ "#EXT-X-KEY:METHOD=NONE\n"
|
||||||
|
+ "#EXTINF:5.005,\n"
|
||||||
|
+ "#EXT-X-GAP \n"
|
||||||
|
+ "../dummy.ts\n"
|
||||||
|
+ "#EXT-X-KEY:METHOD=AES-128,URI=\"https://key-service.bamgrid.com/1.0/key?"
|
||||||
|
+ "hex-value=9FB8989D15EEAAF8B21B860D7ED3072A\",IV=0x410C8AC18AA42EFA18B5155484F5FC34\n"
|
||||||
|
+ "#EXTINF:5.005,\n"
|
||||||
|
+ "02/00/42.ts\n"
|
||||||
|
+ "#EXTINF:5.005,\n"
|
||||||
|
+ "02/00/47.ts\n";
|
||||||
|
InputStream inputStream =
|
||||||
|
new ByteArrayInputStream(playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
|
||||||
|
HlsMediaPlaylist playlist =
|
||||||
|
(HlsMediaPlaylist) new HlsPlaylistParser().parse(playlistUri, inputStream);
|
||||||
|
|
||||||
|
assertThat(playlist.hasEndTag).isFalse();
|
||||||
|
assertThat(playlist.segments.get(1).hasGapTag).isFalse();
|
||||||
|
assertThat(playlist.segments.get(2).hasGapTag).isTrue();
|
||||||
|
assertThat(playlist.segments.get(3).hasGapTag).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -330,11 +330,27 @@ import java.util.List;
|
||||||
Uri chunkUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.url);
|
Uri chunkUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.url);
|
||||||
DataSpec dataSpec = new DataSpec(chunkUri, segment.byterangeOffset, segment.byterangeLength,
|
DataSpec dataSpec = new DataSpec(chunkUri, segment.byterangeOffset, segment.byterangeLength,
|
||||||
null);
|
null);
|
||||||
out.chunk = new HlsMediaChunk(extractorFactory, mediaDataSource, dataSpec, initDataSpec,
|
out.chunk =
|
||||||
selectedUrl, muxedCaptionFormats, trackSelection.getSelectionReason(),
|
new HlsMediaChunk(
|
||||||
trackSelection.getSelectionData(), startTimeUs, startTimeUs + segment.durationUs,
|
extractorFactory,
|
||||||
chunkMediaSequence, discontinuitySequence, isTimestampMaster, timestampAdjuster, previous,
|
mediaDataSource,
|
||||||
mediaPlaylist.drmInitData, encryptionKey, encryptionIv);
|
dataSpec,
|
||||||
|
initDataSpec,
|
||||||
|
selectedUrl,
|
||||||
|
muxedCaptionFormats,
|
||||||
|
trackSelection.getSelectionReason(),
|
||||||
|
trackSelection.getSelectionData(),
|
||||||
|
startTimeUs,
|
||||||
|
startTimeUs + segment.durationUs,
|
||||||
|
chunkMediaSequence,
|
||||||
|
discontinuitySequence,
|
||||||
|
segment.hasGapTag,
|
||||||
|
isTimestampMaster,
|
||||||
|
timestampAdjuster,
|
||||||
|
previous,
|
||||||
|
mediaPlaylist.drmInitData,
|
||||||
|
encryptionKey,
|
||||||
|
encryptionIv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
private final DataSpec initDataSpec;
|
private final DataSpec initDataSpec;
|
||||||
private final boolean isEncrypted;
|
private final boolean isEncrypted;
|
||||||
private final boolean isMasterTimestampSource;
|
private final boolean isMasterTimestampSource;
|
||||||
|
private final boolean hasGapTag;
|
||||||
private final TimestampAdjuster timestampAdjuster;
|
private final TimestampAdjuster timestampAdjuster;
|
||||||
private final boolean shouldSpliceIn;
|
private final boolean shouldSpliceIn;
|
||||||
private final Extractor extractor;
|
private final Extractor extractor;
|
||||||
|
|
@ -97,6 +98,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
* @param endTimeUs The end time of the chunk in microseconds.
|
* @param endTimeUs The end time of the chunk in microseconds.
|
||||||
* @param chunkMediaSequence The media sequence number of the chunk.
|
* @param chunkMediaSequence The media sequence number of the chunk.
|
||||||
* @param discontinuitySequenceNumber The discontinuity sequence number of the chunk.
|
* @param discontinuitySequenceNumber The discontinuity sequence number of the chunk.
|
||||||
|
* @param hasGapTag Whether the chunk is tagged with EXT-X-GAP.
|
||||||
* @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster.
|
* @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster.
|
||||||
* @param timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number.
|
* @param timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number.
|
||||||
* @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null.
|
* @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null.
|
||||||
|
|
@ -119,6 +121,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
long endTimeUs,
|
long endTimeUs,
|
||||||
long chunkMediaSequence,
|
long chunkMediaSequence,
|
||||||
int discontinuitySequenceNumber,
|
int discontinuitySequenceNumber,
|
||||||
|
boolean hasGapTag,
|
||||||
boolean isMasterTimestampSource,
|
boolean isMasterTimestampSource,
|
||||||
TimestampAdjuster timestampAdjuster,
|
TimestampAdjuster timestampAdjuster,
|
||||||
HlsMediaChunk previousChunk,
|
HlsMediaChunk previousChunk,
|
||||||
|
|
@ -141,6 +144,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
this.timestampAdjuster = timestampAdjuster;
|
this.timestampAdjuster = timestampAdjuster;
|
||||||
// Note: this.dataSource and dataSource may be different.
|
// Note: this.dataSource and dataSource may be different.
|
||||||
this.isEncrypted = this.dataSource instanceof Aes128DataSource;
|
this.isEncrypted = this.dataSource instanceof Aes128DataSource;
|
||||||
|
this.hasGapTag = hasGapTag;
|
||||||
Extractor previousExtractor = null;
|
Extractor previousExtractor = null;
|
||||||
if (previousChunk != null) {
|
if (previousChunk != null) {
|
||||||
shouldSpliceIn = previousChunk.hlsUrl != hlsUrl;
|
shouldSpliceIn = previousChunk.hlsUrl != hlsUrl;
|
||||||
|
|
@ -211,7 +215,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
public void load() throws IOException, InterruptedException {
|
public void load() throws IOException, InterruptedException {
|
||||||
maybeLoadInitData();
|
maybeLoadInitData();
|
||||||
if (!loadCanceled) {
|
if (!loadCanceled) {
|
||||||
loadMedia();
|
if (!hasGapTag) {
|
||||||
|
loadMedia();
|
||||||
|
}
|
||||||
|
loadCompleted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -283,7 +290,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
} finally {
|
} finally {
|
||||||
Util.closeQuietly(dataSource);
|
Util.closeQuietly(dataSource);
|
||||||
}
|
}
|
||||||
loadCompleted = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,16 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
||||||
*/
|
*/
|
||||||
public final long byterangeLength;
|
public final long byterangeLength;
|
||||||
|
|
||||||
|
/** Whether the segment is tagged with #EXT-X-GAP. */
|
||||||
|
public final boolean hasGapTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uri See {@link #url}.
|
||||||
|
* @param byterangeOffset See {@link #byterangeOffset}.
|
||||||
|
* @param byterangeLength See {@link #byterangeLength}.
|
||||||
|
*/
|
||||||
public Segment(String uri, long byterangeOffset, long byterangeLength) {
|
public Segment(String uri, long byterangeOffset, long byterangeLength) {
|
||||||
this(uri, 0, -1, C.TIME_UNSET, null, null, byterangeOffset, byterangeLength);
|
this(uri, 0, -1, C.TIME_UNSET, null, null, byterangeOffset, byterangeLength, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -82,10 +90,18 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
||||||
* @param encryptionIV See {@link #encryptionIV}.
|
* @param encryptionIV See {@link #encryptionIV}.
|
||||||
* @param byterangeOffset See {@link #byterangeOffset}.
|
* @param byterangeOffset See {@link #byterangeOffset}.
|
||||||
* @param byterangeLength See {@link #byterangeLength}.
|
* @param byterangeLength See {@link #byterangeLength}.
|
||||||
|
* @param hasGapTag See {@link #hasGapTag}.
|
||||||
*/
|
*/
|
||||||
public Segment(String url, long durationUs, int relativeDiscontinuitySequence,
|
public Segment(
|
||||||
long relativeStartTimeUs, String fullSegmentEncryptionKeyUri,
|
String url,
|
||||||
String encryptionIV, long byterangeOffset, long byterangeLength) {
|
long durationUs,
|
||||||
|
int relativeDiscontinuitySequence,
|
||||||
|
long relativeStartTimeUs,
|
||||||
|
String fullSegmentEncryptionKeyUri,
|
||||||
|
String encryptionIV,
|
||||||
|
long byterangeOffset,
|
||||||
|
long byterangeLength,
|
||||||
|
boolean hasGapTag) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.durationUs = durationUs;
|
this.durationUs = durationUs;
|
||||||
this.relativeDiscontinuitySequence = relativeDiscontinuitySequence;
|
this.relativeDiscontinuitySequence = relativeDiscontinuitySequence;
|
||||||
|
|
@ -94,6 +110,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
||||||
this.encryptionIV = encryptionIV;
|
this.encryptionIV = encryptionIV;
|
||||||
this.byterangeOffset = byterangeOffset;
|
this.byterangeOffset = byterangeOffset;
|
||||||
this.byterangeLength = byterangeLength;
|
this.byterangeLength = byterangeLength;
|
||||||
|
this.hasGapTag = hasGapTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
||||||
private static final String TAG_ENDLIST = "#EXT-X-ENDLIST";
|
private static final String TAG_ENDLIST = "#EXT-X-ENDLIST";
|
||||||
private static final String TAG_KEY = "#EXT-X-KEY";
|
private static final String TAG_KEY = "#EXT-X-KEY";
|
||||||
private static final String TAG_BYTERANGE = "#EXT-X-BYTERANGE";
|
private static final String TAG_BYTERANGE = "#EXT-X-BYTERANGE";
|
||||||
|
private static final String TAG_GAP = "#EXT-X-GAP";
|
||||||
|
|
||||||
private static final String TYPE_AUDIO = "AUDIO";
|
private static final String TYPE_AUDIO = "AUDIO";
|
||||||
private static final String TYPE_VIDEO = "VIDEO";
|
private static final String TYPE_VIDEO = "VIDEO";
|
||||||
|
|
@ -357,6 +358,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
||||||
long segmentByteRangeOffset = 0;
|
long segmentByteRangeOffset = 0;
|
||||||
long segmentByteRangeLength = C.LENGTH_UNSET;
|
long segmentByteRangeLength = C.LENGTH_UNSET;
|
||||||
long segmentMediaSequence = 0;
|
long segmentMediaSequence = 0;
|
||||||
|
boolean hasGapTag = false;
|
||||||
|
|
||||||
String encryptionKeyUri = null;
|
String encryptionKeyUri = null;
|
||||||
String encryptionIV = null;
|
String encryptionIV = null;
|
||||||
|
|
@ -449,6 +451,12 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
||||||
C.msToUs(Util.parseXsDateTime(line.substring(line.indexOf(':') + 1)));
|
C.msToUs(Util.parseXsDateTime(line.substring(line.indexOf(':') + 1)));
|
||||||
playlistStartTimeUs = programDatetimeUs - segmentStartTimeUs;
|
playlistStartTimeUs = programDatetimeUs - segmentStartTimeUs;
|
||||||
}
|
}
|
||||||
|
} else if (line.equals(TAG_GAP)) {
|
||||||
|
hasGapTag = true;
|
||||||
|
} else if (line.equals(TAG_INDEPENDENT_SEGMENTS)) {
|
||||||
|
hasIndependentSegmentsTag = true;
|
||||||
|
} else if (line.equals(TAG_ENDLIST)) {
|
||||||
|
hasEndTag = true;
|
||||||
} else if (!line.startsWith("#")) {
|
} else if (!line.startsWith("#")) {
|
||||||
String segmentEncryptionIV;
|
String segmentEncryptionIV;
|
||||||
if (encryptionKeyUri == null) {
|
if (encryptionKeyUri == null) {
|
||||||
|
|
@ -462,19 +470,24 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
||||||
if (segmentByteRangeLength == C.LENGTH_UNSET) {
|
if (segmentByteRangeLength == C.LENGTH_UNSET) {
|
||||||
segmentByteRangeOffset = 0;
|
segmentByteRangeOffset = 0;
|
||||||
}
|
}
|
||||||
segments.add(new Segment(line, segmentDurationUs, relativeDiscontinuitySequence,
|
segments.add(
|
||||||
segmentStartTimeUs, encryptionKeyUri, segmentEncryptionIV,
|
new Segment(
|
||||||
segmentByteRangeOffset, segmentByteRangeLength));
|
line,
|
||||||
|
segmentDurationUs,
|
||||||
|
relativeDiscontinuitySequence,
|
||||||
|
segmentStartTimeUs,
|
||||||
|
encryptionKeyUri,
|
||||||
|
segmentEncryptionIV,
|
||||||
|
segmentByteRangeOffset,
|
||||||
|
segmentByteRangeLength,
|
||||||
|
hasGapTag));
|
||||||
segmentStartTimeUs += segmentDurationUs;
|
segmentStartTimeUs += segmentDurationUs;
|
||||||
segmentDurationUs = 0;
|
segmentDurationUs = 0;
|
||||||
if (segmentByteRangeLength != C.LENGTH_UNSET) {
|
if (segmentByteRangeLength != C.LENGTH_UNSET) {
|
||||||
segmentByteRangeOffset += segmentByteRangeLength;
|
segmentByteRangeOffset += segmentByteRangeLength;
|
||||||
}
|
}
|
||||||
segmentByteRangeLength = C.LENGTH_UNSET;
|
segmentByteRangeLength = C.LENGTH_UNSET;
|
||||||
} else if (line.equals(TAG_INDEPENDENT_SEGMENTS)) {
|
hasGapTag = false;
|
||||||
hasIndependentSegmentsTag = true;
|
|
||||||
} else if (line.equals(TAG_ENDLIST)) {
|
|
||||||
hasEndTag = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new HlsMediaPlaylist(playlistType, baseUri, tags, startOffsetUs, playlistStartTimeUs,
|
return new HlsMediaPlaylist(playlistType, baseUri, tags, startOffsetUs, playlistStartTimeUs,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue