Skip to main content
Version: 2.x.x

Migration Guide - v1.x.x → v2.0.0

This guide covers what you need to change when upgrading the React Native SDK from v1.0.1 to v2.0.0.

v2.0.0 changes the install and the native setup. MeetingProvider is now RoomProvider, useMeeting() is now useRoom(), and many fields, methods and callbacks are renamed. Some APIs are gone, and several callbacks, defaults and error codes behave differently.

Update the dependency first:

npm install @videosdk.live/react-native-sdk@2.0.0
caution

v2.0.0 raises the platform requirements:

  • Android: set minSdkVersion to 24 (section 2). Run the app on an arm64 (arm64-v8a) device or emulator image.
  • Android permissions: declare INTERNET, ACCESS_NETWORK_STATE, CAMERA, RECORD_AUDIO, MODIFY_AUDIO_SETTINGS, POST_NOTIFICATIONS and the Bluetooth permissions in your app's AndroidManifest.xml. The SDK does not add them.
  • iOS: set your deployment target to 18.0.
  • React Native: 0.74 or later, with React 18.0 or later.

Sections 1–4 change your project setup. The app does not build or start until they are done. Sections 5–10 mostly show up as TypeScript or native compile errors, although several of them fail silently in plain JavaScript. Sections 11–15 can compile without a single error and still change what your app does at runtime, so test each of those paths on a device.


1. Remove register() and the WebRTC packages​

register() is gone. Delete every import and call of it. Most apps call it in index.js, or in the file that calls registerRootComponent() if they use Expo. Some also call it at the top of App.js. A call you leave behind throws a TypeError when the app starts.

Before

index.js
import { register } from "@videosdk.live/react-native-sdk";
import { AppRegistry } from "react-native";
import { name as appName } from "./app.json";
import App from "./src/App";

register();
AppRegistry.registerComponent(appName, () => App);

After

index.js
import { AppRegistry } from "react-native";
import { name as appName } from "./app.json";
import App from "./src/App";

AppRegistry.registerComponent(appName, () => App);

Key points:

  • Uninstall @videosdk.live/react-native-incallmanager (npm uninstall @videosdk.live/react-native-incallmanager). v2.0.0 does not use it. Do the same for @videosdk.live/react-native-webrtc if you added it to your own package.json.
  • Uninstall @videosdk.live/react-native-media-effects as well. It needs react-native-webrtc, so it breaks the native build (section 16).
  • v2.0.0 does not install navigator.mediaDevices or any other WebRTC globals. Use getCameras(), getMics() and the permission methods of useMediaDevice(), and createCameraVideoTrack() / createMicrophoneAudioTrack() to open the camera and microphone. On Android, ask for camera and microphone access yourself before joining (section 14).
  • Importing v1.x.x also installed global TextEncoder, TextDecoder, atob and btoa polyfills, whether or not you called register(). v2.0.0 installs no globals, so add your own polyfill if your code relies on them.

2. Android: remove the react-native-webrtc wiring and raise minSdkVersion​

Delete the react-native-webrtc lines that the v1.x.x setup added to your Android project. They break the Gradle build. Then raise minSdkVersion to 24.

Before

android/settings.gradle
include ':rnwebrtc'
project(':rnwebrtc').projectDir = new File(rootProject.projectDir, '../node_modules/@videosdk.live/react-native-webrtc/android')
android/app/build.gradle
dependencies {
implementation project(':rnwebrtc')
}
MainApplication.kt
import live.videosdk.rnwebrtc.WebRTCModulePackage

add(WebRTCModulePackage())
MainApplication.java
import live.videosdk.rnwebrtc.WebRTCModulePackage;

packages.add(new WebRTCModulePackage());
android/gradle.properties
android.enableDexingArtifactTransform.desugaring=false
android/app/src/main/AndroidManifest.xml
<service android:name="live.videosdk.rnwebrtc.MediaProjectionService" android:foregroundServiceType="mediaProjection" />
<service android:name="live.videosdk.rnwebrtc.MediaProjectionServiceTask" />
android/app/proguard-rules.pro
-keep class org.webrtc.** { *; }

After

android/build.gradle
buildscript {
ext {
minSdkVersion = 24
}
}
android/app/proguard-rules.pro
-keep class videosdk.org.** { *; }
-dontwarn videosdk.org.**

Key points:

  • Set minSdkVersion to 24 or later.
  • Delete everything in the Before block, including every live.videosdk.rnwebrtc entry in AndroidManifest.xml. Those classes no longer exist.
  • If you minify your release build, replace the org.webrtc rule with the two videosdk.org rules shown. The old rule no longer matches anything, so R8 removes classes the SDK needs. Keep both lines: the -keep rule alone fails the R8 step with a missing-class error.
  • The SDK now declares its own screen-share foreground service and the FOREGROUND_SERVICE and FOREGROUND_SERVICE_MEDIA_PROJECTION permissions, so screen share needs no manifest entries from you.
  • The SDK's React Native package is now live.videosdk.rncore.VideoSDKPackage, and autolinking adds it. If you register packages by hand, it replaces live.videosdk.rnpubsub.NativePubsubPackage.
  • If you set VideoSDKPubsub_kotlinVersion in gradle.properties, rename it to VideoSDKReactNative_kotlinVersion.

3. iOS: add the VideoSDKRTC pod​

Add the VideoSDKRTC pod from the podspec inside the package, remove the react-native-webrtc pod, and raise the platform to iOS 18.0.

Before

ios/Podfile
platform :ios, '12.0'

pod 'react-native-webrtc', :path => '../node_modules/@videosdk.live/react-native-webrtc'

After

ios/Podfile
platform :ios, '18.0'

target 'YourApp' do
# ...
pod 'VideoSDKRTC', :podspec => '../node_modules/@videosdk.live/react-native-sdk/ios/VideoSDKRTC.podspec'
end

Key points:

  • Add the line inside your app's target block, run pod install, and set the iOS Deployment Target of your app target to 18.0 in Xcode as well.
  • Without the VideoSDKRTC line, CocoaPods picks an older VideoSDKRTC from the public CocoaPods repository, which v2.0.0 does not work with.
  • The SDK's own pod is now VideoSDKReactNative and is autolinked. It replaces VideoSDKPubsub.
  • Upgrading from a 2.0.0 beta: run pod update VideoSDKRTC once in ios/. pod install keeps the VideoSDKRTC version locked in Podfile.lock.

4. Expo: upgrade the config plugin​

Version 1 of @videosdk.live/expo-config-plugin adds react-native-webrtc to the native projects it generates, which breaks a v2.0.0 build. Version 2 of the plugin is built for v2.0.0, so upgrade it.

npx expo install @videosdk.live/expo-config-plugin@2.0.0

Your plugin entry in app.json stays the same.

Key points:

  • The plugin adds the VideoSDKRTC Podfile line and the camera and microphone usage strings for you. Run npx expo prebuild --clean so the react-native-webrtc lines that version 1 wrote are removed.
  • It does not add Android permissions. Keep them under expo.android.permissions.
  • Uninstall @videosdk.live/react-native-incallmanager. You still need a development build.
  • Remove the event-target-shim workaround from metro.config.js. v2.0.0 does not need it.

5. Meeting names become Room names​

The provider, the hook and most fields, methods and callbacks are renamed. No old name is kept as an alias. In TypeScript every old name is a compile error. In plain JavaScript an old import is undefined, an old config key or callback is ignored, and an old method is not a function, so search your code for each name in the tables below.

Before

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

export default function App() {
return (
<MeetingProvider
config={{
meetingId: "meeting-id",
name: "Test User",
micEnabled: true,
webcamEnabled: true,
debugMode: false,
}}
token="your-token"
>
<MeetingView />
</MeetingProvider>
);
}

function MeetingView() {
const { join, leave, toggleMic, toggleWebcam, localMicOn, localWebcamOn } =
useMeeting({
onMeetingJoined: () => console.log("joined"),
onMeetingLeft: () => console.log("left"),
});

return (
<View>
<Button title="Join" onPress={() => join()} />
<Button title={localMicOn ? "Mute" : "Unmute"} onPress={() => toggleMic()} />
<Button
title={localWebcamOn ? "Camera off" : "Camera on"}
onPress={() => toggleWebcam()}
/>
<Button title="Leave" onPress={() => leave()} />
</View>
);
}

After

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

export default function App() {
return (
<RoomProvider
config={{
roomId: "room-id",
participantName: "Test User",
micEnabled: true,
camEnabled: true,
}}
token="your-token"
>
<RoomView />
</RoomProvider>
);
}

function RoomView() {
const { join, leave, toggleMic, toggleCam, isMicEnabled, isCamEnabled } =
useRoom({
onRoomJoined: () => console.log("joined"),
onRoomLeft: () => console.log("left"),
});

return (
<View>
<Button title="Join" onPress={() => join()} />
<Button title={isMicEnabled ? "Mute" : "Unmute"} onPress={() => toggleMic()} />
<Button
title={isCamEnabled ? "Camera off" : "Camera on"}
onPress={() => toggleCam()}
/>
<Button title="Leave" onPress={() => leave()} />
</View>
);
}

Provider and hooks

v1.0.1v2.0.0
MeetingProviderRoomProvider
MeetingConsumerRoomConsumer
useMeeting()useRoom()

RoomProvider takes config, token, joinWithoutUserInteraction and children. The deviceInfo and reinitialiseMeetingOnConfigChange props are gone.

RoomProvider config

v1.0.1v2.0.0
meetingIdroomId
name (required)participantName, optional. The SDK picks a random name when you leave it out.
webcamEnabled (required)camEnabled, optional, default true
micEnabled (required)micEnabled, optional, default true
customCameraVideoTrackcustomCameraTrack
customMicrophoneAudioTrackcustomMicrophoneTrack
debugMode (required)Removed
notificationRemoved. The SDK never showed it.
—enableBatching, default true: groups PubSub messages into batches

signalingBaseUrl, preferredProtocol and codecSwitchEnabled keep their names. Only roomId is required. Leave out any other option to get the SDK default: a random 8-character participantId, a random 6-character participantName, multiStream true, mode "SEND_AND_RECV", maxResolution "hd", preferredProtocol "UDP_OVER_TCP", autoConsume true and dynaCast false.

useRoom() fields

v1.0.1 (useMeeting())v2.0.0 (useRoom())
meetingroom
meetingIdroomId
localMicOnisMicEnabled
localWebcamOnisCamEnabled
localScreenShareOnisScreenSharing
livestreamStateliveStreamState
hlsUrls.downstreamUrlRemoved. hlsUrls.playbackHlsUrl and hlsUrls.livestreamUrl stay.
mainParticipantRemoved
characters, connectionsRemoved (section 16)
—e2eeEnabled, transcriptionState, translationState, selectedCameraDevice, selectedMicrophoneDevice

presenterId and activeSpeakerId keep their names. getActivePresenterId() and getActiveSpeakerId() return the same values.

useRoom() methods

v1.0.1 (useMeeting())v2.0.0 (useRoom())
enableWebcam(customTrack)enableCam(customCameraTrack)
disableWebcam()disableCam()
changeWebcam(deviceId)changeCam(deviceIdOrTrack)
unmuteMic(customAudioTrack)unmuteMic(customMicrophoneTrack)
toggleWebcam(customTrack)toggleCam(customCameraTrack). toggleMic() and toggleScreenShare() keep their names.
startHls(config), stopHls()startHLS(config, transcription), stopHLS()
switchTo({ meetingId, token })switchTo(roomId, token). token is optional.
requestMediaRelay({ destinationMeetingId, token, kinds })requestMediaRelay({ destinationRoomId, token, kinds }). kinds takes "audio", "video" and "share"; leave it out to relay every kind.
stopMediaRelay(destinationMeetingId)stopMediaRelay(destinationRoomId)
getMics(), getWebcams()getMics() and getCameras() from useMediaDevice() (section 14)
—send() (section 9), setWebcamQuality(), setVideoProcessor(), removeVideoProcessor(), setAudioDevice()

changeCam() and changeMic() with no argument move to the next camera or microphone in the device list, and wrap around after the last one.

Room callbacks (useRoom({ … }) and RoomConsumer)

v1.0.1v2.0.0
onMeetingJoined()onRoomJoined()
onMeetingLeft(reason)onRoomLeft(reason)
onMeetingStateChanged({ state })onRoomStateChanged({ roomState })
onRecordingStateChanged({ status })onRecordingStateChanged({ state })
onLivestreamStateChanged({ status })onLivestreamStateChanged({ state })
onLiveStreamStarted, onLiveStreamStoppedonLivestreamStarted, onLivestreamStopped
onHlsStateChanged({ status, playbackHlsUrl, livestreamUrl, downstreamUrl })onHlsStateChanged({ state, hlsUrl: { playbackHlsUrl, livestreamUrl } })
onHlsStarted, onHlsStoppedRemoved. Use onHlsStateChanged.
onMainParticipantChangedRemoved
onSwitchMeeting, onConnectionOpen, onConnetionClose, onAudioInputSilenceNone of them is called on React Native. Remove them.
onWebcamRequested, onMicRequested with { participantId, accept, reject }Unchanged
onPinStateChanged({ participantId, state, pinnedBy })Unchanged
onMediaRelayStarted({ meetingId })onMediaRelayStarted({ roomId })
onMediaRelayStopped({ meetingId, reason })onMediaRelayStopped({ roomId, reason })
onMediaRelayError({ meetingId, error })onMediaRelayError({ roomId, error })
onMediaRelayRequestResponse({ decision, decidedBy, meetingId })onMediaRelayRequestResponse({ decision, decidedBy, roomId }), where decision is upper case: "ACCEPTED" or "REJECTED" (Constants.RelayDecision)
onMediaRelayRequestReceived({ participantId, meetingId, displayName, accept, reject })onMediaRelayRequestReceived({ participantId, roomId, displayName, accept, reject })
onCodecChanged({ …, causedBy }), where causedBy is an idonCodecChanged({ …, unsupportedRemoteCodec, causedBy }), where causedBy is the Participant
—onExternalCallRinging, onExternalCallAnswered, onExternalCallRejected and onExternalCallHangup, each with { callType }

onEntryRequested still gives you allow() and deny(), which now return a Promise. You can also answer with respondEntry(participantId, decision), where decision is "allowed" or "denied" (Constants.EntryDecision). onEntryResponded still receives two arguments, (participantId, decision).

Transcription, translation and the whiteboard stay on their own hooks: useTranscription(), useTranslation() and useWhiteboard().

useTranscription({ onTranscriptionStateChanged }) receives { state } instead of { status }.

Participants

These changes apply to useParticipant(), useAgentParticipant() and every participant object, such as room.participants, room.localParticipant and the participant that onParticipantJoined receives.

v1.0.1v2.0.0
enableWebcam(), disableWebcam()enableCam(), disableCam()
isMainParticipantRemoved
useRealtimeStore({ onValueChanged: ({ value, updatedBy }) => … })onValueChanged: (value, updatedBy) => …, where updatedBy is the Participant who wrote the value, or null once they have left
—isAgent() on useParticipant(); isAgent(), getAgentId() and getAgentState() on useAgentParticipant(), next to agentId and agentState

useParticipant().videoQuality stays undefined on React Native, and onVideoQualityChanged is not called.

Constants

v1.0.1v2.0.0
Constants.errorsConstants.VideoSDKError
Constants.modesConstants.Mode. CONFERENCE and VIEWER are gone.
Constants.AgentState values "idle", "listening", …"IDLE", "LISTENING", …, the values getAgentState() and onAgentStateChanged report
Constants.errors.*_MEETING_* names*_ROOM_* names, with the same codes (section 15)
—RoomState, FacingMode, MaxResolution, PinType, VideoQuality, MediaType, StreamKind, CaptureFlash, EncodingLayer, CustomCameraTrackConfig, CustomMicrophoneTrackConfig, VideoEffectsConfig, EntryDecision, RelayDecision, CallType, TranslationType, ConfigQuality and ConfigMode

The new enums hold the same strings the SDK already accepted, so a string literal you pass today keeps working.

Key points:

  • RTCView's mirror prop is now setMirror.

6. facingMode is "front" or "back", and some camera presets changed​

This compiles, but behaves differently. facingMode now takes and reports "front" and "back". An old value is not rejected: "user" and "environment" both open the front camera, so a back-camera flow silently shows the wrong camera.

v1.0.1v2.0.0
"user""front"
"environment""back"

Before

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

function BackCameraButton() {
const { getWebcams } = useMeeting();

const openBackCamera = async () => {
const webcams = await getWebcams();
const back = webcams.find((webcam) => webcam.facingMode === "environment");

return createCameraVideoTrack({
cameraId: back?.deviceId,
facingMode: "environment",
encoderConfig: "h144p_w192p",
});
};

return <Button title="Back camera" onPress={openBackCamera} />;
}

After

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

function BackCameraButton() {
const { getCameras } = useMediaDevice();

const openBackCamera = async () => {
const cameras = await getCameras();
const back = cameras.find((camera) => camera.facingMode === "back");

return createCameraVideoTrack({
cameraId: back?.deviceId,
facingMode: "back",
encoderConfig: "h180p_w240p",
});
};

return <Button title="Back camera" onPress={openBackCamera} />;
}

Key points:

  • getCameras() reports "front" or "back". v1.x.x reported "front" or "environment", so only a check for "environment" needs changing. videoConfig.facingMode in runPreCallTest() takes "front" / "back" too. Constants.FacingMode holds both values.
  • h144p_w192p is no longer a camera preset. h120p_w160p and h180p_w240p are the nearest 4:3 sizes.
  • New camera presets: h180p_w320p, h540p_w960p, h1080p_w1920p, h120p_w160p, h180p_w240p and h360p_w480p. Constants.CustomCameraTrackConfig lists every preset.

7. Screen share takes a track​

enableScreenShare() and toggleScreenShare() now take one optional argument: a track from createScreenShareVideoTrack(). The encoder preset and system audio are options of that track. The v1.x.x object argument is not an error in JavaScript, but v2.0.0 ignores it and shares with the default settings. On Android those settings include system audio, even for { enableAudio: false }.

Before

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

function ShareButton() {
const { enableScreenShare } = useMeeting();

const startShare = async () => {
await enableScreenShare({ enableAudio: true });
};

return <Button title="Share screen" onPress={startShare} />;
}

After

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

function ShareButton() {
const { enableScreenShare } = useRoom();

const startShare = async () => {
const track = await createScreenShareVideoTrack({
encoderConfig: "h720p_15fps",
withAudio: true, // Android only
});
await enableScreenShare(track);
};

return <Button title="Share screen" onPress={startShare} />;
}

Key points:

  • withAudio is a boolean that defaults to false. It works on Android only, where system audio is mixed into your microphone track, so muting the mic mutes both. On iOS it has no effect. If you passed withAudio: "enable" or "disable" in v1.x.x, pass true or false instead.
  • encoderConfig takes h360p_30fps, h480p_15fps, h480p_30fps, h720p_5fps, h720p_15fps (the default), h720p_30fps, h1080p_15fps or h1080p_30fps. multiStream defaults to false.
  • On Android, the screen-capture consent dialog appears when you create the track, not when you call enableScreenShare().
  • On Android, enableScreenShare() with no track shares system audio, mixed into your microphone track. That includes onPress={enableScreenShare}. To share without system audio, pass a track from createScreenShareVideoTrack(), where withAudio defaults to false.
  • Sharing has started when the participant's share stream fires onStreamEnabled.

8. E2EE: useKeyProvider() is replaced by setKeyProvider()​

useKeyProvider() and the keyProvider prop of the provider are gone. Create a BaseKeyProvider, set its key, and register it with setKeyProvider() before you join. The key applies to every room in your app.

Before

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

export default function App() {
const keyProvider = useKeyProvider({ discardFrameWhenCryptorNotReady: true });
keyProvider?.setSharedKey("<YOUR-SECURE-KEY>");

return (
<MeetingProvider
config={{
meetingId: "meeting-id",
name: "Test User",
micEnabled: true,
webcamEnabled: true,
debugMode: true,
}}
token="your-token"
keyProvider={keyProvider || null}
>
<MeetingView />
</MeetingProvider>
);
}

After

import {
BaseKeyProvider,
RoomProvider,
setKeyProvider,
} from "@videosdk.live/react-native-sdk";

// Register the key before the room joins
async function enableEncryption() {
const provider = new BaseKeyProvider();
provider.setSharedKey("<YOUR-SECURE-KEY>");
await setKeyProvider(provider);
}

export default function App() {
return (
<RoomProvider
config={{
roomId: "room-id",
participantName: "Test User",
}}
token="your-token"
>
<RoomView />
</RoomProvider>
);
}

Key points:

  • Call setKeyProvider() before join(), or before you render RoomProvider if you use joinWithoutUserInteraction. A provider set after the room joins does not encrypt that room.
  • setSharedKey() stores the key and returns nothing, so do not await it. It throws ERROR_INVALID_PARAMETER for an empty key.
  • new BaseKeyProvider() takes no arguments. discardFrameWhenCryptorNotReady is gone, so remove it.
  • removeKeyProvider() turns E2EE off for the next room. setKeyProvider(null) rejects with ERROR_INVALID_PARAMETER (3073), so call removeKeyProvider() instead. getKeyProvider() returns the provider you registered, or null.
  • setParticipantKey(participantId, key) sets or replaces the key for one participant, also during a room. It is used only when no shared key is set. It rejects with ERROR_INVALID_PARAMETER until you have called setKeyProvider(). To remove a key, pass null as the key, or call removeKey(participantId) on the registered provider.
  • To check whether media is encrypted, read e2eeEnabled from useRoom().
  • onE2EEStateChanged of useParticipant() reports EncryptionSuccess when encryption starts. Decryption failures are not reported.
  • removeKey(participantId) stops decrypting that participant's media at once.

9. onChatMessage is replaced by onDataReceived​

v1.x.x declared onChatMessage but never called it. v2.0.0 replaces it with onDataReceived, which fires for every message another participant sends with send(). send() is new.

Before

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

function ChatListener() {
useMeeting({
onChatMessage: ({ senderId, message }) => {
console.log("message from", senderId, message);
},
});

return null;
}

After

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

function DataChannel() {
const { send } = useRoom({
onDataReceived: ({ senderId, text, data, isBinary, reliable, timestamp }) => {
console.log("message from", senderId, isBinary ? data : text);
},
});

const sendHello = async () => {
try {
await send("hello");
} catch (error) {
console.warn("Not sent:", error.message);
}
};

return <Button title="Send" onPress={sendHello} />;
}

Key points:

  • onDataReceived receives { senderId, text, data, isBinary, reliable, timestamp }. A text message sets text. A binary message sets data, a Uint8Array. timestamp is a number.
  • send(message, reliable) accepts a string, an ArrayBuffer or a typed array, but not a Blob. reliable is a boolean that defaults to true. Pass false to send unreliably: send("hello", false).
  • send() resolves once the message is sent, not when it is delivered. It rejects when the message did not go out.

10. Video processors are set on the room and register in a new place​

Apply a processor with setVideoProcessor() and remove it with removeVideoProcessor(), both from useRoom(). They return a Promise and reject until a room exists, so call them after join(). On Android, native processors no longer register with react-native-webrtc's ProcessorProvider.

Before

import { VideoProcessor } from "@videosdk.live/react-native-webrtc";

function setEffect(enabled) {
if (enabled) {
VideoProcessor.applyVideoProcessor("ProcessorName");
} else {
VideoProcessor.removeVideoProcessor();
}
}
VideoEffectModule.kt
import live.videosdk.rnwebrtc.videoEffects.ProcessorProvider

// YourOwnBackgroundProcessor implements VideoFrameProcessorFactoryInterface
ProcessorProvider.addProcessor("ProcessorName", YourOwnBackgroundProcessor())

After

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

function EffectButton() {
const { setVideoProcessor, removeVideoProcessor } = useRoom();

// Call after join(); before that both reject
const setEffect = async (enabled) => {
try {
if (enabled) {
await setVideoProcessor("ProcessorName");
} else {
await removeVideoProcessor();
}
} catch (error) {
console.warn("Video processor:", error.code, error.message);
}
};

return <Button title="Effect on" onPress={() => setEffect(true)} />;
}
VideoEffectModule.kt
import live.videosdk.rncore.VideoProcessorRegistry

// YourOwnProcessor implements live.videosdk.rtc.android.listeners.VideoFrameProcessor
VideoProcessorRegistry.register("ProcessorName", YourOwnProcessor())

Key points:

  • v2.0.0 does not install @videosdk.live/react-native-webrtc, so an import of VideoProcessor from it no longer resolves.
  • Register each Android processor when your app starts. It now implements live.videosdk.rtc.android.listeners.VideoFrameProcessor directly, with no factory.
  • Its one method is onFrameCaptured(videoFrame: VideoFrame): VideoFrame, where VideoFrame is videosdk.org.webrtc.VideoFrame and arrives as I420. Change every org.webrtc import in a v1.x.x processor to videosdk.org.webrtc. There is no SurfaceTextureHelper argument, and the SDK releases both frames, so do not release them yourself.
  • On iOS, delete processor code that imports react_native_webrtc or calls ProcessorProvider. Both come from react-native-webrtc, so the iOS build fails until that code is gone. Write the processor as a class that conforms to VideoSDKVideoProcessor from VideoSDKRTC, and register it with VideoProcessorRegistry.shared.register(name:processor:) from VideoSDKReactNative. See Video Processor iOS Setup.
  • Applying a name that has no registered processor rejects with ERROR_INVALID_PARAMETER (3073).
  • Background blur and virtual backgrounds from @videosdk.live/react-native-media-effects do not work on v2.0.0. See section 16.

11. RoomProvider leaves the room when it unmounts​

This compiles, but behaves differently. RoomProvider leaves the room when it unmounts. Keep it mounted for as long as the call lasts. If it sits inside a screen that unmounts when the user navigates away, the call ends there.

After

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

// Render RoomProvider above every screen of the call
export default function CallScreen({ roomId, token }) {
return (
<RoomProvider config={{ roomId, participantName: "Test User" }} token={token}>
<CallNavigator />
</RoomProvider>
);
}

Key points:

  • To end the call from a button, call leave() from useRoom().

12. Streams and tracks are no longer WebRTC objects​

v1.x.x gave you react-native-webrtc's MediaStream and live MediaStreamTrack objects. In v2.0.0, a participant's stream (webcamStream, micStream, the values in participant.streams) is an RTCStream. You still render it by wrapping its track in the SDK's MediaStream, so that code does not change. A track you create with createCameraVideoTrack(), createMicrophoneAudioTrack() or createScreenShareVideoTrack() is a CustomStreamTrack, which renders with its own toURL().

Before

import {
createCameraVideoTrack,
MediaStream,
RTCView,
useParticipant,
} from "@videosdk.live/react-native-sdk";

function ParticipantView({ participantId }) {
const { webcamStream, webcamOn } = useParticipant(participantId);

return webcamOn && webcamStream ? (
<RTCView streamURL={new MediaStream([webcamStream.track]).toURL()} />
) : null;
}

const stream = await createCameraVideoTrack({ facingMode: "user" });

const preview = new MediaStream([]);
preview.addTrack(stream.getVideoTracks()[0]);

// objectFit defaulted to "contain"
<RTCView streamURL={preview.toURL()} />;

// Later, release the camera
stream.release();

After

import {
createCameraVideoTrack,
MediaStream,
RTCView,
useParticipant,
} from "@videosdk.live/react-native-sdk";

function ParticipantView({ participantId }) {
const { webcamStream, webcamOn } = useParticipant(participantId);

return webcamOn && webcamStream ? (
<RTCView
streamURL={new MediaStream([webcamStream.track]).toURL()}
objectFit="contain"
/>
) : null;
}

const track = await createCameraVideoTrack({ facingMode: "front" });

// objectFit now defaults to "cover"
<RTCView streamURL={track.toURL()} objectFit="contain" />;

// Later, free the camera if this track was never published
await track.stop();

Key points:

  • An RTCStream has id, kind, participantId, codec, track, paused, pause() and resume(). It has no toURL(): render it with new MediaStream([stream.track]).toURL().
  • A CustomStreamTrack has toURL(), getTracks(), getVideoTracks(), getAudioTracks(), stop(), isEnded and onEnded. stop() replaces release() and returns a Promise. It does nothing once a room has published the track. Set onEnded to a function to learn when the track ends. It is called once: with the error when the device fails, or with null when the track was stopped, replaced or the room was left. Set after the track ended, it is called at once.
  • createCameraVideoTrack() and createScreenShareVideoTrack() no longer take optimizationMode. bitrateMode takes "HIGH_QUALITY", "BALANCED" or "BANDWIDTH_OPTIMIZED".
  • RTCView renders only URLs built from SDK streams and tracks. Its objectFit now defaults to "cover" instead of "contain", so pass objectFit="contain" to keep the v1.x.x look. Its mirror prop is now setMirror.
  • Pass only tracks created by the SDK's track functions to RoomProvider, enableCam(), unmuteMic(), changeCam() and changeMic().
  • A stopped or released track is refused with ERROR_INVALID_CUSTOM_VIDEO_TRACK (3029) for video or ERROR_INVALID_CUSTOM_AUDIO_TRACK (3030) for audio, by enableCam(), changeCam(), unmuteMic(), changeMic() and enableScreenShare(). Create a new track instead.

13. iOS screen share: enableScreenShare() opens the broadcast picker​

This compiles, but behaves differently. In v1.x.x you showed the iOS broadcast picker from your own native module (VideosdkRPK) and called enableScreenShare() once the broadcast started. In v2.0.0, enableScreenShare() shows the picker itself, and capture starts when the user taps Start Broadcast. Call it straight from your button.

Before

import { useMeeting } from "@videosdk.live/react-native-sdk";
import VideosdkRPK from "./VideosdkRPK";

function ShareButton() {
const { enableScreenShare, disableScreenShare } = useMeeting();

useEffect(() => {
VideosdkRPK.addListener("onScreenShare", (event) => {
if (event === "START_BROADCAST") enableScreenShare();
if (event === "STOP_BROADCAST") disableScreenShare();
});
return () => VideosdkRPK.removeSubscription("onScreenShare");
}, []);

return (
<Button title="Share screen" onPress={() => VideosdkRPK.startBroadcast()} />
);
}

After

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

function ShareButton() {
const { enableScreenShare } = useRoom();

const startShare = async () => {
const track = await createScreenShareVideoTrack();
// Shows the iOS broadcast sheet; capture starts on "Start Broadcast"
await enableScreenShare(track);
};

return <Button title="Share screen" onPress={startShare} />;
}

Key points:

  • You no longer need the VideosdkRPK native module or its onScreenShare listener.
  • Keep your Broadcast Upload Extension and the RTCScreenSharingExtension and RTCAppGroupIdentifier keys in your app's Info.plist. v2.0.0 still needs both.
  • With Expo, keep the @videosdk.live/expo-ios-screen-share plugin in app.json. It creates the extension and these keys. Only stop using its VideosdkRPK module.

14. Ask for camera and microphone access on Android; device functions moved​

This compiles, but behaves differently. On Android, v2.0.0 no longer asks the user for camera and microphone access. In v1.x.x the prompts appeared when the room first turned the camera or microphone on. Now the camera and microphone do not start until your app has access, so call requestPermission() before you join. On iOS, createCameraVideoTrack() and createMicrophoneAudioTrack() do not ask either: call requestPermission() before you create a track, or the factory rejects until access is granted.

useMediaDevice() keeps the device lists and permissions. getMics() replaces getAudioDeviceList(), and audio output is switched on the room. requestPermission() resolves with false values when the user refuses, instead of throwing.

Before

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

function DeviceSetup() {
const [devices, setDevices] = useState([]);
const { requestPermission } = useMediaDevice({
onAudioDeviceChanged: ({ selectedAudioDevice, availableDevices }) => {
setDevices(availableDevices);
},
});

const askForAccess = async () => {
try {
await requestPermission(Constants.permission.AUDIO_AND_VIDEO);
} catch (error) {
openSettingsPrompt();
}
};

return null;
}

After

import { Constants, useMediaDevice, useRoom } from "@videosdk.live/react-native-sdk";

function DeviceSetup() {
const [devices, setDevices] = useState([]);
const { requestPermission, getMics } = useMediaDevice({
// devices is a Map of deviceId to device
onAudioDeviceChanged: (devices) => {
setDevices([...devices.values()]);
},
});
const { setAudioDevice } = useRoom();

// On Android, call this before you join
const askForAccess = async () => {
const granted = await requestPermission(Constants.permission.AUDIO_AND_VIDEO);
if (
!granted.get(Constants.permission.AUDIO) ||
!granted.get(Constants.permission.VIDEO)
) {
openSettingsPrompt();
}
};

const listAudioDevices = async () => setDevices(await getMics());

// Call after join(); pass one of the devices from getMics()
const chooseAudioDevice = (device) => setAudioDevice(device);

return null;
}

Device functions

v1.0.1v2.0.0
useMediaDevice().getCameras(), useMeeting().getWebcams()useMediaDevice().getCameras()
useMediaDevice().getAudioDeviceList(), useMeeting().getMics(), top-level getAudioDeviceList()useMediaDevice().getMics()
useMediaDevice().getDevices()Unchanged
useMediaDevice({ onAudioDeviceChanged: ({ selectedAudioDevice, availableDevices }) => … })onAudioDeviceChanged: (devices) => …, where devices is a Map of device id to device
switchAudioDevice(device)useRoom().setAudioDevice(device)
—getSelectedVideoDevice() and getSelectedAudioDevice() on useMediaDevice()

Key points:

  • useMediaDevice() works outside RoomProvider, so you can list devices and ask for access on your join screen. If you use joinWithoutUserInteraction, ask before you render RoomProvider.
  • In a live stream, ask before a host joins, and before changeMode("SEND_AND_RECV") turns a viewer into a host.
  • Cameras report facingMode: "front" or "back". getDevices() lists the microphones first, then the cameras. getMics() returns device objects instead of the route-name strings that the top-level getAudioDeviceList() returned.
  • setAudioDevice() takes a device from getMics() or one of "SPEAKER_PHONE", "EARPIECE", "WIRED_HEADSET" and "BLUETOOTH". Call it after join(). Before that it rejects.
  • EARPIECE is no longer left out of the audio device list when a headset or Bluetooth device is connected.
  • getSelectedVideoDevice() and getSelectedAudioDevice() return { deviceId }, or null while the SDK picks the device.

15. Camera and microphone errors use the general error codes​

This compiles, but behaves differently. Constants.errors is now Constants.VideoSDKError. No error code was renumbered, and no number changed meaning. Nine names that said MEETING now say ROOM (table below). What also changed is which codes reach you. Camera and microphone failures that v1.x.x reported with the React Native–specific ERROR_RN_* codes now arrive with the general codes. The ERROR_RN_* constants are still in Constants.VideoSDKError, so a check against them compiles and never matches.

Renamed, same code:

Codev1.0.1v2.0.0
3035ERROR_ACTION_PERFORMED_BEFORE_MEETING_JOINEDERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED
3044ERROR_MEETING_MEDIA_CONNECTION_FAILEDERROR_ROOM_MEDIA_CONNECTION_FAILED
3047ERROR_UNABLE_TO_JOIN_MEETINGERROR_UNABLE_TO_JOIN_ROOM
3057ERROR_END_MEETING_FAILEDERROR_END_ROOM_FAILED
3065ERROR_MEETING_RECONNECTINGERROR_ROOM_RECONNECTING
4003INVALID_MEETING_IDINVALID_ROOM_ID
4022UNAUTHORIZED_MEETING_IDUNAUTHORIZED_ROOM_ID
4052MEETING_ID_DISABLEDROOM_ID_DISABLED
4076SWITCH_MEETING_FAILEDSWITCH_ROOM_FAILED

Match these on code, or update the names you compare against.

Before

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

const { enableWebcam } = useMeeting();

try {
await enableWebcam();
} catch (error) {
if (error.code === Constants.errors.ERROR_RN_CAMERA_ACCESS_DENIED_OR_DISMISSED) {
showCameraPermissionHelp();
}
}

After

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

// 3017 ERROR_CAMERA_ACCESS_DENIED_OR_DISMISSED
// 3019 ERROR_CAMERA_PERMISSION_DENIED_BY_OS
const CAMERA_ACCESS_DENIED = [3017, 3019];

const { enableCam } = useRoom({
onError: (error) => {
if (CAMERA_ACCESS_DENIED.includes(error.code)) {
showCameraPermissionHelp();
}
},
});

Handle the general code instead:

v1.0.1v2.0.0
3036 ERROR_RN_CAMERA_ACCESS_DENIED_OR_DISMISSED3017 ERROR_CAMERA_ACCESS_DENIED_OR_DISMISSED, or 3019 ERROR_CAMERA_PERMISSION_DENIED_BY_OS when access was already denied in iOS Settings
3037 ERROR_RN_CAMERA_NOT_FOUND3021 ERROR_CAMERA_NOT_FOUND or 3051 ERROR_NO_WEBCAM_DEVICE_FOUND
3038 ERROR_RN_MIC_ACCESS_DENIED_OR_DISMISSED3018 ERROR_MICROPHONE_ACCESS_DENIED_OR_DISMISSED, or 3020 ERROR_MICROPHONE_PERMISSION_DENIED_BY_OS when access was already denied in iOS Settings
3039 ERROR_RN_MIC_NOT_FOUND3022 ERROR_MICROPHONE_NOT_FOUND or 3059 ERROR_NO_MICROPHONE_DEVICE_FOUND
3040 ERROR_RN_CAMERA_ACCESS_UNAVAILABLE3033 ERROR_CAMERA_ACCESS_UNAVAILABLE
3041 ERROR_RN_MIC_ACCESS_UNAVAILABLE3034 ERROR_MICROPHONE_ACCESS_UNAVAILABLE
3042 ERROR_RN_CAMERA_TRACK_ENDED3027 ERROR_WEBCAM_TRACK_ENDED
3043 ERROR_RN_MIC_TRACK_ENDED3028 ERROR_MICROPHONE_TRACK_ENDED

Key points:

  • onError receives { code, name, message }. A failed method call still rejects with an error that has a numeric code and, for every code in Constants.VideoSDKError, the matching name.
  • A call made after join() but before onRoomJoined, or while the room is reconnecting, behaves differently on each platform. On Android, most methods report ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED (3035) or ERROR_ROOM_RECONNECTING (3065) on onError and resolve without doing anything. changeCam(), changeMic(), the useFile() and PubSub methods, and a stream's pause() / resume() report the code on onError and also reject. On iOS, most methods reject with the same codes and do not call onError. changeMode(), the useFile() methods, a stream's pause() / resume() and participant actions such as pin(), unpin() and setQuality() call onError and also reject. A remote participant's enableMic(), disableMic(), enableCam() and disableCam() call onError and resolve. Handle both codes in onError and in catch.
  • On iOS, a camera or microphone that cannot be opened after enableCam() or unmuteMic() is reported only on onError, and the call resolves. Android reports it on onError and also rejects the call. Check these codes in onError.
  • An unexpected failure rejects with the string code ERROR_BRIDGE_UNEXPECTED. PreCallTestError uses the same code.
  • A wrong-typed or missing argument to any SDK call rejects with ERROR_INVALID_PARAMETER (3073). The same code also covers checks such as applying a video processor name that was never registered.
  • captureImage() failures that had no code in v1.x.x now carry SDK error codes. For example, on Android, capturing while the camera is off rejects with 3062 ERROR_WEBCAM_NOT_ENABLED. Call captureImage() only for the local participant.
  • Learn more: React Native SDK Reference

16. Removed APIs​

RemovedUse instead
register()Nothing. Delete every import and call (section 1).
mediaDevices, permissionsuseMediaDevice() for permissions and device lists, and createCameraVideoTrack() / createMicrophoneAudioTrack() for capture.
Live MediaStreamTrack objects, and the MediaStream methods other than toURL(), getTracks(), getVideoTracks() and getAudioTracks()Wrap a stream's track in new MediaStream([track]) only to render it (section 12).
useKeyProvider() and the keyProvider propBaseKeyProvider and setKeyProvider() (section 8)
getAudioDeviceList() and switchAudioDevice() (top-level)useMediaDevice().getMics() and useRoom().setAudioDevice() (section 14)
mainParticipant, isMainParticipant, onMainParticipantChanged, onHlsStarted, onHlsStopped, hlsUrls.downstreamUrlNo replacement. Use onHlsStateChanged for HLS.
debugMode, deviceInfo, reinitialiseMeetingOnConfigChange, notificationNothing. Remove them.
useCharacter(), the CharacterMode and CharacterState enums, useMeeting().characters, the onCharacterJoined and onCharacterLeft callbacks, Constants.characterState, Constants.characterMode and Constants.leaveReason.CHARACTER_REMOVEDNo replacement. For AI agents that join as participants, use useAgentParticipant().
useConnection(), useMeeting().connectionsNo replacement. Remove them.
External video: startVideo(), stopVideo(), pauseVideo(), resumeVideo(), seekVideo(), onVideoStateChanged, onVideoSeekedNo replacement. Remove them.
useParticipant().switchTo()useRoom().switchTo()
Virtual background from @videosdk.live/react-native-media-effects (applyBlurBackground, applyImageBackground, applyColorBackground)No replacement. The package needs react-native-webrtc, so it breaks a v2.0.0 build. Uninstall it. setVideoEffects() offers framing, low-light, style and colour filter effects, but no background blur or replacement.

17. Other changes​

APIv1.0.1v2.0.0
setLogLevel(level)Returned nothing.Returns a Promise. The first call also prints the SDK's logs to the JS console. With no level it applies "INFO". An unknown level rejects with ERROR_INVALID_PARAMETER (3073). Read the level in force with getLogLevel().
runPreCallTest()The result was typed with network, and videoConfig took "user" / "environment".The result is typed with networkQuality, the key the SDK always returned. videoConfig.facingMode takes "front" / "back", and videoConfig.encoderConfig no longer accepts h216p_w384p, h540p_w720p, h1440p_w1920p, h1440p_w2560p or h2160p_w3840p. Stop a running test with stopPreCallTest().
PreCallTestErrorTyped as a plain class.Typed as extending Error (it already did at runtime). An unexpected failure has the code ERROR_BRIDGE_UNEXPECTED.
RTCView propsIts own props only.Also accepts standard View props, and style accepts an array.
startRecording(), startHLS()No transcription argument.Take a post-transcription config as their last argument: { enabled, summary, modelId }.
useStream(streamId, { onStreamStateChanged })—Renamed useRTCStream(), with the same arguments and return values.
useRealtimeStore().setValue()Typed to take a string.Typed to take a string or null. null deletes the key.
Participant stats (getAudioStats(), getVideoStats(), getShareStats())—With nothing to report they resolve an empty array on Android and null on iOS. Handle both.
HLSConfig.recording—Accepted and ignored.

18. New in v2.0.0​

FeatureAPI
Realtime translationuseTranslation(): startTranslation(), changeTranslationLanguage(), stopTranslation(), and the onTranslationStateChanged, onTranslationLanguageChanged and onTranslationText callbacks. Set the languages with config.speakingLanguage and config.translationLanguage.
Camera effectssetVideoEffects(), getAppliedVideoEffectsConfig() and publishFramingSubject(): auto-framing, low-light, style and colour filter effects. Effects render only with pipeline: "GRAPH".
DynaCastconfig.dynaCast on RoomProvider. Needs multiStream: true.
Data channelsend() and onDataReceived on useRoom() (section 9)
Image capture with flashcaptureImage({ flash: "ON" }) (Constants.CaptureFlash.ON), for the local participant only. On a front camera the SDK lights the screen instead of a torch. To set that screen flash up before you capture, call setScreenFlash(true). setScreenFlash(false) or setScreenFlash(null) clears it.
Pre-call statsrunPreCallTest(options, onStatsChange) also takes the stats callback as its second argument
Custom video processors on iOSVideoProcessorRegistry from VideoSDKReactNative (section 10)
Screen-share trackcreateScreenShareVideoTrack({ encoderConfig, multiStream, withAudio }) (section 7)
Track endCustomStreamTrack.isEnded, and onEnded, called once with the error or null when a track you created ends
Microphone processinghighPassFilter in the noiseConfig of createMicrophoneAudioTrack()
Selected devicesgetSelectedVideoDevice() and getSelectedAudioDevice() (section 14)
Room stateroom.roomState and Constants.RoomState
Encryption keyssetParticipantKey() sets or rotates one participant's key, also during a room. removeKeyProvider() turns encryption off, and BaseKeyProvider.removeKey() removes one participant's key (section 8).
Camera settings in useroom.getAppliedVideoConfig()
Video qualitysetWebcamQuality() on useRoom() takes "low", "med" or "high" (Constants.VideoQuality), and setScreenShareQuality() on useParticipant().
Choose whose media you receiveconfig.autoConsume = false, with consumeMicStreams() / consumeWebcamStreams() and the matching stopConsuming…() methods on useParticipant()
Phone callsonExternalCallRinging, onExternalCallAnswered, onExternalCallRejected and onExternalCallHangup on useRoom()
E2EE stream stateonE2EEStateChanged on useParticipant()
Now in the TypeScript typesuseRoom().transcriptionState and translationState, Constants.translationEvents, and ConfigLayout, the type of layout in recording, HLS and livestream configs

Use the room.* methods on the room property of useRoom() (const { room } = useRoom();). room is null until you join.


API Reference​

RoomProvider, useRoom, useParticipant, useMediaDevice, end-to-end encryption, custom tracks, RTCView, Constants and the error codes are described in the React Native SDK Reference.

See also: Release Notes

Got a Question? Ask us on discord