Fix start position for non-precise startOffset and user-set liveOffset
Also added test cases covering this. PiperOrigin-RevId: 374218514
This commit is contained in:
parent
c80355c903
commit
6fd20ddace
@ -572,15 +572,12 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||
if (playlist.startOffsetUs == C.TIME_UNSET || playlist.segments.isEmpty()) {
|
||||
windowDefaultStartPositionUs = 0;
|
||||
} else {
|
||||
// From RFC 8216, section 4.4.2.2: if playlist.startOffsetUs is negative, it indicates the
|
||||
// beginning of the Playlist, whereas if it is beyond the playlist duration it indicates the
|
||||
// end of the playlist.
|
||||
long startOffsetUs = Util.constrainValue(playlist.startOffsetUs, 0, playlist.durationUs);
|
||||
if (playlist.preciseStart || startOffsetUs == playlist.durationUs) {
|
||||
windowDefaultStartPositionUs = startOffsetUs;
|
||||
if (playlist.preciseStart || playlist.startOffsetUs == playlist.durationUs) {
|
||||
windowDefaultStartPositionUs = playlist.startOffsetUs;
|
||||
} else {
|
||||
windowDefaultStartPositionUs =
|
||||
findClosestPrecedingSegment(playlist.segments, startOffsetUs).relativeStartTimeUs;
|
||||
findClosestPrecedingSegment(playlist.segments, playlist.startOffsetUs)
|
||||
.relativeStartTimeUs;
|
||||
}
|
||||
}
|
||||
return new SinglePeriodTimeline(
|
||||
@ -606,17 +603,16 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||
|
||||
private long getLiveWindowDefaultStartPositionUs(
|
||||
HlsMediaPlaylist playlist, long liveEdgeOffsetUs) {
|
||||
if (playlist.startOffsetUs != C.TIME_UNSET && playlist.preciseStart) {
|
||||
// From RFC 8216, section 4.4.2.2: if playlist.startOffsetUs is negative, it indicates the
|
||||
// beginning of the Playlist, whereas if it is beyond the playlist duration it indicates the
|
||||
// end of the playlist.
|
||||
return Util.constrainValue(playlist.startOffsetUs, 0, playlist.durationUs);
|
||||
long startPositionUs =
|
||||
playlist.startOffsetUs != C.TIME_UNSET
|
||||
? playlist.startOffsetUs
|
||||
: playlist.durationUs + liveEdgeOffsetUs - C.msToUs(liveConfiguration.targetOffsetMs);
|
||||
if (playlist.preciseStart) {
|
||||
return startPositionUs;
|
||||
}
|
||||
long maxStartPositionUs =
|
||||
playlist.durationUs + liveEdgeOffsetUs - C.msToUs(liveConfiguration.targetOffsetMs);
|
||||
@Nullable
|
||||
HlsMediaPlaylist.Part part =
|
||||
findClosestPrecedingIndependentPart(playlist.trailingParts, maxStartPositionUs);
|
||||
findClosestPrecedingIndependentPart(playlist.trailingParts, startPositionUs);
|
||||
if (part != null) {
|
||||
return part.relativeStartTimeUs;
|
||||
}
|
||||
@ -624,8 +620,8 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||
return 0;
|
||||
}
|
||||
HlsMediaPlaylist.Segment segment =
|
||||
findClosestPrecedingSegment(playlist.segments, maxStartPositionUs);
|
||||
part = findClosestPrecedingIndependentPart(segment.parts, maxStartPositionUs);
|
||||
findClosestPrecedingSegment(playlist.segments, startPositionUs);
|
||||
part = findClosestPrecedingIndependentPart(segment.parts, startPositionUs);
|
||||
if (part != null) {
|
||||
return part.relativeStartTimeUs;
|
||||
}
|
||||
@ -660,11 +656,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||
HlsMediaPlaylist.ServerControl serverControl = playlist.serverControl;
|
||||
long targetOffsetUs;
|
||||
if (playlist.startOffsetUs != C.TIME_UNSET) {
|
||||
// From RFC 8216, section 4.4.2.2: if playlist.startOffsetUs is negative, it indicates the
|
||||
// beginning of the Playlist, whereas if it is beyond the playlist duration it indicates the
|
||||
// end of the playlist.
|
||||
long startOffsetUs = Util.constrainValue(playlist.startOffsetUs, 0, playlist.durationUs);
|
||||
targetOffsetUs = playlist.durationUs - startOffsetUs;
|
||||
targetOffsetUs = playlist.durationUs - playlist.startOffsetUs;
|
||||
} else if (serverControl.partHoldBackUs != C.TIME_UNSET
|
||||
&& playlist.partTargetDurationUs != C.TIME_UNSET) {
|
||||
// Select part hold back only if the playlist has a part target duration.
|
||||
@ -694,8 +686,8 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the segment that contains {@code positionUs}, or the last sent if the position is beyond
|
||||
* the segments list.
|
||||
* Gets the segment that contains {@code positionUs}, or the last segment if the position is
|
||||
* beyond the segments list.
|
||||
*/
|
||||
private static HlsMediaPlaylist.Segment findClosestPrecedingSegment(
|
||||
List<HlsMediaPlaylist.Segment> segments, long positionUs) {
|
||||
|
@ -15,6 +15,9 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.source.hls.playlist;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -392,8 +395,9 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
||||
/** The type of the playlist. See {@link PlaylistType}. */
|
||||
@PlaylistType public final int playlistType;
|
||||
/**
|
||||
* The start offset in microseconds, as defined by #EXT-X-START, or {@link C#TIME_UNSET} if
|
||||
* undefined.
|
||||
* The start offset in microseconds from the beginning of the playlist, as defined by
|
||||
* #EXT-X-START, or {@link C#TIME_UNSET} if undefined. The value is guaranteed to be between 0 and
|
||||
* {@link #durationUs}, inclusive.
|
||||
*/
|
||||
public final long startOffsetUs;
|
||||
/** Whether the start position should be precise, as defined by #EXT-X-START. */
|
||||
@ -513,10 +517,15 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
||||
} else {
|
||||
durationUs = 0;
|
||||
}
|
||||
// From RFC 8216, section 4.4.2.2: If startOffsetUs is negative, it indicates the offset from
|
||||
// the end of the playlist. If the absolute value exceeds the duration of the playlist, it
|
||||
// indicates the beginning (if negative) or the end (if positive) of the playlist.
|
||||
this.startOffsetUs =
|
||||
startOffsetUs == C.TIME_UNSET
|
||||
? C.TIME_UNSET
|
||||
: startOffsetUs >= 0 ? startOffsetUs : durationUs + startOffsetUs;
|
||||
: startOffsetUs >= 0
|
||||
? min(durationUs, startOffsetUs)
|
||||
: max(0, durationUs + startOffsetUs);
|
||||
this.serverControl = serverControl;
|
||||
}
|
||||
|
||||
|
@ -337,6 +337,44 @@ public class HlsMediaSourceTest {
|
||||
assertThat(window.defaultPositionUs).isEqualTo(4000000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
loadLivePlaylist_withNonPreciseStartTimeAndUserDefinedLiveOffset_startsFromPrecedingSegment()
|
||||
throws TimeoutException, ParserException {
|
||||
String playlistUri = "fake://foo.bar/media0/playlist.m3u8";
|
||||
// The playlist has a duration of 16 seconds, and part hold back, hold back and start time
|
||||
// defined.
|
||||
String playlist =
|
||||
"#EXTM3U\n"
|
||||
+ "#EXT-X-PROGRAM-DATE-TIME:2020-01-01T00:00:00.0+00:00\n"
|
||||
+ "#EXT-X-TARGETDURATION:4\n"
|
||||
+ "#EXT-X-VERSION:3\n"
|
||||
+ "#EXT-X-START:TIME-OFFSET=-10\n"
|
||||
+ "#EXT-X-MEDIA-SEQUENCE:0\n"
|
||||
+ "#EXTINF:4.00000,\n"
|
||||
+ "fileSequence0.ts\n"
|
||||
+ "#EXTINF:4.00000,\n"
|
||||
+ "fileSequence1.ts\n"
|
||||
+ "#EXTINF:4.00000,\n"
|
||||
+ "fileSequence2.ts\n"
|
||||
+ "#EXTINF:4.00000,\n"
|
||||
+ "fileSequence3.ts\n"
|
||||
+ "#EXT-X-SERVER-CONTROL:HOLD-BACK=12,PART-HOLD-BACK=3\n";
|
||||
// The playlist finishes 1 second before the current time.
|
||||
SystemClock.setCurrentTimeMillis(Util.parseXsDateTime("2020-01-01T00:00:17.0+00:00"));
|
||||
HlsMediaSource.Factory factory = createHlsMediaSourceFactory(playlistUri, playlist);
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder().setUri(playlistUri).setLiveTargetOffsetMs(3000).build();
|
||||
HlsMediaSource mediaSource = factory.createMediaSource(mediaItem);
|
||||
|
||||
Timeline timeline = prepareAndWaitForTimeline(mediaSource);
|
||||
|
||||
Timeline.Window window = timeline.getWindow(0, new Timeline.Window());
|
||||
assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(3000);
|
||||
// The default position points to the segment containing the start time.
|
||||
assertThat(window.defaultPositionUs).isEqualTo(4000000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadLivePlaylist_withPreciseStartTime_targetLiveOffsetFromStartTime()
|
||||
throws TimeoutException, ParserException {
|
||||
@ -375,6 +413,43 @@ public class HlsMediaSourceTest {
|
||||
assertThat(window.defaultPositionUs).isEqualTo(6000000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadLivePlaylist_withPreciseStartTimeAndUserDefinedLiveOffset_startsFromStartTime()
|
||||
throws TimeoutException, ParserException {
|
||||
String playlistUri = "fake://foo.bar/media0/playlist.m3u8";
|
||||
// The playlist has a duration of 16 seconds, and part hold back, hold back and start time
|
||||
// defined.
|
||||
String playlist =
|
||||
"#EXTM3U\n"
|
||||
+ "#EXT-X-PROGRAM-DATE-TIME:2020-01-01T00:00:00.0+00:00\n"
|
||||
+ "#EXT-X-TARGETDURATION:4\n"
|
||||
+ "#EXT-X-VERSION:3\n"
|
||||
+ "#EXT-X-START:TIME-OFFSET=-10,PRECISE=YES\n"
|
||||
+ "#EXT-X-MEDIA-SEQUENCE:0\n"
|
||||
+ "#EXTINF:4.00000,\n"
|
||||
+ "fileSequence0.ts\n"
|
||||
+ "#EXTINF:4.00000,\n"
|
||||
+ "fileSequence1.ts\n"
|
||||
+ "#EXTINF:4.00000,\n"
|
||||
+ "fileSequence2.ts\n"
|
||||
+ "#EXTINF:4.00000,\n"
|
||||
+ "fileSequence3.ts\n"
|
||||
+ "#EXT-X-SERVER-CONTROL:HOLD-BACK=12,PART-HOLD-BACK=3";
|
||||
// The playlist finishes 1 second before the current time.
|
||||
SystemClock.setCurrentTimeMillis(Util.parseXsDateTime("2020-01-01T00:00:17.0+00:00"));
|
||||
HlsMediaSource.Factory factory = createHlsMediaSourceFactory(playlistUri, playlist);
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder().setUri(playlistUri).setLiveTargetOffsetMs(3000).build();
|
||||
HlsMediaSource mediaSource = factory.createMediaSource(mediaItem);
|
||||
|
||||
Timeline timeline = prepareAndWaitForTimeline(mediaSource);
|
||||
|
||||
Timeline.Window window = timeline.getWindow(0, new Timeline.Window());
|
||||
assertThat(window.liveConfiguration.targetOffsetMs).isEqualTo(3000);
|
||||
// The default position points to the start time.
|
||||
assertThat(window.defaultPositionUs).isEqualTo(6000000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadLivePlaylist_targetLiveOffsetInMediaItem_targetLiveOffsetPickedFromMediaItem()
|
||||
throws TimeoutException, ParserException {
|
||||
|
Loading…
x
Reference in New Issue
Block a user