Skip to content

Commit

Permalink
fix(hls): Fix X-PRELOAD-HINT failure with LL mode off (#4212)
Browse files Browse the repository at this point in the history
When LL mode was off, the HLS parser would throw on X-PRELOAD-HINT
segments.  The logic for requiring EXTINF has been corrected so that
this will not happen.

Closes #4185
  • Loading branch information
joeyparrish committed May 17, 2022
1 parent 4b3e3b1 commit 5167717
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 11 deletions.
34 changes: 23 additions & 11 deletions lib/hls/hls_parser.js
Expand Up @@ -1739,8 +1739,8 @@ shaka.hls.HlsParser = class {
* @param {number} timestampOffset
* @param {!Map.<string, string>} variables
* @param {string} absoluteMediaPlaylistUri
* @return {!shaka.media.SegmentReference}
* @param {string} type
* @return {shaka.media.SegmentReference}
* @private
*/
createSegmentReference_(
Expand All @@ -1756,9 +1756,23 @@ shaka.hls.HlsParser = class {
let startByte = 0;
let endByte = null;

if (!extinfTag) {
if (hlsSegment.partialSegments.length == 0) {
// EXTINF tag must be available if the segment has no partial segments.
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.HLS_REQUIRED_TAG_MISSING, 'EXTINF');
} else if (!this.lowLatencyMode_) {
// Without EXTINF and without low-latency mode, partial segments get
// ignored.
return null;
}
}

// Create SegmentReferences for the partial segments.
const partialSegmentRefs = [];
if (this.lowLatencyMode_ && hlsSegment.partialSegments.length) {
if (this.lowLatencyMode_) {
for (let i = 0; i < hlsSegment.partialSegments.length; i++) {
const item = hlsSegment.partialSegments[i];
const pPreviousReference = i == 0 ?
Expand Down Expand Up @@ -1799,14 +1813,6 @@ shaka.hls.HlsParser = class {
/* appendWindowEnd= */ Infinity);
partialSegmentRefs.push(partial);
} // for-loop of hlsSegment.partialSegments
} else {
// EXTINF tag must be available if the segment has no partial segments.
if (!extinfTag) {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.HLS_REQUIRED_TAG_MISSING, 'EXTINF');
}
}

// If the segment has EXTINF tag, set the segment's end time, start byte
Expand All @@ -1825,6 +1831,7 @@ shaka.hls.HlsParser = class {
} else {
endTime = partialSegmentRefs[partialSegmentRefs.length - 1].endTime;
}

// If the segment has EXT-X-BYTERANGE tag, set the start byte and end byte
// base on the byterange information. If segment has no EXT-X-BYTERANGE tag
// and has partial segments, set the start byte and end byte base on the
Expand Down Expand Up @@ -2054,7 +2061,9 @@ shaka.hls.HlsParser = class {
playlist.absoluteUri,
type);

references.push(reference);
if (reference) {
references.push(reference);
}
} else if (!this.lowLatencyMode_) {
// If a segment has no extinfTag, it must contain partial segments.
shaka.log.alwaysWarn('Low-latency HLS live stream detected, but ' +
Expand Down Expand Up @@ -2233,6 +2242,9 @@ shaka.hls.HlsParser = class {
variables,
/* absoluteMediaPlaylistUri= */ '',
type);
goog.asserts.assert(
segmentRef != null, 'Segment reference should not be null!');

// If we are updating the manifest, we can usually skip fetching the segment
// by examining the references we already have. This won't be possible if
// there was some kind of lag or delay updating the manifest on the server,
Expand Down
26 changes: 26 additions & 0 deletions test/hls/hls_live_unit.js
Expand Up @@ -699,6 +699,32 @@ describe('HlsParser live', () => {
ManifestParser.verifySegmentIndex(video, [ref, ref2]);
});

// Test for https://github.com/shaka-project/shaka-player/issues/4185
it('does not fail on preload hints with LL mode off', async () => {
// LL mode must be off for this test!
playerInterface.isLowLatencyMode = () => false;

const mediaWithPartialSegments = [
'#EXTM3U\n',
'#EXT-X-TARGETDURATION:5\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:4,\n',
'main.mp4\n',
'#EXT-X-PART:DURATION=2,URI="partial.mp4",BYTERANGE=210@0\n',
'#EXT-X-PRELOAD-HINT:TYPE=PART,URI="partial.mp4",BYTERANGE-START=210\n',
].join('');

fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/video', mediaWithPartialSegments)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData)
.setResponseValue('test:/partial.mp4', segmentData);

// If this throws, the test fails. Otherwise, it passes.
await parser.start('test:/master', playerInterface);
});

describe('update', () => {
it('adds new segments when they appear', async () => {
const ref1 = ManifestParser.makeReference('test:/main.mp4', 2, 4);
Expand Down

0 comments on commit 5167717

Please sign in to comment.