LocalVideoStream interface

extends VideoStream  LOCAL

Your camera stream. Adds device control, settings updates, and one-shot frame capture on top of VideoStream. Access via room.localParticipant.video after publishVideo(). Frame processors are managed globally via VideoSDK.applyVideoProcessor() โ€” not on the stream.

Blueprint

Members added on top of VideoStream.

interface LocalVideoStream extends VideoStream { // Device identity (typed object, not just an id) readonly inputDevice: CameraDeviceInfo; // Device control setInputDevice(device: CameraDeviceInfo): Promise<void>; updateSettings(opts: { resolution?: number | string; frameRate?: number }): Promise<void>; getInputCapabilities(): Promise<VideoCapabilities>; // Encoder hint setContentHint(hint: ContentHint | null): void; // One-shot snapshot โ€” Base64 data URL (LOCAL only) captureFrame(opts?: { quality?: number; width?: number; height?: number; }): Promise<string>; // Lifecycle โ€” PRE-CALL only: abort the preview before join. // Releases the camera, clears VideoSDK.videoStream singleton slot. // Post-join: use me.unpublishVideo() for full teardown (unpublish + release device). stop(): Promise<void>; // === Events fired === // 'ended' ({ timestamp }) โ€” camera unplugged / permission revoked / browser denied access }

Properties (added)

Plus all properties inherited from VideoStream: id, codec, dimensions, frameRate, contentHint, isPlaying, isPaused, isEnded.

inputDevice

Currently selected camera as a CameraDeviceInfo (combines deviceId, groupId, label). Use me.video.inputDevice.label for "Camera: X" UI; me.video.inputDevice.deviceId for storage / comparison.

readonly inputDevice: CameraDeviceInfo

Methods (added)

Plus all methods inherited from VideoStream: attach / detach, createElement, getStats, getMediaStreamTrack.

setInputDevice async

Hot-swap to a different camera. The stream instance is preserved โ€” SDK swaps the underlying track and rebinds all attached <video> elements transparently. Strongly typed โ€” passing a MicrophoneDeviceInfo or SpeakerDeviceInfo is a compile-time error.

setInputDevice(device: CameraDeviceInfo): Promise<void>
Returns

Resolves when the new device is acquired and publishing on the new track. Rejects on permission denied, device busy, or if the device is no longer present.

Example โ€” switch to back camera on mobile
const cameras = await VideoSDK.getCameras();
const back = cameras.find(c => c.label.toLowerCase().includes('back'));
if (back) await me.video.setInputDevice(back);
// Attached <video> elements continue rendering โ€” no re-attach needed

updateSettings async

Change resolution and/or frame rate mid-stream without re-publishing. Internally calls applyConstraints() on the underlying track.

updateSettings(opts: { resolution?: VideoResolution; frameRate?: number }): Promise<void>
Example โ€” bump quality when bandwidth allows
await me.video.updateSettings({ resolution: 'h1080', frameRate: 60 });

setContentHint

Encoder optimization hint. "motion" for high-motion content (gameplay, sports). "detail" for sharpness-critical static content (slide presentations, document cameras).

setContentHint(hint: "motion" | "detail" | null): void
Processors live on VideoSDK, not on the stream. v1 uses a sticky global processor model โ€” call VideoSDK.applyVideoProcessor() to apply a frame transform. It survives publish, unpublish, re-publish, and setInputDevice swaps. No per-stream setProcessor method.

getInputCapabilities async

Reports what the current device can do โ€” max resolution, frame rates, facingModes, etc. Wraps the browser's MediaStreamTrack.getCapabilities().

getInputCapabilities(): Promise<VideoCapabilities>
Example โ€” clamp UI to what the device supports
const caps = await me.video.getInputCapabilities();
console.log(`Max ${caps.width.max}ร—${caps.height.max} @ ${caps.frameRate.max}fps`);
console.log('Facing modes:', caps.facingMode);

stop async PRE-CALL

Aborts a pre-call preview stream. Releases the underlying camera (MediaStreamTrack stopped) and clears the VideoSDK.videoStream singleton slot so the next createVideoStream succeeds. After stop(), all methods on this instance throw STREAM_STOPPED.

stop(): Promise<void>
Pre-call only. Once VideoSDK.join auto-promotes this stream into me.video, use me.unpublishVideo() instead โ€” it does full teardown (unpublish + release device).
Example โ€” abort pre-call
const preview = await VideoSDK.createVideoStream({ device: cameras[0] });
preview.attach(previewEl);

// User clicks "Cancel" instead of "Join"
await preview.stop();
// camera released, VideoSDK.videoStream is null again

Related open questions: Q18 โ€” Pre-call preview lifecycle (singleton vs render-only)

captureFrame async LOCAL ONLY

One-shot snapshot of the current frame as a Base64-encoded data URL. Useful for thumbnails, profile-photo capture, AI vision input, or sending a snapshot to a server.

Not available on remote streams โ€” to snapshot a remote video, use canvas.drawImage() on the user-attached <video> element.

captureFrame(opts?: { quality?: number; width?: number; height?: number }): Promise<string>
Parameters
  • opts.quality โ€” 0..1 for JPEG only, default 0.9. Ignored for PNG.
  • opts.width / opts.height โ€” optional override; defaults to the source frame size.
Returns

Resolves with a data URL string: "data:image/png;base64,..." โ€” assignable directly to img.src, postable to a server, or savable to disk.

Example โ€” save a snapshot, send to server, preview in img
const dataUrl = await me.video.captureFrame({ quality: 0.85 });

// Show as preview
document.querySelector('#preview').src = dataUrl;

// Upload
await fetch('/snapshot', { method: 'POST', body: JSON.stringify({ image: dataUrl }) });

// Download as file
const a = document.createElement('a');
a.href = dataUrl;
a.download = 'snapshot.jpg';
a.click();

Events

There are no publish/unpublish events on LocalParticipant โ€” the publishVideo Promise delivers the stream directly.

EventPayloadWhen
ended{ timestamp: number }Camera source ended โ€” device unplugged, OS revoked permission, browser tab lost device access.
Decoder-side states (frozen / stuck) and routing transitions (paused / ended) live on the merged streamState + state-changed event of RemoteVideoStream. Local streams have no routing state โ€” they describe the local capture pipeline, not a receive-side decoder.

See also: VideoStream LocalParticipant VideoFrameProcessor RemoteVideoStream