feat: Add ABR startup quality attributes#1315
Conversation
Add three opt-in attributes to improve ABR startup quality by counteracting TCP slow start's effect on initial bandwidth estimates: - initial-bandwidth-estimate-kbps: override HLS.js default estimate - initial-estimate-segments: keep using initial estimate for first N segments - min-preload-segments: buffer N segments before starting playback All three require explicit opt-in, no defaults changed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When minPreloadSegments is set and autoplay="muted", if the user manually plays before segments buffer, the deferred autoplay would still fire and force-mute the session. Check hasPlayed before triggering deferred autoplay. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When minPreloadSegments is set, intercept play events and pause playback until the buffer threshold is met. Resumes automatically once enough segments have buffered. Also guards against setting pendingAutoplay when autoplay is falsy. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@jondahl is attempting to deploy a commit to the Mux Team on Vercel. A member of the Team first needs to authorize it. |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Visual test page for verifying initial-bandwidth-estimate-kbps, initial-estimate-segments, and min-preload-segments behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds initial-bandwidth-estimate-kbps, initial-estimate-segments, and min-preload-segments to all remaining media elements and wrappers: - mux-audio: attribute constants + getter/setter properties - mux-video-react, mux-audio-react: PropTypes entries - mux-player-react, mux-player-astro: TypeScript type definitions - Reference docs for mux-video, mux-player, mux-player-react, mux-player-astro Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1315 +/- ##
==========================================
- Coverage 68.09% 68.04% -0.06%
==========================================
Files 47 47
Lines 7645 7864 +219
Branches 536 565 +29
==========================================
+ Hits 5206 5351 +145
- Misses 2435 2509 +74
Partials 4 4
🚀 New features to boost your workflow:
|
luwes
left a comment
There was a problem hiding this comment.
Questioning minPreloadSegments
Register the preload-gate `play` and `pause` listeners via `addEventListenerWithTeardown` so they are removed when the media element fires `teardown` (e.g. on source change). Previously they were attached with raw `addEventListener` and only removed inside the `FRAG_BUFFERED` callback once the preload threshold was reached. If the player tore down mid-preload, the listeners leaked, and the `onPlay` closure captured `preloadReady = false` permanently, pausing every subsequent play attempt for any future source on the same media element. Made-with: Cursor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
luwes
left a comment
There was a problem hiding this comment.
LGTM! I do think the API needs some more rethinking to make it more developer friendly. I'd like to help with that in Video.js.
|
actually, we need to trigger a manual waiting event for the minPreloadSegments. looking into it |
Replace play/pause interception with playbackRate=0 clamping while preloading. Lets autoplay and user-initiated play run unchanged — the element just doesn't advance frames — and avoids racing on async play/pause event ordering. Restores autoplay.ts to its pre-PR state and isolates the new behavior in setupMinPreload, called as a sibling step in index.ts. Captures user-driven ratechange events during preload so the user's intended playback rate is preserved when the buffer threshold is reached. Made-with: Cursor
Made-with: Cursor
The synchronous flag never blocked anything because `ratechange` is queued as a media element task and dispatched async, so the flag was always cleared by the time the handler ran. The actual safeguard was the `playbackRate !== 0` check filtering our zero-writes, plus removing the listener before the restoring (non-zero) write. Drop the dead flag and document the real invariant so future maintainers don't add a non-zero internal write while the listener is attached. Made-with: Cursor
If teardown fires before the min-preload threshold is reached, the ratechange listener is removed but playbackRate stays at 0, leaving the media element frozen on any subsequent reuse without minPreloadSegments. Add a teardown listener that restores the captured userPlaybackRate when not yet restored. Made-with: Cursor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 960f52f. Configure here.
| mediaEl.removeEventListener('ratechange', onRateChange); | ||
| mediaEl.playbackRate = userPlaybackRate; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Video stuck if segments fewer than minPreloadSegments threshold
Medium Severity
If the total number of main segments in a video is less than minPreloadSegments, the FRAG_BUFFERED threshold is never reached and playbackRate remains pinned at 0 indefinitely. The video appears to be "playing" (play button toggles to pause) but never visually advances. Only a teardown (source change / destroy) would restore playbackRate. There's no fallback for when all segments have been buffered but the count is still below the threshold — e.g. a short video with 5 segments and min-preload-segments="10" would be permanently stuck.
Reviewed by Cursor Bugbot for commit 960f52f. Configure here.
| (hls as any).abrController.resetEstimator(hls.config.abrEwmaDefaultEstimate); | ||
| } | ||
| }); | ||
| }; |
There was a problem hiding this comment.
setupInitialEstimate never removes its FRAG_BUFFERED listener
Low Severity
setupInitialEstimate registers an hls.on(FRAG_BUFFERED) listener that never gets removed — neither after the initial estimate period ends, nor on teardown. In contrast, setupMinPreload carefully detaches its FRAG_BUFFERED handler both on completion and via a teardown listener. The listener here continues to fire for every main fragment for the entire playback session, needlessly incrementing mainSegmentsBuffered and evaluating the condition.
Reviewed by Cursor Bugbot for commit 960f52f. Configure here.
🤖 I have created a release *beep* *boop* --- <details><summary>@mux/playback-core: 0.35.0</summary> ## [0.35.0](https://github.com/muxinc/elements/compare/@mux/playback-core@0.34.1...@mux/playback-core@0.35.0) (2026-04-28) ### Features * Add ABR startup quality attributes ([#1315](#1315)) ([3cdf0bc](3cdf0bc)) </details> <details><summary>@mux/mux-player: 3.13.0</summary> ## [3.13.0](https://github.com/muxinc/elements/compare/@mux/mux-player@3.12.0...@mux/mux-player@3.13.0) (2026-04-28) ### Features * Add ABR startup quality attributes ([#1315](#1315)) ([3cdf0bc](3cdf0bc)) ### Dependencies * The following workspace dependencies were updated * dependencies * @mux/mux-video bumped from 0.30.7 to 0.31.0 * @mux/playback-core bumped from 0.34.1 to 0.35.0 </details> <details><summary>@mux/mux-player-astro: 3.13.0</summary> ## [3.13.0](https://github.com/muxinc/elements/compare/@mux/mux-player-astro@3.12.0...@mux/mux-player-astro@3.13.0) (2026-04-28) ### Features * Add ABR startup quality attributes ([#1315](#1315)) ([3cdf0bc](3cdf0bc)) ### Dependencies * The following workspace dependencies were updated * dependencies * @mux/mux-player bumped from 3.12.0 to 3.13.0 * @mux/playback-core bumped from 0.34.1 to 0.35.0 </details> <details><summary>@mux/mux-player-react: 3.13.0</summary> ## [3.13.0](https://github.com/muxinc/elements/compare/@mux/mux-player-react@3.12.0...@mux/mux-player-react@3.13.0) (2026-04-28) ### Features * Add ABR startup quality attributes ([#1315](#1315)) ([3cdf0bc](3cdf0bc)) ### Dependencies * The following workspace dependencies were updated * dependencies * @mux/mux-player bumped from 3.12.0 to 3.13.0 * @mux/playback-core bumped from 0.34.1 to 0.35.0 </details> <details><summary>@mux/mux-audio: 0.16.0</summary> ## [0.16.0](https://github.com/muxinc/elements/compare/@mux/mux-audio@0.15.26...@mux/mux-audio@0.16.0) (2026-04-28) ### Features * Add ABR startup quality attributes ([#1315](#1315)) ([3cdf0bc](3cdf0bc)) ### Dependencies * The following workspace dependencies were updated * dependencies * @mux/playback-core bumped from 0.34.1 to 0.35.0 </details> <details><summary>@mux/mux-audio-react: 0.16.0</summary> ## [0.16.0](https://github.com/muxinc/elements/compare/@mux/mux-audio-react@0.15.26...@mux/mux-audio-react@0.16.0) (2026-04-28) ### Features * Add ABR startup quality attributes ([#1315](#1315)) ([3cdf0bc](3cdf0bc)) ### Dependencies * The following workspace dependencies were updated * dependencies * @mux/playback-core bumped from 0.34.1 to 0.35.0 </details> <details><summary>@mux/mux-video: 0.31.0</summary> ## [0.31.0](https://github.com/muxinc/elements/compare/@mux/mux-video@0.30.7...@mux/mux-video@0.31.0) (2026-04-28) ### Features * Add ABR startup quality attributes ([#1315](#1315)) ([3cdf0bc](3cdf0bc)) ### Dependencies * The following workspace dependencies were updated * dependencies * @mux/playback-core bumped from 0.34.1 to 0.35.0 </details> <details><summary>@mux/mux-video-react: 0.31.0</summary> ## [0.31.0](https://github.com/muxinc/elements/compare/@mux/mux-video-react@0.30.7...@mux/mux-video-react@0.31.0) (2026-04-28) ### Features * Add ABR startup quality attributes ([#1315](#1315)) ([3cdf0bc](3cdf0bc)) ### Dependencies * The following workspace dependencies were updated * dependencies * @mux/playback-core bumped from 0.34.1 to 0.35.0 </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: this PR is primarily version/changelog/lockfile updates with dependency bumps and no direct source code changes in the diff. > > **Overview** > Publishes a coordinated release across `@mux/playback-core`, `@mux/mux-player` (+ React/Astro wrappers), and `@mux/mux-video`/`@mux/mux-audio` (+ React wrappers), adding changelog entries for **ABR startup quality attributes**. > > Updates package versions and workspace dependency pins (notably `@mux/playback-core` to `0.35.0` and `@mux/mux-video` to `0.31.0`), along with the release manifest and `package-lock.json` to reflect the new release set. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit a854f91. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>


Summary
Adds three new opt-in HTML attributes to improve ABR startup quality across all Mux media elements:
initial-bandwidth-estimate-kbps— Seeds the HLS.js EWMA bandwidth estimator instead of relying on TCP slow start, which poisons the first measurement and causes segments 2-5 to drop to low renditions.initial-estimate-segments— Number of segments that use the initial estimate before switching to measured bandwidth. Setting to3means the first three segments use the initial estimate, segment four is the first to use real data.min-preload-segments— Buffers N segments before allowing playback to start (autoplay or user-initiated). Gives ABR real bandwidth data and builds buffer runway before playback begins.No defaults changed. All three attributes are opt-in and must be explicitly set.
Packages updated
playback-coremux-videomux-audiomux-playergetProps(), template forwarding to<mux-video>mux-video-reactmux-audio-reactmux-player-reactMuxMediaPropTypesmux-player-astroMuxPlayerPropsKey implementation details
initialBandwidthEstimateKbpssetsabrEwmaDefaultEstimatein HLS.js config via conditional spread (noRecord<string, any>type widening)initialEstimateSegmentsresets the EWMA estimator after each of the first N-1 segments viaFRAG_BUFFEREDeventsminPreloadSegmentsgates playback insetupAutoplay()by intercepting play events, pausing immediately, and resuming once enough segments buffer. Handles both autoplay and user-initiated play, with proper cleanup of event listenersTest Coverage
initial-bandwidth-estimate-kbps(3 tests) andinitial-estimate-segments(3 tests)hls.trigger()withstats: { aborted: true }, elementaryStreams: {}stubs to avoid HLS.js internal handler crashesReference docs updated
packages/mux-video/README.mdpackages/mux-player/REFERENCE.mdpackages/mux-player-react/REFERENCE.mdpackages/mux-player-astro/REFERENCE.mdTest plan
abr-startup-test.htmlharness (included in examples/)🤖 Generated with Claude Code
Note
Medium Risk
Touches core playback/ABR behavior (HLS.js config and fragment buffering event handling) and manipulates
playbackRateduring startup, which could affect playback edge-cases, though changes are opt-in and covered by new tests.Overview
Adds three new opt-in ABR startup controls across Mux media elements:
initial-bandwidth-estimate-kbps,initial-estimate-segments, andmin-preload-segments.In
@mux/playback-core, HLS.js initialization can now seedabrEwmaDefaultEstimatefrominitialBandwidthEstimateKbps, reset the EWMA estimator for the first N buffered segments (initialEstimateSegments), and delay visible playback until N main segments buffer by clampingmediaEl.playbackRateto0(minPreloadSegments).Plumbs these attributes through
mux-video,mux-audio, andmux-player(props extraction + template forwarding), updates React/Astro typings/docs, adds playback-core tests for estimate seeding/reset behavior, and includes a newabr-startup-test.htmlmanual test harness.Reviewed by Cursor Bugbot for commit 960f52f. Bugbot is set up for automated code reviews on this repo. Configure here.