Skip to content

Re-elect penalized level after configurable penalty expiry#7771

Merged
robwalch merged 6 commits intovideo-dev:masterfrom
Harshit661000143:hadokani/penalty-expiry
Apr 1, 2026
Merged

Re-elect penalized level after configurable penalty expiry#7771
robwalch merged 6 commits intovideo-dev:masterfrom
Harshit661000143:hadokani/penalty-expiry

Conversation

@Harshit661000143
Copy link
Copy Markdown
Contributor

@Harshit661000143 Harshit661000143 commented Mar 28, 2026

This PR will...

  • Introduces errorPenaltyExpireMs config option (default 0 = disabled) that allows a penalized level (loadError > 0) to become eligible for re-election after the configured duration
  • Adds isPenaltyExpired helper used by both error-controller (candidate scan) and abr-controller (upswitch tolerance check)
  • Stamps loadErrorTime alongside every loadError++ so expiry can be measured accurately

Why is this Pull Request needed?

A level that exhausts its retries accumulates loadError > 0, which excludes it from ABR upswitch and error recovery candidate selection. Its errors only clear on a successful buffer append — but it can't
buffer because it won't be selected. This leaves the player stuck at a lower quality indefinitely until stopLoad is called.

Are there any points in the code the reviewer needs to double check?

inline question about unsure change but did that for consistency.

Resolves issues:

When errorPenaltyExpireMs > 0, isPenaltyExpired bypasses the loadError === 0 gate in both selection paths once the penalty window has elapsed, giving the level a chance to recover without requiring a full stopLoad.

Checklist

  • changes have been done against master branch, and PR does not conflict
  • new unit / functional tests have been added (whenever applicable)
  • API or design changes are documented in API.md

@Harshit661000143 Harshit661000143 changed the title fix: re-elect penalized level after configurable penalty expiry Bugfix: re-elect penalized level after configurable penalty expiry Mar 28, 2026
Comment thread src/controller/error-controller.ts Outdated
if (levels[i].videoRange !== 'SDR') {
levels[i].fragmentError++;
levels[i].loadError++;
levels[i].loadErrorTime = self.performance.now();
Copy link
Copy Markdown
Contributor Author

@Harshit661000143 Harshit661000143 Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were hitting

level.loadError++;
but because we are incrementing loadError here too, added same change here as well, but not familiar with this code path. Thanks for double checking.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If playback needs to fallback from HDR to SDR then it should remain there. You can leave this as-is since adaptation will remain within the same codec and video color range unless a manual switch forces the change.

Comment thread src/controller/error-controller.ts Outdated
if (levels[i].videoRange !== 'SDR') {
levels[i].fragmentError++;
levels[i].loadError++;
levels[i].loadErrorTime = self.performance.now();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If playback needs to fallback from HDR to SDR then it should remain there. You can leave this as-is since adaptation will remain within the same codec and video color range unless a manual switch forces the change.

Comment thread src/controller/abr-controller.ts Outdated
Comment on lines 965 to 966
isPenaltyExpired(levelInfo, this.hls.config.errorPenaltyExpireMs) ||
(levelInfo.loadError === 0 && levelInfo.fragmentError === 0)) &&
Copy link
Copy Markdown
Collaborator

@robwalch robwalch Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer if the more complex (and less likely) isPenaltyExpired check came after the 0 error counts check:

Suggested change
isPenaltyExpired(levelInfo, this.hls.config.errorPenaltyExpireMs) ||
(levelInfo.loadError === 0 && levelInfo.fragmentError === 0)) &&
(levelInfo.loadError === 0 && levelInfo.fragmentError === 0) ||
isPenaltyExpired(levelInfo, this.hls.config.errorPenaltyExpireMs)) &&

robwalch
robwalch previously approved these changes Mar 31, 2026
Copy link
Copy Markdown
Collaborator

@robwalch robwalch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Harshit661000143,

There is just the issue of prettier formatting needed for the build to pass (the current build task is failing on lint check: https://github.com/video-dev/hls.js/actions/runs/23777534228/job/69410199645?pr=7771).

Please run npm run prettier && npm run sanity-check and stage and commit changed files to fix. Thanks 😄

@robwalch
Copy link
Copy Markdown
Collaborator

robwalch commented Mar 31, 2026

@Harshit661000143,

Almost there: https://github.com/video-dev/hls.js/actions/runs/23823070835/job/69440861387?pr=7771

Warning: You have changed the API signature for this project. Please copy the file "api-extractor/report-temp/hls.js.api.md" to "api-extractor/report/hls.js.api.md", or perform a local build (which does this automatically). See the Git repo documentation for more info.

When running the command npm run prettier && npm run sanity-check any API changes will be reflected in api-extractor/report/hls.js.api.md. Those changes need to be included in the commit too.

@robwalch robwalch added this to the 1.7.0 milestone Apr 1, 2026
@robwalch robwalch changed the title Bugfix: re-elect penalized level after configurable penalty expiry Re-elect penalized level after configurable penalty expiry Apr 1, 2026
@robwalch robwalch merged commit 6373b33 into video-dev:master Apr 1, 2026
12 checks passed
@github-project-automation github-project-automation Bot moved this from Top priorities to Done in HLS.js Release Planning and Backlog Apr 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants