Skip to main content
Version: 1.x.x

Precall Setup - React Native

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.

note

This page uses the following asynchronous methods. Refer to their API reference for the errors each Promise may reject with, and handle these rejections appropriately based on your use case.

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.

note

Ensure you're using the latest version of @videosdk.live/react-native-incallmanager and @videosdk.live/react-native-sdk.

Upgrade with:

npm update @videosdk.live/react-native-sdk @videosdk.live/react-native-incallmanager

Using PreCall Functions

Check Permissions

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

const { checkPermission } = useMediaDevice();

const checkMediaPermission = async () => {
//These methods return a Promise that resolve to a Map<string, boolean> object.
const checkAudioPermission = await checkPermission("audio"); //For getting audio permission
const checkVideoPermission = await checkPermission("video"); //For getting video permission
const checkAudioVideoPermission = await checkPermission("audio_video"); //For getting both audio and video permissions
let checkBTPermission;
try {
checkBTPermission = await checkBlueToothPermission(); // For getting bluetooth permission
} catch (err) {
console.error("checkBlueToothPermission failed:", err);
}
// 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
*/
};

Request Permissions (if necessary)

  • If permissions are not granted, use the requestPermission() and requestBluetoothPermission methods of the useMediaDevice hook to prompt users to grant access to their devices.
const requestAudioVideoPermission = async () => {
try {
//These methods return a Promise that resolve to a Map<string, boolean> object.
let requestAudioPermission;
try {
requestAudioPermission = await requestPermission("audio"); //For Requesting Audio Permission
} catch (err) {
console.error("requestPermission failed:", err);
}
let requestVideoPermission;
try {
requestVideoPermission = await requestPermission("video"); //For Requesting Video Permission
} catch (err) {
console.error("requestPermission failed:", err);
}
let requestAudioVideoPermission;
try {
requestAudioVideoPermission = await requestPermission("audio_video"); //For Requesting Audio and Video Permissions
} catch (err) {
console.error("requestPermission failed:", err);
}

// Applicable only to Android; not required for iOS
let checkBTPermission;
try {
checkBTPermission = await requestBluetoothPermission(); //For requesting Bluetooth Permission.
} catch (err) {
console.error("requestBluetoothPermission failed:", err);
}
} catch (ex) {
console.log("Error in requestPermission ", ex);
}
};

Render Device List

  • Once you have the necessary permissions, Fetch and render list of available camera, microphone, and list of all devices using the getCameras(), getAudioDeviceList() and getDevices() methods of the useMediaDevice hook respectively.
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;
try {
webcams = await getCameras();
} catch (err) {
console.error("getCameras failed:", err);
}
console.log("List of Devices:", webcams);
//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;
try {
mics = await getAudioDeviceList();
} catch (err) {
console.error("getAudioDeviceList failed:", err);
}
console.log("List of Microphone:", mics);
//Method to get all available cameras and playback devices.
//It returns a list of the currently available media input and output devices, such as microphones, cameras, headsets, and so forth
let deivces;
try {
deivces = await getDevices();
} catch (err) {
console.error("getDevices failed:", err);
}
console.log("List of Cameras:", devices);
} catch (err) {
console.log("Error in getting audio or video devices", err);
}
};

Handle Device Changes

  • Implement the onAudioDeviceChanged 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({ onAudioDeviceChanged });

//Fetch camera, mic and speaker devices again using this function.
function onAudioDeviceChanged(device) {
console.log("Device Changed", device)
}

Create Media Tracks

  • 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.
  • Hold both streams in your component state, so the pre-call test and the MeetingProvider below can use them.
import {
createCameraVideoTrack,
createMicrophoneAudioTrack,
} from "@videosdk.live/react-native-sdk";

//For Getting Audio Tracks
const getMediaTracks = async () => {
try {
//Returns a MediaStream object, containing the Audio Stream from the selected Mic Device.
let customTrack = await createMicrophoneAudioTrack({
encoderConfig: "speech_standard",
noiseConfig: {
noiseSuppression: true,
echoCancellation: true,
autoGainControl: true,
},
});
} 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.
let customVideoTrack = await createCameraVideoTrack({
optimizationMode: "motion",
encoderConfig: "h720p_w1280p",
facingMode: "user",
});
//To retrive video tracks that will be displayed to the user from the stream.
const videoTracks = customVideoTrack?.getVideoTracks();
const videoTrack = videoTracks.length ? videoTracks[0] : null;
} catch (error) {
console.log("Error in getting Video Track", error);
}
};

Network Quality Assessment

important

The getNetworkStats() method has been removed in React Native SDK v0.11.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 how well the network carries a real call — uplink and downlink, audio and video — and returns a quality score along with the raw stats behind it.

Parameters

The runPreCallTest() method accepts the following parameters:

  • token:

    • The authentication token used to authorize the test.
    • 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, between 10000 and 120000.
    • This is an OPTIONAL parameter.
    • Default: 15000 ms
  • videoTrack:

    • A camera MediaStream you have already created with createCameraVideoTrack(), such as the one rendered on your precall screen.
    • A track you pass in is never stopped for you, whatever the outcome — it stays yours to manage after the test.
    • This is an OPTIONAL parameter.
  • audioTrack:

    • A microphone MediaStream you have already created with createMicrophoneAudioTrack().
    • A track you pass in is never stopped for you, whatever the outcome — it stays yours to manage after the test.
    • This is an OPTIONAL parameter.
  • videoConfig:

    • Configuration used to acquire the camera track when videoTrack is not provided.
    • Accepts the same parameters as createCameraVideoTrack()cameraId, encoderConfig, facingMode, optimizationMode, multiStream, bitrateMode and maxLayer.
    • The track created from this configuration is returned live in the result, so you can hand it straight to MeetingProvider.
    • This is an OPTIONAL parameter.
  • audioConfig:

    • Configuration used to acquire the microphone track when audioTrack is not provided.
    • Accepts the same parameters as createMicrophoneAudioTrack()microphoneId, encoderConfig and noiseConfig (echoCancellation, autoGainControl, noiseSuppression).
    • The track created from this configuration is returned live in the result, so you can hand it straight to MeetingProvider.
    • This is an OPTIONAL parameter.
  • audioOnly:

    • Skips the camera test and runs the pre-call test using audio only; the result's camera field is then null.
    • It has to be of Boolean type.
    • This is an OPTIONAL parameter.
    • Default: false
  • onStatsChange:

    • A callback invoked roughly once per second during sampling. It receives the same { uplink, downlink } object that networkQuality carries in the result.
    • It has to be of Function type.
    • This is an OPTIONAL parameter.
caution

Each of these rules rejects the test with ERROR_PRECALL_INVALID_CONFIG, and the specific cause is preserved in err.message as a trailing detail:

  • samplingDuration has to be a finite number inside the 10000–120000 ms range.
  • Do not pass videoTrack and videoConfig together, and do not pass audioTrack and audioConfig together — only one option from each pair is allowed.
  • Do not combine audioOnly: true with videoTrack or videoConfig.
  • The encoderConfig you pass has to be one of the supported profiles.

Example

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

//Starts the pre-call test and returns a Promise that resolves with the test result.
//Here, customTrack and customVideoTrack are the tracks created in the previous step.
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,
//Camera MediaStream to use for the test.
videoTrack: customVideoTrack,
//Microphone MediaStream to use for the test.
audioTrack: customTrack,
//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 exposed on the Promise that runPreCallTest() returns, not on the promises that .then() produces. Keep a reference to the original Promise, as in the example above, when you need to cancel the test.

caution

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

Result

The Promise resolves with an object containing:

  • aborted: true when the test was cancelled with .stop(), otherwise false.
  • testDuration: Total test time in milliseconds.
  • camera: Camera result. On success it carries status: true, the track that was tested as a MediaStream, its captureResolution and fps, and the negotiated codec. When the camera could not be acquired it carries status: false and an error object with the code and message. It is null in audio-only mode and when the test was cancelled before the media check finished.
  • microphone: Microphone result, with the same success and failure shapes as camera apart from captureResolution and fps. It is null when the test was cancelled before the media check finished.
  • networkQuality: An object with uplink and downlink. It is null whenever the test was cancelled.

Each of networkQuality.uplink and networkQuality.downlink contains:

  • quality: Overall score from 1 (BAD) to 5 (EXCELLENT), taken as the lower of the audio and video sub-scores. It is 0 when no sub-score could be computed.
  • factors: An array of strings explaining the score — rtt, packetLoss, jitter, the encoder's qualityLimitationReason (bandwidth, cpu or other) on uplink, and freeze, framesDropped and audioConcealment on downlink.
  • audio: Audio metrics with their own quality sub-score. It is null when the microphone could not be acquired.
  • video: Video metrics with their own quality sub-score. It is null in audio-only mode and when the camera could not be acquired.

Both audio and video carry rtt, bitrate, packetLoss and jitter; video adds fps and resolution. The rest differs by direction: uplink reports what was sent (bytesSent, plus qualityLimitationReason on video), while downlink reports what was received (bytesReceived, plus framesDropped, framesDroppedRatio, freezeCount and totalFreezesDuration on video).

Errors

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

import { PreCallTestError } from "@videosdk.live/react-native-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_CONFIGsamplingDuration is not a finite number or falls outside the 10000–120000 ms range; videoTrack and videoConfig (or audioTrack and audioConfig) were passed together; audioOnly: true was combined with videoTrack or videoConfig; or an encoderConfig is not a known profile.
ERROR_PRECALL_TEST_ALREADY_RUNNINGAnother pre-call test is still in flight. Stop it before starting a new one.
ERROR_PRECALL_AFTER_INITA meeting has already been initialized. Run the test before MeetingProvider initializes the meeting.
ERROR_PRECALL_MEDIA_CHECK_FAILEDNeither the camera nor the 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 is available.
ERROR_CAMERA_ACCESS_DENIED_OR_DISMISSED / ERROR_MICROPHONE_ACCESS_DENIED_OR_DISMISSEDThe permission prompt was denied or dismissed.
ERROR_CAMERA_IN_USE / ERROR_MICROPHONE_IN_USEThe device is held by another application.
ERROR_CAMERA_CONSTRAINT_NOT_SATISFIED / ERROR_MICROPHONE_CONSTRAINT_NOT_SATISFIEDThe requested resolution, frame rate, sample rate, or channel count cannot be produced.
ERROR_WEBCAM_TRACK_ENDED / ERROR_MICROPHONE_TRACK_ENDEDThe track is not live — either a track you supplied has already ended, or a newly created one came back not live.
ERROR_INVALID_CUSTOM_VIDEO_TRACK / ERROR_INVALID_CUSTOM_AUDIO_TRACKThe value you passed is not a MediaStream, or contains no track of that kind.
ERROR_VIDEO_SOURCE_INITIATION_FAILED / ERROR_AUDIO_SOURCE_INITIATION_FAILEDAny other device-acquisition failure.

The message on the error carries the human-readable text for the code, with the specific cause appended after an em dash where one applies.

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,
//customVideoTrack is the Video Stream of the user's selected Webcam device, created in the Create Media Tracks step.
customCameraVideoTrack: customVideoTrack,
//customTrack is the Audio Stream of the user's selected Microphone device, created in the Create Media Tracks step.
customMicrophoneAudioTrack: customTrack
}
}
//The user already chose to join on this precall screen, so there is no
//join button inside MeetingProvider. Let the SDK join for you, and do not
//call join() from a mount effect: it runs before MeetingProvider has
//configured the token, and fails with 4002 INVALID_TOKEN.
//This is a prop on MeetingProvider, not a key inside config.
joinWithoutUserInteraction={true}
>
</MeetingProvider>
note

You can explore the complete implementation of the Precall functions in the official React Native 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