Camera and microphone playground
Editable examples of the real capture components — pick a device, toggle it, preview it locally.
The capture half of @video/video-client-react — choosing a camera and microphone, enabling
and disabling them, and previewing the result locally. Playback components are on the
player playground.
Nothing here reaches a server. The preview is a local getUserMedia stream, so the examples
run without a token, a call, or an account — and nothing starts until you ask it to.
Try it
One button each, switching itself between the two states. Like TogglePlayButton, the prop names describe the state moved TO.
<Encoder> <ToggleCameraButton /> <ToggleMicButton /> </Encoder>
The local Encoder helper
Encoder is the one thing on this page the SDK does not ship. It composes
usePreviewPlayer — which returns both a MediaStreamControllerAPI (the capture side) and a
PlayerAPI (a local preview of it) — with the two providers the controls read from.
import { components, context, hooks } from '@video/video-client-react';
function Encoder({ children }) {
const { mediaStreamController, previewPlayer } = hooks.usePreviewPlayer({});
// Both arrive asynchronously. Mounting a control against a null controller
// throws inside useMediaStreamControllerAPI, so render nothing until both exist.
if (!mediaStreamController || !previewPlayer) return <div className="placeholder" />;
return (
<context.MediaStreamControllerAPIProvider
mediaStreamControllerAPI={mediaStreamController}>
<context.PlayerAPIProvider playerAPI={previewPlayer}>
<components.Video muted playsInline />
{children}
</context.PlayerAPIProvider>
</context.MediaStreamControllerAPIProvider>
);
}
usePreviewPlayer calls getUserMedia as soon as it mountsThere is no "start" option to pass it. If you render it on load, the browser asks for camera permission on load. The playground above wraps it in a gate for that reason — the hook lives in a child that is not mounted until you press Start camera. A conditional guard is not enough: hooks cannot be called conditionally, so only leaving the component unmounted actually prevents the prompt.
Components
Each control finds the capture controller through context. All of them require
MediaStreamControllerAPIProvider, which Encoder supplies.
| Component | What it does | Requires |
|---|---|---|
ToggleCameraButton | Enable/disable the camera in one button, named for the state moved to | — |
EnableCameraButton | Enables the camera — one half of the toggle | — |
DisableCameraButton | Disables it — the other half | — |
ToggleMicButton | Enable/disable the microphone in one button | — |
EnableMicButton | Enables the microphone | — |
DisableMicButton | Disables it | — |
VideoSourceSelect | Camera picker, listing the devices the browser reports | Device permission before the list populates |
AudioSourceSelect | Microphone picker | Device permission before the list populates |
ResolutionSelect | Requests a capture resolution | A camera that advertises more than one |
ResolutionInUseSelect | Reports the resolution actually being captured, which can differ from the one requested | — |
VideoSourceInUseSelect | Reports the camera actually in use | — |
Video | The preview element. Encoder renders one; use it directly for your own chrome | — |
Customizing a control
Identical to the playback controls: children replace a label, style and other DOM props
pass through, and className replaces the SDK's class. See the Customizations presets above.
Symptoms and causes
| Symptom | Cause |
|---|---|
| The device selects are empty | The browser has not granted permission yet. Device labels — and often the devices themselves — are withheld until a stream has been opened once |
Error: must be used within a <PlayerProvider /> | A control is rendered beside Encoder rather than inside it. Every control calls a context hook that throws rather than returning null |
| The camera prompt appears without being asked for | usePreviewPlayer was mounted on load — see the warning above |
ResolutionInUseSelect disagrees with ResolutionSelect | Expected. The first reports what the device actually delivered; a camera may not honour the requested mode |
Broadcasting
Broadcast and call controls — StartBroadcastButton, PauseBroadcastButton,
UnpauseBroadcastButton, EndBroadcastButton, CreateCallButton, JoinCallButton,
EndCallButton — read useBroadcastAPI() and useCallAPI(), which need an authenticated
call. They are not live on this page: without credentials they would throw, which would
read as a broken example rather than an unavailable one.
The shape is the same as everything above — a provider supplying an API, and controls that find it through context:
<context.BroadcastAPIProvider broadcastAPI={broadcastAPI}>
<components.StartBroadcastButton />
<components.PauseBroadcastButton />
<components.EndBroadcastButton />
</context.BroadcastAPIProvider>
Getting a broadcastAPI requires authentication — see Broadcast a stream
and the SDK reference.