MediaPlayer
A clip's poster and Mux's transport, on the app's clock
Usage
import * as Option from "effect/Option";
import { MediaPlayer, type MediaFrame } from "@gpuixui/media";
// The caller decodes or generates each frame: a file, a stream or a sequence.
const frameAt = (playhead: number): Option.Option<MediaFrame> =>
Option.some(frames[Math.floor(playhead / 1000)] ?? firstFrame);
<MediaPlayer
duration={24_000}
frameAt={frameAt}
alt="A clip's frame"
label="Generated clip"
width={640}
height={360}
buffered={18_000}
qualities={["Auto", "1080p", "720p"]}
onPlayheadChange={(playhead) => savePosition(playhead)}
/>;API
| Prop | Type | Default |
|---|---|---|
duration | number (milliseconds) | required |
frameAt | (playhead) => Option<MediaFrame> (a value that is not a function reads as no source) | — (no picture) |
alt | string | required (a blank name reads as the player's own) |
label | string | "Media player" (a blank name reads as the default) |
width | number | 640 |
height | number | 360 |
buffered | number (milliseconds) | the whole clip |
defaultPlayhead | number | 0 |
onPlayheadChange | (playhead: number) => void | — |
defaultPlaying | boolean | false |
onPlayingChange | (playing: boolean) => void | — |
defaultVolume | number (0…1) | 1 |
onVolumeChange | (volume: number) => void | — |
defaultMuted | boolean | false |
onMutedChange | (muted: boolean) => void | — |
rates | ReadonlyArray<number> | [0.5, 1, 1.5, 2] |
defaultRate | number | 1 |
onRateChange | (rate: number) => void | — |
qualities | ReadonlyArray<string> | [] (the settings menu offers speed only) |
defaultQuality | string | the first quality |
onQualityChange | (quality: string) => void | — |
tickMs | number | 100 |
seekStep | number | 1000 |
largeSeekStep | number | 10000 |
idleMs | number | 3000 |
onFullscreenChange | (fullscreen: boolean) => void | — |
playLabel | string | "Play" (a blank label reads as the default) |
pauseLabel | string | "Pause" (a blank label reads as the default) |
disabled | boolean | false |
className | string | — |
style | StyleDesc | — |
testId | string | "media-player" |
A MediaFrame is { width, height, pixels }, where pixels are straight RGBA
bytes (width * height * 4). The player pushes them with
ImgInstance.setImagePixels from a layout effect; the route is measured by the
media-frame-lifecycle probe. A frame whose box or buffer cannot be laid out
paints no new picture rather than a broken one.
Keyboard
The player is a focusable group, so its keys work while it, its timeline or any control inside it holds the focus:
| Key | What it does |
|---|---|
Space / K | play or pause |
Left / Right | seek back or forward by seekStep |
Shift + Left / Right | seek back or forward by largeSeekStep |
J / L | seek back or forward by largeSeekStep |
Up / Down | raise or lower the volume by five points |
Home / End | seek to the clip's start or end (and stop a running clip) |
M | mute or unmute |
F | ask the window for fullscreen |
Every control is a shipped Button/IconButton with its own name, so Enter and
Space activate the focused control as they do elsewhere in the library.
Pointer
The timeline is a seek control: a press seeks to the share of the clip the
pointer names, and a drag while the primary button is held scrubs. The painted
rail is 4 points tall with a 12-point thumb, and the hit target is the
24-point band around it (targetSize.minimum); the rail is
pointer-transparent inside that band. The player reads the band's box from the
renderer at press time — the route the shipped Slider uses — so a resize is
picked up without a second measurement. A press outside the band's ends clamps
to 0:00 or the clip's end, a secondary press begins no scrub, and a move that
holds no button ends the gesture.
The bar fades out after idleMs without a pointer move, key or focus, and any
of those brings it back. A popup keeps it painted while it is open.
Edit this page on GitHubpackages/media/docs/media-player.mdx