Skip to content

Keep injected silent audio frames from starting before initPTS - #8110

Merged
robwalch merged 1 commit into
video-dev:masterfrom
stevan-borus:bugfix/silent-frames-before-timeline-start
Oct 22, 2026
Merged

robwalch merged 1 commit into
video-dev:masterfrom
stevan-borus:bugfix/silent-frames-before-timeline-start

Conversation

@stevan-borus

Copy link
Copy Markdown
Contributor

This PR will...

Stop the AAC silence fill in remuxAudio from placing injected frames before the start of the timeline. The guard that trims fill frames compares nextPts against 0, but nextPts is an absolute input-timescale value that includes initPTS, so the comparison never fires. It now compares against initTime.

Why is this Pull Request needed?

With B-frames, initPTS anchors on the first video DTS and the first audio sample can start one or more audio frames later. On a 29.97 fps iPhone encode (libx264 defaults, 44.1 kHz AAC) the first audio PTS is 6006 ticks (67 ms) after initPTS, so the fill rounds to three frames and places the first one 263 ticks (2.9 ms) before initPTS. The log reads Injecting 3 audio frames @ -0.003s.

After the controller applies timestampOffset = -initPTS, that frame has a negative presentation time. WebKit (MediaSource and ManagedMediaSource, measured on headless WebKit 26.4) drops it, so the audio buffered range starts at 0.020 instead of where the fragment says it starts; Chromium trims it. Either way the fragment's startPTS is negative and describes a frame that is not in the buffer. With the fix, two frames are injected and the track starts 20 ms after initPTS, which matches what ends up buffered.

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

  • nextPts is absolute at this point (nextAudioTs + initTime at loop start, and pts - missing * inputSampleDuration here), so initTime is the right zero. The subsequent this.nextAudioTs = nextAudioTs = nextPts - initTime on i === 0 already treats it that way.
  • The unit test reproduces the numbers above and fails on master with three frames injected instead of two.

Resolves issues:

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

The silence-fill guard compared nextPts against 0, but nextPts is an
absolute input-timescale value that includes initPTS, so fill frames
could land before the start of the timeline. After the controller applies
timestampOffset = -initPTS such a frame has a negative presentation time:
WebKit drops it and Chromium trims it, while the fragment's startPTS
reports a start that is not in the buffer. Comparing against initTime
drops only the frames that would land before 0.
@robwalch

Copy link
Copy Markdown
Collaborator

Thanks for the fix. This was missed in #7311.

@robwalch
robwalch merged commit 816efee into video-dev:master Oct 22, 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 Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Confirmed Bug report confirmed or reproduced.

Development

Successfully merging this pull request may close these issues.

2 participants