fix: recover from QuotaExceededError by evicting back buffer and retr…#7749
Open
alchemyyy wants to merge 1 commit intovideo-dev:masterfrom
Open
fix: recover from QuotaExceededError by evicting back buffer and retr…#7749alchemyyy wants to merge 1 commit intovideo-dev:masterfrom
alchemyyy wants to merge 1 commit intovideo-dev:masterfrom
Conversation
…ying append When a SourceBuffer append throws QuotaExceededError, intercept the error before it propagates, evict the minimum number of back buffer segments needed to fit the new data, and retry the append with the original data still in memory — avoiding the re-download loop that plagues hls.js upstream (video-dev#6776, video-dev#6711). Eviction uses stats.loaded (actual bytes received, always populated after fragment completion) rather than frag.byteLength (which depends on stats.total from Content-Length and may be 0 during progressive loading or with chunked transfer), so the calculation is reliable regardless of how segments are served. A class-level _quotaEvictionPending flag per SourceBuffer type prevents progressive loading chunks from each independently triggering eviction or emitting BUFFER_FULL_ERROR. The first QuotaExceededError triggers eviction; subsequent errors on the same type piggyback by queuing retries behind the pending remove. If eviction + retry fails, falls through to the existing BUFFER_FULL_ERROR path. Changes: - buffer-controller: on QuotaExceededError, calculate eviction target and queue a remove + retry append instead of emitting an error - buffer-controller: add _quotaEvictionPending flag gating per type - buffer-controller: add getQuotaEvictionFlushOp that clears the pending flag on complete/error - fragment-tracker: add getBackBufferEvictionEnd() which walks buffered fragments oldest-first accumulating byte sizes to find the minimum eviction point - buffer-operation-queue: add insertNext() to queue operations after the current one for remove-then-retry sequencing
b0d1280 to
8661a00
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR will...
Recover gracefully from
QuotaExceededErrorduringSourceBuffer.appendBuffer()by intercepting the error, evicting the minimum number of back buffer segments needed to make room, and retrying the append with the original data still in memory — eliminating the segment re-download loop.Why is this Pull Request needed?
The current
BUFFER_FULL_ERRORrecovery path reducesmaxMaxBufferLength(a time-based knob) but never evicts back buffer whenbackBufferLengthisInfinity(the default). This causes a loop where the same segment is re-downloaded repeatedly because the SourceBuffer is still full — the back buffer is the problem but nothing removes it. This is documented in #6776 and #6711, both taggedRevisit-at-later-release-cycle.This PR fixes the root cause: when
appendBufferthrowsQuotaExceededError, the buffer-controller now queries the fragment tracker for actual byte sizes of back buffer segments, calculates exactly how many oldest segments to remove to fit the new data, queues aSourceBuffer.remove()followed by a retry of the same append, and does all of this without emitting an error event or discarding the segment data. If the retry also fails (e.g. too early in playback with no back buffer to evict), it falls through to the existingBUFFER_FULL_ERRORpath unchanged.Are there any points in the code the reviewer needs to double check?
insertNextmethod onBufferOperationQueuesplices operations after the current (failed) one. AfteronErrorreturns,shiftAndExecuteNextremoves the failed op, so the remove operation executes next, followed by the retry append. Worth verifying this sequencing holds in all edge cases (e.g. concurrent audio/video queues).getBackBufferEvictionEndusesstats.loaded(actual bytes received, always populated after fragment completion) as the primary byte measure, falling back tofrag.byteLength(fromstats.total/ Content-Length). If neither is available for a fragment (e.g. preloaded or side-loaded), that fragment is skipped in the byte calculation, which could underestimate freeable space.quotaEvictionAttemptedflag is scoped per append operation closure, so each new segment gets one eviction attempt. A class-level_quotaEvictionPendingflag per SourceBuffer type prevents progressive loading chunks from each independently triggering eviction or emittingBUFFER_FULL_ERROR— subsequentQuotaExceededErrors while an eviction is in flight piggyback by queuing retries behind the pending remove rather than starting new evictions.Resolves issues:
Fixes #6776
Fixes #6711
Checklist