# Virtual devices

Register any `MediaStream` as a camera or microphone using `call.camera.registerVirtualDevice()` or `call.microphone.registerVirtualDevice()`. The SDK lists the virtual device in `listDevices()`, lets the user select it like any real device, and publishes the stream you provide.

Common use cases:

- Publish a pre-recorded video file, a canvas, or a `<video>` element as a camera.
- Publish synthetic or mixed audio as a microphone. For example, Web Audio output, a tone generator, or music played alongside the user's voice.
- Replace the device with a processed stream you produce yourself, when the built-in [Video & Audio filters](/video/docs/react/advanced/apply-video-filters/) pipeline is not a fit.

To transform the live camera or microphone (background blur, noise reduction, color grading), use [Video & Audio filters](/video/docs/react/advanced/apply-video-filters/) instead. Virtual devices replace the source; filters transform it.

## Register a virtual device

Pass a `label` and a `getUserMedia` function that receives the resolved constraints the SDK would otherwise pass to a real device, then returns the `MediaStream` to publish plus an optional `stop` callback that runs when the SDK releases the stream:

```ts
const video = document.createElement("video");
video.src = URL.createObjectURL(file);
video.loop = true;
video.muted = true;

const handle = call.camera.registerVirtualDevice({
  label: file.name,
  getUserMedia: async (constraints: MediaTrackConstraints) => {
    await video.play();
    return {
      // @ts-expect-error `captureStream()` is not present in the standard TypeScript DOM typings.
      stream: video.captureStream(),
      stop: () => {
        video.pause();
        video.currentTime = 0;
      },
    };
  },
});
```

For cameras, `constraints` can include values such as `width`, `height`, and `facingMode`. For microphones, it can include audio constraints such as `sampleRate` or `channelCount`. Your virtual device can honor these values or ignore them.

In the example above, `HTMLMediaElement.captureStream()` is convenient for turning a local `<video>` element into a virtual camera, but it is not available in every browser. Check [MDN documentation for `HTMLMediaElement.captureStream()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/captureStream) for compatibility and provide a fallback code path when needed

The same API exists on `call.microphone` for audio sources. Return a stream with at least one audio track.

## Select the device

Pass `handle.deviceId` to `select()` to switch to the virtual device:

```ts
await call.camera.select(handle.deviceId);
```

Virtual devices also appear in `call.camera.listDevices()` (and the `useCameraState` hook), so they show up in any device picker UI you build. Their `deviceId` starts with the `VIRTUAL_DEVICE_PREFIX` constant exported from `@stream-io/video-client`, which you can use to render virtual entries differently:

```ts
import { VIRTUAL_DEVICE_PREFIX } from "@stream-io/video-client";

const isVirtual = device.deviceId.startsWith(VIRTUAL_DEVICE_PREFIX);
```

## Unregister

Call `handle.unregister()` to remove the device. If it is currently selected, the SDK falls back to the default device and runs your `stop` callback.

```ts
await handle.unregister();
```


---

This page was last updated at 2026-05-27T11:59:09.342Z.

For the most recent version of this documentation, visit [https://getstream.io/video/docs/react/advanced/virtual-devices/](https://getstream.io/video/docs/react/advanced/virtual-devices/).