Commit 1d4bc7dd by aquilescanta Committed by Oliver Woodman

Add initial support for EXT-X-GAP

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=185676652
parent c8e95053
......@@ -69,6 +69,7 @@
([#3622](https://github.com/google/ExoPlayer/issues/3622)).
* Use long for media sequence numbers
([#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.
* Audio:
* Support TrueHD passthrough for rechunked samples in Matroska files
......
......@@ -33,7 +33,7 @@ import junit.framework.TestCase;
*/
public class HlsMediaPlaylistParserTest extends TestCase {
public void testParseMediaPlaylist() {
public void testParseMediaPlaylist() throws IOException {
Uri playlistUri = Uri.parse("https://example.com/test.m3u8");
String playlistString = "#EXTM3U\n"
+ "#EXT-X-VERSION:3\n"
......@@ -69,9 +69,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
+ "#EXT-X-ENDLIST";
InputStream inputStream = new ByteArrayInputStream(
playlistString.getBytes(Charset.forName(C.UTF8_NAME)));
try {
HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUri, inputStream);
assertThat(playlist).isNotNull();
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
assertThat(mediaPlaylist.playlistType).isEqualTo(HlsMediaPlaylist.PLAYLIST_TYPE_VOD);
......@@ -136,9 +134,41 @@ public class HlsMediaPlaylistParserTest extends TestCase {
assertThat(segment.byterangeLength).isEqualTo(C.LENGTH_UNSET);
assertThat(segment.byterangeOffset).isEqualTo(0);
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);
DataSpec dataSpec = new DataSpec(chunkUri, segment.byterangeOffset, segment.byterangeLength,
null);
out.chunk = new HlsMediaChunk(extractorFactory, mediaDataSource, dataSpec, initDataSpec,
selectedUrl, muxedCaptionFormats, trackSelection.getSelectionReason(),
trackSelection.getSelectionData(), startTimeUs, startTimeUs + segment.durationUs,
chunkMediaSequence, discontinuitySequence, isTimestampMaster, timestampAdjuster, previous,
mediaPlaylist.drmInitData, encryptionKey, encryptionIv);
out.chunk =
new HlsMediaChunk(
extractorFactory,
mediaDataSource,
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 boolean isEncrypted;
private final boolean isMasterTimestampSource;
private final boolean hasGapTag;
private final TimestampAdjuster timestampAdjuster;
private final boolean shouldSpliceIn;
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 chunkMediaSequence The media 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 timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number.
* @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 chunkMediaSequence,
int discontinuitySequenceNumber,
boolean hasGapTag,
boolean isMasterTimestampSource,
TimestampAdjuster timestampAdjuster,
HlsMediaChunk previousChunk,
......@@ -141,6 +144,7 @@ import java.util.concurrent.atomic.AtomicInteger;
this.timestampAdjuster = timestampAdjuster;
// Note: this.dataSource and dataSource may be different.
this.isEncrypted = this.dataSource instanceof Aes128DataSource;
this.hasGapTag = hasGapTag;
Extractor previousExtractor = null;
if (previousChunk != null) {
shouldSpliceIn = previousChunk.hlsUrl != hlsUrl;
......@@ -211,8 +215,11 @@ import java.util.concurrent.atomic.AtomicInteger;
public void load() throws IOException, InterruptedException {
maybeLoadInitData();
if (!loadCanceled) {
if (!hasGapTag) {
loadMedia();
}
loadCompleted = true;
}
}
// Internal loading methods.
......@@ -283,7 +290,6 @@ import java.util.concurrent.atomic.AtomicInteger;
} finally {
Util.closeQuietly(dataSource);
}
loadCompleted = true;
}
/**
......
......@@ -69,8 +69,16 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
*/
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) {
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 byterangeOffset See {@link #byterangeOffset}.
* @param byterangeLength See {@link #byterangeLength}.
*/
public Segment(String url, long durationUs, int relativeDiscontinuitySequence,
long relativeStartTimeUs, String fullSegmentEncryptionKeyUri,
String encryptionIV, long byterangeOffset, long byterangeLength) {
* @param hasGapTag See {@link #hasGapTag}.
*/
public Segment(
String url,
long durationUs,
int relativeDiscontinuitySequence,
long relativeStartTimeUs,
String fullSegmentEncryptionKeyUri,
String encryptionIV,
long byterangeOffset,
long byterangeLength,
boolean hasGapTag) {
this.url = url;
this.durationUs = durationUs;
this.relativeDiscontinuitySequence = relativeDiscontinuitySequence;
......@@ -94,6 +110,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
this.encryptionIV = encryptionIV;
this.byterangeOffset = byterangeOffset;
this.byterangeLength = byterangeLength;
this.hasGapTag = hasGapTag;
}
@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_KEY = "#EXT-X-KEY";
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_VIDEO = "VIDEO";
......@@ -357,6 +358,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
long segmentByteRangeOffset = 0;
long segmentByteRangeLength = C.LENGTH_UNSET;
long segmentMediaSequence = 0;
boolean hasGapTag = false;
String encryptionKeyUri = 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)));
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("#")) {
String segmentEncryptionIV;
if (encryptionKeyUri == null) {
......@@ -462,19 +470,24 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
if (segmentByteRangeLength == C.LENGTH_UNSET) {
segmentByteRangeOffset = 0;
}
segments.add(new Segment(line, segmentDurationUs, relativeDiscontinuitySequence,
segmentStartTimeUs, encryptionKeyUri, segmentEncryptionIV,
segmentByteRangeOffset, segmentByteRangeLength));
segments.add(
new Segment(
line,
segmentDurationUs,
relativeDiscontinuitySequence,
segmentStartTimeUs,
encryptionKeyUri,
segmentEncryptionIV,
segmentByteRangeOffset,
segmentByteRangeLength,
hasGapTag));
segmentStartTimeUs += segmentDurationUs;
segmentDurationUs = 0;
if (segmentByteRangeLength != C.LENGTH_UNSET) {
segmentByteRangeOffset += segmentByteRangeLength;
}
segmentByteRangeLength = C.LENGTH_UNSET;
} else if (line.equals(TAG_INDEPENDENT_SEGMENTS)) {
hasIndependentSegmentsTag = true;
} else if (line.equals(TAG_ENDLIST)) {
hasEndTag = true;
hasGapTag = false;
}
}
return new HlsMediaPlaylist(playlistType, baseUri, tags, startOffsetUs, playlistStartTimeUs,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment