Block HLS playlist requests at part level
Issue: #5011 PiperOrigin-RevId: 340625816
This commit is contained in:
parent
4332dc2304
commit
7f49b33fea
@ -30,6 +30,7 @@ import com.google.android.exoplayer2.source.MediaLoadData;
|
|||||||
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
|
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
|
||||||
import com.google.android.exoplayer2.source.hls.HlsDataSourceFactory;
|
import com.google.android.exoplayer2.source.hls.HlsDataSourceFactory;
|
||||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant;
|
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant;
|
||||||
|
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Part;
|
||||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
|
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||||
@ -40,6 +41,7 @@ import com.google.android.exoplayer2.upstream.Loader.LoadErrorAction;
|
|||||||
import com.google.android.exoplayer2.upstream.ParsingLoadable;
|
import com.google.android.exoplayer2.upstream.ParsingLoadable;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -465,6 +467,7 @@ public final class DefaultHlsPlaylistTracker
|
|||||||
private final class MediaPlaylistBundle implements Loader.Callback<ParsingLoadable<HlsPlaylist>> {
|
private final class MediaPlaylistBundle implements Loader.Callback<ParsingLoadable<HlsPlaylist>> {
|
||||||
|
|
||||||
private static final String BLOCK_MSN_PARAM = "_HLS_msn";
|
private static final String BLOCK_MSN_PARAM = "_HLS_msn";
|
||||||
|
private static final String BLOCK_PART_PARAM = "_HLS_part";
|
||||||
private static final String SKIP_PARAM = "_HLS_skip";
|
private static final String SKIP_PARAM = "_HLS_skip";
|
||||||
|
|
||||||
private final Uri playlistUrl;
|
private final Uri playlistUrl;
|
||||||
@ -729,16 +732,25 @@ public final class DefaultHlsPlaylistTracker
|
|||||||
return playlistUrl;
|
return playlistUrl;
|
||||||
}
|
}
|
||||||
Uri.Builder uriBuilder = playlistUrl.buildUpon();
|
Uri.Builder uriBuilder = playlistUrl.buildUpon();
|
||||||
if (playlistSnapshot.serverControl.skipUntilUs != C.TIME_UNSET) {
|
|
||||||
uriBuilder.appendQueryParameter(
|
|
||||||
SKIP_PARAM, playlistSnapshot.serverControl.canSkipDateRanges ? "v2" : "YES");
|
|
||||||
}
|
|
||||||
if (playlistSnapshot.serverControl.canBlockReload) {
|
if (playlistSnapshot.serverControl.canBlockReload) {
|
||||||
long reloadMediaSequence =
|
long targetMediaSequence =
|
||||||
playlistSnapshot.mediaSequence
|
playlistSnapshot.mediaSequence
|
||||||
+ playlistSnapshot.segments.size()
|
+ playlistSnapshot.segments.size()
|
||||||
+ playlistSnapshot.skippedSegmentCount;
|
+ playlistSnapshot.skippedSegmentCount;
|
||||||
uriBuilder.appendQueryParameter(BLOCK_MSN_PARAM, String.valueOf(reloadMediaSequence));
|
uriBuilder.appendQueryParameter(BLOCK_MSN_PARAM, String.valueOf(targetMediaSequence));
|
||||||
|
if (playlistSnapshot.partTargetDurationUs != C.TIME_UNSET) {
|
||||||
|
List<Part> trailingParts = playlistSnapshot.trailingParts;
|
||||||
|
int targetPartIndex = trailingParts.size();
|
||||||
|
if (!trailingParts.isEmpty() && Iterables.getLast(trailingParts).isPreload) {
|
||||||
|
// Ignore the preload part.
|
||||||
|
targetPartIndex--;
|
||||||
|
}
|
||||||
|
uriBuilder.appendQueryParameter(BLOCK_PART_PARAM, String.valueOf(targetPartIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (playlistSnapshot.serverControl.skipUntilUs != C.TIME_UNSET) {
|
||||||
|
uriBuilder.appendQueryParameter(
|
||||||
|
SKIP_PARAM, playlistSnapshot.serverControl.canSkipDateRanges ? "v2" : "YES");
|
||||||
}
|
}
|
||||||
return uriBuilder.build();
|
return uriBuilder.build();
|
||||||
}
|
}
|
||||||
|
@ -176,6 +176,8 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||||||
|
|
||||||
/** Whether the part is independent. */
|
/** Whether the part is independent. */
|
||||||
public final boolean isIndependent;
|
public final boolean isIndependent;
|
||||||
|
/** Whether the part is a preloading part. */
|
||||||
|
public final boolean isPreload;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
@ -192,6 +194,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||||||
* @param byteRangeLength See {@link #byteRangeLength}.
|
* @param byteRangeLength See {@link #byteRangeLength}.
|
||||||
* @param hasGapTag See {@link #hasGapTag}.
|
* @param hasGapTag See {@link #hasGapTag}.
|
||||||
* @param isIndependent See {@link #isIndependent}.
|
* @param isIndependent See {@link #isIndependent}.
|
||||||
|
* @param isPreload See {@link #isPreload}.
|
||||||
*/
|
*/
|
||||||
public Part(
|
public Part(
|
||||||
String url,
|
String url,
|
||||||
@ -205,7 +208,8 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||||||
long byteRangeOffset,
|
long byteRangeOffset,
|
||||||
long byteRangeLength,
|
long byteRangeLength,
|
||||||
boolean hasGapTag,
|
boolean hasGapTag,
|
||||||
boolean isIndependent) {
|
boolean isIndependent,
|
||||||
|
boolean isPreload) {
|
||||||
super(
|
super(
|
||||||
url,
|
url,
|
||||||
initializationSegment,
|
initializationSegment,
|
||||||
@ -219,6 +223,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||||||
byteRangeLength,
|
byteRangeLength,
|
||||||
hasGapTag);
|
hasGapTag);
|
||||||
this.isIndependent = isIndependent;
|
this.isIndependent = isIndependent;
|
||||||
|
this.isPreload = isPreload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,8 +507,13 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||||||
// The media sequences are equal.
|
// The media sequences are equal.
|
||||||
int segmentCount = segments.size() + skippedSegmentCount;
|
int segmentCount = segments.size() + skippedSegmentCount;
|
||||||
int otherSegmentCount = other.segments.size() + other.skippedSegmentCount;
|
int otherSegmentCount = other.segments.size() + other.skippedSegmentCount;
|
||||||
return segmentCount > otherSegmentCount
|
if (segmentCount != otherSegmentCount) {
|
||||||
|| (segmentCount == otherSegmentCount && hasEndTag && !other.hasEndTag);
|
return segmentCount > otherSegmentCount;
|
||||||
|
}
|
||||||
|
int partCount = trailingParts.size();
|
||||||
|
int otherPartCount = other.trailingParts.size();
|
||||||
|
return partCount > otherPartCount
|
||||||
|
|| (partCount == otherPartCount && hasEndTag && !other.hasEndTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -824,7 +824,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
byteRangeStart,
|
byteRangeStart,
|
||||||
byteRangeLength,
|
byteRangeLength,
|
||||||
/* hasGapTag= */ false,
|
/* hasGapTag= */ false,
|
||||||
/* isIndependent= */ false);
|
/* isIndependent= */ false,
|
||||||
|
/* isPreload= */ true);
|
||||||
} else if (line.startsWith(TAG_PART)) {
|
} else if (line.startsWith(TAG_PART)) {
|
||||||
@Nullable
|
@Nullable
|
||||||
String segmentEncryptionIV =
|
String segmentEncryptionIV =
|
||||||
@ -869,7 +870,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
partByteRangeOffset,
|
partByteRangeOffset,
|
||||||
partByteRangeLength,
|
partByteRangeLength,
|
||||||
isGap,
|
isGap,
|
||||||
isIndependent));
|
isIndependent,
|
||||||
|
/* isPreload= */ false));
|
||||||
partStartTimeUs += partDurationUs;
|
partStartTimeUs += partDurationUs;
|
||||||
if (partByteRangeLength != C.LENGTH_UNSET) {
|
if (partByteRangeLength != C.LENGTH_UNSET) {
|
||||||
partByteRangeOffset += partByteRangeLength;
|
partByteRangeOffset += partByteRangeLength;
|
||||||
|
@ -64,6 +64,21 @@ public class DefaultHlsPlaylistTrackerTest {
|
|||||||
"media/m3u8/live_low_latency_media_can_block_reload";
|
"media/m3u8/live_low_latency_media_can_block_reload";
|
||||||
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_NEXT =
|
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_NEXT =
|
||||||
"media/m3u8/live_low_latency_media_can_block_reload_next";
|
"media/m3u8/live_low_latency_media_can_block_reload_next";
|
||||||
|
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY =
|
||||||
|
"media/m3u8/live_low_latency_media_can_block_reload_low_latency";
|
||||||
|
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY_NEXT =
|
||||||
|
"media/m3u8/live_low_latency_media_can_block_reload_low_latency_next";
|
||||||
|
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY_FULL_SEGMENT =
|
||||||
|
"media/m3u8/live_low_latency_media_can_block_reload_low_latency_full_segment";
|
||||||
|
private static final String
|
||||||
|
SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY_FULL_SEGMENT_NEXT =
|
||||||
|
"media/m3u8/live_low_latency_media_can_block_reload_low_latency_full_segment_next";
|
||||||
|
private static final String
|
||||||
|
SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY_FULL_SEGMENT_PRELOAD =
|
||||||
|
"media/m3u8/live_low_latency_media_can_block_reload_low_latency_full_segment_preload";
|
||||||
|
private static final String
|
||||||
|
SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY_FULL_SEGMENT_PRELOAD_NEXT =
|
||||||
|
"media/m3u8/live_low_latency_media_can_block_reload_low_latency_full_segment_preload_next";
|
||||||
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL_AND_BLOCK_RELOAD =
|
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL_AND_BLOCK_RELOAD =
|
||||||
"media/m3u8/live_low_latency_media_can_skip_until_and_block_reload";
|
"media/m3u8/live_low_latency_media_can_skip_until_and_block_reload";
|
||||||
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL_AND_BLOCK_RELOAD_NEXT =
|
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL_AND_BLOCK_RELOAD_NEXT =
|
||||||
@ -255,6 +270,96 @@ public class DefaultHlsPlaylistTrackerTest {
|
|||||||
assertThat(mediaPlaylists.get(1).mediaSequence).isEqualTo(11);
|
assertThat(mediaPlaylists.get(1).mediaSequence).isEqualTo(11);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
start_playlistCanBlockReloadLowLatency_requestBlockingReloadWithCorrectMediaSequenceAndPart()
|
||||||
|
throws IOException, TimeoutException, InterruptedException {
|
||||||
|
List<HttpUrl> httpUrls =
|
||||||
|
enqueueWebServerResponses(
|
||||||
|
new String[] {
|
||||||
|
"/master.m3u8",
|
||||||
|
"/media0/playlist.m3u8",
|
||||||
|
"/media0/playlist.m3u8?_HLS_msn=14&_HLS_part=1"
|
||||||
|
},
|
||||||
|
getMockResponse(SAMPLE_M3U8_LIVE_MASTER),
|
||||||
|
getMockResponse(SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY),
|
||||||
|
getMockResponse(SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY_NEXT));
|
||||||
|
|
||||||
|
List<HlsMediaPlaylist> mediaPlaylists =
|
||||||
|
runPlaylistTrackerAndCollectMediaPlaylists(
|
||||||
|
new DefaultHttpDataSourceFactory(),
|
||||||
|
Uri.parse(mockWebServer.url("/master.m3u8").toString()),
|
||||||
|
/* awaitedMediaPlaylistCount= */ 2);
|
||||||
|
|
||||||
|
assertRequestUrlsCalled(httpUrls);
|
||||||
|
assertThat(mediaPlaylists.get(0).mediaSequence).isEqualTo(10);
|
||||||
|
assertThat(mediaPlaylists.get(0).segments).hasSize(4);
|
||||||
|
assertThat(mediaPlaylists.get(0).trailingParts).hasSize(2);
|
||||||
|
assertThat(mediaPlaylists.get(1).mediaSequence).isEqualTo(10);
|
||||||
|
assertThat(mediaPlaylists.get(1).segments).hasSize(4);
|
||||||
|
assertThat(mediaPlaylists.get(1).trailingParts).hasSize(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void start_playlistCanBlockReloadLowLatencyFullSegment_correctMsnAndPartParams()
|
||||||
|
throws IOException, TimeoutException, InterruptedException {
|
||||||
|
List<HttpUrl> httpUrls =
|
||||||
|
enqueueWebServerResponses(
|
||||||
|
new String[] {
|
||||||
|
"/master.m3u8",
|
||||||
|
"/media0/playlist.m3u8",
|
||||||
|
"/media0/playlist.m3u8?_HLS_msn=14&_HLS_part=0"
|
||||||
|
},
|
||||||
|
getMockResponse(SAMPLE_M3U8_LIVE_MASTER),
|
||||||
|
getMockResponse(SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY_FULL_SEGMENT),
|
||||||
|
getMockResponse(SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY_FULL_SEGMENT_NEXT));
|
||||||
|
|
||||||
|
List<HlsMediaPlaylist> mediaPlaylists =
|
||||||
|
runPlaylistTrackerAndCollectMediaPlaylists(
|
||||||
|
new DefaultHttpDataSourceFactory(),
|
||||||
|
Uri.parse(mockWebServer.url("/master.m3u8").toString()),
|
||||||
|
/* awaitedMediaPlaylistCount= */ 2);
|
||||||
|
|
||||||
|
assertRequestUrlsCalled(httpUrls);
|
||||||
|
assertThat(mediaPlaylists.get(0).mediaSequence).isEqualTo(10);
|
||||||
|
assertThat(mediaPlaylists.get(0).segments).hasSize(4);
|
||||||
|
assertThat(mediaPlaylists.get(0).trailingParts).isEmpty();
|
||||||
|
assertThat(mediaPlaylists.get(1).mediaSequence).isEqualTo(10);
|
||||||
|
assertThat(mediaPlaylists.get(1).segments).hasSize(4);
|
||||||
|
assertThat(mediaPlaylists.get(1).trailingParts).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void start_playlistCanBlockReloadLowLatencyFullSegmentWithPreloadPart_ignoresPreloadPart()
|
||||||
|
throws IOException, TimeoutException, InterruptedException {
|
||||||
|
List<HttpUrl> httpUrls =
|
||||||
|
enqueueWebServerResponses(
|
||||||
|
new String[] {
|
||||||
|
"/master.m3u8",
|
||||||
|
"/media0/playlist.m3u8",
|
||||||
|
"/media0/playlist.m3u8?_HLS_msn=14&_HLS_part=0"
|
||||||
|
},
|
||||||
|
getMockResponse(SAMPLE_M3U8_LIVE_MASTER),
|
||||||
|
getMockResponse(
|
||||||
|
SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY_FULL_SEGMENT_PRELOAD),
|
||||||
|
getMockResponse(
|
||||||
|
SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY_FULL_SEGMENT_PRELOAD_NEXT));
|
||||||
|
|
||||||
|
List<HlsMediaPlaylist> mediaPlaylists =
|
||||||
|
runPlaylistTrackerAndCollectMediaPlaylists(
|
||||||
|
new DefaultHttpDataSourceFactory(),
|
||||||
|
Uri.parse(mockWebServer.url("/master.m3u8").toString()),
|
||||||
|
/* awaitedMediaPlaylistCount= */ 2);
|
||||||
|
|
||||||
|
assertRequestUrlsCalled(httpUrls);
|
||||||
|
assertThat(mediaPlaylists.get(0).mediaSequence).isEqualTo(10);
|
||||||
|
assertThat(mediaPlaylists.get(0).segments).hasSize(4);
|
||||||
|
assertThat(mediaPlaylists.get(0).trailingParts).hasSize(1);
|
||||||
|
assertThat(mediaPlaylists.get(1).mediaSequence).isEqualTo(10);
|
||||||
|
assertThat(mediaPlaylists.get(1).segments).hasSize(4);
|
||||||
|
assertThat(mediaPlaylists.get(1).trailingParts).hasSize(2);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void start_httpBadRequest_forcesFullNonBlockingPlaylistRequest()
|
public void start_httpBadRequest_forcesFullNonBlockingPlaylistRequest()
|
||||||
throws IOException, TimeoutException, InterruptedException {
|
throws IOException, TimeoutException, InterruptedException {
|
||||||
@ -263,9 +368,9 @@ public class DefaultHlsPlaylistTrackerTest {
|
|||||||
new String[] {
|
new String[] {
|
||||||
"/master.m3u8",
|
"/master.m3u8",
|
||||||
"/media0/playlist.m3u8",
|
"/media0/playlist.m3u8",
|
||||||
"/media0/playlist.m3u8?_HLS_skip=YES&_HLS_msn=16",
|
"/media0/playlist.m3u8?_HLS_msn=16&_HLS_skip=YES",
|
||||||
"/media0/playlist.m3u8",
|
"/media0/playlist.m3u8",
|
||||||
"/media0/playlist.m3u8?_HLS_skip=YES&_HLS_msn=17"
|
"/media0/playlist.m3u8?_HLS_msn=17&_HLS_skip=YES"
|
||||||
},
|
},
|
||||||
getMockResponse(SAMPLE_M3U8_LIVE_MASTER),
|
getMockResponse(SAMPLE_M3U8_LIVE_MASTER),
|
||||||
getMockResponse(SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL_AND_BLOCK_RELOAD),
|
getMockResponse(SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL_AND_BLOCK_RELOAD),
|
||||||
|
@ -354,6 +354,7 @@ public class HlsMediaPlaylistParserTest {
|
|||||||
assertThat(firstPart.durationUs).isEqualTo(2_000_000);
|
assertThat(firstPart.durationUs).isEqualTo(2_000_000);
|
||||||
assertThat(firstPart.relativeStartTimeUs).isEqualTo(playlist.segments.get(0).durationUs);
|
assertThat(firstPart.relativeStartTimeUs).isEqualTo(playlist.segments.get(0).durationUs);
|
||||||
assertThat(firstPart.isIndependent).isTrue();
|
assertThat(firstPart.isIndependent).isTrue();
|
||||||
|
assertThat(firstPart.isPreload).isFalse();
|
||||||
assertThat(firstPart.hasGapTag).isTrue();
|
assertThat(firstPart.hasGapTag).isTrue();
|
||||||
assertThat(firstPart.url).isEqualTo("part267.1.ts");
|
assertThat(firstPart.url).isEqualTo("part267.1.ts");
|
||||||
HlsMediaPlaylist.Part secondPart = playlist.segments.get(1).parts.get(1);
|
HlsMediaPlaylist.Part secondPart = playlist.segments.get(1).parts.get(1);
|
||||||
@ -518,6 +519,7 @@ public class HlsMediaPlaylistParserTest {
|
|||||||
assertThat(preloadPart.byteRangeOffset).isEqualTo(1234);
|
assertThat(preloadPart.byteRangeOffset).isEqualTo(1234);
|
||||||
assertThat(preloadPart.initializationSegment.url).isEqualTo("map.mp4");
|
assertThat(preloadPart.initializationSegment.url).isEqualTo("map.mp4");
|
||||||
assertThat(preloadPart.encryptionIV).isEqualTo("0x410C8AC18AA42EFA18B5155484F5FC34");
|
assertThat(preloadPart.encryptionIV).isEqualTo("0x410C8AC18AA42EFA18B5155484F5FC34");
|
||||||
|
assertThat(preloadPart.isPreload).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -539,6 +541,7 @@ public class HlsMediaPlaylistParserTest {
|
|||||||
|
|
||||||
assertThat(playlist.trailingParts).hasSize(2);
|
assertThat(playlist.trailingParts).hasSize(2);
|
||||||
assertThat(playlist.trailingParts.get(1).url).isEqualTo("filePart267.2.ts");
|
assertThat(playlist.trailingParts.get(1).url).isEqualTo("filePart267.2.ts");
|
||||||
|
assertThat(playlist.trailingParts.get(1).isPreload).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
16
testdata/src/test/assets/media/m3u8/live_low_latency_media_can_block_reload_low_latency
vendored
Normal file
16
testdata/src/test/assets/media/m3u8/live_low_latency_media_can_block_reload_low_latency
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#EXTM3U
|
||||||
|
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES
|
||||||
|
#EXT-X-TARGETDURATION:4
|
||||||
|
#EXT-X-PART-INF:PART-TARGET=1.000000
|
||||||
|
#EXT-X-VERSION:3
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:10
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence10.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence11.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence12.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence13.ts
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence14.0.ts"
|
||||||
|
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="fileSequence14.1.ts"
|
@ -0,0 +1,18 @@
|
|||||||
|
#EXTM3U
|
||||||
|
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES
|
||||||
|
#EXT-X-TARGETDURATION:4
|
||||||
|
#EXT-X-PART-INF:PART-TARGET=1.000000
|
||||||
|
#EXT-X-VERSION:3
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:10
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence10.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence11.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence12.ts
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.0.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.1.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.2.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.3.ts"
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence13.ts
|
@ -0,0 +1,19 @@
|
|||||||
|
#EXTM3U
|
||||||
|
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES
|
||||||
|
#EXT-X-TARGETDURATION:4
|
||||||
|
#EXT-X-PART-INF:PART-TARGET=1.000000
|
||||||
|
#EXT-X-VERSION:3
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:10
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence10.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence11.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence12.ts
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.0.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.1.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.2.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.3.ts"
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence13.ts
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence14.0.ts"
|
@ -0,0 +1,19 @@
|
|||||||
|
#EXTM3U
|
||||||
|
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES
|
||||||
|
#EXT-X-TARGETDURATION:4
|
||||||
|
#EXT-X-PART-INF:PART-TARGET=1.000000
|
||||||
|
#EXT-X-VERSION:3
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:10
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence10.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence11.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence12.ts
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.0.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.1.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.2.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.3.ts"
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence13.ts
|
||||||
|
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="fileSequence14.0.ts"
|
@ -0,0 +1,20 @@
|
|||||||
|
#EXTM3U
|
||||||
|
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES
|
||||||
|
#EXT-X-TARGETDURATION:4
|
||||||
|
#EXT-X-PART-INF:PART-TARGET=1.000000
|
||||||
|
#EXT-X-VERSION:3
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:10
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence10.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence11.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence12.ts
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.0.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.1.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.2.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence13.3.ts"
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence13.ts
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence14.0.ts"
|
||||||
|
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="fileSequence14.1.ts"
|
17
testdata/src/test/assets/media/m3u8/live_low_latency_media_can_block_reload_low_latency_next
vendored
Normal file
17
testdata/src/test/assets/media/m3u8/live_low_latency_media_can_block_reload_low_latency_next
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#EXTM3U
|
||||||
|
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES
|
||||||
|
#EXT-X-TARGETDURATION:4
|
||||||
|
#EXT-X-PART-INF:PART-TARGET=1.000000
|
||||||
|
#EXT-X-VERSION:3
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:10
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence10.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence11.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence12.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence13.ts
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence14.0.ts"
|
||||||
|
#EXT-X-PART:DURATION=1.00000,URI="fileSequence14.1.ts"
|
||||||
|
#EXT-X-PRELOAD-HINT:TYPE=PART,URI="fileSequence14.2.ts"
|
Loading…
x
Reference in New Issue
Block a user