Skip to main content
Version: 0.x.x

Precall Setup - React

Picture this: before diving into the depths of a video call, imagine giving your setup a quick check-up, like a tech-savvy doctor ensuring all systems are a go. That's essentially what a precall experience does- it’s like your extensive debug session before the main code execution—a crucial step in ensuring your app's performance is top-notch.

Why is it necessary?

Why invest time and effort into crafting a precall experience, you wonder? Well, picture this scenario: your users eagerly join a video call, only to encounter a myriad of technical difficulties—muted microphones, pixelated cameras, and laggy connections. Not exactly the smooth user experience you had in mind, right?

By integrating a robust precall process into your app, developers become the unsung heroes, preemptively addressing potential pitfalls and ensuring that users step into their video calls with confidence.

Step-by-Step Guide: Integrating Precall Feature

Step 1: Check Permissions

  • Begin by ensuring that your application has the necessary permissions to access user devices such as cameras, microphones, and speakers.
  • Utilize the checkPermissions() method of the useMediaDevice hook to verify if permissions are granted.
import { useMediaDevice } from "@videosdk.live/react-sdk";

const { checkPermissions } = useMediaDevice();

const checkMediaPermission = async () => {
//These methods return a Promise that resolve to a Map<string, boolean> object.
const checkAudioPermission = await checkPermissions("audio"); //For getting audio permission
const checkVideoPermission = await checkPermissions("video"); //For getting video permission
const checkAudioVideoPermission = await checkPermissions("audio_video"); //For getting both audio and video permissions
// Output: Map object for both audio and video permission:
/*
Map(2)
0 : {"audio" => true}
key: "audio"
value: true
1 : {"video" => true}
key: "video"
value: true
*/
};
  • When microphone and camera permissions are blocked, rendering device lists is not possible:

Step 2: Request Permissions (if necessary)

  • If permissions are not granted, use the requestPermission() method of the useMediaDevice hook to prompt users to grant access to their devices.
note

In case permissions are blocked by the user, the browser's permission request dialogue cannot be re-rendered programmatically. In such cases, consider providing guidance to users on manually adjusting their browser settings.

const requestAudioVideoPermission = async () => {
try {
//These methods return a Promise that resolve to a Map<string, boolean> object.
const requestAudioPermission = await requestPermission("audio"); //For Requesting Audio Permission
const requestVideoPermission = await requestPermission("video"); //For Requesting Video Permission
const requestAudioVideoPermission = await requestPermission("audio_video"); //For Requesting Audio and Video Permissions
} catch (ex) {
console.log("Error in requestPermission ", ex);
}
};
  • Requesting permissions if not already granted:

Request Permissions

Step 3: Render Device List

  • Once you have the necessary permissions, Fetch and render list of available camera, microphone, and speaker devices using the getCameras(), getMicrophones(), and getPlaybackDevices() methods of the useMediaDevice hook respectively.
  • Enable users to select their preferred devices from these lists.
const getMediaDevices = async () => {
try {
//Method to get all available webcams.
//It returns a Promise that is resolved with an array of CameraDeviceInfo objects describing the video input devices.
let webcams = await getCameras();
//Method to get all available Microphones.
//It returns a Promise that is resolved with an array of MicrophoneDeviceInfo objects describing the audio input devices.
let mics = await getMicrophones();
//Method to get all available speakers.
//It returns a Promise that is resolved with an array of PlaybackDeviceInfo objects describing the playback devices.
let speakers = await getPlaybackDevices();
} catch (err) {
console.log("Error in getting audio or video devices", err);
}
};
  • Displaying device lists once permissions are granted:

Step 4: Handle Device Changes

  • Implement the OnDeviceChanged callback of the useMediaDevice hook to dynamically re-render device lists whenever new devices are attached or removed from the system.
  • Ensure that users can seamlessly interact with newly connected devices without disruptions.
const {
...
} = useMediaDevice({ onDeviceChanged });

//Fetch camera, mic and speaker devices again using this function.
function onDeviceChanged(devices) {
console.log("Device Changed", devices)
}
  • Dynamically updating device lists when new devices are connected or disconnected:

Step 5: Create Media Tracks

  • Upon user selection of devices, create media tracks for the selected microphone and camera using the createMicrophoneAudioTrack() and createCameraVideoTrack() methods.
  • Ensure that these tracks originate from the user-selected devices for accurate testing.
import {
createCameraVideoTrack,
createMicrophoneAudioTrack,
} from "@videosdk.live/react-sdk";

//For Getting Audio Tracks
const getMediaTracks = async () => {
try {
//Returns a MediaStream object, containing the Audio Stream from the selected Mic Device.
const customAudioStream = await createMicrophoneAudioTrack({
// Here, selectedMicId should be the microphone id of the device selected by the user.
microphoneId: selectedMicId,
});
//To retrive audio tracks that will be displayed to the user from the stream.
const audioTracks = stream?.getAudioTracks();
const audioTrack = audioTracks.length ? audioTracks[0] : null;
} catch (error) {
console.log("Error in getting Audio Track", error);
}

//For Getting Video Tracks
try {
//Returns a MediaStream object, containing the Video Stream from the selected Webcam Device.
const customVideoStream = await createCameraVideoTrack({
// Here, selectedWebcamId should be the webcam id of the device selected by the user.
cameraId: selectedWebcamId,
encoderConfig: encoderConfig ? encoderConfig : "h540p_w960p",
optimizationMode: "motion",
multiStream: false,
});
//To retrive video tracks that will be displayed to the user from the stream.
const videoTracks = stream?.getVideoTracks();
const videoTrack = videoTracks.length ? videoTracks[0] : null;
} catch (error) {
console.log("Error in getting Video Track", error);
}
};
  • Rendering Media Tracks when necessary permissions are available:

Media Tracks

Step 6: Testing Microphone

  • The process of testing microphone device provides valuable insights into microphone quality and ensures users can optimize their audio setup for clear communication.
  • To facilitate this functionality, incorporate a recording feature that enables users to capture audio for a specified duration. After recording, users can playback the audio to evaluate microphone performance accurately.
  • For implementing this functionality, you can refer to the official guide of MediaRecorder for comprehensive instructions and best practices.

Step 7: Testing Speakers

  • Testing the speaker device allows users to assess audio playback clarity and fidelity, enabling them to fine-tune settings for optimal sound quality in calls and meetings.
  • To facilitate effective speaker testing, integrate sound playback functionality into your application.
  • This functionality empowers users to play a predefined audio sample, providing a precise evaluation of their speaker output quality.
const testSpeakers = () => {
//Here, you have to path of your desired test sound.
const test_sound_path = "test_sound_path";
//Create an audio tag using a test sound of your choice.
const audio = new Audio(test_sound_path);
try {
//Set the sinkId of the audio to the speaker's device Id, as selected by the user.
audio.setSinkId(selectedSpeakerDeviceId).then(() => {
audio.play();
});
} catch (error) {
console.log(error);
}
};

Step 8: Network Quality Assessment

important

The getNetworkStats() method has been removed in React SDK v0.10.0. Use the runPreCallTest() method instead.

  • Run runPreCallTest() as a pre-flight check before the user joins the meeting.
  • It verifies that the camera and microphone work, then measures the network's ability to carry a real WebRTC session — uplink and downlink, audio and video — and returns a quality score plus the raw stats behind it.

Parameters

The runPreCallTest() method accepts the following parameters:

  • token:

    • The authentication token used to connect to the SFU.
    • It has to be of String type.
    • This is a REQUIRED parameter.
  • samplingDuration:

    • Controls how long (in milliseconds) the network-stats phase runs.
    • It has to be of Number type.
    • This is an OPTIONAL parameter.
    • Default: 15000 ms
info

If samplingDuration is not a finite number or falls outside the 10000–120000 ms range, the test rejects with ERROR_PRECALL_INVALID_CONFIG.

  • videoTrack:

    • A pre-existing camera MediaStream (for example, from your join-screen preview) to use for the test.
    • The SDK will not stop this track after the test.
    • This is an OPTIONAL parameter.
  • audioTrack:

    • A pre-existing microphone MediaStream to use for the test.
    • The SDK will not stop this track after the test.
    • This is an OPTIONAL parameter.
  • videoConfig:

    • Config used by the SDK to acquire a camera track when videoTrack is not provided.
    • Accepts the same parameters as createCameraVideoTrack()cameraId, encoderConfig, facingMode, optimizationMode, multiStream, bitrateMode, maxLayer, etc.
    • The SDK creates the camera track from these params and runs the test on it. The created track is returned in the result, which you can pass to MeetingProvider.
    • This is an OPTIONAL parameter.
  • audioConfig:

    • Config used by the SDK to acquire a microphone track when audioTrack is not provided.
    • Accepts the same parameters as createMicrophoneAudioTrack()microphoneId, encoderConfig, noiseConfig (echoCancellation, autoGainControl, noiseSuppression), etc.
    • The SDK creates the microphone track from these params and runs the test on it. The created track is returned in the result, which you can pass to MeetingProvider.
    • This is an OPTIONAL parameter.
caution
  • Do not pass both videoTrack and videoConfig together (the same applies to audioTrack and audioConfig) — only one option from each pair can be provided.
  • The provided encoderConfig must match one of the supported profiles.

If either rule is violated, the test will be rejected with ERROR_PRECALL_INVALID_CONFIG.

  • audioOnly:
    • Skips the video entirely when set to true. The result's camera field will be null.
    • It has to be of Boolean type.
    • This is an OPTIONAL parameter.
caution

Setting audioOnly: true together with videoTrack or videoConfig rejects with ERROR_PRECALL_INVALID_CONFIG.

  • onStatsChange:
    • A callback invoked roughly once per second with live network stats during sampling.
    • It has to be of Function type.
    • This is an OPTIONAL parameter.
note

In every case above the err.code is the same ERROR_PRECALL_INVALID_CONFIG string, and the specific cause is preserved in err.message as a trailing detail.

Example

import { runPreCallTest, PreCallTestError } from "@videosdk.live/react-sdk";

//Initiates the pre-call test and returns a Promise that resolves with the test result.
//Here, customVideoStream and customAudioStream are the tracks created in Step 5.
const test = runPreCallTest({
//Authentication token.
token: "<YOUR_AUTH_TOKEN>",
//Duration (in ms) for which the network-stats phase runs.
samplingDuration: 15000,
//Set to true to skip the camera test entirely.
audioOnly: false,
//Pre-existing camera MediaStream to use for the test.
videoTrack: customVideoStream,
//Pre-existing microphone MediaStream to use for the test.
audioTrack: customAudioStream,
//Callback invoked roughly once per second with live network stats during sampling.
onStatsChange: (stats) => {
console.log("Live stats:", stats);
},
});

test
.then((result) => {
console.log("Pre-call test result:", result);
})
.catch((err) => {
if (err instanceof PreCallTestError) {
console.log("Pre-call test failed:", err.code, err.message);
}
});

//To cancel the test before it completes:
// test.stop();
note

.stop() is available only on the original Promise returned by runPreCallTest(). It is not available on chained promises, so calling test.then(...).stop() will not work. Store the original promise reference if you need to cancel the test.

caution

Only one test can run at a time. Calling runPreCallTest while another is in flight rejects with ERROR_PRECALL_TEST_ALREADY_RUNNING.

Result

The promise resolves with an object containing:

  • aborted: true if .stop() was called, otherwise false.
  • testDuration: Total test time in milliseconds.
  • camera: Camera status, track, capture resolution, fps, and codec. null in audio-only mode or if the test was aborted early.
  • microphone: Microphone status, track, and codec. null if the test was aborted early.
  • networkQuality: An object with uplink and downlink results. null if the test was aborted before the network phase.

Each of networkQuality.uplink and networkQuality.downlink contains:

  • quality: Overall score from 1 (BAD) to 5 (EXCELLENT).
  • factors: An array of strings explaining the score (e.g. rtt, packetLoss, jitter, bandwidth, cpu, freeze).
  • audio: A stats object with raw audio metrics.
  • video: A stats object with raw video metrics.

Errors

Every failure rejects with a PreCallTestError carrying a code and a message.

import { PreCallTestError } from "@videosdk.live/react-sdk";

runPreCallTest({ token: "<YOUR_AUTH_TOKEN>" }).catch((err) => {
if (err instanceof PreCallTestError) {
console.log(err.code, err.message);
}
});
CodeWhen it fires
ERROR_PRECALL_INVALID_TOKENtoken is missing, empty, or not a string
ERROR_PRECALL_INVALID_CONFIGConflicting options or invalid samplingDuration / encoderConfig
ERROR_PRECALL_TEST_ALREADY_RUNNINGA previous test hasn't finished yet
ERROR_PRECALL_MEDIA_CHECK_FAILEDNeither camera nor microphone could be acquired
ERROR_PRECALL_TEST_FAILEDThe pre-call network test could not be completed.
ERROR_CAMERA_NOT_FOUND / ERROR_MICROPHONE_NOT_FOUNDNo such device on this machine
ERROR_CAMERA_ACCESS_DENIED_OR_DISMISSED / ERROR_MICROPHONE_ACCESS_DENIED_OR_DISMISSEDUser denied / dismissed the permission prompt
ERROR_CAMERA_IN_USE / ERROR_MICROPHONE_IN_USEDevice is held by another application
ERROR_CAMERA_CONSTRAINT_NOT_SATISFIED / ERROR_MICROPHONE_CONSTRAINT_NOT_SATISFIEDRequested resolution / fps / sample rate can't be produced
ERROR_WEBCAM_TRACK_ENDED / ERROR_MICROPHONE_TRACK_ENDEDThe caller-supplied track has already ended
ERROR_INVALID_CUSTOM_VIDEO_TRACK / ERROR_INVALID_CUSTOM_AUDIO_TRACKThe caller-supplied stream isn't a valid MediaStream of the right kind
ERROR_VIDEO_SOURCE_INITIATION_FAILED / ERROR_AUDIO_SOURCE_INITIATION_FAILEDUnable to initiate video/audio source. Please verify browser settings for video permissions.

Step 9: Passing States to Meeting

  • Ensure that all relevant states, such as microphone and camera status (on/off), and selected devices, are passed into the meeting from the precall screen.
  • This can be accomplished by passing these crucial states and media streams onto the VideoSDK MeetingProvider.
  • By ensuring this integration, users can seamlessly transition from the precall setup to the actual meeting while preserving their preferred settings.
<MeetingProvider
config={
{
...
//Status of Mircophone Device as selected by the user (On/Off).
micEnabled: micOn,
//Status of Webcam Device as selected by the user (On/Off).
webcamEnabled: webcamOn,
//Use the camera track returned from runPreCallTest() in Step 8.
customCameraVideoTrack: result.camera.track,
//Use the microphone track returned from runPreCallTest() in Step 8.
customMicrophoneAudioTrack: result.microphone.track
}
} >
</MeetingProvider>

By following these step-by-step instructions, you can seamlessly integrate a precall feature into your application, empowering users to optimize their audio and video setup for a superior communication experience.

note

You can explore the complete implementation of the Precall feature in the official React JS SDK example available here.

API Reference

The API references for all the methods utilized in this guide are provided below.

Got a Question? Ask us on discord