DEV Community

Chetan Sandanshiv for Video SDK

Posted on • Originally published at videosdk.live on

Integrate Screen Share in React Native (Android) Video Call App?

πŸ“Œ Introduction

How to Integrate Screen Share in React Native (Android) Video Call App?

In today's digital world, good communication and teamwork are crucial. As people look for more effective methods to connect, video conferencing solutions have become indispensable. In that scenario, the Screen sharing feature has evolved into an integral element of modern communication and collaboration platforms, allowing users to share their device displays with others during meetings, presentations, and distant collaborations.

In this article, we'll look into how you can smoothly incorporate Screen Share capabilities with VideoSDK, allowing your users to easily share their displays. Let's start.

🎯 Goals

By the end of this article, we'll:

  1. Create a VideoSDK account and generate your VideoSDK auth token.
  2. Integrate the VideoSDK library and dependencies into your project.
  3. Implement core functionalities for video calls using VideoSDK.
  4. Enable Screen Sharing feature.

πŸš€ Getting Started with VideoSDK

To take advantage of the chat functionality, we must use the capabilities that the VideoSDK offers. Before diving into the implementation steps, let's 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)
  • Android Studio or Xcode installed

πŸ”Œ Integrate VideoSDK

It is necessary to set up VideoSDK within your project before going into the details of integrating the Screen Share feature. 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"
Enter fullscreen mode Exit fullscreen mode
  • For Yarn
yarn add "@videosdk.live/react-native-sdk" "@videosdk.live/react-native-incallmanager"
Enter fullscreen mode Exit fullscreen mode

Project Configuration

Before integrating the Screen Share functionality, ensure that your project is correctly prepared to handle the integration. This setup consists of a sequence of steps for configuring rights, dependencies, and platform-specific parameters so that VideoSDK can function seamlessly inside your application context.

Android Setup

  • Add the required permissions in the AndroidManifest.xml file.
<manifest
  xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.cool.app"
>
    <!-- Give all the required permissions to app -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!-- Needed to communicate with already-paired Bluetooth devices. (Legacy up to Android 11) -->
    <uses-permission
        android:name="android.permission.BLUETOOTH"
        android:maxSdkVersion="30" />
    <uses-permission
        android:name="android.permission.BLUETOOTH_ADMIN"
        android:maxSdkVersion="30" />

    <!-- Needed to communicate with already-paired Bluetooth devices. (Android 12 upwards)-->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <application>
   <meta-data
      android:name="live.videosdk.rnfgservice.notification_channel_name"
      android:value="Meeting Notification"
     />
    <meta-data
    android:name="live.videosdk.rnfgservice.notification_channel_description"
    android:value="Whenever meeting started notification will appear."
    />
    <meta-data
    android:name="live.videosdk.rnfgservice.notification_color"
    android:resource="@color/red"
    />
    <service android:name="live.videosdk.rnfgservice.ForegroundService" android:foregroundServiceType="mediaProjection"></service>
    <service android:name="live.videosdk.rnfgservice.ForegroundServiceTask"></service>
  </application>
</manifest>
Enter fullscreen mode Exit fullscreen mode
  • Update your colors.xml file for internal dependencies: android/app/src/main/res/values/colors.xml
<resources>
  <item name="red" type="color">
    #FC0303
  </item>
  <integer-array name="androidcolors">
    <item>@color/red</item>
  </integer-array>
</resources>
Enter fullscreen mode Exit fullscreen mode
  • Link the necessary VideoSDK Dependencies: android/app/build.gradle
  dependencies {
   implementation project(':rnwebrtc')
   implementation project(':rnfgservice')
  }
Enter fullscreen mode Exit fullscreen mode
  • android/settings.gradle
include ':rnwebrtc'
project(':rnwebrtc').projectDir = new File(rootProject.projectDir, '../node_modules/@videosdk.live/react-native-webrtc/android')

include ':rnfgservice'
project(':rnfgservice').projectDir = new File(rootProject.projectDir, '../node_modules/@videosdk.live/react-native-foreground-service/android')
Enter fullscreen mode Exit fullscreen mode
  • MainApplication.java
import live.videosdk.rnwebrtc.WebRTCModulePackage;
import live.videosdk.rnfgservice.ForegroundServicePackage;

public class MainApplication extends Application implements ReactApplication {
  private static List<ReactPackage> getPackages() {
      @SuppressWarnings("UnnecessaryLocalVariable")
      List<ReactPackage> packages = new PackageList(this).getPackages();
      // Packages that cannot be autolinked yet can be added manually here, for example:

      packages.add(new ForegroundServicePackage());
      packages.add(new WebRTCModulePackage());

      return packages;
  }
}

/* This one fixes a weird WebRTC runtime problem on some devices. */
android.enableDexingArtifactTransform.desugaring=false
Enter fullscreen mode Exit fullscreen mode
  • Include the following line in your proguard-rules.pro file (optional: if you are using Proguard)
-keep class org.webrtc.** { *; }
Enter fullscreen mode Exit fullscreen mode
  • In your build.gradle file, update the minimum OS/SDK version to 23.
buildscript {
  ext {
      minSdkVersion = 23
  }
}
Enter fullscreen mode Exit fullscreen mode

Register Service

Register VideoSDK services in your root index.js file for the initialization service.

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

register();

AppRegistry.registerComponent(appName, () => App);
Enter fullscreen mode Exit fullscreen mode

πŸŽ₯ Essential Steps for Building the Video Calling Functionality

By following essential steps, you can seamlessly implement video into your applications.

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;
};
Enter fullscreen mode Exit fullscreen mode

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 props. 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} />
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Implement Join Screen​

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

  • JoinScreen Component
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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Implement Controls​

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

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

  • ControlsContainer Component
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() {
  const { join, leave, toggleWebcam, toggleMic } = useMeeting();
  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>
  );
}
Enter fullscreen mode Exit fullscreen mode
  • MeetingView Component
function ParticipantList() {
  return null;
}
function MeetingView() {
  const { meetingId } = useMeeting({});

  return (
    <View style={{ flex: 1 }}>
      {meetingId ? (
        <Text style={{ fontSize: 18, padding: 12 }}>
          Meeting Id :{meetingId}
        </Text>
      ) : null}
      <ParticipantList />
      <ControlsContainer/>
    </View>
  );
}
Enter fullscreen mode Exit fullscreen mode

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.

  • ParticipantList Component
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>
  );
}
Enter fullscreen mode Exit fullscreen mode
  • MeetingView Component
function MeetingView() {
  // Get `participants` from useMeeting Hook
  const { participants } = useMeeting({});
  const participantsArrId = [...participants.keys()];

  return (
    <View style={{ flex: 1 }}>
      <ParticipantList participants={participantsArrId} />
      <ControlsContainer />
    </View>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Handling Participant's Media​

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

1. 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);
Enter fullscreen mode Exit fullscreen mode

2. MediaStream API

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

  • useParticipant Hook Example
<RTCView
  streamURL={new MediaStream([webcamStream.track]).toURL()}
  objectFit={"cover"}
  style={{
    height: 300,
    marginVertical: 8,
    marginHorizontal: 8,
  }}
/>
Enter fullscreen mode Exit fullscreen mode

Rendering Participant Media

  • ParticipantView Component
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>
  );
}
Enter fullscreen mode Exit fullscreen mode

Congratulations! By following these steps, you're on your way to unlocking the video capabilities within your application. Now, we are moving forward to integrate the feature that builds immersive video experiences for your users!

πŸ–₯️ Integrate Screen Sharing Feature

Adding the Screen Share functionality to your application improves cooperation by allowing users to share their device screens during meetings or presentations. It allows everyone in the conference to view precisely what you see on your screen, which is useful for presentations, demos, and collaborations.

Enable Screen Share

  • By using the enableScreenShare() function of the useMeeting hook, the local participant can share their mobile screen with other participants.
  • The Screen Share stream of a participant can be accessed from the screenShareStream property of the useParticipant hook.

Disable Screen Share

  • By using the disableScreenShare() function of the useMeeting hook, the local participant can stop sharing their mobile screen with other participants.

Toggle Screen Share

  • By using the toggleScreenShare() function of the useMeeting hook, the local participant can start or stop sharing their mobile screen with other participants based on the current state of the screen sharing.
  • The Screen Share stream of a participant can be accessed from the screenShareStream property of the useParticipant hook.
const ControlsContainer = () => {
  //Getting the screen-share method from hook
  const { toggleScreenShare } = useMeeting();

  return (
    //...
    //...
    <Button
      onPress={() => {
        toggleScreenShare();
      }}
      buttonText={"Toggle ScreenShare"}
      backgroundColor={"#1178F8"}
    />
    //...
    //...
  );
};
Enter fullscreen mode Exit fullscreen mode

Rendering Screen Share Stream​

  • To render the screenshare, you will need the participantId of the user presenting the screen. This can be obtained from the presenterId property of the useMeeting hook.
function MeetingView() {
  const { presenterId } = useMeeting({});

  return (
    <View style={{ flex: 1 }}>
      //..
      {presenterId && <PresenterView presenterId={presenterId} />}
      <ParticipantList />
      // ...
    </View>
  );
}

const PresenterView = ({ presenterId }) => {
  return <Text>PresenterView</Text>;
};
Enter fullscreen mode Exit fullscreen mode
  • Now that you have the presenterId, you can obtain the screenShareStream using the useParticipant hook and play it in the RTCView component.
const PresenterView = ({ presenterId }) => {
  const { screenShareStream, screenShareOn } = useParticipant(presenterId);

  return (
    <>
      // playing the media stream in the RTCView
      {screenShareOn && screenShareStream ? (
        <RTCView
          streamURL={new MediaStream([screenShareStream.track]).toURL()}
          objectFit={"contain"}
          style={{
            flex: 1,
          }}
        />
      ) : null}
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

Events associated with toggleScreenShare​

  • Every Participant will receive a callback on onStreamEnabled() event of the useParticipant() hook with the Stream object, if the screen share broadcasting was started.
  • Every Participant will receive a callback on onStreamDisabled() event of the useParticipant() hook with the Stream object, if the screen share broadcasting was stopped.
  • Every Participant will receive the onPresenterChanged() callback of the useMeeting hook, providing the participantId as the presenterId of the participant who started the screen share or null if the screen share was turned off.
import { useParticipant, useMeeting } from "@videosdk.live/react-native-sdk";

const MeetingView = () => {
  //Callback for when the presenter changes
  function onPresenterChanged(presenterId) {
    if(presenterId){
      console.log(presenterId, "started screen share");
    }else{
      console.log("someone stopped screen share");
    }
  }

  const { participants } = useMeeting({
    onPresenterChanged,
    ...
  });

  return <>...</>
}

const ParticipantView = (participantId) => {
  //Callback for when the participant starts a stream
  function onStreamEnabled(stream) {
    if(stream.kind === 'share'){
      console.log("Share Stream On: onStreamEnabled", stream);
    }
  }

  //Callback for when the participant stops a stream
  function onStreamDisabled(stream) {
    if(stream.kind === 'share'){
      console.log("Share Stream Off: onStreamDisabled", stream);
    }
  }

  const {
    displayName
    ...
  } = useParticipant(participantId,{
    onStreamEnabled,
    onStreamDisabled
    ...
  });
  return <> Participant View </>;
}
Enter fullscreen mode Exit fullscreen mode

By following the steps outlined in this guide, you can seamlessly integrate the Screen Share feature and empower your users to share their screens with ease, fostering better communication and collaboration.

πŸ”š Conclusion

With VideoSDK's Screen Share functionality, you can create immersive and engaging video experiences that connect people from all over the world. Screen Share enables participants to easily share their ideas, presentations, and information during corporate meetings, educational sessions, or creative collaborations.

To unlock the full potential of VideoSDK and create easy-to-use video experiences, developers are encouraged to Sign up with VideoSDK today and Get 10000 minutes free to take the video app to the next level!

Top comments (0)