Skip to content

Commit

Permalink
Retry audio and subtitle playlist loading after failure on level swit…
Browse files Browse the repository at this point in the history
…ch when no alternate is available
  • Loading branch information
robwalch committed Feb 25, 2023
1 parent 90984f9 commit 981875c
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 24 deletions.
6 changes: 3 additions & 3 deletions api-extractor/report/hls.js.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,9 @@ export class BasePlaylistController implements NetworkComponentAPI {
// (undocumented)
protected requestScheduled: number;
// (undocumented)
protected shouldLoadPlaylist(playlist: Level | MediaPlaylist): boolean;
protected shouldLoadPlaylist(playlist: Level | MediaPlaylist | null | undefined): boolean;
// (undocumented)
protected shouldReloadPlaylist(playlist: Level | MediaPlaylist | null | undefined): boolean;
// (undocumented)
startLoad(): void;
// (undocumented)
Expand Down Expand Up @@ -903,8 +905,6 @@ export class ErrorController implements NetworkComponentAPI {
// (undocumented)
onErrorOut(event: Events.ERROR, data: ErrorData): void;
// (undocumented)
sendAlternateToPenaltyBox(data: ErrorData): void;
// (undocumented)
startLoad(startPosition: number): void;
// (undocumented)
stopLoad(): void;
Expand Down
4 changes: 4 additions & 0 deletions src/controller/audio-track-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ class AudioTrackController extends BasePlaylistController {
this.hls.trigger(Events.AUDIO_TRACKS_UPDATED, audioTracksUpdated);

this.selectInitialTrack();
} else if (this.shouldReloadPlaylist(this.currentTrack)) {
// Retry playlist loading if no playlist is or has been loaded yet
this.setAudioTrack(this.trackId);
}
}

Expand All @@ -154,6 +157,7 @@ class AudioTrackController extends BasePlaylistController {
data.context.id === this.trackId &&
data.context.groupId === this.groupId
) {
this.requestScheduled = -1;
this.checkRetry(data);
}
}
Expand Down
16 changes: 14 additions & 2 deletions src/controller/base-playlist-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,27 @@ export default class BasePlaylistController implements NetworkComponentAPI {
// Loading is handled by the subclasses
}

protected shouldLoadPlaylist(playlist: Level | MediaPlaylist): boolean {
protected shouldLoadPlaylist(
playlist: Level | MediaPlaylist | null | undefined
): boolean {
return (
this.canLoad &&
playlist &&
!!playlist &&
!!playlist.url &&
(!playlist.details || playlist.details.live)
);
}

protected shouldReloadPlaylist(
playlist: Level | MediaPlaylist | null | undefined
): boolean {
return (
this.timer === -1 &&
this.requestScheduled === -1 &&
this.shouldLoadPlaylist(playlist)
);
}

protected playlistLoaded(
index: number,
data: LevelLoadedData | AudioTrackLoadedData | TrackLoadedData,
Expand Down
40 changes: 26 additions & 14 deletions src/controller/error-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ export default class ErrorController implements NetworkComponentAPI {
case ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT:
case ErrorDetails.SUBTITLE_LOAD_ERROR:
case ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT:
// Switch to redundant level when track fails to load
if (context) {
const level = hls.levels[hls.loadLevel];
if (
Expand All @@ -130,11 +129,16 @@ export default class ErrorController implements NetworkComponentAPI {
level.textGroupIds &&
context.groupId === level.textGroupIds[level.urlId]))
) {
// redundant failover
data.errorAction = {
action: NetworkErrorAction.SendAlternateToPenaltyBox,
flags: ErrorActionFlags.MoveAllAlternatesMatchingHost,
};
// Perform Pathway switch or Redundant failover if possible for fastest recovery
// otherwise allow playlist retry count to reach max error retries
data.errorAction = this.getPlaylistRetryOrSwitchAction(
data,
hls.loadLevel
);
data.errorAction.action =
NetworkErrorAction.SendAlternateToPenaltyBox;
data.errorAction.flags =
ErrorActionFlags.MoveAllAlternatesMatchingHost;
return;
}
}
Expand Down Expand Up @@ -335,7 +339,7 @@ export default class ErrorController implements NetworkComponentAPI {
}
}

sendAlternateToPenaltyBox(data: ErrorData) {
private sendAlternateToPenaltyBox(data: ErrorData) {
const hls = this.hls;
const errorAction = data.errorAction;
if (!errorAction) {
Expand All @@ -345,13 +349,7 @@ export default class ErrorController implements NetworkComponentAPI {

switch (flags) {
case ErrorActionFlags.None:
if (nextAutoLevel !== undefined) {
this.warn(`${data.details}: switching to level ${nextAutoLevel}`);
this.hls.nextAutoLevel = nextAutoLevel;
errorAction.resolved = true;
// Stream controller is responsible for this but won't switch on false start
this.hls.nextLoadLevel = this.hls.nextAutoLevel;
}
this.switchLevel(data, nextAutoLevel);
break;
case ErrorActionFlags.MoveAllAlternatesMatchingHost:
{
Expand All @@ -375,6 +373,20 @@ export default class ErrorController implements NetworkComponentAPI {
);
break;
}
// If not resolved by previous actions try to switch to next level
if (!errorAction.resolved) {
this.switchLevel(data, nextAutoLevel);
}
}

private switchLevel(data: ErrorData, levelIndex: number | undefined) {
if (levelIndex !== undefined && data.errorAction) {
this.warn(`${data.details}: switching to level ${levelIndex}`);
this.hls.nextAutoLevel = levelIndex;
data.errorAction.resolved = true;
// Stream controller is responsible for this but won't switch on false start
this.hls.nextLoadLevel = this.hls.nextAutoLevel;
}
}

private redundantFailover(levelIndex: number): boolean {
Expand Down
11 changes: 6 additions & 5 deletions src/controller/subtitle-track-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,11 @@ class SubtitleTrackController extends BasePlaylistController {
if (!levelInfo?.textGroupIds) {
return;
}

const textGroupId = levelInfo.textGroupIds[levelInfo.urlId];
const lastTrack = this.tracksInGroup
? this.tracksInGroup[this.trackId]
: undefined;
if (this.groupId !== textGroupId) {
const lastTrack = this.tracksInGroup
? this.tracksInGroup[this.trackId]
: undefined;

const subtitleTracks = this.tracks.filter(
(track): boolean => !textGroupId || track.groupId === textGroupId
);
Expand All @@ -224,6 +222,9 @@ class SubtitleTrackController extends BasePlaylistController {
if (initialTrackId !== -1) {
this.setSubtitleTrack(initialTrackId, lastTrack);
}
} else if (this.shouldReloadPlaylist(lastTrack)) {
// Retry playlist loading if no playlist is or has been loaded yet
this.setSubtitleTrack(this.trackId, lastTrack);
}
}

Expand Down

0 comments on commit 981875c

Please sign in to comment.