Skip to content

Commit 9b32a7a

Browse files
vanyaxkavelad
authored andcommitted
perf(HLS): skip merging known segments on live playlist updates (#9998)
This PR reduces redundant work on every live HLS playlist poll by avoiding a full segment array re-merge when no new segments have arrived - hls.js does this in a similar fashion in mergeDetails: https://github.com/video-dev/hls.js/blob/master/src/utils/level-helper.ts
1 parent 1824679 commit 9b32a7a

1 file changed

Lines changed: 34 additions & 14 deletions

File tree

lib/hls/hls_parser.js

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -589,9 +589,12 @@ shaka.hls.HlsParser = class {
589589
this.playerInterface_.newDrmInfo(stream);
590590
}
591591

592-
const {segments, bandwidth} = this.createSegments_(
592+
// On redirect the segment base URIs change, so all existing refs need to
593+
// be replaced with the freshly built ones that carry the redirected URI.
594+
const wasRedirected = response.uri !== response.originalUri;
595+
const {segments, totalCount, bandwidth} = this.createSegments_(
593596
playlist, mediaSequenceToStartTime, mediaVariables,
594-
streamInfo.getUris, streamInfo.type);
597+
streamInfo.getUris, streamInfo.type, /* isNewStream= */ wasRedirected);
595598
if (bandwidth) {
596599
stream.bandwidth = bandwidth;
597600
}
@@ -604,17 +607,21 @@ shaka.hls.HlsParser = class {
604607
}
605608
}
606609

607-
stream.segmentIndex.mergeAndEvict(
608-
segments, this.presentationTimeline_.getSegmentAvailabilityStart());
609610
if (segments.length) {
611+
stream.segmentIndex.mergeAndEvict(
612+
segments,
613+
this.presentationTimeline_.getSegmentAvailabilityStart());
614+
}
615+
if (totalCount) {
610616
const mediaSequenceNumber = shaka.hls.Utils.getFirstTagWithNameAsNumber(
611617
playlist.tags, 'EXT-X-MEDIA-SEQUENCE', 0);
612618
const skipTag = shaka.hls.Utils.getFirstTagWithName(
613619
playlist.tags, 'EXT-X-SKIP');
614620
const skippedSegments =
615621
skipTag ? Number(skipTag.getAttributeValue('SKIPPED-SEGMENTS')) : 0;
616622
const {nextMediaSequence, nextPart} =
617-
this.getNextMediaSequenceAndPart_(mediaSequenceNumber, segments);
623+
this.getNextMediaSequenceAndPart_(mediaSequenceNumber, segments,
624+
totalCount);
618625
streamInfo.nextMediaSequence = nextMediaSequence + skippedSegments;
619626
streamInfo.nextPart = nextPart;
620627
const playlistStartTime = mediaSequenceToStartTime.get(
@@ -625,10 +632,9 @@ shaka.hls.HlsParser = class {
625632
if (oldSegment) {
626633
streamInfo.minTimestamp = oldSegment.startTime;
627634

628-
const newestSegment = segments[segments.length - 1];
629-
goog.asserts.assert(newestSegment, 'Should have segments!');
630-
631-
streamInfo.maxTimestamp = newestSegment.endTime;
635+
if (segments.length) {
636+
streamInfo.maxTimestamp = segments[segments.length - 1].endTime;
637+
}
632638
}
633639

634640
// Once the last segment has been added to the playlist,
@@ -3238,7 +3244,8 @@ shaka.hls.HlsParser = class {
32383244
this.mediaSequenceToStartTimeByType_.get(type) : new Map();
32393245

32403246
const {segments, bandwidth} = this.createSegments_(
3241-
playlist, mediaSequenceToStartTime, variables, getUris, type);
3247+
playlist, mediaSequenceToStartTime, variables, getUris, type,
3248+
/* isNewStream= */ true);
32423249

32433250
if (!mimeType && type == shaka.util.ManifestParserUtils.ContentType.TEXT) {
32443251
mimeType = await this.guessMimeType_(type, codecs, segments);
@@ -3382,8 +3389,9 @@ shaka.hls.HlsParser = class {
33823389
* @return {{nextMediaSequence: number, nextPart:number}}}
33833390
* @private
33843391
*/
3385-
getNextMediaSequenceAndPart_(mediaSequenceNumber, segments) {
3386-
const currentMediaSequence = mediaSequenceNumber + segments.length - 1;
3392+
getNextMediaSequenceAndPart_(mediaSequenceNumber, segments,
3393+
totalCount = segments.length) {
3394+
const currentMediaSequence = mediaSequenceNumber + totalCount - 1;
33873395
let nextMediaSequence = currentMediaSequence;
33883396
let nextPart = -1;
33893397
if (!segments.length) {
@@ -4536,12 +4544,15 @@ shaka.hls.HlsParser = class {
45364544
* @param {!Map<string, string>} variables
45374545
* @param {function(): !Array<string>} getUris
45384546
* @param {string} type
4547+
* @param {boolean} isNewStream When true, segments contains all parsed refs.
4548+
* When false (live update), segments contains only newly seen refs.
45394549
* @return {{segments: !Array<!shaka.media.SegmentReference>,
4550+
* totalCount: number,
45404551
* bandwidth: (number|undefined)}}
45414552
* @private
45424553
*/
45434554
createSegments_(playlist, mediaSequenceToStartTime, variables,
4544-
getUris, type) {
4555+
getUris, type, isNewStream) {
45454556
/** @type {Array<!shaka.hls.Segment>} */
45464557
const hlsSegments = playlist.segments;
45474558
goog.asserts.assert(hlsSegments.length, 'Playlist should have segments!');
@@ -4582,6 +4593,9 @@ shaka.hls.HlsParser = class {
45824593
/** @type {!Array<!shaka.media.SegmentReference>} */
45834594
const references = [];
45844595

4596+
/** @type {!Array<!shaka.media.SegmentReference>} */
4597+
const newReferences = [];
4598+
45854599
let previousReference = null;
45864600

45874601
/** @type {!Array<{bitrate: number, duration: number}>} */
@@ -4651,6 +4665,8 @@ shaka.hls.HlsParser = class {
46514665
}
46524666
}
46534667

4668+
const isNewSegment =
4669+
isNewStream || !mediaSequenceToStartTime.has(position);
46544670
mediaSequenceToStartTime.set(position, startTime);
46554671

46564672

@@ -4711,6 +4727,9 @@ shaka.hls.HlsParser = class {
47114727
// method.
47124728
} else {
47134729
references.push(reference);
4730+
if (isNewSegment) {
4731+
newReferences.push(reference);
4732+
}
47144733
}
47154734
}
47164735
}
@@ -4813,7 +4832,8 @@ shaka.hls.HlsParser = class {
48134832
}
48144833

48154834
return {
4816-
segments: references,
4835+
segments: isNewStream ? references : newReferences,
4836+
totalCount: references.length,
48174837
bandwidth,
48184838
};
48194839
}

0 commit comments

Comments
 (0)