feat(spf): keep track selection within the initial codec family - #2289
Conversation
✅ Deploy Preview for vjs10-site ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
@videojs/core
@videojs/element
@videojs/html
@videojs/media
@videojs/react
@videojs/spf
@videojs/store
@videojs/utils
commit: |
📦 Bundle Size Report🎨 @videojs/html — 4 small size changes
Presets (7)
Media (20)
Players (5)
Skins (29)
UI Components (43)
⚛️ @videojs/react — 6 small size changes
Presets (7)
Media (23)
Players (5)
Skins (26)
UI Components (37)
🧩 @videojs/core — no changesEntries (76)
🏷️ @videojs/element — no changesEntries (2)
📦 @videojs/store — no changesEntries (3)
🔧 @videojs/utils — no changesEntries (13)
📦 @videojs/media — no changesEntries (20)
📦 @videojs/spf — 9 small size changes
Entries (10)
ℹ️ How to interpretEach entry is independently bundled, minified, and brotli-compressed. Initial size includes its static import graph; lazy dynamic chunks are reported separately. Entries are not additive because their dependency graphs overlap. Preset rows represent realistic combined bundles. Changes of 300 B or less across initial, lazy, and total size are collapsed, not discarded. Run |
SPF implements no SourceBuffer.changeType(), so on a mixed-codec source (Apple's bipbop advanced example muxes HEVC + AVC renditions) a mid-stream re-pick across codec families appends undecodable data into a buffer created under the initial family's mimetype. Two new soft rules in the switchVideoTrack/switchAudioTrack chains close that hole: - stickToSelectedCodecs: re-picks narrow to the selected track's codec- family set. Head of the chain, ahead of user intent — physics, not preference — so a cross-family user pick falls through unhonored instead of killing playback. - preferCodecFamilies: the initial pick prefers configurable codec families via `preferredCodecs` (default AVC + AAC, the broadest-decode pair), deciding which family the sticky rule then holds. Behind the user filter, so an explicit initial pick may land in any family. The pinned selectVideoTrack (background compositions) never re-picks, so it stays unwired by construction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…vc-only workaround Promote Apple's official bipbop advanced fMP4 example (HEVC + AVC renditions of the same content) from a spf-segment-loading harness preset to the shared source registry as `hls-mixed-codec`, so every preset picker and `?source=` deep link reaches it. The harness's AVC-only toggle existed solely to keep ABR inside one codec family on that stream (no SourceBuffer.changeType()). The engine now does that by default — `preferredCodecs` lands the initial pick on AVC/AAC and the sticky codec-family rule holds it — so the toggle, its `avcOnly` query param, and the harness-local preset are removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
a5c8e29 to
708e9e3
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ 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 bb09627. Configure here.
…cies An effect that writes a signal one of its own dependency computeds reads ended its run clean-but-stale: the write marked the intermediate dirty while the effect was the in-flight consumer, so no watcher notification fired — and every later external change routed through that intermediate was deduped against its standing dirty flag, leaving the effect permanently deaf on that path. Pull each source once after an effect run (`revalidateSources`): a dirtied intermediate recomputes and its flag clears, so the next change through it propagates normally. The guarantee is liveness, not immediacy — an effect is deliberately not re-run just for writing into its own graph; it catches up on the next genuine change, which is exactly what used to be lost. Clean or equals-gated sources cost a cached read. Surfaced by moving `stickToSelectedCodecs` into the constraints pre-pass (the candidate-set computed then reads the selection slot the picker writes); reproduced in ten lines of pure signals and pinned by the new effect.test.ts for initial-run, flush-run, and convergence cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
stickToSelectedCodecs keeps its relational definition — the codec families of the currently selected track, looked up in the presentation — and moves from the rule chain into the constraints pre-pass, where its semantics are truthful: it's playability physics (no SourceBuffer.changeType()), not preference. Survivors stay playable by construction, a cross-family user pick is pruned before the user filter's early-bail can honor it, and a vanished selected family becomes a reported no-supported-track stop (recovering when the family returns) instead of a silent cross-family pick. Reading the selection from the pre-pass — a slot the picking effect itself writes — is safe only atop the effect-scheduler fix in the previous commit; the constraint's JSDoc points at it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bb09627 to
058648c
Compare
Several comments on this branch narrated the change's history ("used
to", "before the fix", "the old soft rule", "anymore") instead of the
current behavior. Restate them as present-tense contracts; no code
changes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Refs #1864
Summary
SPF implements no
SourceBuffer.changeType(), so on a mixed-codec source — Apple's bipbop advanced example muxes HEVC + AVC renditions of the same content — a mid-stream re-pick across codec families appends undecodable data into aSourceBuffercreated under the initial family's mimetype. This adds a hard codec-family sticky constraint (relational: the selected track's families), a configurable preference (default AVC/AAC) for which family the initial pick lands in, and the effect-scheduler fix that makes the constraint expressible.Changes
stickToSelectedCodecs(new selection constraint, video + audioswitch*pre-passes): prunes candidates whose codec-family set doesn't equal the currently selected track's (set-equality, so a muxedhvc1,mp4arendition can't pass as a match foravc1,mp4aon its audio half). Purely relational — no state of its own. Constraint semantics are the point: pruned pre-pass, a cross-family user pick mid-stream never reaches the user filter's early-bail and falls through unhonored instead of killing playback, and a vanished selected family becomes a reportedSVTAno-supported-track stop (recovering when the family returns) instead of a silent cross-family pick.fix(core): effects that self-write through their own dependencies no longer go deaf. A constraint reading the selection slot — which the picking effect itself writes — exposed a real signals-engine bug: the self-write left an intermediate computed dirty with no notification, and the standing dirty flag then deduped away every later external change through it (failover and relabel re-picks went unseen).effect.tsnow pulls an effect's sources after each run; the guarantee is liveness, not immediacy (an effect isn't re-run just for writing into its own graph — it catches up on the next genuine change, which is what used to be lost). Reproduced in ten lines of pure signals; pinned by newcore/signals/tests/effect.test.tscases for initial-run, flush-run, and convergence.preferCodecFamilies(new selection scope): narrows the initial pick to thepreferredCodecsfamilies — the family the constraint then holds. Behind the user filter, so an explicit initial pick may land in any family. Soft: an HEVC-only ladder under the AVC default plays exactly as before.preferredCodecsconfig onSwitchVideoTrackConfig,HlsVideoEngineConfig, andHlsAudioEngineConfig. Default['avc1', 'avc3', 'mp4a'];[]disables the preference (ABR then picks the initial family and still can't leave it).selectVideoTrackevaluates once and never re-picks, so it's immune by construction.hls-mixed-codec, reachable from every preset picker and?source=deep link. Thespf-segment-loadingharness's AVC-only toggle (plus itsavcOnlyquery param and harness-local bipbop preset) is removed — it existed solely as the workaround this PR replaces with engine defaults.Implementation details
track-switching-model.md(hevc-variant-selectionwas already anticipated there as a codec scope); constraint placement is what guarantees a cross-family user pick can't early-bail past the policy.equalson the candidate-set computed does not avoid the scheduler bug —sameCandidateSetwas in place the whole time.equalsgates notification after a recomputation; the lost wakeup is the dirty flag set at write time, upstream of any comparison. Only a pull clears it, which is whatrevalidateSourcesdoes; it never writes or schedules, so it cannot loop.DEFAULT_PREFERRED_CODECS: a ladder whose top rungs are HEVC-only caps at the top AVC rung under the default.measure-size.js— main was already at 99.9% of it before this branch.Testing
pnpm -F @videojs/spf test— full suite passes. Constraint coverage: mixed-ladder initial pick lands on AVC over a better-fitting HEVC rung, upgrades stay in-family, cross-family user selections are pruned while in-family ones are honored, a user's initial HEVC pick locks HEVC, muxed family sets compare whole, audio prefers AAC over a higher-bitrate E-AC-3 and holds it against a cross-family language request, and a failover that removes the selected family reports + clears, then recovers when the CDN returns. Scheduler coverage:effect.test.tspins the self-write lost-wakeup for initial-run and flush-run plus convergence.pnpm -F @videojs/spf build,pnpm typecheck,pnpm -F @videojs/spf size, lint — all clean (size note above).Sandbox (Vercel preview, this branch)
Base: v10-sandbox. The mixed-codec source is in the shared registry, so it's also reachable from any preset's picker.
avc1.*/mp4a.*(rendition buttons +window.state()). The AVC-only toggle is gone — no workaround needed🤖 Generated with Claude Code
Note
Medium Risk
Touches core effect scheduling and video/audio track selection, so mixed-codec and failover re-picks can change. Default AVC/AAC preference can cap quality on HEVC-top ladders unless
preferredCodecsis overridden.Overview
Keeps ABR inside the codec family of the first pick, because SPF still has no
SourceBuffer.changeType(). Mixed HEVC+AVC (and AAC vs E-AC-3) sources no longer append undecodable data mid-stream.Selection:
stickToSelectedCodecsis a hard pre-pass on video/audioswitch*— later picks must match the selected track’s full codec-family set. Cross-family user picks are ignored; if the family disappears (e.g. CDN cooldown), selection clears with a no-supported-track error and recovers when it returns.preferCodecFamilies(defaultavc1/avc3/mp4a) only shapes the initial pick;preferredCodecs: []disables it. HEVC-only ladders are unchanged. PinnedselectVideoTrackis left unwired.Signals: Effects that write a signal an intermediate computed in their own graph reads no longer go deaf. After each run,
revalidateSourcespulls sources so a standing dirty flag cannot swallow later wakeups. Guarantee is liveness, not an extra re-run.Sandbox: Apple bipbop is a shared
hls-mixed-codecsource. The segment-loading AVC-only toggle/workaround is gone.Reviewed by Cursor Bugbot for commit 856577c. Bugbot is set up for automated code reviews on this repo. Configure here.