Refresh HlsMediaPlaylist
with delivery directives when possible
When refreshing the media playlist, we should try to request via a url with [delivery directives](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-6.2.5). However, when initially loading the media playlist or when the last loading with delivery directives encountered an error, we should not allow using those directives. PiperOrigin-RevId: 629060177
This commit is contained in:
parent
b0e48175f0
commit
e180e263a5
@ -225,7 +225,7 @@ public final class DefaultHlsPlaylistTracker
|
||||
|
||||
@Override
|
||||
public void refreshPlaylist(Uri url) {
|
||||
playlistBundles.get(url).loadPlaylist();
|
||||
playlistBundles.get(url).loadPlaylist(/* allowDeliveryDirectives= */ true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -275,7 +275,7 @@ public final class DefaultHlsPlaylistTracker
|
||||
// We don't need to load the playlist again. We can use the same result.
|
||||
primaryBundle.processLoadedPlaylist((HlsMediaPlaylist) result, loadEventInfo);
|
||||
} else {
|
||||
primaryBundle.loadPlaylist();
|
||||
primaryBundle.loadPlaylist(/* allowDeliveryDirectives= */ false);
|
||||
}
|
||||
loadErrorHandlingPolicy.onLoadTaskConcluded(loadable.loadTaskId);
|
||||
eventDispatcher.loadCompleted(loadEventInfo, C.DATA_TYPE_MANIFEST);
|
||||
@ -552,8 +552,8 @@ public final class DefaultHlsPlaylistTracker
|
||||
|| lastSnapshotLoadMs + snapshotValidityDurationMs > currentTimeMs;
|
||||
}
|
||||
|
||||
public void loadPlaylist() {
|
||||
loadPlaylistInternal(playlistUrl);
|
||||
public void loadPlaylist(boolean allowDeliveryDirectives) {
|
||||
loadPlaylistInternal(allowDeliveryDirectives ? getMediaPlaylistUriForReload() : playlistUrl);
|
||||
}
|
||||
|
||||
public void maybeThrowPlaylistRefreshError() throws IOException {
|
||||
@ -642,7 +642,7 @@ public final class DefaultHlsPlaylistTracker
|
||||
// Service Unavailable (503). In such cases, force a full, non-blocking request (see RFC
|
||||
// 8216, section 6.2.5.2 and 6.3.7).
|
||||
earliestNextLoadTimeMs = SystemClock.elapsedRealtime();
|
||||
loadPlaylist();
|
||||
loadPlaylist(/* allowDeliveryDirectives= */ false);
|
||||
castNonNull(eventDispatcher)
|
||||
.loadError(loadEventInfo, loadable.type, error, /* wasCanceled= */ true);
|
||||
return Loader.DONT_RETRY;
|
||||
|
@ -78,6 +78,9 @@ public class DefaultHlsPlaylistTrackerTest {
|
||||
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_NEXT2 =
|
||||
"media/m3u8/live_low_latency_media_can_block_reload_low_latency_full_segment_next2";
|
||||
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";
|
||||
@ -446,6 +449,80 @@ public class DefaultHlsPlaylistTrackerTest {
|
||||
assertThat(mediaPlaylists.get(1).trailingParts).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
start_refreshPlaylistWithAllowingDeliveryDirectives_requestWithCorrectDeliveryDirectives()
|
||||
throws Exception {
|
||||
List<HttpUrl> httpUrls =
|
||||
enqueueWebServerResponses(
|
||||
new String[] {
|
||||
"/multivariant.m3u8",
|
||||
"/media0/playlist.m3u8",
|
||||
"/media0/playlist.m3u8?_HLS_msn=14&_HLS_part=0",
|
||||
"/media0/playlist.m3u8?_HLS_msn=14&_HLS_part=1"
|
||||
},
|
||||
getMockResponse(SAMPLE_M3U8_LIVE_MULTIVARIANT),
|
||||
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),
|
||||
getMockResponse(
|
||||
SAMPLE_M3U8_LIVE_MEDIA_CAN_BLOCK_RELOAD_LOW_LATENCY_FULL_SEGMENT_NEXT2));
|
||||
|
||||
DefaultHlsPlaylistTracker defaultHlsPlaylistTracker =
|
||||
new DefaultHlsPlaylistTracker(
|
||||
dataType -> new DefaultHttpDataSource.Factory().createDataSource(),
|
||||
new DefaultLoadErrorHandlingPolicy(),
|
||||
new DefaultHlsPlaylistParserFactory());
|
||||
List<HlsMediaPlaylist> mediaPlaylists = new ArrayList<>();
|
||||
AtomicInteger playlistCounter = new AtomicInteger();
|
||||
AtomicReference<TimeoutException> playlistRefreshExceptionRef = new AtomicReference<>();
|
||||
defaultHlsPlaylistTracker.addListener(
|
||||
new HlsPlaylistTracker.PlaylistEventListener() {
|
||||
@Override
|
||||
public void onPlaylistChanged() {
|
||||
// Upon the first call of onPlaylistChanged(), we call refreshPlaylist(Uri) on the
|
||||
// same url.
|
||||
defaultHlsPlaylistTracker.refreshPlaylist(
|
||||
defaultHlsPlaylistTracker.getMultivariantPlaylist().mediaPlaylistUrls.get(0));
|
||||
try {
|
||||
// Make sure that playlist reload triggered by refreshPlaylist(Uri) call comes before
|
||||
// the one triggered by the regular scheduling, to ensure the playlists to be
|
||||
// verified are in the expected order.
|
||||
RobolectricUtil.runMainLooperUntil(() -> playlistCounter.get() >= 2);
|
||||
} catch (TimeoutException e) {
|
||||
playlistRefreshExceptionRef.set(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPlaylistError(
|
||||
Uri url, LoadErrorHandlingPolicy.LoadErrorInfo loadErrorInfo, boolean forceRetry) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
defaultHlsPlaylistTracker.start(
|
||||
Uri.parse(mockWebServer.url("/multivariant.m3u8").toString()),
|
||||
new MediaSourceEventListener.EventDispatcher(),
|
||||
mediaPlaylist -> {
|
||||
mediaPlaylists.add(mediaPlaylist);
|
||||
playlistCounter.addAndGet(1);
|
||||
});
|
||||
RobolectricUtil.runMainLooperUntil(() -> playlistCounter.get() >= 3);
|
||||
defaultHlsPlaylistTracker.stop();
|
||||
|
||||
assertThat(playlistRefreshExceptionRef.get()).isNull();
|
||||
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);
|
||||
assertThat(mediaPlaylists.get(2).mediaSequence).isEqualTo(10);
|
||||
assertThat(mediaPlaylists.get(2).segments).hasSize(4);
|
||||
assertThat(mediaPlaylists.get(2).trailingParts).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void start_httpBadRequest_forcesFullNonBlockingPlaylistRequest()
|
||||
throws IOException, TimeoutException, InterruptedException {
|
||||
|
@ -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-PART:DURATION=1.00000,URI="fileSequence14.1.ts"
|
Loading…
x
Reference in New Issue
Block a user