Parse the sequence number at discontinuities.
This is required to match up segments in one playlist (e.g. VTT) to those in another (e.g. Audio/Video).
This commit is contained in:
parent
6f62b499c5
commit
ddaa9092ec
@ -37,6 +37,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
|
||||
+ "#EXT-X-VERSION:3\n"
|
||||
+ "#EXT-X-TARGETDURATION:8\n"
|
||||
+ "#EXT-X-MEDIA-SEQUENCE:2679\n"
|
||||
+ "#EXT-X-DISCONTINUITY-SEQUENCE:4\n"
|
||||
+ "#EXT-X-ALLOW-CACHE:YES\n"
|
||||
+ "\n"
|
||||
+ "#EXTINF:7.975,\n"
|
||||
@ -79,7 +80,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
|
||||
assertNotNull(segments);
|
||||
assertEquals(5, segments.size());
|
||||
|
||||
assertEquals(false, segments.get(0).discontinuity);
|
||||
assertEquals(4, segments.get(0).discontinuitySequenceNumber);
|
||||
assertEquals(7.975, segments.get(0).durationSecs);
|
||||
assertEquals(false, segments.get(0).isEncrypted);
|
||||
assertEquals(null, segments.get(0).encryptionKeyUri);
|
||||
@ -88,7 +89,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
|
||||
assertEquals(0, segments.get(0).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2679.ts", segments.get(0).url);
|
||||
|
||||
assertEquals(false, segments.get(1).discontinuity);
|
||||
assertEquals(4, segments.get(1).discontinuitySequenceNumber);
|
||||
assertEquals(7.975, segments.get(1).durationSecs);
|
||||
assertEquals(true, segments.get(1).isEncrypted);
|
||||
assertEquals("https://priv.example.com/key.php?r=2680", segments.get(1).encryptionKeyUri);
|
||||
@ -97,7 +98,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
|
||||
assertEquals(51370, segments.get(1).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2680.ts", segments.get(1).url);
|
||||
|
||||
assertEquals(false, segments.get(2).discontinuity);
|
||||
assertEquals(4, segments.get(2).discontinuitySequenceNumber);
|
||||
assertEquals(7.941, segments.get(2).durationSecs);
|
||||
assertEquals(false, segments.get(2).isEncrypted);
|
||||
assertEquals(null, segments.get(2).encryptionKeyUri);
|
||||
@ -106,7 +107,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
|
||||
assertEquals(102871, segments.get(2).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2681.ts", segments.get(2).url);
|
||||
|
||||
assertEquals(true, segments.get(3).discontinuity);
|
||||
assertEquals(5, segments.get(3).discontinuitySequenceNumber);
|
||||
assertEquals(7.975, segments.get(3).durationSecs);
|
||||
assertEquals(true, segments.get(3).isEncrypted);
|
||||
assertEquals("https://priv.example.com/key.php?r=2682", segments.get(3).encryptionKeyUri);
|
||||
@ -117,7 +118,7 @@ public class HlsMediaPlaylistParserTest extends TestCase {
|
||||
assertEquals(154372, segments.get(3).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2682.ts", segments.get(3).url);
|
||||
|
||||
assertEquals(false, segments.get(4).discontinuity);
|
||||
assertEquals(5, segments.get(4).discontinuitySequenceNumber);
|
||||
assertEquals(7.975, segments.get(4).durationSecs);
|
||||
assertEquals(true, segments.get(4).isEncrypted);
|
||||
assertEquals("https://priv.example.com/key.php?r=2682", segments.get(4).encryptionKeyUri);
|
||||
|
@ -364,10 +364,12 @@ public class HlsChunkSource {
|
||||
Extractor extractor = new Mp3Extractor(startTimeUs);
|
||||
extractorWrapper = new HlsExtractorWrapper(trigger, format, startTimeUs, extractor,
|
||||
switchingVariantSpliced, adaptiveMaxWidth, adaptiveMaxHeight);
|
||||
} else if (previousTsChunk == null || segment.discontinuity || liveDiscontinuity
|
||||
} else if (previousTsChunk == null || liveDiscontinuity
|
||||
|| previousTsChunk.discontinuitySequenceNumber != segment.discontinuitySequenceNumber
|
||||
|| !format.equals(previousTsChunk.format)) {
|
||||
// MPEG-2 TS segments, but we need a new extractor.
|
||||
if (previousTsChunk == null || segment.discontinuity || liveDiscontinuity
|
||||
if (previousTsChunk == null || liveDiscontinuity
|
||||
|| previousTsChunk.discontinuitySequenceNumber != segment.discontinuitySequenceNumber
|
||||
|| ptsTimestampAdjuster == null) {
|
||||
// TODO: Use this for AAC as well, along with the ID3 PRIV priv tag values with owner
|
||||
// identifier com.apple.streaming.transportStreamTimestamp.
|
||||
@ -382,7 +384,8 @@ public class HlsChunkSource {
|
||||
}
|
||||
|
||||
out.chunk = new TsChunk(dataSource, dataSpec, trigger, format, startTimeUs, endTimeUs,
|
||||
chunkMediaSequence, extractorWrapper, encryptionKey, encryptionIv);
|
||||
chunkMediaSequence, segment.discontinuitySequenceNumber, extractorWrapper, encryptionKey,
|
||||
encryptionIv);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,9 +29,9 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
||||
*/
|
||||
public static final class Segment implements Comparable<Long> {
|
||||
|
||||
public final boolean discontinuity;
|
||||
public final double durationSecs;
|
||||
public final String url;
|
||||
public final double durationSecs;
|
||||
public final int discontinuitySequenceNumber;
|
||||
public final long startTimeUs;
|
||||
public final boolean isEncrypted;
|
||||
public final String encryptionKeyUri;
|
||||
@ -39,12 +39,12 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
||||
public final int byterangeOffset;
|
||||
public final int byterangeLength;
|
||||
|
||||
public Segment(String uri, double durationSecs, boolean discontinuity, long startTimeUs,
|
||||
boolean isEncrypted, String encryptionKeyUri, String encryptionIV, int byterangeOffset,
|
||||
int byterangeLength) {
|
||||
public Segment(String uri, double durationSecs, int discontinuitySequenceNumber,
|
||||
long startTimeUs, boolean isEncrypted, String encryptionKeyUri, String encryptionIV,
|
||||
int byterangeOffset, int byterangeLength) {
|
||||
this.url = uri;
|
||||
this.durationSecs = durationSecs;
|
||||
this.discontinuity = discontinuity;
|
||||
this.discontinuitySequenceNumber = discontinuitySequenceNumber;
|
||||
this.startTimeUs = startTimeUs;
|
||||
this.isEncrypted = isEncrypted;
|
||||
this.encryptionKeyUri = encryptionKeyUri;
|
||||
|
@ -40,6 +40,7 @@ public final class HlsPlaylistParser implements UriLoadable.Parser<HlsPlaylist>
|
||||
private static final String STREAM_INF_TAG = "#EXT-X-STREAM-INF";
|
||||
private static final String MEDIA_TAG = "#EXT-X-MEDIA";
|
||||
private static final String DISCONTINUITY_TAG = "#EXT-X-DISCONTINUITY";
|
||||
private static final String DISCONTINUITY_SEQUENCE_TAG = "#EXT-X-DISCONTINUITY-SEQUENCE";
|
||||
private static final String MEDIA_DURATION_TAG = "#EXTINF";
|
||||
private static final String MEDIA_SEQUENCE_TAG = "#EXT-X-MEDIA-SEQUENCE";
|
||||
private static final String TARGET_DURATION_TAG = "#EXT-X-TARGETDURATION";
|
||||
@ -122,6 +123,7 @@ public final class HlsPlaylistParser implements UriLoadable.Parser<HlsPlaylist>
|
||||
|| line.startsWith(KEY_TAG)
|
||||
|| line.startsWith(BYTERANGE_TAG)
|
||||
|| line.equals(DISCONTINUITY_TAG)
|
||||
|| line.equals(DISCONTINUITY_SEQUENCE_TAG)
|
||||
|| line.equals(ENDLIST_TAG)) {
|
||||
extraLines.add(line);
|
||||
return parseMediaPlaylist(new LineIterator(extraLines, reader), connectionUrl);
|
||||
@ -208,7 +210,7 @@ public final class HlsPlaylistParser implements UriLoadable.Parser<HlsPlaylist>
|
||||
List<Segment> segments = new ArrayList<>();
|
||||
|
||||
double segmentDurationSecs = 0.0;
|
||||
boolean segmentDiscontinuity = false;
|
||||
int discontinuitySequenceNumber = 0;
|
||||
long segmentStartTimeUs = 0;
|
||||
int segmentByterangeOffset = 0;
|
||||
int segmentByterangeLength = C.LENGTH_UNBOUNDED;
|
||||
@ -249,8 +251,10 @@ public final class HlsPlaylistParser implements UriLoadable.Parser<HlsPlaylist>
|
||||
if (splitByteRange.length > 1) {
|
||||
segmentByterangeOffset = Integer.parseInt(splitByteRange[1]);
|
||||
}
|
||||
} else if (line.startsWith(DISCONTINUITY_SEQUENCE_TAG)) {
|
||||
discontinuitySequenceNumber = Integer.parseInt(line.substring(line.indexOf(':') + 1));
|
||||
} else if (line.equals(DISCONTINUITY_TAG)) {
|
||||
segmentDiscontinuity = true;
|
||||
discontinuitySequenceNumber++;
|
||||
} else if (!line.startsWith("#")) {
|
||||
String segmentEncryptionIV;
|
||||
if (!isEncrypted) {
|
||||
@ -264,11 +268,10 @@ public final class HlsPlaylistParser implements UriLoadable.Parser<HlsPlaylist>
|
||||
if (segmentByterangeLength == C.LENGTH_UNBOUNDED) {
|
||||
segmentByterangeOffset = 0;
|
||||
}
|
||||
segments.add(new Segment(line, segmentDurationSecs, segmentDiscontinuity,
|
||||
segments.add(new Segment(line, segmentDurationSecs, discontinuitySequenceNumber,
|
||||
segmentStartTimeUs, isEncrypted, encryptionKeyUri, segmentEncryptionIV,
|
||||
segmentByterangeOffset, segmentByterangeLength));
|
||||
segmentStartTimeUs += (long) (segmentDurationSecs * C.MICROS_PER_SECOND);
|
||||
segmentDiscontinuity = false;
|
||||
segmentDurationSecs = 0.0;
|
||||
if (segmentByterangeLength != C.LENGTH_UNBOUNDED) {
|
||||
segmentByterangeOffset += segmentByterangeLength;
|
||||
|
@ -31,6 +31,11 @@ import java.io.IOException;
|
||||
*/
|
||||
public final class TsChunk extends MediaChunk {
|
||||
|
||||
/**
|
||||
* The discontinuity sequence number of the chunk.
|
||||
*/
|
||||
public final int discontinuitySequenceNumber;
|
||||
|
||||
/**
|
||||
* The wrapped extractor into which this chunk is being consumed.
|
||||
*/
|
||||
@ -48,16 +53,18 @@ public final class TsChunk extends MediaChunk {
|
||||
* @param format The format of the stream to which this chunk belongs.
|
||||
* @param startTimeUs The start time of the media contained by the chunk, in microseconds.
|
||||
* @param endTimeUs The end time of the media contained by the chunk, in microseconds.
|
||||
* @param discontinuitySequenceNumber The discontinuity sequence number of the chunk.
|
||||
* @param chunkIndex The index of the chunk.
|
||||
* @param extractorWrapper A wrapped extractor to parse samples from the data.
|
||||
* @param encryptionKey For AES encryption chunks, the encryption key.
|
||||
* @param encryptionIv For AES encryption chunks, the encryption initialization vector.
|
||||
*/
|
||||
public TsChunk(DataSource dataSource, DataSpec dataSpec, int trigger, Format format,
|
||||
long startTimeUs, long endTimeUs, int chunkIndex, HlsExtractorWrapper extractorWrapper,
|
||||
byte[] encryptionKey, byte[] encryptionIv) {
|
||||
long startTimeUs, long endTimeUs, int chunkIndex, int discontinuitySequenceNumber,
|
||||
HlsExtractorWrapper extractorWrapper, byte[] encryptionKey, byte[] encryptionIv) {
|
||||
super(buildDataSource(dataSource, encryptionKey, encryptionIv), dataSpec, trigger, format,
|
||||
startTimeUs, endTimeUs, chunkIndex);
|
||||
this.discontinuitySequenceNumber = discontinuitySequenceNumber;
|
||||
this.extractorWrapper = extractorWrapper;
|
||||
// Note: this.dataSource and dataSource may be different.
|
||||
this.isEncrypted = this.dataSource instanceof Aes128DataSource;
|
||||
|
Loading…
x
Reference in New Issue
Block a user