fix(web-ui): batch init segment appends to fix silent audio on TS streams#528
Merged
Conversation
…eation from racing media engine init
Each worker message is delivered in its own event-loop task. Appending the
video init segment and then yielding lets the UA finish parsing it and lock
the SourceBuffer set, so the audio addSourceBuffer that arrives in the next
task throws QuotaExceededError ("reached the limit of SourceBuffer objects")
and the stream plays without sound. Reproduced 100% on Chrome with any
TS audio+video stream since the worker-based pipeline (#524).
The player now stashes init-segment messages and flushes them together right
before the first non-init message, creating all SourceBuffers in a single
task — the buffer append algorithm runs as a queued task, so no init segment
parse can complete in between.
Also switch mid-stream codec changes to SourceBuffer.changeType(): calling
addSourceBuffer for an existing track always throws once the media engine
has initialized.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
|
Azure Static Web Apps: Your stage site is ready! Visit it here: https://thankful-water-0a297bf00-528.eastasia.1.azurestaticapps.net |
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.
Summary
Fixes the
addSourceBufferrace that makes audio+video MPEG-TS streams play without sound on Chrome (reproduced 100% locally; likely behind the "CCTV 4K 无声音" report in #448).Root cause
The race dates back to the mpegts.js fork 3.0 rewrite (Feb 2026), which moved transmuxing permanently into a Worker with one
postMessageper event. Upstream mpegts.js ran the demux→remux→MSE chain synchronously on the main thread, so bothaddSourceBuffercalls always happened in a single event-loop task and the race could not occur.With the Worker architecture, each init segment arrives in its own task. The player appended the video init segment as soon as its message arrived, then yielded; the media engine finished parsing it and locked the SourceBuffer set. When the audio init-segment message arrived in the next task,
addSourceBufferthrewQuotaExceededError("This MediaSource has reached the limit of SourceBuffer objects"), and the audio track was silently dropped.Instrumented timeline before the fix (Chrome):
Fix
init-segmentmessages and flushes them together right before the first non-init message, creating all SourceBuffers in a single task — restoring the invariant upstream had implicitly. The MSE buffer-append algorithm runs as a queued task, so no init segment parse can complete between the twoaddSourceBuffercalls.SourceBuffer.changeType()instead ofaddSourceBuffer(), which always throws for an existing track once the media engine has initialized.Notes / known limitation
pendingSourceBufferInit) and ManagedMediaSource paths already batched init appends and are unchanged.Verification
Same instrumented harness after the fix — both SourceBuffers created in one task, zero player errors, and
webkitAudioDecodedByteCountconfirms audio is actually decoding:Also verified against the spliced catchup stream from #525: plays to the end with audio, single sane buffered range.
Test plan