Skip to content

Commit

Permalink
feat(HLS): Add support to _HLS_msn query param in LL streams (shaka-p…
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad committed Jun 7, 2023
1 parent 258962f commit 2ece86f
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 17 deletions.
34 changes: 28 additions & 6 deletions lib/hls/hls_parser.js
Expand Up @@ -342,10 +342,21 @@ shaka.hls.HlsParser = class {
async updateStream_(streamInfo) {
const manifestUri = streamInfo.absoluteMediaPlaylistUri;
const uriObj = new goog.Uri(manifestUri);
if (this.lowLatencyMode_ && streamInfo.canSkipSegments) {
// Enable delta updates. This will replace older segments with
// 'EXT-X-SKIP' tag in the media playlist.
uriObj.setQueryData(new goog.Uri.QueryData('_HLS_skip=YES'));
if (this.lowLatencyMode_) {
const queryData = new goog.Uri.QueryData();
if (streamInfo.canSkipSegments) {
// Enable delta updates. This will replace older segments with
// 'EXT-X-SKIP' tag in the media playlist.
queryData.add('_HLS_skip', 'YES');
}
if (streamInfo.lastMediaSequence >= 0) {
// Indicates that the server must hold the request until a Playlist
// contains a Media Segment with Media Sequence
queryData.add('_HLS_msn', String(streamInfo.lastMediaSequence + 1));
}
if (queryData.getCount()) {
uriObj.setQueryData(queryData);
}
}
const response =
await this.requestManifest_(uriObj.toString(), /* isPlaylist= */ true);
Expand Down Expand Up @@ -388,15 +399,17 @@ shaka.hls.HlsParser = class {
this.playerInterface_.newDrmInfo(stream);
}

const mediaSequenceNumber = shaka.hls.Utils.getFirstTagWithNameAsNumber(
playlist.tags, 'EXT-X-MEDIA-SEQUENCE', 0);
streamInfo.lastMediaSequence = mediaSequenceNumber;

const segments = this.createSegments_(
streamInfo.verbatimMediaPlaylistUri, playlist, stream.type,
stream.mimeType, mediaSequenceToStartTime, mediaVariables);

stream.segmentIndex.mergeAndEvict(
segments, this.presentationTimeline_.getSegmentAvailabilityStart());
if (segments.length) {
const mediaSequenceNumber = shaka.hls.Utils.getFirstTagWithNameAsNumber(
playlist.tags, 'EXT-X-MEDIA-SEQUENCE', 0);
const playlistStartTime = mediaSequenceToStartTime.get(
mediaSequenceNumber);
stream.segmentIndex.evict(playlistStartTime);
Expand Down Expand Up @@ -1941,6 +1954,7 @@ shaka.hls.HlsParser = class {
canSkipSegments: false,
hasEndList: false,
firstSequenceNumber: -1,
lastMediaSequence: -1,
loadedOnce: false,
};

Expand Down Expand Up @@ -1987,6 +2001,7 @@ shaka.hls.HlsParser = class {
streamInfo.hasEndList = realStreamInfo.hasEndList;
streamInfo.mediaSequenceToStartTime =
realStreamInfo.mediaSequenceToStartTime;
streamInfo.lastMediaSequence = realStreamInfo.lastMediaSequence;
streamInfo.loadedOnce = true;
stream.segmentIndex = realStream.segmentIndex;
stream.encrypted = realStream.encrypted;
Expand Down Expand Up @@ -2272,6 +2287,9 @@ shaka.hls.HlsParser = class {
const canSkipSegments = serverControlTag ?
serverControlTag.getAttribute('CAN-SKIP-UNTIL') != null : false;

const lastMediaSequence = shaka.hls.Utils.getFirstTagWithNameAsNumber(
playlist.tags, 'EXT-X-MEDIA-SEQUENCE', 0);

const stream = this.makeStreamObject_(codecs, type, language, primary, name,
channelsCount, closedCaptions, characteristics, forced, spatialAudio);
stream.segmentIndex = segmentIndex;
Expand All @@ -2290,6 +2308,7 @@ shaka.hls.HlsParser = class {
canSkipSegments,
hasEndList: false,
firstSequenceNumber: -1,
lastMediaSequence,
mediaSequenceToStartTime,
loadedOnce: false,
};
Expand Down Expand Up @@ -3603,6 +3622,7 @@ shaka.hls.HlsParser = class {
* canSkipSegments: boolean,
* hasEndList: boolean,
* firstSequenceNumber: number,
* lastMediaSequence: number,
* loadedOnce: boolean
* }}
*
Expand Down Expand Up @@ -3635,6 +3655,8 @@ shaka.hls.HlsParser = class {
* True if the stream has an EXT-X-ENDLIST tag.
* @property {number} firstSequenceNumber
* The sequence number of the first reference. Only calculated if needed.
* @property {number} lastMediaSequence
* The last media sequence seen.
* @property {boolean} loadedOnce
* True if the stream has been loaded at least once.
*/
Expand Down
36 changes: 25 additions & 11 deletions test/hls/hls_live_unit.js
Expand Up @@ -163,14 +163,28 @@ describe('HlsParser live', () => {
* @param {shaka.extern.Manifest} manifest
* @param {string} updatedMedia
* @param {Array=} updatedReferences
* @param {?number=} sequenceNumber
*/
async function testUpdate(manifest, updatedMedia, updatedReferences=null) {
async function testUpdate(manifest, updatedMedia, updatedReferences=null,
sequenceNumber=null) {
// Replace the entries with the updated values.
fakeNetEngine
.setResponseText('test:/video', updatedMedia)
.setResponseText('test:/redirected/video', updatedMedia)
.setResponseText('test:/video2', updatedMedia)
.setResponseText('test:/audio', updatedMedia);
if (sequenceNumber == null) {
fakeNetEngine
.setResponseText('test:/video', updatedMedia)
.setResponseText('test:/redirected/video', updatedMedia)
.setResponseText('test:/video2', updatedMedia)
.setResponseText('test:/audio', updatedMedia);
} else {
fakeNetEngine
.setResponseText('test:/video?_HLS_msn=' + sequenceNumber,
updatedMedia)
.setResponseText('test:/redirected/video?_HLS_msn=' + sequenceNumber,
updatedMedia)
.setResponseText('test:/video2?_HLS_msn=' + sequenceNumber,
updatedMedia)
.setResponseText('test:/audio?_HLS_msn=' + sequenceNumber,
updatedMedia);
}

await delayForUpdatePeriod();

Expand Down Expand Up @@ -906,7 +920,7 @@ describe('HlsParser live', () => {
'#EXTM3U\n',
'#EXT-X-TARGETDURATION:5\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXT-X-MEDIA-SEQUENCE:0\n',
'#EXT-X-MEDIA-SEQUENCE:1\n',
'#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=60.0\n',
'#EXT-X-SKIP:SKIPPED-SEGMENTS=1\n',
'#EXTINF:2,\n',
Expand All @@ -916,7 +930,7 @@ describe('HlsParser live', () => {
].join('');

fakeNetEngine.setResponseText(
'test:/video?_HLS_skip=YES', mediaWithSkippedSegments);
'test:/video?_HLS_skip=YES&_HLS_msn=1', mediaWithSkippedSegments);

playerInterface.isLowLatencyMode = () => true;

Expand All @@ -926,7 +940,7 @@ describe('HlsParser live', () => {
await delayForUpdatePeriod();

fakeNetEngine.expectRequest(
'test:/video?_HLS_skip=YES',
'test:/video?_HLS_skip=YES&_HLS_msn=1',
shaka.net.NetworkingEngine.RequestType.MANIFEST,
{type:
shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_PLAYLIST});
Expand Down Expand Up @@ -960,7 +974,7 @@ describe('HlsParser live', () => {
// and ref1 should be in the SegmentReferences list.
// ref3 should be appended to the SegmentReferences list.
await testUpdate(
manifest, mediaWithSkippedSegments, [ref1, ref2, ref3]);
manifest, mediaWithSkippedSegments, [ref1, ref2, ref3], 1);
});

it('skips older segments with discontinuity', async () => {
Expand Down Expand Up @@ -1025,7 +1039,7 @@ describe('HlsParser live', () => {
// and ref1,ref2 should be in the SegmentReferences list.
// ref3,ref4 should be appended to the SegmentReferences list.
await testUpdate(
manifest, mediaWithSkippedSegments2, [ref1, ref2, ref3, ref4]);
manifest, mediaWithSkippedSegments2, [ref1, ref2, ref3, ref4], 1);
});

it('updates encryption keys', async () => {
Expand Down

0 comments on commit 2ece86f

Please sign in to comment.