Skip to main content

MediaPlayer

import { MediaPlayer } from '@react-x11/components/media-player';

<MediaPlayer
src={file}
aspectRatio="16:9"
volume={0.8}
style={{ flexGrow: 1 }}
onProgress={({ position, duration }) => setScrub(position / duration)}
onEnded={next}
/>;

mpv or VLC, embedded in a react-x11 window, with real transport control rather than a respawn per action. The same component as <Terminal> with different argv — both are built on embed and core's <foreign>.

backend defaults to 'auto': mpv if it is installed, else VLC. With neither, fallback renders and onError gets a BackendUnavailableError naming what was looked for. That is an ordinary state of a healthy machine, not an exception.

X11-only. Both players are embedded by handing them an X window id (--wid, --drawable-xid), which is XEmbed's mechanism, and macOS has no cross-process window embedding at all — react-x11's own macOS backend document names this component among what has no equivalent there.

On that backend the player says so up front, rather than spawning at a window id that does not exist: status is 'unavailable' from the first render, fallback renders, and onError gets an EmbedUnsupportedError saying this backend cannot embed another program's window. Nothing is looked for on PATH and nothing is spawned. It is the same ordinary state as a machine with no player, told apart by the error's class, because "install mpv" is the wrong advice there. Unlike <Terminal>, which has backend="vt" to fall to, this one has no native counterpart: a Cocoa app that needs video wants a different component than this one.

Props

Source and playback

PropTypeNotes
srcstringA path or a URL. Changing it does not restart the player — the new source is loaded into the running one, so the window never blinks.
backendMediaBackendName'auto' (default), 'mpv', 'vlc'.
autoPlaybooleanDefault true.
pausedbooleanDrive it if you want to. Leave it out and the player is uncontrolled — autoPlay decides how it starts and the handle moves it from there.
volumenumber0–1. Default: the player's own.
mutedboolean
loopboolean
aspectRatiostring'16:9', '4:3'. The player letterboxes inside the element's rect.
osdbooleanThe player's own on-screen controls. Off by default: they are drawn by the player and will not match the app's theme.
extraArgsreadonly string[]Appended to the player's command line verbatim.

Process and layout

PropTypeNotes
enabledbooleanFalse holds off spawning entirely.
stopSignalstringSent on unmount and restart. Default SIGTERM.
processesProcessHostWhere the process runs. See embed.
focusablebooleanA video surface is not a control; default false, unlike <Terminal>.
fallbackReactNodeRendered instead of the surface when no player is installed, or none can be embedded on this backend.
styleStyle | Style[]

Events

PropTypeNotes
onProgress(p: MediaProgress) => voidPosition and duration as the player reports them. mpv only.
onEnded(info) => voidThe file reached its end. Not fired for stop() or a new src.
onPlayingChange(playing: boolean) => void
onExit(info: ExitInfo) => voidThe player process ended.
onError(err: Error) => voidSpawn failures, control-channel failures, BackendUnavailableError, and EmbedUnsupportedError.

MediaPlayerHandle

player.current.seek(90);
if (player.current.reportsProgress) showScrubber();

play(), pause(), seek(seconds), setVolume(0–1), stop() (playback stops, the window stays, idle), restart(), signal(sig?), plus the read-only pid, windowId, backend, status and reportsProgress.

Live commands, and the VLC asymmetry

src, volume, muted and paused are live commands, sent over mpv's JSON IPC socket — changing them does not respawn the player.

Under VLC that channel is write-only: play/pause/seek/volume work and onProgress never fires. handle.reportsProgress says which you have, so a scrubber can be hidden rather than sit at zero.

The player's window stacks above everything you draw

Same rule <glarea> has, and the same one <Terminal>'s embedded backends have. A transport bar cannot be a <box> over the video — put it beside the element, or in a sibling <popup>. There is no vt-style native backend here: decoding video is the player's job.

Example

npm run examples:media-player -- <file>

Needs a real $DISPLAY and mpv or VLC installed.