const call = useCall();
const camera = call.camera;
// or using hooks
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useCameraState } = useCallStateHooks();
const { camera } = useCameraState();
await camera.enable();Camera & Microphone
The SDK simplifies WebRTC complexity (MediaStream, MediaDeviceInfo, etc.) through APIs on the call instance and utility hooks.
Best Practices
- Check
hasBrowserPermissionbefore enabling camera/microphone - users get one chance to grant access. - Use
usePersistedDevicePreferencesto remember device selections across sessions. - Always await
enable(),disable(), andtoggle()calls; the SDK handles race conditions. - Use
useDeviceList()for device listings - it includes a "Default" fallback option. - Implement a lobby preview so users can verify their setup before joining.
Camera management
Access the camera through the call.camera object or the useCameraState hook:
Call settings
The default state of the camera is determined by the call type settings:
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useCallSettings } = useCallStateHooks();
const settings = useCallSettings();
console.log(settings?.video.camera_default_on);Start-stop camera
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useCameraState } = useCallStateHooks();
const { camera, isMute } = useCameraState();
console.log(`Camera is ${isMute ? "off" : "on"}`);
await camera.toggle();
// or, alternatively
await camera.enable();
await camera.disable();Always await these calls. The SDK handles race conditions (last call wins). Use optimisticIsMute for immediate UI feedback.
List and select devices
import { useCallStateHooks, useDeviceList } from "@stream-io/video-react-sdk";
const { useCameraState } = useCallStateHooks();
const { camera, selectedDevice, devices } = useCameraState();
const { deviceList, selectedDeviceInfo } = useDeviceList(
devices,
selectedDevice,
);
console.log("current camera device:", selectedDeviceInfo);
console.log("available devices:", deviceList);
const preferredDevice = deviceList.find((d) => d.label === "My Camera");
await camera.select(preferredDevice.deviceId);useDeviceList() includes a "Default" fallback when no device is selected:
const { devices, selectedDevice } = useCameraState();
const { deviceList, selectedDeviceInfo, selectedIndex } = useDeviceList(
devices,
selectedDevice,
);
// `deviceList` contains useful properties, like `deviceList[0].isSelected`.
// `selectedDeviceInfo` is always defined, but can point to the "Default" device.Camera permissions
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useCameraState } = useCallStateHooks();
const { hasBrowserPermission, isPromptingPermission } = useCameraState();
if (hasBrowserPermission) {
console.log("User has granted camera permissions!");
} else {
console.log("User has denied or not granted camera permissions!");
}
if (isPromptingPermission) {
// Useful to show certain UI while the prompt is pending
console.log("Waiting for user to respond to the browser permission prompt");
}Lobby preview
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useCameraState } = useCallStateHooks();
const { camera, mediaStream } = useCameraState();
// will turn on the camera
await camera.enable();
// play the video preview
<video srcObject={mediaStream} autoPlay muted />;Or use the VideoPreview component.
Access to the Camera's MediaStream
Access mediaStream for custom use cases (local recording, etc.):
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useCameraState } = useCallStateHooks();
const { mediaStream } = useCameraState();
const [videoTrack] = mediaStream.getVideoTracks();
console.log("Video track", videoTrack);Microphone management
Access the microphone through call.microphone or useMicrophoneState:
const call = useCall();
const microphone = call.microphone;
// or using hooks
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useMicrophoneState } = useCallStateHooks();
const { microphone } = useMicrophoneState();Call settings
The default state of the microphone is determined by the call settings:
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useCallSettings } = useCallStateHooks();
const settings = useCallSettings();
console.log(settings?.audio.mic_default_on);Start-stop microphone
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useMicrophoneState } = useCallStateHooks();
const { microphone, isMute } = useMicrophoneState();
console.log(`Microphone is ${isMute ? "off" : "on"}`);
await microphone.toggle();
// or, alternatively
await microphone.enable();
await microphone.disable();Always await these calls. The SDK handles race conditions (last call wins). Use optimisticIsMute for immediate UI feedback.
List and select devices
import { useCallStateHooks, useDeviceList } from "@stream-io/video-react-sdk";
const { useMicrophoneState } = useCallStateHooks();
const { microphone, selectedDevice, devices } = useMicrophoneState();
const { deviceList, selectedDeviceInfo } = useDeviceList(
devices,
selectedDevice,
);
console.log("current microphone device:", selectedDeviceInfo);
console.log("available devices:", deviceList);
const preferredDevice = deviceList.find((d) => d.label === "My Mic");
await microphone.select(preferredDevice.deviceId);useDeviceList() includes a "Default" fallback when no device is selected:
const { devices, selectedDevice } = useMicrophoneState();
const { deviceList, selectedDeviceInfo, selectedIndex } = useDeviceList(
devices,
selectedDevice,
);
// `deviceList` contains useful properties, like `deviceList[0].isSelected`.
// `selectedDeviceInfo` is always defined, but can point to the "Default" device.Microphone permissions
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useMicrophoneState } = useCallStateHooks();
const { hasBrowserPermission } = useMicrophoneState();
if (hasBrowserPermission) {
console.log("User has granted microphone permissions!");
} else {
console.log("User has denied or not granted microphone permissions!");
}
if (isPromptingPermission) {
// Useful to show certain UI while the prompt is pending
console.log("Waiting for user to respond to the browser permission prompt");
}Speaking while muted detection
Detects when users speak while muted. Enabled by default (unless user lacks audio permission).
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useMicrophoneState } = useCallStateHooks();
const { isSpeakingWhileMuted, microphone } = useMicrophoneState();
if (isSpeakingWhileMuted) {
// your custom logic comes here
console.log("You are speaking while muted!");
}
// to disable this feature completely:
await microphone.disableSpeakingWhileMutedNotification();
// to enable it back:
await microphone.enableSpeakingWhileMutedNotification();Access to the Microphone's MediaStream
Access mediaStream for custom use cases (local recording, etc.):
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useMicrophoneState } = useCallStateHooks();
const { mediaStream } = useMicrophoneState();
const [audioTrack] = mediaStream.getAudioTracks();
console.log("Audio track", audioTrack);Hi-Fi and Stereo Audio
Configure high-fidelity stereo capture. Available modes:
VOICE_STANDARD: standard quality mono audio, suitable for voice calls, default modeVOICE_HIGH_QUALITY: high-quality mono audio, suitable for podcastsMUSIC_HIGH_QUALITY: high-quality stereo audio, suitable for music calls
Update with setAudioBitrateProfile:
import { useCallStateHooks, SfuModels } from "@stream-io/video-react-sdk";
const { useMicrophoneState } = useCallStateHooks();
const { microphone } = useMicrophoneState();
await microphone.setAudioBitrateProfile(
SfuModels.AudioBitrateProfile.MUSIC_HIGH_QUALITY,
);MUSIC_HIGH_QUALITY disables echo cancellation and noise suppression. Users may experience echo without headphones.
Noise Cancellation
Check our Noise Cancellation guide.
Camera and Microphone AI/ML Filters
Apply filters to media streams:
- Background blur/replacement
- Video filters (color correction, face detection)
- Audio filters (noise reduction)
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useCameraState, useMicrophoneState } = useCallStateHooks();
const { camera } = useCameraState();
const { microphone } = useMicrophoneState();
// apply a custom video filter
const { unregister: unregisterMyVideoFilter } = camera.registerFilter(
function myVideoFilter(inputMediaStream: MediaStream) {
// initialize the video filter, do some magic and
// return the modified media stream
return {
output: mediaStreamWithFilterApplied,
stop: () => {
// optional cleanup function
},
};
},
);
// apply a custom audio filter
const { unregister: unregisterMyAudioFilter } = microphone.registerFilter(
function myAudioFilter(inputMediaStream: MediaStream) {
// initialize the audio filter, do some magic and
// return the modified media stream
return {
output: mediaStreamWithFilterApplied,
stop: () => {
// optional cleanup function
},
};
},
);
// unregister the filters
unregisterMyVideoFilter();
unregisterMyAudioFilter();Filters can be registered/unregistered anytime. They chain in registration order.
For async filters, return output as a promise:
camera.registerFilter(function myVideoFilter(inputMediaStream: MediaStream) {
// note that output is returned synchronously!
return {
output: new Promise((resolve) => resolve(mediaStreamWithFilterApplied)),
stop: () => {
// optional cleanup function
},
};
});Await registered to know when the filter is active:
const { registered } = camera.registerFilter(
(inputMediaStream: MediaStream) => {
// your video filter
},
);
await registered;
// at this point, the filter is registeredThe SDK sends the filtered MediaStream to remote participants.
More: Apply Video Filters
Speaker management
Browser support
Speaker selection isn't supported by all browsers. Check availability:
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useSpeakerState } = useCallStateHooks();
const { isDeviceSelectionSupported } = useSpeakerState();
console.log("is speaker selection supported:", isDeviceSelectionSupported);List and select devices
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useSpeakerState } = useCallStateHooks();
const { speaker, selectedDevice, devices } = useSpeakerState();
console.log("current mic-id:", selectedDevice);
console.log("available devices:", devices);
const preferredDevice = devices.find((d) => d.label === "My Speakers");
await speaker.select(preferredDevice.deviceId);Set master output volume
import { useCallStateHooks } from "@stream-io/video-react-sdk";
const { useSpeakerState } = useCallStateHooks();
const { speaker } = useSpeakerState();
speaker.setVolume(0.5); // 0.5 is 50% of the maximum volumeSet participant volume
import {
useCallStateHooks,
StreamVideoParticipant,
} from "@stream-io/video-react-sdk";
let p: StreamVideoParticipant;
const { useSpeakerState } = useCallStateHooks();
const { speaker } = useSpeakerState();
// will set the volume of the participant to 50%
speaker.setParticipantVolume(p.sessionId, 0.5);
// will mute the participant
speaker.setParticipantVolume(p.sessionId, 0);
// will reset the volume to the default value
speaker.setParticipantVolume(p.sessionId, undefined);Persisting user's device preferences
Use usePersistedDevicePreferences to save device choices via localStorage. Render in a lobby component before calling call.get(), call.getOrCreate(), or call.join():
import {
StreamVideo,
StreamCall,
usePersistedDevicePreferences,
} from "@stream-io/video-react-sdk";
export const MyApp = () => {
// ... client and call initialization
return (
<StreamVideo client={client}>
<StreamCall call={call}>
<MyCallShell />
</StreamCall>
</StreamVideo>
);
};
const MyCallShell = () => {
usePersistedDevicePreferences("my-custom-preference-key");
return <div>...</div>;
};