Introduction

Integrating a Video SDK with Expo (React Native) can be a powerful way to add real-time video communication features to your mobile application. In this guide, we'll walk you through the process of integrating the VideoSDK with your Expo project, step by step. By the end of this tutorial, you'll have a solid understanding of how to set up and configure the VideoSDK, and use its features in your Expo app.

Getting Started

We must use the capabilities that the VideoSDK offers. Before dipping into the implementation steps, ensure you complete the necessary prerequisites.

Create a VideoSDK Account

Go to your VideoSDK dashboard and sign up if you don't have an account. This account gives you access to the required Video SDK token, which acts as an authentication key that allows your application to interact with VideoSDK functionality.

Generate your Auth Token

Visit your VideoSDK dashboard and navigate to the "API Key" section to generate your auth token. This token is crucial in authorizing your application to use VideoSDK features.

For a more visual understanding of the account creation and token generation process, consider referring to the provided tutorial.

Prerequisites and Setup

Make sure your development environment meets the following requirements:

  • Node.js v12+
  • NPM v6+ (comes installed with newer Node versions)

Project Structure

Directory Structure

   root
   ├── node_modules
   ├── App.js
   ├── api.js

Install VideoSDK Config.

It is necessary to set up VideoSDK within your project before going into the details. Installing VideoSDK using NPM or Yarn will depend on the needs of your project.

For NPM

npm install "@videosdk.live/react-native-sdk"  "@videosdk.live/react-native-incallmanager"

For Yarn

yarn add "@videosdk.live/react-native-sdk" "@videosdk.live/react-native-incallmanager"

Integrating React Native VideoSDK with Expo

Video SDK Image

This guide details integrating VideoSDK seamlessly into your Expo project. Expo is a popular framework for building cross-platform mobile applications using React Native.

Our SDK utilizes native code and cannot be directly used in the Expo Go app. However, you can leverage the expo-dev-client library to run your Expo app with a development build for testing purposes.

Creating a New Expo Project

Open your terminal and run the following command to create a new Expo project:

npx create-expo-app my-video-app
  • Replace my-video-app with your desired project name.

Navigate to your project directory:

cd my-video-app

Installing the VideoSDK

Use the following commands to install the VideoSDK and its dependencies:

npx expo install @videosdk.live/react-native-sdk
npx expo install @videosdk.live/react-native-webrtc
npx expo install @videosdk.live/react-native-incallmanager
npx expo install @videosdk.live/react-native-react-native-foreground-service
npx expo install @videosdk.live/expo-config-plugin
npx expo install @config-plugins/react-native-webrtc

Adding Configuration Plugins

Update your app.json file to include the configuration plugins:

{
  "expo": {
    // ...
    "plugins": [
      "@videosdk.live/expo-config-plugin",
      [
        "@config-plugins/react-native-webrtc",
        {
          "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera",
          "microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone"
        }
      ]
    ]
  }
}

This adds the @videosdk.live/expo-config-plugin and @config-plugins/react-native-webrtc plugins.
The @config-plugins/react-native-webrtc plugin configuration optionally allows you to customize permission request messages for iOS.

Updating Native Folders

Run the following command to update the native folders with the added plugins:

npx expo prebuild

Troubleshooting: Expo 50+ Compatibility

Expo versions above 50 use event-target-shim@5, which conflicts with react-native-webrtc's dependency on event-target-shim@6. To resolve this:

if the project doesn't have metro.config.js file, then create a new metro.config.js file and paste below code.

const { getDefaultConfig } = require("expo/metro-config");
const resolveFrom = require("resolve-from");

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

config.resolver.resolveRequest = (context, moduleName, platform) => {
  if (
    // If the bundle is resolving "event-target-shim" from a module that is part of "react-native-webrtc".
    moduleName.startsWith("event-target-shim") &&
    context.originModulePath.includes("@videosdk.live/react-native-webrtc")
  ) {
    const updatedModuleName = moduleName.endsWith("/index")
      ? moduleName.replace("/index", "")
      : moduleName;

    // Resolve event-target-shim relative to the react-native-webrtc package to use v6.
    // React Native requires v5 which is not compatible with react-native-webrtc.
    const eventTargetShimPath = resolveFrom(
      context.originModulePath,
      updatedModuleName
    );

    return {
      filePath: eventTargetShimPath,
      type: "sourceFile",
    };
  }

  // Ensure you call the default resolver.
  return context.resolveRequest(context, moduleName, platform);
};

module.exports = config;

Registering Services

In your project's main App.js file, register the VideoSDK services:

import { register } from "@videosdk.live/react-native-sdk";
import { registerRootComponent } from "expo";

// Register VideoSDK services before app component registration
register();
registerRootComponent(App);

export default function App() {}

After installing the VideoSDK in your Expo project, you can start using its features to add real-time video communication to your app. The VideoSDK provides a comprehensive set of APIs and components that allow you to create, join, and manage video meetings, as well as control various aspects of the meeting experience.

Essential Steps to Implement the Video Calling Functionality

Step 1: Get started with api.js

Before moving on, you must create an API request to generate a unique meetingId. You will need an authentication token, which you can create either through the videosdk-rtc-api-server-examples or directly from the VideoSDK Dashboard for developers.

export const token = "<Generated-from-dashbaord>";
// API call to create meeting
export const createMeeting = async ({ token }) => {
  const res = await fetch(`https://api.videosdk.live/v2/rooms`, {
    method: "POST",
    headers: {
      authorization: `${token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({}),
  });

  const { roomId } = await res.json();
  return roomId;
};

Step 2: Wireframe App.js with all the components

To build up a wireframe of App.js, you need to use VideoSDK Hooks and Context Providers. VideoSDK provides MeetingProvider, MeetingConsumer, useMeeting, and useParticipant hooks.

First, you need to understand the Context Provider and Consumer. Context is primarily used when some data needs to be accessible by many components at different nesting levels.

  • MeetingProvider: This is the Context Provider. It accepts value config and token as a prop. The Provider component accepts a value prop to be passed to consuming components that are descendants of this Provider. One Provider can be connected to many consumers. Providers can be nested to override values deeper within the tree.
  • MeetingConsumer: This is the Context Consumer. All consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes.
  • useMeeting: This is the meeting hook API. It includes all the information related to meetings such as join, leave, enable/disable the mic or webcam, etc.
  • useParticipant: This is the participant hook API. It is responsible for handling all the events and props related to one particular participant such as name, webcamStream, micStream, etc.

The Meeting Context provides a way to listen for any changes that occur when a participant joins the meeting or makes modifications to their microphone, camera, and other settings.

Begin by making a few changes to the code in the App.js file.

import React, { useState } from "react";
import {
  SafeAreaView,
  TouchableOpacity,
  Text,
  TextInput,
  View,
  FlatList,
} from "react-native";
import {
  MeetingProvider,
  useMeeting,
  useParticipant,
  MediaStream,
  RTCView,
} from "@videosdk.live/react-native-sdk";
import { createMeeting, token } from "./api";

function JoinScreen(props) {
  return null;
}

function ControlsContainer() {
  return null;
}

function MeetingView() {
  return null;
}

export default function App() {
  const [meetingId, setMeetingId] = useState(null);

  const getMeetingId = async (id) => {
    const meetingId = id == null ? await createMeeting({ token }) : id;
    setMeetingId(meetingId);
  };

  return meetingId ? (
    <SafeAreaView style={{ flex: 1, backgroundColor: "#F6F6FF" }}>
      <MeetingProvider
        config={{
          meetingId,
          micEnabled: false,
          webcamEnabled: true,
          name: "Test User",
        }}
        token={token}
      >
        <MeetingView />
      </MeetingProvider>
    </SafeAreaView>
  ) : (
    <JoinScreen getMeetingId={getMeetingId} />
  );
}

Step 3: Implement Join Screen

The join screen will serve as a medium to either schedule a new meeting or join an existing one.

function JoinScreen(props) {
  const [meetingVal, setMeetingVal] = useState("");
  return (
    <SafeAreaView
      style={{
        flex: 1,
        backgroundColor: "#F6F6FF",
        justifyContent: "center",
        paddingHorizontal: 6 * 10,
      }}
    >
      <TouchableOpacity
        onPress={() => {
          props.getMeetingId();
        }}
        style={{ backgroundColor: "#1178F8", padding: 12, borderRadius: 6 }}
      >
        <Text style={{ color: "white", alignSelf: "center", fontSize: 18 }}>
          Create Meeting
        </Text>
      </TouchableOpacity>

      <Text
        style={{
          alignSelf: "center",
          fontSize: 22,
          marginVertical: 16,
          fontStyle: "italic",
          color: "grey",
        }}
      >
        ---------- OR ----------
      </Text>
      <TextInput
        value={meetingVal}
        onChangeText={setMeetingVal}
        placeholder={"XXXX-XXXX-XXXX"}
        style={{
          padding: 12,
          borderWidth: 1,
          borderRadius: 6,
          fontStyle: "italic",
        }}
      />
      <TouchableOpacity
        style={{
          backgroundColor: "#1178F8",
          padding: 12,
          marginTop: 14,
          borderRadius: 6,
        }}
        onPress={() => {
          props.getMeetingId(meetingVal);
        }}
      >
        <Text style={{ color: "white", alignSelf: "center", fontSize: 18 }}>
          Join Meeting
        </Text>
      </TouchableOpacity>
    </SafeAreaView>
  );
}

Step 4: Implement Controls

The next step is to create a ControlsContainer component to manage features such as Join or leave a Meeting and Enable or Disable the Webcam/Mic.

In this step, the useMeeting hook is utilized to acquire all the required methods such as join(), leave(), toggleWebcam and toggleMic.

const Button = ({ onPress, buttonText, backgroundColor }) => {
  return (
    <TouchableOpacity
      onPress={onPress}
      style={{
        backgroundColor: backgroundColor,
        justifyContent: "center",
        alignItems: "center",
        padding: 12,
        borderRadius: 4,
      }}
    >
      <Text style={{ color: "white", fontSize: 12 }}>{buttonText}</Text>
    </TouchableOpacity>
  );
};

function ControlsContainer({ join, leave, toggleWebcam, toggleMic }) {
  return (
    <View
      style={{
        padding: 24,
        flexDirection: "row",
        justifyContent: "space-between",
      }}
    >
      <Button
        onPress={() => {
          join();
        }}
        buttonText={"Join"}
        backgroundColor={"#1178F8"}
      />
      <Button
        onPress={() => {
          toggleWebcam();
        }}
        buttonText={"Toggle Webcam"}
        backgroundColor={"#1178F8"}
      />
      <Button
        onPress={() => {
          toggleMic();
        }}
        buttonText={"Toggle Mic"}
        backgroundColor={"#1178F8"}
      />
      <Button
        onPress={() => {
          leave();
        }}
        buttonText={"Leave"}
        backgroundColor={"#FF0000"}
      />
    </View>
  );
}

ControlsContainer Component

function ParticipantList() {
  return null;
}
function MeetingView() {
  const { join, leave, toggleWebcam, toggleMic, meetingId } = useMeeting({});

  return (
    <View style={{ flex: 1 }}>
      {meetingId ? (
        <Text style={{ fontSize: 18, padding: 12 }}>
          Meeting Id :{meetingId}
        </Text>
      ) : null}
      <ParticipantList />
      <ControlsContainer
        join={join}
        leave={leave}
        toggleWebcam={toggleWebcam}
        toggleMic={toggleMic}
      />
    </View>
  );
}

MeetingView Component

Step 5: Render Participant List

After implementing the controls, the next step is to render the joined participants.

You can get all the joined participants from the useMeeting Hook.

function ParticipantView() {
  return null;
}

function ParticipantList({ participants }) {
  return participants.length > 0 ? (
    <FlatList
      data={participants}
      renderItem={({ item }) => {
        return <ParticipantView participantId={item} />;
      }}
    />
  ) : (
    <View
      style={{
        flex: 1,
        backgroundColor: "#F6F6FF",
        justifyContent: "center",
        alignItems: "center",
      }}
    >
      <Text style={{ fontSize: 20 }}>Press Join button to enter meeting.</Text>
    </View>
  );
}

ParticipantList Component

function MeetingView() {
  // Get `participants` from useMeeting Hook
  const { join, leave, toggleWebcam, toggleMic, participants } = useMeeting({});
  const participantsArrId = [...participants.keys()];

  return (
    <View style={{ flex: 1 }}>
      <ParticipantList participants={participantsArrId} />
      <ControlsContainer
        join={join}
        leave={leave}
        toggleWebcam={toggleWebcam}
        toggleMic={toggleMic}
      />
    </View>
  );
}

MeetingView Component

Step 6: Handling Participant's Media

Before Handling the Participant's Media, you need to understand a couple of concepts.

[a] useParticipant Hook

The useParticipant hook is responsible for handling all the properties and events of one particular participant who joined the meeting. It will take participantId as argument.

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

useParticipant Hook Example

[b] MediaStream API

The MediaStream API is beneficial for adding a MediaTrack to the RTCView component, enabling the playback of audio or video.

<RTCView
  streamURL={new MediaStream([webcamStream.track]).toURL()}
  objectFit={"cover"}
  style={{
    height: 300,
    marginVertical: 8,
    marginHorizontal: 8,
  }}
/>

useParticipant Hook Example

[c] Rendering Participant Media

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

  return webcamOn && webcamStream ? (
    <RTCView
      streamURL={new MediaStream([webcamStream.track]).toURL()}
      objectFit={"cover"}
      style={{
        height: 300,
        marginVertical: 8,
        marginHorizontal: 8,
      }}
    />
  ) : (
    <View
      style={{
        backgroundColor: "grey",
        height: 300,
        justifyContent: "center",
        alignItems: "center",
      }}
    >
      <Text style={{ fontSize: 16 }}>NO MEDIA</Text>
    </View>
  );
}

We hope this guide has been helpful in getting you started with the VideoSDK in your Expo app. If you have any further questions or need additional support, be sure to check out the VideoSDK documentation and community resources.

Start a Video & Audio Call in React Native - Video SDK Docs | Video SDK
Build customizable real-time video & audio calling applications in React Native Android SDK using Video SDK add live Video & Audio conferencing to your applications.
Video SDK Image

Conclusion

In this guide, we have covered the steps to integrate the VideoSDK with your Expo (React Native) project. By following these steps, you will be able to add real-time video communication features to your mobile application, allowing your users to connect and collaborate in new ways.

By integrating the VideoSDK, you can provide your users with a powerful and flexible video communication solution that is easy to set up and customize. With features like screen sharing, recording, and moderation tools, you can create a meeting experience that meets the needs of your users and your business.

We hope this guide has been helpful in getting you started with the VideoSDK in your Expo app. If you have any further questions or need additional support, be sure to check out the VideoSDK documentation and community resources.

Remember, VideoSDK offers a generous free tier that includes 10,000 minutes allowing you to experiment and build your video call application without initial investment. Start your free trial today and unlock the potential streaming in your React Native app!