mirror of
https://github.com/samsonjs/media.git
synced 2026-03-29 10:05:48 +00:00
Track HLS discontinuities when playlist does not declare sequence
This is an initial version that does not handle cross-playlists adjustment in an ideal way. Issue:#1789 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=144692969
This commit is contained in:
parent
7d7a159195
commit
6e481178ea
5 changed files with 136 additions and 96 deletions
|
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls.playlist;
|
|||
|
||||
import android.net.Uri;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
|
@ -73,59 +74,64 @@ public class HlsMediaPlaylistParserTest extends TestCase {
|
|||
|
||||
assertEquals(2679, mediaPlaylist.mediaSequence);
|
||||
assertEquals(3, mediaPlaylist.version);
|
||||
assertEquals(true, mediaPlaylist.hasEndTag);
|
||||
List<HlsMediaPlaylist.Segment> segments = mediaPlaylist.segments;
|
||||
assertTrue(mediaPlaylist.hasEndTag);
|
||||
List<Segment> segments = mediaPlaylist.segments;
|
||||
assertNotNull(segments);
|
||||
assertEquals(5, segments.size());
|
||||
|
||||
assertEquals(4, segments.get(0).discontinuitySequenceNumber);
|
||||
assertEquals(7975000, segments.get(0).durationUs);
|
||||
assertEquals(false, segments.get(0).isEncrypted);
|
||||
assertEquals(null, segments.get(0).encryptionKeyUri);
|
||||
assertEquals(null, segments.get(0).encryptionIV);
|
||||
assertEquals(51370, segments.get(0).byterangeLength);
|
||||
assertEquals(0, segments.get(0).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2679.ts", segments.get(0).url);
|
||||
Segment segment = segments.get(0);
|
||||
assertEquals(4, mediaPlaylist.discontinuitySequence + segment.relativeDiscontinuitySequence);
|
||||
assertEquals(7975000, segment.durationUs);
|
||||
assertFalse(segment.isEncrypted);
|
||||
assertEquals(null, segment.encryptionKeyUri);
|
||||
assertEquals(null, segment.encryptionIV);
|
||||
assertEquals(51370, segment.byterangeLength);
|
||||
assertEquals(0, segment.byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2679.ts", segment.url);
|
||||
|
||||
assertEquals(4, segments.get(1).discontinuitySequenceNumber);
|
||||
assertEquals(7975000, segments.get(1).durationUs);
|
||||
assertEquals(true, segments.get(1).isEncrypted);
|
||||
assertEquals("https://priv.example.com/key.php?r=2680", segments.get(1).encryptionKeyUri);
|
||||
assertEquals("0x1566B", segments.get(1).encryptionIV);
|
||||
assertEquals(51501, segments.get(1).byterangeLength);
|
||||
assertEquals(2147483648L, segments.get(1).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2680.ts", segments.get(1).url);
|
||||
segment = segments.get(1);
|
||||
assertEquals(0, segment.relativeDiscontinuitySequence);
|
||||
assertEquals(7975000, segment.durationUs);
|
||||
assertTrue(segment.isEncrypted);
|
||||
assertEquals("https://priv.example.com/key.php?r=2680", segment.encryptionKeyUri);
|
||||
assertEquals("0x1566B", segment.encryptionIV);
|
||||
assertEquals(51501, segment.byterangeLength);
|
||||
assertEquals(2147483648L, segment.byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2680.ts", segment.url);
|
||||
|
||||
assertEquals(4, segments.get(2).discontinuitySequenceNumber);
|
||||
assertEquals(7941000, segments.get(2).durationUs);
|
||||
assertEquals(false, segments.get(2).isEncrypted);
|
||||
assertEquals(null, segments.get(2).encryptionKeyUri);
|
||||
assertEquals(null, segments.get(2).encryptionIV);
|
||||
assertEquals(51501, segments.get(2).byterangeLength);
|
||||
assertEquals(2147535149L, segments.get(2).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2681.ts", segments.get(2).url);
|
||||
segment = segments.get(2);
|
||||
assertEquals(0, segment.relativeDiscontinuitySequence);
|
||||
assertEquals(7941000, segment.durationUs);
|
||||
assertFalse(segment.isEncrypted);
|
||||
assertEquals(null, segment.encryptionKeyUri);
|
||||
assertEquals(null, segment.encryptionIV);
|
||||
assertEquals(51501, segment.byterangeLength);
|
||||
assertEquals(2147535149L, segment.byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2681.ts", segment.url);
|
||||
|
||||
assertEquals(5, segments.get(3).discontinuitySequenceNumber);
|
||||
assertEquals(7975000, segments.get(3).durationUs);
|
||||
assertEquals(true, segments.get(3).isEncrypted);
|
||||
assertEquals("https://priv.example.com/key.php?r=2682", segments.get(3).encryptionKeyUri);
|
||||
segment = segments.get(3);
|
||||
assertEquals(1, segment.relativeDiscontinuitySequence);
|
||||
assertEquals(7975000, segment.durationUs);
|
||||
assertTrue(segment.isEncrypted);
|
||||
assertEquals("https://priv.example.com/key.php?r=2682", segment.encryptionKeyUri);
|
||||
// 0xA7A == 2682.
|
||||
assertNotNull(segments.get(3).encryptionIV);
|
||||
assertEquals("A7A", segments.get(3).encryptionIV.toUpperCase(Locale.getDefault()));
|
||||
assertEquals(51740, segments.get(3).byterangeLength);
|
||||
assertEquals(2147586650L, segments.get(3).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2682.ts", segments.get(3).url);
|
||||
assertNotNull(segment.encryptionIV);
|
||||
assertEquals("A7A", segment.encryptionIV.toUpperCase(Locale.getDefault()));
|
||||
assertEquals(51740, segment.byterangeLength);
|
||||
assertEquals(2147586650L, segment.byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2682.ts", segment.url);
|
||||
|
||||
assertEquals(5, segments.get(4).discontinuitySequenceNumber);
|
||||
assertEquals(7975000, segments.get(4).durationUs);
|
||||
assertEquals(true, segments.get(4).isEncrypted);
|
||||
assertEquals("https://priv.example.com/key.php?r=2682", segments.get(4).encryptionKeyUri);
|
||||
segment = segments.get(4);
|
||||
assertEquals(1, segment.relativeDiscontinuitySequence);
|
||||
assertEquals(7975000, segment.durationUs);
|
||||
assertTrue(segment.isEncrypted);
|
||||
assertEquals("https://priv.example.com/key.php?r=2682", segment.encryptionKeyUri);
|
||||
// 0xA7B == 2683.
|
||||
assertNotNull(segments.get(4).encryptionIV);
|
||||
assertEquals("A7B", segments.get(4).encryptionIV.toUpperCase(Locale.getDefault()));
|
||||
assertEquals(C.LENGTH_UNSET, segments.get(4).byterangeLength);
|
||||
assertEquals(0, segments.get(4).byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2683.ts", segments.get(4).url);
|
||||
assertNotNull(segment.encryptionIV);
|
||||
assertEquals("A7B", segment.encryptionIV.toUpperCase(Locale.getDefault()));
|
||||
assertEquals(C.LENGTH_UNSET, segment.byterangeLength);
|
||||
assertEquals(0, segment.byterangeOffset);
|
||||
assertEquals("https://priv.example.com/fileSequence2683.ts", segment.url);
|
||||
} catch (IOException exception) {
|
||||
fail(exception.getMessage());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,8 +270,10 @@ import java.util.Locale;
|
|||
|
||||
// Compute start time of the next chunk.
|
||||
long startTimeUs = mediaPlaylist.startTimeUs + segment.relativeStartTimeUs;
|
||||
int discontinuitySequence = mediaPlaylist.discontinuitySequence
|
||||
+ segment.relativeDiscontinuitySequence;
|
||||
TimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster(
|
||||
segment.discontinuitySequenceNumber, startTimeUs);
|
||||
discontinuitySequence, startTimeUs);
|
||||
|
||||
// Configure the data source and spec for the chunk.
|
||||
Uri chunkUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.url);
|
||||
|
|
@ -279,9 +281,8 @@ import java.util.Locale;
|
|||
null);
|
||||
out.chunk = new HlsMediaChunk(dataSource, dataSpec, initDataSpec, variants[newVariantIndex],
|
||||
trackSelection.getSelectionReason(), trackSelection.getSelectionData(),
|
||||
startTimeUs, startTimeUs + segment.durationUs, chunkMediaSequence,
|
||||
segment.discontinuitySequenceNumber, isTimestampMaster, timestampAdjuster, previous,
|
||||
encryptionKey, encryptionIv);
|
||||
startTimeUs, startTimeUs + segment.durationUs, chunkMediaSequence, discontinuitySequence,
|
||||
isTimestampMaster, timestampAdjuster, previous, encryptionKey, encryptionIv);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||
|
||||
public final String url;
|
||||
public final long durationUs;
|
||||
public final int discontinuitySequenceNumber;
|
||||
public final int relativeDiscontinuitySequence;
|
||||
public final long relativeStartTimeUs;
|
||||
public final boolean isEncrypted;
|
||||
public final String encryptionKeyUri;
|
||||
|
|
@ -43,12 +43,12 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||
this(uri, 0, -1, C.TIME_UNSET, false, null, null, byterangeOffset, byterangeLength);
|
||||
}
|
||||
|
||||
public Segment(String uri, long durationUs, int discontinuitySequenceNumber,
|
||||
public Segment(String uri, long durationUs, int relativeDiscontinuitySequence,
|
||||
long relativeStartTimeUs, boolean isEncrypted, String encryptionKeyUri, String encryptionIV,
|
||||
long byterangeOffset, long byterangeLength) {
|
||||
this.url = uri;
|
||||
this.durationUs = durationUs;
|
||||
this.discontinuitySequenceNumber = discontinuitySequenceNumber;
|
||||
this.relativeDiscontinuitySequence = relativeDiscontinuitySequence;
|
||||
this.relativeStartTimeUs = relativeStartTimeUs;
|
||||
this.isEncrypted = isEncrypted;
|
||||
this.encryptionKeyUri = encryptionKeyUri;
|
||||
|
|
@ -67,6 +67,8 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||
|
||||
public final long startOffsetUs;
|
||||
public final long startTimeUs;
|
||||
public final boolean hasDiscontinuitySequence;
|
||||
public final int discontinuitySequence;
|
||||
public final int mediaSequence;
|
||||
public final int version;
|
||||
public final long targetDurationUs;
|
||||
|
|
@ -76,11 +78,14 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||
public final List<Segment> segments;
|
||||
public final long durationUs;
|
||||
|
||||
public HlsMediaPlaylist(String baseUri, long startOffsetUs, long startTimeUs, int mediaSequence,
|
||||
int version, long targetDurationUs, boolean hasEndTag, boolean hasProgramDateTime,
|
||||
public HlsMediaPlaylist(String baseUri, long startOffsetUs, long startTimeUs,
|
||||
boolean hasDiscontinuitySequence, int discontinuitySequence, int mediaSequence, int version,
|
||||
long targetDurationUs, boolean hasEndTag, boolean hasProgramDateTime,
|
||||
Segment initializationSegment, List<Segment> segments) {
|
||||
super(baseUri, HlsPlaylist.TYPE_MEDIA);
|
||||
this.startTimeUs = startTimeUs;
|
||||
this.hasDiscontinuitySequence = hasDiscontinuitySequence;
|
||||
this.discontinuitySequence = discontinuitySequence;
|
||||
this.mediaSequence = mediaSequence;
|
||||
this.version = version;
|
||||
this.targetDurationUs = targetDurationUs;
|
||||
|
|
@ -123,19 +128,18 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a playlist identical to this one except for the start time, which is set to the
|
||||
* specified value. If the start time already equals the specified value then the playlist will
|
||||
* return itself.
|
||||
* Returns a playlist identical to this one except for the start time, the discontinuity sequence
|
||||
* and {@code hasDiscontinuitySequence} values. The first two are set to the specified values,
|
||||
* {@code hasDiscontinuitySequence} is set to true.
|
||||
*
|
||||
* @param startTimeUs The start time for the returned playlist.
|
||||
* @param discontinuitySequence The discontinuity sequence for the returned playlist.
|
||||
* @return The playlist.
|
||||
*/
|
||||
public HlsMediaPlaylist copyWithStartTimeUs(long startTimeUs) {
|
||||
if (this.startTimeUs == startTimeUs) {
|
||||
return this;
|
||||
}
|
||||
return new HlsMediaPlaylist(baseUri, startOffsetUs, startTimeUs, mediaSequence, version,
|
||||
targetDurationUs, hasEndTag, hasProgramDateTime, initializationSegment, segments);
|
||||
public HlsMediaPlaylist copyWith(long startTimeUs, int discontinuitySequence) {
|
||||
return new HlsMediaPlaylist(baseUri, startOffsetUs, startTimeUs, true, discontinuitySequence,
|
||||
mediaSequence, version, targetDurationUs, hasEndTag, hasProgramDateTime,
|
||||
initializationSegment, segments);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -148,8 +152,9 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||
if (this.hasEndTag) {
|
||||
return this;
|
||||
}
|
||||
return new HlsMediaPlaylist(baseUri, startOffsetUs, startTimeUs, mediaSequence, version,
|
||||
targetDurationUs, true, hasProgramDateTime, initializationSegment, segments);
|
||||
return new HlsMediaPlaylist(baseUri, startOffsetUs, startTimeUs, hasDiscontinuitySequence,
|
||||
discontinuitySequence, mediaSequence, version, targetDurationUs, true, hasProgramDateTime,
|
||||
initializationSegment, segments);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,7 +266,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||
List<Segment> segments = new ArrayList<>();
|
||||
|
||||
long segmentDurationUs = 0;
|
||||
int discontinuitySequenceNumber = 0;
|
||||
boolean hasDiscontinuitySequence = false;
|
||||
int playlistDiscontinuitySequence = 0;
|
||||
int relativeDiscontinuitySequence = 0;
|
||||
long playlistStartTimeUs = 0;
|
||||
long segmentStartTimeUs = 0;
|
||||
long segmentByteRangeOffset = 0;
|
||||
|
|
@ -323,9 +325,10 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||
segmentByteRangeOffset = Long.parseLong(splitByteRange[1]);
|
||||
}
|
||||
} else if (line.startsWith(TAG_DISCONTINUITY_SEQUENCE)) {
|
||||
discontinuitySequenceNumber = Integer.parseInt(line.substring(line.indexOf(':') + 1));
|
||||
hasDiscontinuitySequence = true;
|
||||
playlistDiscontinuitySequence = Integer.parseInt(line.substring(line.indexOf(':') + 1));
|
||||
} else if (line.equals(TAG_DISCONTINUITY)) {
|
||||
discontinuitySequenceNumber++;
|
||||
relativeDiscontinuitySequence++;
|
||||
} else if (line.startsWith(TAG_PROGRAM_DATE_TIME)) {
|
||||
if (playlistStartTimeUs == 0) {
|
||||
long programDatetimeUs =
|
||||
|
|
@ -345,7 +348,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||
if (segmentByteRangeLength == C.LENGTH_UNSET) {
|
||||
segmentByteRangeOffset = 0;
|
||||
}
|
||||
segments.add(new Segment(line, segmentDurationUs, discontinuitySequenceNumber,
|
||||
segments.add(new Segment(line, segmentDurationUs, relativeDiscontinuitySequence,
|
||||
segmentStartTimeUs, isEncrypted, encryptionKeyUri, segmentEncryptionIV,
|
||||
segmentByteRangeOffset, segmentByteRangeLength));
|
||||
segmentStartTimeUs += segmentDurationUs;
|
||||
|
|
@ -358,7 +361,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||
hasEndTag = true;
|
||||
}
|
||||
}
|
||||
return new HlsMediaPlaylist(baseUri, startOffsetUs, playlistStartTimeUs, mediaSequence, version,
|
||||
return new HlsMediaPlaylist(baseUri, startOffsetUs, playlistStartTimeUs,
|
||||
hasDiscontinuitySequence, playlistDiscontinuitySequence, mediaSequence, version,
|
||||
targetDurationUs, hasEndTag, playlistStartTimeUs != 0, initializationSegment, segments);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -334,47 +334,71 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Track discontinuities for media playlists that don't include the discontinuity number.
|
||||
private HlsMediaPlaylist getLatestPlaylistSnapshot(HlsMediaPlaylist oldPlaylist,
|
||||
HlsMediaPlaylist loadedPlaylist) {
|
||||
if (loadedPlaylist.hasProgramDateTime) {
|
||||
if (loadedPlaylist.isNewerThan(oldPlaylist)) {
|
||||
return loadedPlaylist;
|
||||
} else {
|
||||
if (!loadedPlaylist.isNewerThan(oldPlaylist)) {
|
||||
if (loadedPlaylist.hasEndTag) {
|
||||
// If the loaded playlist has an end tag but is not newer than the old playlist then we have
|
||||
// an inconsistent state. This is typically caused by the server incorrectly resetting the
|
||||
// media sequence when appending the end tag. We resolve this case as best we can by
|
||||
// returning the old playlist with the end tag appended.
|
||||
return loadedPlaylist.hasEndTag ? oldPlaylist.copyWithEndTag() : oldPlaylist;
|
||||
return oldPlaylist.copyWithEndTag();
|
||||
} else {
|
||||
return oldPlaylist;
|
||||
}
|
||||
}
|
||||
// TODO: Once playlist type support is added, the snapshot's age can be added by using the
|
||||
// target duration.
|
||||
long startTimeUs = getLoadedPlaylistStartTimeUs(oldPlaylist, loadedPlaylist);
|
||||
int discontinuitySequence = getLoadedPlaylistDiscontinuitySequence(oldPlaylist, loadedPlaylist);
|
||||
return loadedPlaylist.copyWith(startTimeUs, discontinuitySequence);
|
||||
}
|
||||
|
||||
private long getLoadedPlaylistStartTimeUs(HlsMediaPlaylist oldPlaylist,
|
||||
HlsMediaPlaylist loadedPlaylist) {
|
||||
if (loadedPlaylist.hasProgramDateTime) {
|
||||
return loadedPlaylist.startTimeUs;
|
||||
}
|
||||
long primarySnapshotStartTimeUs = primaryUrlSnapshot != null
|
||||
? primaryUrlSnapshot.startTimeUs : 0;
|
||||
if (oldPlaylist == null) {
|
||||
if (loadedPlaylist.startTimeUs == primarySnapshotStartTimeUs) {
|
||||
// Playback has just started or is VOD so no adjustment is needed.
|
||||
return loadedPlaylist;
|
||||
} else {
|
||||
return loadedPlaylist.copyWithStartTimeUs(primarySnapshotStartTimeUs);
|
||||
}
|
||||
return primarySnapshotStartTimeUs;
|
||||
}
|
||||
if (!loadedPlaylist.isNewerThan(oldPlaylist)) {
|
||||
// See comment above.
|
||||
return loadedPlaylist.hasEndTag ? oldPlaylist.copyWithEndTag() : oldPlaylist;
|
||||
int oldPlaylistSize = oldPlaylist.segments.size();
|
||||
Segment firstOldOverlappingSegment = getFirstOldOverlappingSegment(oldPlaylist, loadedPlaylist);
|
||||
if (firstOldOverlappingSegment != null) {
|
||||
return oldPlaylist.startTimeUs + firstOldOverlappingSegment.relativeStartTimeUs;
|
||||
} else if (oldPlaylistSize == loadedPlaylist.mediaSequence - oldPlaylist.mediaSequence) {
|
||||
return oldPlaylist.getEndTimeUs();
|
||||
} else {
|
||||
// No segments overlap, we assume the new playlist start coincides with the primary playlist.
|
||||
return primarySnapshotStartTimeUs;
|
||||
}
|
||||
List<Segment> oldSegments = oldPlaylist.segments;
|
||||
int oldPlaylistSize = oldSegments.size();
|
||||
}
|
||||
|
||||
private int getLoadedPlaylistDiscontinuitySequence(HlsMediaPlaylist oldPlaylist,
|
||||
HlsMediaPlaylist loadedPlaylist) {
|
||||
if (loadedPlaylist.hasDiscontinuitySequence) {
|
||||
return loadedPlaylist.discontinuitySequence;
|
||||
}
|
||||
// TODO: Improve cross-playlist discontinuity adjustment.
|
||||
int primaryUrlDiscontinuitySequence = primaryUrlSnapshot != null
|
||||
? primaryUrlSnapshot.discontinuitySequence : 0;
|
||||
if (oldPlaylist == null) {
|
||||
return primaryUrlDiscontinuitySequence;
|
||||
}
|
||||
Segment firstOldOverlappingSegment = getFirstOldOverlappingSegment(oldPlaylist, loadedPlaylist);
|
||||
if (firstOldOverlappingSegment != null) {
|
||||
return oldPlaylist.discontinuitySequence
|
||||
+ firstOldOverlappingSegment.relativeDiscontinuitySequence
|
||||
- loadedPlaylist.segments.get(0).relativeDiscontinuitySequence;
|
||||
}
|
||||
return primaryUrlDiscontinuitySequence;
|
||||
}
|
||||
|
||||
private static Segment getFirstOldOverlappingSegment(HlsMediaPlaylist oldPlaylist,
|
||||
HlsMediaPlaylist loadedPlaylist) {
|
||||
int mediaSequenceOffset = loadedPlaylist.mediaSequence - oldPlaylist.mediaSequence;
|
||||
if (mediaSequenceOffset <= oldPlaylistSize) {
|
||||
long adjustedNewPlaylistStartTimeUs = mediaSequenceOffset == oldPlaylistSize
|
||||
? oldPlaylist.getEndTimeUs()
|
||||
: oldPlaylist.startTimeUs + oldSegments.get(mediaSequenceOffset).relativeStartTimeUs;
|
||||
return loadedPlaylist.copyWithStartTimeUs(adjustedNewPlaylistStartTimeUs);
|
||||
}
|
||||
// No segments overlap, we assume the new playlist start coincides with the primary playlist.
|
||||
return loadedPlaylist.copyWithStartTimeUs(primarySnapshotStartTimeUs);
|
||||
List<Segment> oldSegments = oldPlaylist.segments;
|
||||
return mediaSequenceOffset < oldSegments.size() ? oldSegments.get(mediaSequenceOffset) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue