Files
v10/internal/design/spf/features/audio-playback.md
T

252 lines
13 KiB
Markdown

---
status: implemented
date: 2026-05-20
definition: sketched
---
# Audio playback
Today's single-rendition audio playback in the HLS engine. The engine
parses audio renditions from the multivariant playlist, picks one via
the default picker at source load, fetches its media playlist, sets up
an audio `SourceBuffer`, and loads segments. The selected rendition is
locked for the lifetime of the source — mid-stream switching belongs to
[`multi-language-audio`](./multi-language-audio.md) (coarse, not yet
implemented).
Symmetric with `video-abr` (video) and `subtitles` (text) in the
per-track-type capability set. Most of the implementation surface is
*owned architecturally* by other features (`mse-mms-pipeline`,
`buffer-management`, `source-replacement`); this doc captures what's
audio-specific.
## Status
- **Composition:** `createSimpleHlsEngine` (HLS VoD)
- **Definition depth:** sketched — capability surface documented;
language-aware selection exists in `media/primitives/` but isn't
wired (see *What's not implemented*)
## Phases of complexity
Capability slices around today's audio playback contract.
| Phase | What | Notes |
|---|---|---|
| Audio rendition recognition | `parseMultivariantPlaylist` surfaces audio renditions from `#EXT-X-MEDIA:TYPE=AUDIO` lines with `language`, `name`, `default`, `autoselect`, `channels`, `codecs`, `uri` | Engine consumes at most one today; parsing handles multiple — the foundation `multi-language-audio` builds on |
| Default audio rendition selection | `selectAudioTrack` runs the default picker once on `'presentation-resolved'` entry. Default picker today is `pickFirstTrackId` (first track in the audio selection set) — language-unaware | Config-overridable via `SelectAudioTrackConfig.picker`. The language-aware `pickAudioTrack` exists in `media/primitives/` but isn't wired — see *What's not implemented* |
| Audio media playlist resolution | `resolveAudioTrack` (sibling of resolveVideoTrack / resolveTextTrack, shared `setupTrackResolution` helper) fetches the selected rendition's media playlist on entry; aborts on source un-resolve via state-bound `AbortController` | Same shape as video / text resolution |
| Audio SourceBuffer + actor setup | `setupAudioBufferActors` creates the audio `SourceBuffer` + `SourceBufferActor` + `SegmentLoaderActor`. Uses plain `fetchStream` (no bandwidth sampling — there's no audio ABR yet) | Owned architecturally by `mse-mms-pipeline`; the Firefox `mozHasAudio` cross-type invariant lives in that feature's documentation |
| Audio segment loading | `loadAudioSegments` dispatches the per-type 4-state segment-load FSM (`'preconditions-unmet' / 'dormant' / 'metadata-only' / 'full-range'`) consuming `(preload, loadActivated)`. Same gate behavior as video | Owned architecturally by `buffer-management` |
| Source-change clearance | On source un-resolve, `selectedAudioTrackId` clears, audio actors tear down, in-flight playlist fetch aborts. New source's audio rendition picked fresh on the next resolve | Owned architecturally by `source-replacement` |
## What's not implemented
- **Multi-rendition recognition + programmatic selection + mid-stream
switching** — covered by
[`multi-language-audio`](./multi-language-audio.md) (partial, sketched —
Tier 1 + most of Tier 2 implemented). Default selection now uses
`pickAudioTrack`'s three-tier picker (`preferredAudioLanguage`
`DEFAULT=YES` → first-track), so `preferredAudioLanguage` config
takes effect. Tier 2 programmatic selection via
`userAudioTrackSelection` filter and same-codec mid-stream switching
with next-segment-boundary flush also implemented. Persistence and
A/V sync policy refinements deferred.
- **Audio ABR** — covered by [audio-abr](./audio-abr.md).
`setupAudioBufferActors` uses plain `fetchStream`; the
bandwidth-sampling `createTrackedFetch` isn't wired into the audio
fetch path. The sampling primitive exists (used by video); audio
needs a parallel `switchAudioQuality` behavior to consume it.
- **Channels-aware selection (5.1 / surround)** — covered by
[5.1-surround-selection](./5.1-surround-selection.md). Audio tracks
carry a `channels` field surfaced by the parser, but it's not yet
used for selection or capability filtering.
- **Audio-only composition optimizations** — the engine tolerates
audio-only sources today (basic coverage via `engine.test.ts`
"handles audio-only stream"), but the default engine doesn't
*explicitly* compose for audio-only contexts (no shorter buffer
targets, no subtracted video-related work). The explicit variant
ships via the
[audio-only-mode-override](../use-cases/audio-only-mode-override.md)
use case (Phase 1 landed); detect-from-parser routing in the default
engine to supplant the tolerance is future work tracked there.
- **A/V sync handling** — delegated entirely to the browser's MSE
pipeline. The engine doesn't intervene on audio/video sync.
## Implementation surface
**Composition:** `packages/spf/src/playback/engines/hls/engine.ts`
audio behaviors composed alongside video and text:
```ts
// Track selection (reads config for initial preferences).
selectAudioTrack,
selectTextTrack,
// Resolve selected tracks (fetch media playlists)
resolveVideoTrack,
resolveAudioTrack,
resolveTextTrack,
// ...
// MSE setup
// ...
setupAudioBufferActors,
// Segment loading
// ...
loadAudioSegments,
```
**Behaviors:**
| Behavior | File | Responsibility |
|---|---|---|
| `selectAudioTrack` | `packages/spf/src/playback/behaviors/select-tracks.ts` | Default audio rendition selection on source load. Lifecycle-only; mutually exclusive with `switchAudioTrack` |
| `switchAudioTrack` | `packages/spf/src/playback/behaviors/dom/switch-audio-track.ts` | Filter-reactive slot owner + mid-stream flush dispatcher. **Owned architecturally by [`multi-language-audio`](./multi-language-audio.md)** — composed in both `createSimpleHlsEngine` and `createHlsAudioOnlyEngine` today |
| `resolveAudioTrack` | `packages/spf/src/playback/behaviors/resolve-track.ts` | Fetches the selected audio media playlist |
| `setupAudioBufferActors` | `packages/spf/src/playback/behaviors/dom/setup-buffer-actors.ts` | Audio SourceBuffer + actor setup. **Owned architecturally by `mse-mms-pipeline`** |
| `loadAudioSegments` | `packages/spf/src/playback/behaviors/dom/load-segments.ts` | Audio segment loading dispatcher. **Owned architecturally by `buffer-management`** |
**Helpers:**
| Helper | File | Status |
|---|---|---|
| `pickAudioTrack(presentation, config)` | `packages/spf/src/media/primitives/select-tracks.ts` | **Default picker today** — language-aware three-tier (`preferredAudioLanguage``DEFAULT=YES` → first-track). Wired by [`multi-language-audio`](./multi-language-audio.md) Tier 1 |
| `pickFirstTrackId(presentation, 'audio')` | `packages/spf/src/media/primitives/select-tracks.ts` | Simple first-track fallback (still available for callers overriding via `SelectAudioTrackConfig.picker`) |
**State slots:**
- `selectedAudioTrackId` — single-writer. Owner depends on which audio-
selection behavior is composed: `selectAudioTrack` (lifecycle-only) or
`switchAudioTrack` (filter-reactive + mid-stream flush; the variant
composed in `createSimpleHlsEngine` and `createHlsAudioOnlyEngine`
today). Becomes `switchAudioQuality`'s responsibility when
[audio-abr](./audio-abr.md) lands (extends `switchAudioTrack`).
- `userAudioTrackSelection` — added by [`multi-language-audio`](./multi-language-audio.md).
Consumer-driven `Partial<AudioTrack>` filter narrowing the audio
candidate set before `switchAudioTrack`'s picker runs.
- Reads `presentation` (audio renditions surface in
`presentation.selectionSets`)
**Manifest parsing:** `parseMultivariantPlaylist`
(`packages/spf/src/media/hls/parse-multivariant.ts`) extracts audio
renditions from `#EXT-X-MEDIA:TYPE=AUDIO` lines.
## Config surface
```ts
{
preferredAudioLanguage?: string; // Exposed but inert with the
// default picker; see What's
// not implemented
}
```
Plus the behavior-level override:
```ts
// SelectAudioTrackConfig (consumed via composition config)
{
picker?: TrackPicker<SelectAudioTrackConfig>; // Override default
// pickFirstTrackId
}
```
Consumers wanting language-aware selection today must override
`picker` — e.g., to use `pickAudioTrack` from
`@videojs/spf` primitives.
## Verification
- **Unit tests:**
- `packages/spf/src/playback/engines/hls/tests/engine.test.ts`
"orchestrates complete pipeline" — asserts `selectedAudioTrackId`
defined, audio track resolved, audio buffer actor present
- `packages/spf/src/playback/engines/hls/tests/engine.test.ts`
"handles audio-only stream" — basic audio-only coverage
- `packages/spf/src/playback/behaviors/dom/tests/setup-buffer-actors.test.ts`
— per-type audio setup
- **Coverage gap:** no dedicated `select-tracks.test.ts` coverage
specifically asserting audio picker behavior (the default
`pickFirstTrackId` path is exercised through the orchestration
tests; the `pickAudioTrack` primitive has its own unit tests in
`media/primitives/tests/`)
- **Sandbox:**
- `apps/sandbox/src/spf-segment-loading/` — exercises audio
playback as part of HLS playback
## Open questions
- **Audio-only composition guarantees.** The engine *tolerates*
audio-only sources today, but is it *designed* for them? The
[audio-only-mode-override](../use-cases/audio-only-mode-override.md)
use case (Phase 1 landed) supplies the explicit variant; whether
the default engine routes to it for audio-only sources via
detect-from-parser is future work tracked there.
- **Channels exposure vs use.** The parser surfaces `channels` on
audio tracks but no selection logic consumes it. Stays inert until
[5.1-surround-selection](./5.1-surround-selection.md) wires it.
## Related features
- **[multi-language-audio](./multi-language-audio.md)** *(partial, sketched)*
the extension covering multi-rendition surfacing (free via parser),
language-aware default selection (wires `pickAudioTrack`), programmatic
selection via `userAudioTrackSelection` filter, and same-codec mid-stream
switching via next-segment-boundary flush. Today's `audio-playback`
is the baseline it builds on.
- **subtitles** — parallel structure (default picker + per-type
segment loading; same `setupTrackResolution` helper). Text-track
selection is user opt-in by default; audio's default selection
always picks something (or nothing if no audio renditions).
- **video-abr** — video equivalent. Video has ABR overlay; audio
doesn't (yet — see [audio-abr](./audio-abr.md)).
- **mse-mms-pipeline** — owns audio SourceBuffer setup
(`setupAudioBufferActors`). The Firefox `mozHasAudio` cross-type
invariant lives in that feature's documentation.
- **buffer-management** — owns audio segment loading
(`loadAudioSegments`). Same 4-state FSM as video / text.
- **source-replacement** — audio actors and `selectedAudioTrackId`
tear down via the resolved/unresolved cascade.
- **preload-modes** — gates audio loading via the same FSM that
gates video and text.
- **[audio-abr](./audio-abr.md)** — bandwidth sampling + quality
switching for audio. Parallel sibling of video-abr on the audio
axis; consumes audio-playback's single-rendition baseline + (when
it lands) multi-language-audio's rendition-group machinery.
- **[5.1-surround-selection](./5.1-surround-selection.md)** —
channels-aware codec-change selection. Consumer of capability-
probing on the audio channel-count axis. Wires the `channels` field
surfaced by the parser into selection logic.
- **[audio-only-mode-override](../use-cases/audio-only-mode-override.md)** *(use case; Phase 1 landed)*
engine variant optimized for audio-only delivery; covers both
truly-audio-only sources and mixed sources delivered as audio-only.
- **capability-probing** *(candidate)* — narrows the audio candidate
set selection runs over; unsupported codecs filtered upstream of
the `selectAudioTrack` picker.
## Use cases that compose this feature
- **[`audio-only-mode-override`](../use-cases/audio-only-mode-override.md)**
*(partial — Phase 1 landed)* — Phase 1 baseline constituent. The
audio-only delivery variant composes this feature's rendition
selection, media playlist resolution, and segment loading as-is via
`createHlsAudioOnlyEngine`.
## See also
- [presentation-modeling.md](../presentation-modeling.md) —
architectural deep-dive on the format-neutral data shape and parser
interface that surfaces audio renditions (the `parsePresentation`
contract this feature's recognition phase relies on)
- [multi-language-audio.md](./multi-language-audio.md) — future
extension on top of this feature
- [clusters.md § Track & variant registry](./clusters.md#track--variant-registry)
- [conventions/behaviors.md](../conventions/behaviors.md) — per-type
specialization pattern (`setupTrackResolution`)
- [packages/spf/docs/hls-engine.md](../../../../packages/spf/docs/hls-engine.md)
— engine composition walkthrough (Stage 2: track selection; Stage 3:
track resolution)