mirror of
https://github.com/zoriya/v10.git
synced 2026-08-05 05:37:21 +00:00
feat(spf): background looping video (phase 1) (#1602)
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
---
|
||||
status: draft
|
||||
date: 2026-05-22
|
||||
definition: technical
|
||||
---
|
||||
|
||||
# Background-looping video
|
||||
|
||||
Engine variant for silent, autoplay-looping video on ambient/decorative
|
||||
surfaces (hero backgrounds, GIF-replacement loops, editorial previews).
|
||||
The Case-2 Player feature per
|
||||
[`../features/clusters.md` § Feature classification axes](../features/clusters.md#feature-classification-axes);
|
||||
ground-truth source the Mux [`mux-background-video`](https://github.com/muxinc/mux-background-video)
|
||||
product adapted to Video.js 10. Phase 1 is the SPF foundation for
|
||||
`<mux-background-video>` under parent epic
|
||||
[#873](https://github.com/videojs/v10/issues/873); the composition
|
||||
demonstrates SPF composability by *removing* behaviors from the standard
|
||||
playback engine rather than adding new logic.
|
||||
|
||||
Distinct from [`video-only-mode-override`](./video-only-mode-override.md):
|
||||
both subtract audio from mixed-source manifests, but background-looping-
|
||||
video also commits to single-rendition playback, loop semantics,
|
||||
autoplay-via-initial-state, and a product-shaped adapter — `mode-override`
|
||||
is the narrower "deliver video-only despite mixed source" use case.
|
||||
|
||||
## Status
|
||||
|
||||
Phase 1 implemented ([#1586](https://github.com/videojs/v10/issues/1586)):
|
||||
`createBackgroundLoopingVideoEngine`, `BackgroundLoopingVideoMediaElement`,
|
||||
and the `pickMaxResolutionVideoTrack` primitive ship under
|
||||
`@videojs/spf/background-looping-video`. Phases 2-3 (decorator composition
|
||||
of audio and preload) and Phase 4 (Video.js component shell — out of SPF
|
||||
scope) stay coarser.
|
||||
|
||||
## Phases
|
||||
|
||||
Adopts the parent-epic [#873](https://github.com/videojs/v10/issues/873)
|
||||
phase structure — phases map to discrete epic deliverables.
|
||||
|
||||
| Phase | What |
|
||||
|---|---|
|
||||
| **1 — Composition + adapter** ([#1586](https://github.com/videojs/v10/issues/1586)) | Subtractive composition removing audio, text, ABR, preload-monitoring, and play/seek-monitoring behaviors; adds `selectVideoTrack` with a max-resolution picker; seeds `loadActivated: true`; ships independent adapter parallel to `SimpleHlsMediaElement`. HLS multivariant source; native `mediaElement.loop = true`. |
|
||||
| **2 — `withAudio()` decoration** | Composes audio-side behaviors back in for surfaces needing audio (user-initiated unmute, audible ambient). Decorator shape TBD. |
|
||||
| **3 — `withPreload()` + optimizations** | Composes preload-state monitoring back in for lazy/viewport-gated tiles. Co-scoped with loop-around forward-buffer fetching, GPU/thermal-aware quality caps, and a sampling-strip alt-impl of `loadVideoSegments`. |
|
||||
| **4 — Video.js component** | Full `<mux-background-video>` integration. **Out of SPF scope** — adapter / consumer territory. |
|
||||
|
||||
## Composition specifics
|
||||
|
||||
Phase 1 uses three mechanisms — subtract, add, and alternative default
|
||||
configuration. Alternative-implementation buckets surface in Phase 3.
|
||||
|
||||
### Subtracted
|
||||
|
||||
From [`createSimpleHlsEngine`](../../../../packages/spf/src/playback/engines/hls/engine.ts):
|
||||
|
||||
- `syncPreload`, `trackLoadTriggers` — no preload-state monitoring or DOM `play`/`seeking` activation; replaced by `loadActivated: true` initial state.
|
||||
- `selectAudioTrack`, `selectTextTrack`, `resolveAudioTrack`, `resolveTextTrack`, `setupAudioBufferActors`, `loadAudioSegments` — no audio side.
|
||||
- `syncTextTracks`, `setupTextTrackActors`, `loadTextTrackSegments` — no text-track machinery.
|
||||
- `switchVideoQuality` — no ABR; commits to a single rendition for the session.
|
||||
|
||||
### Added
|
||||
|
||||
`selectVideoTrack` (from [`select-tracks.ts`](../../../../packages/spf/src/playback/behaviors/select-tracks.ts))
|
||||
with a config-provided `picker`, defaulting to `pickMaxResolutionVideoTrack`.
|
||||
Per `switchVideoQuality`'s docstring: *"Composing `selectVideoTrack` alone
|
||||
tree-shakes out the ABR code path."* — exactly the affordance Phase 1 wants.
|
||||
|
||||
### Alternative default configurations
|
||||
|
||||
- **`initialState.loadActivated: true`** *(Phase 1)* — seeds the composition into the post-preload-gate state from frame 0. Combined with the `syncPreload` / `trackLoadTriggers` subtractions, every downstream behavior that would otherwise gate on `!isBlockingPreload(preload) || loadActivated` sees `loadActivated` truthy from the start.
|
||||
- **`picker`** *(Phase 1)* — defaults to `pickMaxResolutionVideoTrack`; overridable for mobile/content-aware caps.
|
||||
- **Back-buffer tuning** *(Phase 3 candidate)* — larger back-buffer for gapless wrap-around (per [`buffer-management`](../features/buffer-management.md)'s `BackBufferConfig.keepSegments`).
|
||||
- **GPU/thermal-aware quality caps** *(Phase 3 candidate)* — shared concern with `video-only-mode-override`.
|
||||
|
||||
### Alternative implementations *(Phase 3 candidates)*
|
||||
|
||||
- **Sampling-strip `loadVideoSegments`** — the standard loader samples bandwidth into `state.bandwidthState` to feed ABR. With `switchVideoQuality` subtracted, no consumer reads it; sampling is harmless but wasted work. Per [`README.md` § Implementation note](./README.md#implementation-note-customizing-behaviors-for-use-cases), likely Path B (the sampling assumption is structurally tied to ABR).
|
||||
- **Loop-around forward-buffer fetching** — pre-fetch the wrap-around for gapless restart. See [`buffer-management`](../features/buffer-management.md) "What's not implemented".
|
||||
|
||||
## Customer-policy surface
|
||||
|
||||
Independent adapter parallel to `SimpleHlsMediaElement`:
|
||||
|
||||
```ts
|
||||
const bgPlayer = new BackgroundLoopingVideoMediaElement({ picker: maxResolutionPicker });
|
||||
bgPlayer.src = sourceUrl;
|
||||
bgPlayer.loop = true; // native HTMLMediaElement.loop
|
||||
bgPlayer.muted = true; // browser autoplay policy
|
||||
bgPlayer.play();
|
||||
```
|
||||
|
||||
Engine config: **`picker`** (TrackPicker, default max-resolution); later
|
||||
phases add `withAudio()` / `withPreload()` decorator hooks. Native `loop`
|
||||
/ `muted` live on the underlying media element. Adapter-layer concerns
|
||||
(autoplay-muted defaults, loop policy refinements, GPU/thermal caps, the
|
||||
Video.js component shell) live above the SPF engine.
|
||||
|
||||
## Variant-decision signal source
|
||||
|
||||
**Adapter-upfront.** Selecting `BackgroundLoopingVideoMediaElement` *is*
|
||||
the variant choice — no parser detection, no runtime config branch. Same
|
||||
resolution as [`video-only-mode-override`](./video-only-mode-override.md)
|
||||
and [`audio-only-mode-override`](./audio-only-mode-override.md): Case-2
|
||||
use cases resolve via adapter choice.
|
||||
|
||||
## Constituent features
|
||||
|
||||
Phase 1 baseline:
|
||||
|
||||
- **[`video-only-composition`](../features/video-only-composition.md)** — used at the *composition-mechanism* level; same audio-side subtraction pattern as the Case-1 feature, driven by adapter choice instead of source-shape detection. Plus further subtractions (text, ABR, preload).
|
||||
- **[`engine-adapter-integration`](../features/engine-adapter-integration.md)** — variant adapter parallels `SimpleHlsMediaElement` via the same `SimpleHlsMediaMixin` / `shareSignals` pattern.
|
||||
- **[`mse-mms-pipeline`](../features/mse-mms-pipeline.md)** — used as-is. Firefox `mozHasAudio=false` verification under subtractive-audio composition is **joint Phase 1 scope** with `video-only-mode-override` and the Case-1 `video-only-composition` feature.
|
||||
- **[`buffer-management`](../features/buffer-management.md)** — as-is in Phase 1; Phase 3 surfaces back-buffer tuning and loop-around forward-buffer fetching (the "loop-around buffer fetching" candidate in that feature's *What's not implemented* directly targets this use case).
|
||||
- **[`preload-modes`](../features/preload-modes.md)** — alternative initial state (`loadActivated: true`) plus subtraction of `syncPreload` + `trackLoadTriggers`. Semantic contract preserved; the variant just seeds the gate-passable state from composition time.
|
||||
|
||||
Subtracted (cross-link discipline):
|
||||
|
||||
- **[`video-abr`](../features/video-abr.md)** — single rendition for the session.
|
||||
- **[`multi-language-audio`](../features/multi-language-audio.md)** — audio fully subtracted.
|
||||
- **[`subtitles`](../features/subtitles.md)** — text fully subtracted; may resurface if a `withCaptions()`-style extension is scoped.
|
||||
|
||||
Phase 2 (decorations TBD): **[`audio-playback`](../features/audio-playback.md)**, **[`audio-abr`](../features/audio-abr.md)**.
|
||||
|
||||
## Likely cross-cutting impact
|
||||
|
||||
- **Shared engine factory.** Three use cases now want subtractive-audio composition (this, `video-only-mode-override`, Case-1 `video-only-composition`). Lean: shared factory at the subtractive-audio level, with this use case layering further subtractions (text, ABR, preload) and an initial-state override on top.
|
||||
- **Firefox `mozHasAudio` verification.** Joint scope with the two sibling cases — same mixed-source-with-audio-subtracted permutation.
|
||||
- **Adapter proliferation.** N+1 adapter parallel to `SimpleHlsMediaElement`; three adapters share the `SimpleHlsMediaMixin` / `shareSignals` pattern — cost is configuration surface, not architecture.
|
||||
- **`loadActivated: true` initial-state pattern.** Pioneered here. If a second use case wants the same shape, consider a shared `withAutoLoad()`-style helper or document treatment in [`preload-modes`](../features/preload-modes.md).
|
||||
|
||||
## Open questions
|
||||
|
||||
- **Phase 2/3 decorator pattern shape.** Decorator on the engine factory? Composable-feature abstraction? Engine-config flags re-including subtracted behaviors? Resolves when Phase 2/3 are scoped.
|
||||
- **Shared engine factory.** Joint with `video-only-mode-override` and Case-1 `video-only-composition`. Lean: shared, with this case composing further subtractions.
|
||||
- **Sampling-strip alt-impl Path A vs B.** Likely Path B per [`README.md` § Implementation note](./README.md#implementation-note-customizing-behaviors-for-use-cases).
|
||||
- **GPU/thermal-aware quality caps boundary.** Engine-variant (compose a thermal-aware behavior) or adapter (cap the picker candidate set). Likely engine-variant given the product context.
|
||||
|
||||
Resolved Phase 1: ~~picker location~~ (`pickMaxResolutionVideoTrack` ships in [`media/primitives/select-tracks.ts`](../../../../packages/spf/src/media/primitives/select-tracks.ts) next to `pickFirstTrackId`); ~~adapter naming~~ (`BackgroundLoopingVideoMediaElement`; product-shell naming `<mux-background-video>` lives in the adapter layer).
|
||||
|
||||
## See also
|
||||
|
||||
- [`video-only-mode-override.md`](./video-only-mode-override.md) — peer use case; shares constituent features and Firefox `mozHasAudio` scope; differs in delivery-scenario specificity (this is the Mux-product-shaped variant with loop, single-rendition, autoplay-via-initial-state).
|
||||
- [`audio-only-mode-override.md`](./audio-only-mode-override.md) — inverse-axis sibling; shares adapter-upfront pattern and the shared-engine-factory open question.
|
||||
- [`README.md`](./README.md) — use-case-composition doc-type spec.
|
||||
- [`../features/clusters.md` § Composition vs Policy vs middle pattern](../features/clusters.md#composition-vs-policy-vs-middle-pattern) · [`../conventions/behaviors.md` § Inverse: behaviors that operate uniformly across tracks](../conventions/behaviors.md#inverse-behaviors-that-operate-uniformly-across-tracks).
|
||||
- [`../../../../packages/spf/docs/hls-engine.md`](../../../../packages/spf/docs/hls-engine.md) — HLS engine composition baseline the variant subtracts from.
|
||||
- [`select-tracks.ts`](../../../../packages/spf/src/playback/behaviors/select-tracks.ts) (add target) · [`engine.ts`](../../../../packages/spf/src/playback/engines/hls/engine.ts) (subtraction baseline).
|
||||
- [GitHub #1586](https://github.com/videojs/v10/issues/1586) (Phase 1) · [#873](https://github.com/videojs/v10/issues/873) (parent epic) · [`mux-background-video`](https://github.com/muxinc/mux-background-video) (prior art) · [SPF Epics Working Doc](https://www.notion.so/35f97a7f89d08123a13fecab1ca1cac4).
|
||||
Reference in New Issue
Block a user