Raven Docs

Audio & Video

Camera, microphone, and the create-then-publish pattern — on Web, React, React Native, and Flutter.

Enabling a device captures it and publishes it to the room in one call, on every SDK — there's no separate publish() step for the common case.

Prerequisites: joined a room (see Quickstart) with a token whose publish permission is true — see Authentication.

The simple path

await room.enableCamera();      // captures and publishes in one call
await room.enableMicrophone();
 
await room.disableCamera();     // stops publishing and releases the device
await room.disableMicrophone();

Create first, publish later

For a device preview before joining — showing the user their own camera before committing to the call. Web-only today; React Native and Flutter always capture-and-publish together via enableCamera().

const track = await client.createCameraTrack();
// show a preview, let the user confirm...
await room.publish(track);

client.createMicrophoneTrack() works the same way. room.unpublish(track) stops publishing without touching the enable/disable toggle state enableCamera()/disableCamera() manage.

Filters & Effects

A published camera track can run through a Raven Effects pipeline before anyone downstream sees it — brightness, contrast, saturation, and presets like cinematic/vivid, without touching SDP, WebGL, or a canvas directly:

import { effects } from '@corvidhq/effects';
 
const camera = await room.enableCamera();
const pipeline = effects.createPipeline();
pipeline.applyPreset(effects.presets.cinematic);
 
await camera.attachEffects(pipeline);

attachEffects() swaps the published track in place via the SFU adapter's replaceTrack() — the room stays connected, audio is untouched, and remote participants receive the processed video through the ordinary trackSubscribed event. See Effects → RTC Integration for the full API, and Effects → React Native / Effects → Flutter for current mobile status.

Errors

Camera and microphone failures raise a typed error, never a raw platform exception, on every SDK:

try {
  await room.enableCamera();
} catch (error) {
  if (error.code === 'CAMERA_PERMISSION_DENIED') {
    // show your own "please allow camera access" UI
  } else if (error.code === 'DEVICE_NOT_FOUND') {
    // no camera attached
  }
}

Muting vs. unpublishing

track.mute() stops sending media but keeps the track published — the remote side sees a muted indicator via trackMuted/trackUnmuted rather than the participant disappearing. room.unpublish(track) is a harder stop: the track is gone from the room until republished.

Use mute for a user-facing mute button. Use unpublish when the feature itself is going away (leaving a screen share, for instance).

Expected behavior

Enabling a device that's already enabled is a no-op. Disabling a device that was never enabled is also a no-op — neither throws. A remote participant's trackMuted/trackUnmuted/trackSubscribed/ trackUnsubscribed events fire regardless of which SDK published the track; the events are the same across platforms because it's the same Room class underneath.

Production notes

  • Request camera/microphone access close to when you actually need it (joining a call), not on app launch — an unexplained permission prompt gets declined far more often than one with context.
  • On React Native/Flutter, a denied-vs-blocked distinction changes the UI you should show — see Permissions.
  • Don't poll device state; subscribe to trackMuted/trackUnmuted/ localTrackPublished/localTrackUnpublished instead.
  • Screen Sharing — the same enable/disable shape.
  • Permissions — OS-level camera/microphone access across platforms.
  • Diagnostics — resolution, framerate, and bitrate for whatever you've published.