Skip to content

feat(media): UidVideo, UidAudio and the @dskripchenko/ui/hls adapter - #42

Merged
dskripchenko merged 2 commits into
mainfrom
feature/media-players
Oct 26, 2026
Merged

dskripchenko merged 2 commits into
mainfrom
feature/media-players

Conversation

@dskripchenko

Copy link
Copy Markdown
Owner

Dependency-free media players on the native <video> / <audio> elements, plus a lazily loaded HLS entry.

What's inside

  • useMediaController: the core both players share. It keeps a normalised reactive state and offers play/pause/seek/skip, volume, mute, rate, full screen (with the iOS webkitEnterFullscreen fallback) and picture-in-picture (standard API or Safari's presentation mode). It also has the hotkey map, allows one active player per page (with an exclusive opt-out) and resumes from a saved position (resumeKey). Errors come as readable, localised kinds, and a 404 is reported as a network error rather than "unsupported format".
  • UidVideo:
    • src/sources with fallback; labelled sources form the quality menu.
    • Scrub bar with buffered ranges, chapter gaps and a hover preview showing time, chapter and sprite thumbnail.
    • Subtitles and captions drawn by the player.
    • Speed, quality and captions menus rendered inside the player, so they work in full screen. Two groups sit side by side, and in a small player the menu becomes a sheet.
    • Touch: double-tap seeks, and the centre button toggles play/pause.
    • Controls hide automatically; reduced motion is respected; ARIA throughout.
  • UidAudio:
    • Compact row, plus a mini variant for table rows.
    • Waveform decoded in the browser: low sample-rate decodeAudioData, peaks computed in chunks with idle yields, cached per src, decoded one file at a time and only when near the viewport, with size and duration caps. Precomputed peaks are also accepted.
    • Playlist with prev/next, auto-advance and Media Session.
    • Narrow containers get a two-line layout.
  • @dskripchenko/ui/hls (separate entry, loaded with a dynamic import):
    • Native HLS where the browser has it; otherwise MSE.
    • Master and media playlists, fMP4/CMAF with EXT-X-MAP and byte ranges.
    • MPEG-TS → fMP4 transmuxer: PAT/PMT/PES demux, H.264 Annex-B → AVCC with avcC built from SPS/PPS, ADTS → raw AAC with esds, timestamps kept continuous across 33-bit wrap and discontinuities. Other containers can plug in their own converter.
    • AES-128 decryption via WebCrypto.
    • Live playlists with reload and sliding window, discontinuities, buffer-ahead and eviction, gap jumping.
    • ABR from an EWMA bandwidth estimate, capped to the player size, with manual level selection.
    • Retries with backoff.
  • en/ru locale bag media, docs docs/{en,ru,de,zh}/{video,audio}.md (with a formats table and HLS notes), stories, a changeset (minor).

Checks

  • Vitest covers mediaUtils (VTT, thumbnails, sources), waveform downsampling, the controller, playlist parsing, ABR, the mp4 reader, the TS demuxer/transmuxer (on a real ffmpeg TS fixture), and UidVideo/UidAudio behaviour.
  • Real-browser matrix: pnpm media:fixtures && PLAYWRIGHT=… pnpm media:check (Playwright with ffmpeg-generated fixtures). All 24 checks pass in Chromium 156, Chrome 156 and WebKit 27:
    • MP4 and WebM playback, seeking, volume, rate, captions, chapters, thumbnails, hotkeys.
    • Source fallback, the progressive quality switch, and errors for broken and missing files.
    • HLS fMP4 and MPEG-TS, both native and through MSE, including the 360p↔720p switch.
    • AES-128, a discontinuity, live TS and live fMP4.
    • Five audio formats with the waveform, the exclusive player, and the playlist.
  • Firefox could not be launched in the local environment, so it is untested.

Sizes

  • dist/index.mjs grows by +60 KB raw (+15.4 KB min+gzip). The components are tree-shakeable.
  • dist/hls/index.mjs is 40.8 KB raw, 31.6 KB min, 11.7 KB min+gzip. It is loaded only when an HLS source appears.

Not included

SAMPLE-AES/DRM, HEVC/MP3 inside MPEG-TS, alternate audio renditions (EXT-X-MEDIA TYPE=AUDIO with a URI), LL-HLS parts, HLS in UidAudio.

🤖 Generated with Claude Code

dskripchenko and others added 2 commits October 25, 2026 23:37
…enko/ui/hls adapter

- useMediaController: normalised state, commands, hotkeys, one active player
  per page, resume position, readable localised errors (404 told apart).
- UidVideo: sources with fallback and quality menu, scrub bar with buffered
  ranges, chapters and sprite thumbnails, subtitles drawn by the player,
  speed/quality/captions menus inside the player (sheet in small players),
  full screen with the iOS fallback, picture-in-picture, touch gestures,
  auto-hiding controls.
- UidAudio: compact row, mini variant, in-browser waveform (chunked,
  cached, size-capped, lazy) or precomputed peaks, playlist with
  auto-advance and Media Session.
- @dskripchenko/ui/hls (lazy entry): native HLS where available, otherwise
  MSE with fMP4/CMAF and MPEG-TS (built-in H.264/AAC transmuxer), AES-128,
  live playlists, discontinuities, ABR and manual quality, retries.
- Real-browser checks (scripts/media-check), docs in en/ru/de/zh, stories.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… player polish

- The HLS engine runs one stream per SourceBuffer: video (or muxed) and a
  separate audio rendition (EXT-X-MEDIA TYPE=AUDIO with a URI), sharing one
  timestamp offset per discontinuity so they stay in sync across seeking and
  quality switches. Rendition choice: audioLanguage, DEFAULT, AUTOSELECT in
  the browser language. setAudioTrack(), stats(); Safari's native
  audioTracks are exposed too.
- Packed audio segments (ID3 timestamp + ADTS) go through the transmuxer.
- UidVideo: audio-track group in the settings menu; compact speed chips in
  the small-player sheet. Scrub preview hides while a menu is open or the
  controls are hidden.
- UidAudio: HLS sources (audio-only master or media playlist); playlist
  durations prefetched from metadata ("—" until known); shimmering
  placeholder while a waveform is pending; narrow layout truncates the
  title/artist and puts the time under the bar.
- Fixtures (video-only renditions + en/ru audio as fMP4, TS and packed AAC,
  audio-only HLS) and browser checks for all of it, incl. tone detection of
  the playing rendition in Chromium and a header geometry check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dskripchenko
dskripchenko merged commit cd25b49 into main Oct 26, 2026
2 checks passed
@dskripchenko
dskripchenko deleted the feature/media-players branch October 26, 2026 04:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant