mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
229 lines
14 KiB
Markdown
229 lines
14 KiB
Markdown
---
|
||
status: draft
|
||
date: 2026-05-20
|
||
definition: technical
|
||
---
|
||
|
||
# Rendition selection caps
|
||
|
||
Policy-driven constraints that narrow the candidate set of video
|
||
renditions before quality selection runs. Each cap is a separate
|
||
constraint slot read by `selectQuality` (or the ABR equivalent) and
|
||
applied as a filter on `presentation.videoTracks` before selection.
|
||
The selection slot itself (`selectedVideoTrackId`) remains
|
||
single-writer; caps participate as constraints, not as competing
|
||
selectors.
|
||
|
||
A **Player feature** in the framing from
|
||
[clusters.md § Feature classification axes](./clusters.md#feature-classification-axes):
|
||
additive functionality not tied to making any source play, used by
|
||
the player to bias delivery for billing, device, viewport, or
|
||
other consumer-side reasons.
|
||
|
||
## Status
|
||
|
||
- **Composition:** not implemented in `createSimpleHlsEngine`. Today
|
||
`selectQuality` operates over all `presentation.videoTracks` with
|
||
no candidate-set narrowing beyond `userVideoTrackSelection`
|
||
(single-track manual override) and bandwidth-driven safety
|
||
margins.
|
||
- **Definition depth:** technical — scope and constraints
|
||
articulated; no implementation. Source material: [SPF Epics
|
||
Working Doc](https://www.notion.so/35f97a7f89d08123a13fecab1ca1cac4)
|
||
Cluster E entries #13 (1080p+ Resolution Cap, eng S, validation S)
|
||
and NEW-C (Screen-Size / Player-Size Resolution Cap, eng S–M,
|
||
validation S), unified per that doc's "Resolution-cap
|
||
unification" open question.
|
||
|
||
## Phases of complexity
|
||
|
||
Phases. The first four are motivation slices — different signal
|
||
sources (config / `ResizeObserver`) feeding the same constraint+filter
|
||
mechanism (a per-cap state slot read by `selectQuality` as a filter on
|
||
the candidate set before selection). The fifth is meta-policy on top:
|
||
it modifies how the combination of caps resolves, not a cap of its
|
||
own.
|
||
|
||
| Phase | What | Mechanism |
|
||
|---|---|---|
|
||
| Billing-driven max-height cap | Config-driven `maxHeight` (e.g., 1080) excludes higher-resolution variants from the candidate set. Drives the Mux billing use case (cap delivery at 1080p+ for tier-pricing alignment) | **Policy** — config field consumed by `selectQuality`'s filter step; no new behavior. Matching axis is configurable: height (default, simpler) or total pixels (e.g., for exact alignment with resolution-based pricing tiers — `1080p = 2,073,600 pixels` ensures anamorphic / non-standard-aspect variants are matched correctly) |
|
||
| Viewport-driven height cap | Cap the candidate set to renditions that fit the player element's rendered dimensions. Avoids serving higher-resolution variants the viewer can't perceive | **Middle pattern** — `ResizeObserver` monitor writes cap state on player-element size changes; `selectQuality` reads. New behavior. Pairs with the cap-floor phase below — the floor counters this phase's tendency to over-aggressively downgrade on small players |
|
||
| Max-bitrate cap | Config-driven `maxBitrate` excludes variants above the threshold. Useful for bandwidth-constrained delivery contexts (mobile, billing) | **Policy** — same shape as max-height cap |
|
||
| Max-FPS cap | Config-driven `maxFps` excludes high-frame-rate variants. Less common motivation but mentioned in [video-abr.md](./video-abr.md)'s "What's not implemented" | **Policy** — same shape |
|
||
| Cap floor (minimum effective cap) | Counter-pressure to other caps: when the combination of upper-bound caps (viewport, max-bitrate, etc.) would narrow the effective candidate set below a configured floor (e.g., 720p), the floor wins — the effective cap is `max(otherCaps, floor)`. Motivation: viewport-driven caps over-aggressively downgrade quality on small players; the floor prevents perceptual quality from dropping too far when the player is small | **Meta-policy** — not a cap of its own. New constraint slot (e.g., `videoMinMaxHeight`) read by `selectQuality`'s filter step alongside the other cap slots; combination takes the `max` over the other caps' effective bounds. Mux Player's [`MinCapLevelController.minMaxResolution = 720`](https://github.com/muxinc/elements/blob/main/packages/playback-core/src/min-cap-level-controller.ts) is the reference implementation |
|
||
|
||
The first four phases share a single mechanism: a constraint slot +
|
||
filter step before `selectQuality` consumes the candidates. They
|
||
differ in signal source (one-shot config vs reactive `ResizeObserver`)
|
||
and constraint axis (height / bitrate / FPS). The cap-floor phase is
|
||
meta-policy on top — it modifies how the combination of caps resolves,
|
||
not a cap of its own. Per-phase implementation is small (S / S-M per
|
||
the Epics doc).
|
||
|
||
## What's in scope vs out of scope
|
||
|
||
**In scope:**
|
||
- All five phases above for video renditions
|
||
- Constraint+filter pattern: per-cap state slots + filter step in
|
||
`selectQuality` before bandwidth-based selection runs
|
||
- Config surface under existing `quality?: {…}` engine config
|
||
- Edge case handling: empty candidate set after cap filtering (open
|
||
question — fallback policy)
|
||
|
||
**Out of scope (separate candidate features):**
|
||
- **[multi-signal-abr](./multi-signal-abr.md)** — incorporates non-
|
||
bandwidth signals (CPU / thermal / network type / battery / viewport
|
||
/ customer preference) into the ABR algorithm itself. Different
|
||
concern: caps narrow the candidate set; multi-signal-abr biases
|
||
selection within it. Caps could feed multi-signal-abr as one input,
|
||
but the algorithm-modification work is its own feature.
|
||
- **[audio-abr](./audio-abr.md)** — audio quality switching now
|
||
documented; its "Audio caps inclusion" open question explicitly
|
||
references this doc as the resolution candidate. When audio-ABR
|
||
ships, the audio-caps integration question becomes load-bearing.
|
||
- **[`audio-only-mode-override`](../use-cases/audio-only-mode-override.md) / [`video-only-mode-override`](../use-cases/video-only-mode-override.md)** —
|
||
use-case compositions that subtract behaviors for mode-only delivery.
|
||
Different mechanism (composition, not constraint).
|
||
|
||
**Out of scope (different architectural layer):**
|
||
- Above-engine consumers that write cap state from React / HTML
|
||
observers (e.g., a React hook that observes the player container
|
||
and writes a cap value). This feature owns the SPF-side state
|
||
slots and filter logic; *where* the writer lives (engine-side
|
||
`ResizeObserver` behavior vs adapter-level observation writing to
|
||
the slot) is partly an open question (see below).
|
||
- The Mux Video element's customer-facing attributes
|
||
(`max-resolution`, `cap-rendition-to-player-size`) — those are
|
||
adapter-layer API surfaces that *consume* this feature's config /
|
||
state.
|
||
|
||
## Likely cross-cutting impact
|
||
|
||
Things this feature probably forces decisions on, not just additions:
|
||
|
||
- **`selectQuality` filter-then-select shape.** Today's `selectQuality`
|
||
takes `(candidates, bandwidth, config)` and returns a track.
|
||
Adding caps means either (a) pre-filtering candidates outside
|
||
`selectQuality` and passing the filtered set in, or (b) extending
|
||
`selectQuality`'s signature to take the constraint slots and
|
||
filter internally. Option (a) keeps `selectQuality` pure and
|
||
matches the `userVideoTrackSelection` precedent (where the
|
||
filtering happens in `switchVideoQuality` before invoking
|
||
`selectQuality`). Option (b) bakes caps into the ABR module
|
||
directly.
|
||
- **State-slot granularity.** Per-cap slots (`videoMaxHeight`,
|
||
`videoMaxBitrate`, `videoMaxFps`, `videoViewportHeight`) vs a
|
||
unified slot (`videoRenditionConstraints: { maxHeight?,
|
||
maxBitrate?, maxFps?, viewportHeight? }`). Per-cap slots align
|
||
with the constraint+filter pattern's "one writer per slot"
|
||
shape; a unified slot complicates multi-writer semantics if
|
||
different caps have different writers (config-driven for some,
|
||
`ResizeObserver`-driven for others).
|
||
- **Viewport-driven cap signal-source location.** The cap state is
|
||
SPF-side, but the player-element dimensions are DOM-side. A new
|
||
engine-side behavior could read `mediaSource.media` (the
|
||
`<video>` element) and `ResizeObserver` it, or adapter-level code
|
||
could observe the player container and write the cap state slot.
|
||
Affects which layer owns the DOM dependency.
|
||
- **Empty-candidate-set fallback.** If caps narrow candidates to
|
||
zero (e.g., `maxHeight: 480` on a source with only 720p+
|
||
variants), `selectQuality` has nothing to return. Fallback
|
||
options: ignore the cap entirely, pick lowest available
|
||
rendition above the cap, or surface an engine error.
|
||
Configurable behavior or engine-level decision.
|
||
- **Cap-combination semantics.** When multiple caps are active
|
||
(viewport + max-bitrate, or viewport + cap-floor), they combine
|
||
via *intersection* on the candidate set — each upper-bound cap
|
||
narrows independently. The cap-floor inverts: it computes the
|
||
most restrictive other-cap result and enforces a lower bound on
|
||
it (`max()`). Where this combination lives is part of the
|
||
filter-then-select shape question above: option (a) puts it in
|
||
the dispatcher (`switchVideoQuality`) before `selectQuality` is
|
||
invoked; option (b) bakes it into `selectQuality` itself.
|
||
|
||
## Open questions
|
||
|
||
- **Per-cap slots vs unified constraint slot.** See cross-cutting
|
||
bullet above. Open until the second cap lands and the multi-writer
|
||
shape becomes concrete.
|
||
- **Viewport-driven cap signal-source location.** Engine-side
|
||
`ResizeObserver` on `mediaSource.media`, or adapter-side observation
|
||
writing to the cap slot? Affects feature shape (one behavior vs
|
||
one slot) and DOM dependency placement.
|
||
- **Empty-candidate-set fallback policy.** Ignore / pick-lowest /
|
||
error? Config-driven or engine-fixed?
|
||
- **max-FPS cap inclusion.** Listed in video-abr.md's "What's not
|
||
implemented" but lower priority than max-height / max-bitrate.
|
||
Document as Phase 4 or defer?
|
||
- **Audio caps.** Audio renditions don't have height analogs but
|
||
could have max-bitrate or max-channels caps. Extend this feature
|
||
to cover audio or carry audio caps separately?
|
||
- **Interaction with [capability-probing](./capability-probing.md).**
|
||
Capability filtering narrows candidates to "what the browser can
|
||
play"; caps narrow to "what the player chooses to deliver." Apply
|
||
capability filter first, then policy caps (per current cluster
|
||
framing). Confirm ordering when both land.
|
||
- **Cap-floor scope: policy caps only, or all candidate-narrowing
|
||
signals?** The floor counters the viewport-driven cap (and
|
||
potentially max-bitrate / max-FPS caps), but should not override
|
||
capability-filter physics. If [capability-probing](./capability-probing.md)
|
||
removes unsupported renditions, the floor can't add them back.
|
||
Confirm the boundary: floor operates over the policy-cap-narrowed
|
||
set, not over the capability-filtered set.
|
||
- **Cap-floor default value.** Mux Player hardcodes 720p as
|
||
`MinCapLevelController.minMaxResolution`. For SPF, should the
|
||
floor default to a value, or require explicit opt-in? Defaulting
|
||
risks unexpected high-bitrate delivery on tiny embeds; not
|
||
defaulting means most consumers won't get the perceptual benefit
|
||
unless they configure it.
|
||
|
||
## Related features
|
||
|
||
- **[video-abr](./video-abr.md)** — primary consumer. `selectQuality`
|
||
is the read-side; `userVideoTrackSelection` is the existing
|
||
constraint+filter precedent. Cap slots layer on top of the same
|
||
pattern.
|
||
- **[multi-signal-abr](./multi-signal-abr.md)** — different concern
|
||
(ABR algorithm extension) but caps could feed it as inputs.
|
||
- **[audio-abr](./audio-abr.md)** — audio quality switching that
|
||
may extend this feature with audio-side caps; see that doc's
|
||
"Audio caps inclusion in rendition-selection-caps.md" open
|
||
question.
|
||
- **[capability-probing](./capability-probing.md)** — adjacent. Both
|
||
narrow the candidate set, but along different axes (capability =
|
||
physics, caps = policy). The cluster framing places caps in
|
||
cluster E (selection policy) and capability filtering in cluster
|
||
D (capability probing primitive); the line matters for
|
||
composition order.
|
||
- **[hevc-variant-selection](./hevc-variant-selection.md)** — sibling
|
||
constraint+filter feature on the codec axis. Same filter-then-select
|
||
shape; the open question about per-cap slots vs unified slot is
|
||
shared.
|
||
- **[`audio-only-mode-override`](../use-cases/audio-only-mode-override.md) / [`video-only-mode-override`](../use-cases/video-only-mode-override.md)** —
|
||
use-case compositions for mode-only delivery. Different mechanism
|
||
than caps (subtract behaviors vs filter candidates).
|
||
|
||
## See also
|
||
|
||
- [clusters.md § Selection policy](./clusters.md#selection-policy)
|
||
— cluster E description; this feature is the first concrete cluster
|
||
E member, and the cluster's documented foundation
|
||
- [clusters.md § Feature classification axes](./clusters.md#feature-classification-axes)
|
||
— the Player-feature framing this doc instantiates; mixes
|
||
Policy mechanism (config-driven caps) and Middle-pattern
|
||
mechanism (viewport-driven cap)
|
||
- [clusters.md § Constraint + filter](./clusters.md#constraint--filter)
|
||
— cross-cluster pattern this feature instantiates; `video-abr`'s
|
||
`userVideoTrackSelection` is the precedent
|
||
- [video-abr.md](./video-abr.md) — primary consumer; `selectQuality`
|
||
is the read-side, `userVideoTrackSelection` the constraint
|
||
precedent
|
||
- [SPF Epics Working Doc](https://www.notion.so/35f97a7f89d08123a13fecab1ca1cac4)
|
||
— source material; Epic #13 (1080p+ Resolution Cap) and NEW-C
|
||
(Screen-Size / Player-Size Resolution Cap), unified per that
|
||
doc's "Resolution-cap unification" open question
|
||
- [Mux Video Permutations Matrix](https://www.notion.so/32c97a7f89d08191b84dd30f06685490)
|
||
— Stream Type / Selection Policy section; consumer-facing API
|
||
surfaces (`max-resolution`, `cap-rendition-to-player-size`)
|
||
documented in the Mux Video element README
|