Skip to content

Commit

Permalink
Determine canSkip based on age of last playlist request (#6300)
Browse files Browse the repository at this point in the history
(cherry picked from commit d802d60)
  • Loading branch information
mcintyrehh authored and robwalch committed Apr 10, 2024
1 parent 5611274 commit 1c77fde
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 22 deletions.
2 changes: 1 addition & 1 deletion api-extractor/report/hls.js.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ export class BasePlaylistController implements NetworkComponentAPI {
// (undocumented)
stopLoad(): void;
// (undocumented)
protected switchParams(playlistUri: string, previous: LevelDetails | undefined): HlsUrlParameters | undefined;
protected switchParams(playlistUri: string, previous: LevelDetails | undefined, current: LevelDetails | undefined): HlsUrlParameters | undefined;
// (undocumented)
protected timer: number;
// (undocumented)
Expand Down
6 changes: 5 additions & 1 deletion src/controller/audio-track-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,11 @@ class AudioTrackController extends BasePlaylistController {
if (trackLoaded) {
return;
}
const hlsUrlParameters = this.switchParams(track.url, lastTrack?.details);
const hlsUrlParameters = this.switchParams(
track.url,
lastTrack?.details,
track.details,
);
this.loadPlaylist(hlsUrlParameters);
}

Expand Down
10 changes: 4 additions & 6 deletions src/controller/base-playlist-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
protected switchParams(
playlistUri: string,
previous: LevelDetails | undefined,
current: LevelDetails | undefined,
): HlsUrlParameters | undefined {
const renditionReports = previous?.renditionReports;
if (renditionReports) {
Expand Down Expand Up @@ -92,11 +93,8 @@ export default class BasePlaylistController implements NetworkComponentAPI {
part += 1;
}
}
return new HlsUrlParameters(
msn,
part >= 0 ? part : undefined,
HlsSkip.No,
);
const skip = current && getSkipValue(current);
return new HlsUrlParameters(msn, part >= 0 ? part : undefined, skip);
}
}
}
Expand Down Expand Up @@ -298,7 +296,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
msn?: number,
part?: number,
): HlsUrlParameters {
let skip = getSkipValue(details, msn);
let skip = getSkipValue(details);
if (previousDeliveryDirectives?.skip && details.deltaUpdateFailed) {
msn = previousDeliveryDirectives.msn;
part = previousDeliveryDirectives.part;
Expand Down
6 changes: 5 additions & 1 deletion src/controller/level-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,11 @@ export default class LevelController extends BasePlaylistController {
const levelDetails = level.details;
if (!levelDetails || levelDetails.live) {
// level not retrieved yet, or live playlist we need to (re)load it
const hlsUrlParameters = this.switchParams(level.uri, lastLevel?.details);
const hlsUrlParameters = this.switchParams(
level.uri,
lastLevel?.details,
levelDetails,
);
this.loadPlaylist(hlsUrlParameters);
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/controller/subtitle-track-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,11 @@ class SubtitleTrackController extends BasePlaylistController {
type,
url,
});
const hlsUrlParameters = this.switchParams(track.url, lastTrack?.details);
const hlsUrlParameters = this.switchParams(
track.url,
lastTrack?.details,
track.details,
);
this.loadPlaylist(hlsUrlParameters);
}

Expand Down
11 changes: 7 additions & 4 deletions src/types/level.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,13 @@ export const enum HlsSkip {
v2 = 'v2',
}

export function getSkipValue(details: LevelDetails, msn?: number): HlsSkip {
const { canSkipUntil, canSkipDateRanges, endSN } = details;
const snChangeGoal = msn !== undefined ? msn - endSN : 0;
if (canSkipUntil && snChangeGoal < canSkipUntil) {
export function getSkipValue(details: LevelDetails): HlsSkip {
const { canSkipUntil, canSkipDateRanges, age } = details;
// A Client SHOULD NOT request a Playlist Delta Update unless it already
// has a version of the Playlist that is no older than one-half of the Skip Boundary.
// @see: https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-6.3.7
const playlistRecentEnough = age < canSkipUntil / 2;
if (canSkipUntil && playlistRecentEnough) {
if (canSkipDateRanges) {
return HlsSkip.v2;
}
Expand Down
23 changes: 15 additions & 8 deletions tests/unit/controller/level-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ type LevelControllerTestable = Omit<LevelController, 'onManifestLoaded'> & {
switchParams: (
playlistUri: string,
previous: LevelDetails | undefined,
current: LevelDetails | undefined,
) => void;
redundantFailover: (levelIndex: number) => void;
};
Expand Down Expand Up @@ -582,47 +583,51 @@ vfrag3.m4v
#EXT-X-RENDITION-REPORT:URI="chunklist_vfrag100.m3u8",LAST-MSN=4,LAST-PART=1`;

it('returns RENDITION-REPORT query values for the selected playlist URI', function () {
const levelDetails = M3U8Parser.parseLevelPlaylist(
const previousLevelDetails = M3U8Parser.parseLevelPlaylist(
mediaPlaylist,
'http://example.com/playlist.m3u8?abc=deg',
0,
PlaylistLevelType.MAIN,
0,
{},
);
const mockCurrentDetails = undefined;
const selectedUri = 'http://example.com/chunklist_vfrag1500.m3u8';
const hlsUrlParameters = levelController.switchParams(
selectedUri,
levelDetails,
previousLevelDetails,
mockCurrentDetails,
);
expect(hlsUrlParameters).to.have.property('msn').which.equals(4);
expect(hlsUrlParameters).to.have.property('part').which.equals(1);
expect(hlsUrlParameters).to.have.property('skip').which.equals('');
expect(hlsUrlParameters).to.have.property('skip').to.be.undefined;
});

it('returns RENDITION-REPORT query values for the selected playlist URI with additional query params', function () {
const levelDetails = M3U8Parser.parseLevelPlaylist(
const previousDetails = M3U8Parser.parseLevelPlaylist(
mediaPlaylist,
'http://example.com/playlist.m3u8?abc=deg',
0,
PlaylistLevelType.MAIN,
0,
{},
);
const mockCurrentDetails = undefined;
const selectedUriWithQuery =
'http://example.com/chunklist_vfrag1500.m3u8?abc=123';
const hlsUrlParameters = levelController.switchParams(
selectedUriWithQuery,
levelDetails,
previousDetails,
mockCurrentDetails,
);
expect(hlsUrlParameters).to.not.be.undefined;
expect(hlsUrlParameters).to.have.property('msn').which.equals(4);
expect(hlsUrlParameters).to.have.property('part').which.equals(1);
expect(hlsUrlParameters).to.have.property('skip').which.equals('');
expect(hlsUrlParameters).to.have.property('skip').to.be.undefined;
});

it('returns RENDITION-REPORT exact URI match over partial match for playlist URIs with additional query params', function () {
const levelDetails = M3U8Parser.parseLevelPlaylist(
const previousLevelDetails = M3U8Parser.parseLevelPlaylist(
`#EXTM3U
#EXT-X-VERSION:7
#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES
Expand All @@ -644,11 +649,13 @@ vfrag3.m4v
0,
{},
);
const mockCurrentDetails = undefined;
const selectedUriWithQuery =
'http://example.com/chunklist.m3u8?token=123';
const hlsUrlParameters = levelController.switchParams(
selectedUriWithQuery,
levelDetails,
previousLevelDetails,
mockCurrentDetails,
);
expect(hlsUrlParameters).to.not.be.undefined;
expect(hlsUrlParameters).to.have.property('msn').which.equals(6);
Expand Down

0 comments on commit 1c77fde

Please sign in to comment.