mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Reload HLS media playlist when merging delta update fails
Issue: #5011 PiperOrigin-RevId: 350550204
This commit is contained in:
parent
6dec83238b
commit
6e8af81ddc
@ -603,11 +603,16 @@ public final class DefaultHlsPlaylistTracker
|
|||||||
loadDurationMs,
|
loadDurationMs,
|
||||||
loadable.bytesLoaded());
|
loadable.bytesLoaded());
|
||||||
boolean isBlockingRequest = loadable.getUri().getQueryParameter(BLOCK_MSN_PARAM) != null;
|
boolean isBlockingRequest = loadable.getUri().getQueryParameter(BLOCK_MSN_PARAM) != null;
|
||||||
if (isBlockingRequest && error instanceof HttpDataSource.InvalidResponseCodeException) {
|
boolean deltaUpdateFailed = error instanceof HlsPlaylistParser.DeltaUpdateException;
|
||||||
int responseCode = ((HttpDataSource.InvalidResponseCodeException) error).responseCode;
|
if (isBlockingRequest || deltaUpdateFailed) {
|
||||||
if (responseCode == 400 || responseCode == 503) {
|
int responseCode = Integer.MAX_VALUE;
|
||||||
// Intercept bad request and service unavailable to force a full, non-blocking request
|
if (error instanceof HttpDataSource.InvalidResponseCodeException) {
|
||||||
// (see RFC 8216, section 6.2.5.2).
|
responseCode = ((HttpDataSource.InvalidResponseCodeException) error).responseCode;
|
||||||
|
}
|
||||||
|
if (deltaUpdateFailed || responseCode == 400 || responseCode == 503) {
|
||||||
|
// Intercept failed delta updates and blocking requests producing a Bad Request (400) and
|
||||||
|
// 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();
|
earliestNextLoadTimeMs = SystemClock.elapsedRealtime();
|
||||||
loadPlaylist();
|
loadPlaylist();
|
||||||
castNonNull(eventDispatcher)
|
castNonNull(eventDispatcher)
|
||||||
|
@ -69,6 +69,9 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
|
|||||||
*/
|
*/
|
||||||
public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlaylist> {
|
public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlaylist> {
|
||||||
|
|
||||||
|
/** Exception thrown when merging a delta update fails. */
|
||||||
|
public static final class DeltaUpdateException extends IOException {}
|
||||||
|
|
||||||
private static final String LOG_TAG = "HlsPlaylistParser";
|
private static final String LOG_TAG = "HlsPlaylistParser";
|
||||||
|
|
||||||
private static final String PLAYLIST_HEADER = "#EXTM3U";
|
private static final String PLAYLIST_HEADER = "#EXTM3U";
|
||||||
@ -744,8 +747,10 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
checkState(previousMediaPlaylist != null && segments.isEmpty());
|
checkState(previousMediaPlaylist != null && segments.isEmpty());
|
||||||
int startIndex = (int) (mediaSequence - castNonNull(previousMediaPlaylist).mediaSequence);
|
int startIndex = (int) (mediaSequence - castNonNull(previousMediaPlaylist).mediaSequence);
|
||||||
int endIndex = startIndex + skippedSegmentCount;
|
int endIndex = startIndex + skippedSegmentCount;
|
||||||
if (startIndex >= 0 && endIndex <= previousMediaPlaylist.segments.size()) {
|
if (startIndex < 0 || endIndex > previousMediaPlaylist.segments.size()) {
|
||||||
// Merge only if all skipped segments are available in the previous playlist.
|
// Throw to force a reload if not all segments are available in the previous playlist.
|
||||||
|
throw new DeltaUpdateException();
|
||||||
|
}
|
||||||
for (int i = startIndex; i < endIndex; i++) {
|
for (int i = startIndex; i < endIndex; i++) {
|
||||||
Segment segment = previousMediaPlaylist.segments.get(i);
|
Segment segment = previousMediaPlaylist.segments.get(i);
|
||||||
if (mediaSequence != previousMediaPlaylist.mediaSequence) {
|
if (mediaSequence != previousMediaPlaylist.mediaSequence) {
|
||||||
@ -774,7 +779,6 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|
|||||||
}
|
}
|
||||||
segmentMediaSequence++;
|
segmentMediaSequence++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if (line.startsWith(TAG_KEY)) {
|
} else if (line.startsWith(TAG_KEY)) {
|
||||||
String method = parseStringAttr(line, REGEX_METHOD, variableDefinitions);
|
String method = parseStringAttr(line, REGEX_METHOD, variableDefinitions);
|
||||||
String keyFormat =
|
String keyFormat =
|
||||||
|
@ -37,7 +37,6 @@ import okhttp3.mockwebserver.MockWebServer;
|
|||||||
import okio.Buffer;
|
import okio.Buffer;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
@ -50,6 +49,8 @@ public class DefaultHlsPlaylistTrackerTest {
|
|||||||
"media/m3u8/live_low_latency_master_media_uri_with_param";
|
"media/m3u8/live_low_latency_master_media_uri_with_param";
|
||||||
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL =
|
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL =
|
||||||
"media/m3u8/live_low_latency_media_can_skip_until";
|
"media/m3u8/live_low_latency_media_can_skip_until";
|
||||||
|
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL_FULL_RELOAD_AFTER_ERROR =
|
||||||
|
"media/m3u8/live_low_latency_media_can_skip_until_full_reload_after_error";
|
||||||
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_DATERANGES =
|
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_DATERANGES =
|
||||||
"media/m3u8/live_low_latency_media_can_skip_dateranges";
|
"media/m3u8/live_low_latency_media_can_skip_dateranges";
|
||||||
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_SKIPPED =
|
private static final String SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_SKIPPED =
|
||||||
@ -168,18 +169,21 @@ public class DefaultHlsPlaylistTrackerTest {
|
|||||||
assertThat(mergedPlaylist.segments.get(1).relativeStartTimeUs).isEqualTo(4000000);
|
assertThat(mergedPlaylist.segments.get(1).relativeStartTimeUs).isEqualTo(4000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore // Test disabled because playlist delta updates are temporarily disabled.
|
|
||||||
@Test
|
@Test
|
||||||
public void start_playlistCanSkip_missingSegments_correctedMediaSequence()
|
public void start_playlistCanSkip_missingSegments_reloadsWithoutSkipping()
|
||||||
throws IOException, TimeoutException, InterruptedException {
|
throws IOException, TimeoutException, InterruptedException {
|
||||||
List<HttpUrl> httpUrls =
|
List<HttpUrl> httpUrls =
|
||||||
enqueueWebServerResponses(
|
enqueueWebServerResponses(
|
||||||
new String[] {
|
new String[] {
|
||||||
"/master.m3u8", "/media0/playlist.m3u8", "/media0/playlist.m3u8?_HLS_skip=YES"
|
"/master.m3u8",
|
||||||
|
"/media0/playlist.m3u8",
|
||||||
|
"/media0/playlist.m3u8?_HLS_skip=YES",
|
||||||
|
"/media0/playlist.m3u8"
|
||||||
},
|
},
|
||||||
getMockResponse(SAMPLE_M3U8_LIVE_MASTER),
|
getMockResponse(SAMPLE_M3U8_LIVE_MASTER),
|
||||||
getMockResponse(SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL),
|
getMockResponse(SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL),
|
||||||
getMockResponse(SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_SKIPPED_MEDIA_SEQUENCE_NO_OVERLAPPING));
|
getMockResponse(SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_SKIPPED_MEDIA_SEQUENCE_NO_OVERLAPPING),
|
||||||
|
getMockResponse(SAMPLE_M3U8_LIVE_MEDIA_CAN_SKIP_UNTIL_FULL_RELOAD_AFTER_ERROR));
|
||||||
|
|
||||||
List<HlsMediaPlaylist> mediaPlaylists =
|
List<HlsMediaPlaylist> mediaPlaylists =
|
||||||
runPlaylistTrackerAndCollectMediaPlaylists(
|
runPlaylistTrackerAndCollectMediaPlaylists(
|
||||||
@ -192,8 +196,8 @@ public class DefaultHlsPlaylistTrackerTest {
|
|||||||
assertThat(initialPlaylistWithAllSegments.mediaSequence).isEqualTo(10);
|
assertThat(initialPlaylistWithAllSegments.mediaSequence).isEqualTo(10);
|
||||||
assertThat(initialPlaylistWithAllSegments.segments).hasSize(6);
|
assertThat(initialPlaylistWithAllSegments.segments).hasSize(6);
|
||||||
HlsMediaPlaylist mergedPlaylist = mediaPlaylists.get(1);
|
HlsMediaPlaylist mergedPlaylist = mediaPlaylists.get(1);
|
||||||
assertThat(mergedPlaylist.mediaSequence).isEqualTo(22);
|
assertThat(mergedPlaylist.mediaSequence).isEqualTo(20);
|
||||||
assertThat(mergedPlaylist.segments).hasSize(4);
|
assertThat(mergedPlaylist.segments).hasSize(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=24,CAN-BLOCK-RELOAD=YES
|
#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=24,CAN-BLOCK-RELOAD=YES
|
||||||
#EXT-X-TARGETDURATION:4
|
#EXT-X-TARGETDURATION:4
|
||||||
#EXT-X-VERSION:3
|
#EXT-X-VERSION:3
|
||||||
#EXT-X-SKIP:SKIPPED-SEGMENTS=2
|
|
||||||
#EXT-X-MEDIA-SEQUENCE:12
|
#EXT-X-MEDIA-SEQUENCE:12
|
||||||
|
#EXT-X-SKIP:SKIPPED-SEGMENTS=2
|
||||||
#EXTINF:4.00000,
|
#EXTINF:4.00000,
|
||||||
fileSequence14.ts
|
fileSequence14.ts
|
||||||
#EXTINF:4.00000,
|
#EXTINF:4.00000,
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
#EXTM3U
|
||||||
|
#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=24
|
||||||
|
#EXT-X-TARGETDURATION:4
|
||||||
|
#EXT-X-VERSION:3
|
||||||
|
#EXT-X-MEDIA-SEQUENCE:20
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence20.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence21.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence22.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence23.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence24.ts
|
||||||
|
#EXTINF:4.00000,
|
||||||
|
fileSequence25.ts
|
Loading…
x
Reference in New Issue
Block a user