Skip to content

feat(core): expose video tracks & renditions on the SPF media adapter#1803

Open
spuppo-mux wants to merge 3 commits into
videojs:mainfrom
spuppo-mux:feat/spf-adapter-video-tracks-renditions
Open

feat(core): expose video tracks & renditions on the SPF media adapter#1803
spuppo-mux wants to merge 3 commits into
videojs:mainfrom
spuppo-mux:feat/spf-adapter-video-tracks-renditions

Conversation

@spuppo-mux

@spuppo-mux spuppo-mux commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Part 1 of #1795 (video → audio → streamType → live → parity/autoplay).

What: SimpleHlsMedia now projects the SPF engine's video renditions onto the standard videoTracks / videoRenditions DOM surface (read, write, events) so a quality menu binds to it with no adapter-specific code, matching HlsJsMedia.

How:

  • The HLS SPF engine gains a derived videoRenditions signal (new deriveVideoRenditions behavior, mirroring deriveCdnPriority): projects the resolved presentation's video switching set, clears on src unload, skips writes when a reload carries the same set.
  • A core projection mixin (SimpleHlsMediaMediaTracksMixin) subscribes to that signal via effect, builds one selected main video track + a VideoRendition per level, and writes selection back:
    • selectedIndexuserVideoTrackSelection = { selectedIndex },
    • -1 → clear (ABR).

Design decisions:

  • Tried to follow the no SPF -> core dependency rule. Initially had everything implemented in core, then moved part to SPF with the behavior.
  • SPF publishes model vocabulary (bandwidth, codecs[], FrameRate); core owns DOM mapping (codecs.join(','), FrameRate → number). Kept them in the mixin file since they are only used there, but could move as helpers later.
  • Re-wire on src: the adapter currently destroys+recreates the engine per src, so subscriptions re-wire via one AbortController per connect.
  • Order = manifest declaration order (what SPF already handled), deterministic but unsorted. Not sorted here; a bandwidth-ascending sort in the selector is a one-liner if we later want hls.js-style ordering.

Trade-offs / open questions:

  • First selection/switching set only: CMAF permits multiple selection sets and multiple switching sets; fine for single-video-track HLS, generalizing is a follow-up.
  • Adapter destroy-recreate vs. in-place source replacement is unresolved (source-replacement.md / engine-adapter-integration.md). The src re-wire exists solely to compensate for destroy-recreate; if that's resolved toward in-place, the projection collapses to a one-time constructor wire.

Test plan:

  • spf: deriveVideoRenditions behavior (derive/skip-unchanged/clear); getVideoRenditions selector.
  • core: projection read/write/events, active reflection, frameRate → number, rebuild-on-set-change, re-wire-on-src, destroy.
  • Full core dom/media (338) + spf engine/behaviors (417) green; typecheck, lint, check:workspace clean.

Note

Medium Risk
Touches playback quality selection and DOM track projection with bidirectional sync; scope is bounded by tests and mirrors existing patterns, but wrong wiring could affect ABR or UI selection state.

Overview
SimpleHlsMedia now exposes the standard videoTracks / videoRenditions surface (read, write, events) so quality UIs can bind without adapter-specific APIs, aligned with the Hls.js media path.

On the SPF HLS engine, a new deriveVideoRenditions behavior (patterned after deriveCdnPriority) derives selectable quality levels from the resolved presentation’s first video switching set into a videoRenditions signal, clears on unload, and skips redundant signal writes when a reload keeps the same rendition ids. getVideoRenditions in track-selection utilities performs the presentation → VideoRenditionInfo mapping.

In core, SimpleHlsMediaMediaTracksMixin subscribes to that signal, builds one main track with a VideoRendition per level (including FrameRate → numeric frame rate), reflects active playback and selected manual pin from engine state, and maps videoRenditions.selectedIndex to userVideoTrackSelection ({ id } pins a level; -1 clears for ABR). Subscriptions re-wire on each src change because the adapter recreates the engine per assignment.

Reviewed by Cursor Bugbot for commit e98b473. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

@spuppo-mux is attempting to deploy a commit to the Mux Team on Vercel.

A member of the Team first needs to authorize it.

@netlify

netlify Bot commented Jul 3, 2026

Copy link
Copy Markdown

Deploy Preview for vjs10-site ready!

Name Link
🔨 Latest commit e98b473
🔍 Latest deploy log https://app.netlify.com/projects/vjs10-site/deploys/6a4c0092a0934300087a5139
😎 Deploy Preview https://deploy-preview-1803--vjs10-site.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d499e24. Configure here.

Comment thread packages/core/src/dom/media/simple-hls/media-tracks.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants