From 14e61a7368ddbd66c4b10f3b0475840cc50512bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Velad=20Galv=C3=A1n?= Date: Tue, 10 May 2022 22:19:46 +0200 Subject: [PATCH] feat(hls): Add support for EXT-X-GAP (#4208) Related to 42eecc84 Closes #1308 --- lib/hls/hls_parser.js | 12 +++++++++++- lib/media/streaming_engine.js | 7 +++++++ lib/player.js | 23 ++++++++++++++++++----- lib/util/error.js | 6 ++++++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/lib/hls/hls_parser.js b/lib/hls/hls_parser.js index 9f667f73cd..82d62e4fd7 100644 --- a/lib/hls/hls_parser.js +++ b/lib/hls/hls_parser.js @@ -1902,6 +1902,11 @@ shaka.hls.HlsParser = class { const pAbsoluteUri = shaka.hls.Utils.constructAbsoluteUri( absoluteMediaPlaylistUri, pUri); + let partialStatus = shaka.media.SegmentReference.Status.AVAILABLE; + if (item.getAttributeValue('GAP') == 'YES') { + partialStatus = shaka.media.SegmentReference.Status.MISSING; + } + const partial = new shaka.media.SegmentReference( pStartTime, pEndTime, @@ -1911,7 +1916,12 @@ shaka.hls.HlsParser = class { initSegmentReference, /* timestampOffset= */ 0, // This value is ignored in sequence mode. /* appendWindowStart= */ 0, - /* appendWindowEnd= */ Infinity); + /* appendWindowEnd= */ Infinity, + /* partialReferences= */ [], + /* tilesLayout= */ '', + /* tileDuration= */ null, + /* syncTime= */ null, + partialStatus); partialSegmentRefs.push(partial); } // for-loop of hlsSegment.partialSegments } else { diff --git a/lib/media/streaming_engine.js b/lib/media/streaming_engine.js index 3d79bdc645..ef55a7fdad 100644 --- a/lib/media/streaming_engine.js +++ b/lib/media/streaming_engine.js @@ -1215,6 +1215,13 @@ shaka.media.StreamingEngine = class { mediaState.performingUpdate = true; try { + if (reference.getStatus() == + shaka.media.SegmentReference.Status.MISSING) { + throw new shaka.util.Error( + shaka.util.Error.Severity.RECOVERABLE, + shaka.util.Error.Category.NETWORK, + shaka.util.Error.Code.SEGMENT_MISSING); + } await this.initSourceBuffer_(mediaState, reference); this.destroyer_.ensureNotDestroyed(); if (this.fatalError_) { diff --git a/lib/player.js b/lib/player.js index 45a4e8f61f..4e42b7f563 100644 --- a/lib/player.js +++ b/lib/player.js @@ -5521,17 +5521,30 @@ shaka.Player = class extends shaka.util.FakeEventTarget { * @private */ tryToRecoverFromError_(error) { - if (error.code !== shaka.util.Error.Code.HTTP_ERROR || - error.category !== shaka.util.Error.Category.NETWORK) { + if ((error.code != shaka.util.Error.Code.HTTP_ERROR && + error.code != shaka.util.Error.Code.SEGMENT_MISSING) || + error.category != shaka.util.Error.Category.NETWORK) { return false; } - const maxDisabledTime = this.config_.streaming.maxDisabledTime; + let maxDisabledTime = this.config_.streaming.maxDisabledTime; if (maxDisabledTime == 0) { - return false; + if (error.code == shaka.util.Error.Code.SEGMENT_MISSING) { + // Spec: https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-6.3.3 + // The client SHOULD NOT attempt to load Media Segments that have been + // marked with an EXT-X-GAP tag, or to load Partial Segments with a + // GAP=YES attribute. Instead, clients are encouraged to look for + // another Variant Stream of the same Rendition which does not have the + // same gap, and play that instead. + maxDisabledTime = 1; + } else { + return false; + } } - shaka.log.debug('Recoverable NETWORK HTTP_ERROR, trying to recover...'); + if (error.code == shaka.util.Error.Code.HTTP_ERROR) { + shaka.log.debug('Recoverable NETWORK HTTP_ERROR, trying to recover...'); + } // Obtain the active variant and disable it from manifest variants const activeVariantTrack = this.getVariantTracks().find((t) => t.active); diff --git a/lib/util/error.js b/lib/util/error.js index 8185ced008..f15eddce1c 100644 --- a/lib/util/error.js +++ b/lib/util/error.js @@ -267,6 +267,12 @@ shaka.util.Error.Code = { */ 'ATTEMPTS_EXHAUSTED': 1010, + /** + * The segment is missing. + *
error.data[0] is the URI. + */ + 'SEGMENT_MISSING': 1011, + /** The text parser failed to parse a text stream due to an invalid header. */ 'INVALID_TEXT_HEADER': 2000,