# The HLS Engine A reference walkthrough of how SPF composes a playback engine. The HLS engine in `@videojs/spf/hls` is one specific composition — manifest resolution, track selection, MSE setup, segment loading, end-of-stream — but the patterns it uses are how any SPF playback engine is built. This doc walks the composition stage-by-stage and calls out the patterns that recur. It assumes familiarity with HLS, MSE, and adaptive streaming. If you want to learn what a "switching set" is, look elsewhere. If you want to learn how SPF lets you turn those concepts into a composable, declarative engine, you're in the right place. For SPF primitives (signals, reactors, tasks, actors), see [fundamentals.md](./fundamentals.md). --- ## The engine at a glance `createSimpleHlsEngine` is a thin wrapper around `createComposition`. The full composition, lifted from `src/playback/engines/hls/engine.ts`: ```ts export function createSimpleHlsEngine( config: SimpleHlsEngineConfig = {} ): Composition { return createComposition( [ syncPreloadAttribute, trackPlaybackInitiated, resolvePresentation, // Track selection (reads config for initial preferences) selectVideoTrack, selectAudioTrack, selectTextTrack, // Resolve selected tracks (fetch media playlists) resolveVideoTrack, resolveAudioTrack, resolveTextTrack, // Presentation duration calculatePresentationDuration, // MSE setup setupMediaSource, updateDuration, setupSourceBuffers, // Playback tracking trackCurrentTime, switchQuality, // Segment loading loadVideoSegments, loadAudioSegments, // End of stream coordination endOfStream, // Text tracks syncTextTracks, setupTextTrackActors, loadTextTrackCues, ], { config, initialState, initialOwners } ); } ``` Read top to bottom, the engine tells a story: resolve a manifest, pick tracks, set up MSE, load segments, coordinate end-of-stream, render text tracks. Each line is a behavior — a small, focused unit of logic that owns one job. To build a different engine — fewer behaviors, different protocol, different platform — you change the list. Three things are doing the work here: 1. **`createComposition`** — the SPF primitive that wires the behaviors together. It owns the lifecycle and gives each behavior access to two shared reactive channels — `state` and `owners` — plus the engine's static `config`. 2. **The behaviors** — independent functions, each declaring its slice of the state and owners shapes and its job. 3. **The engine wrapper** — a few small helpers that adapt behaviors to the engine's specific needs (closing over media types, threading config). Nothing structural; just glue. The rest of this doc walks each stage. --- ## State, owners, and config Every SPF composition is parameterized by three shapes: ```ts export interface SimpleHlsEngineState { /** * The presentation being played. A caller writes `{ url }`; * `resolvePresentation` parses the manifest and populates the rest. */ presentation?: MaybeResolvedPresentation; preload?: 'auto' | 'metadata' | 'none'; selectedVideoTrackId?: string; selectedAudioTrackId?: string; selectedTextTrackId?: string; bandwidthState?: BandwidthState; abrDisabled?: boolean; currentTime?: number; playbackInitiated?: boolean; mediaSourceReadyState?: MediaSource['readyState']; } export interface SimpleHlsEngineOwners { mediaElement?: HTMLMediaElement; mediaSource?: MediaSource; videoBuffer?: SourceBuffer; audioBuffer?: SourceBuffer; videoBufferActor?: SourceBufferActor; audioBufferActor?: SourceBufferActor; textTracksActor?: TextTracksActor; segmentLoaderActor?: TextTrackSegmentLoaderActor; } export interface SimpleHlsEngineConfig { initialBandwidth?: number; preferredAudioLanguage?: string; preferredSubtitleLanguage?: string; includeForcedTracks?: boolean; enableDefaultTrack?: boolean; } ``` The split: - **State** holds reactive playback data — the manifest, selected track ids, current time, bandwidth estimate. It flows through the composition over time. Each field is a slot in the state signal that any behavior can read or write. - **Owners** holds **resources** — values with identity and imperative interfaces, not just data. The `