Walkthrough

Building a React Native Mobile App with Huddle01

In this walkthrough guide, we will be creating a very basic video conferencing app using Huddle01’s React Native SDK. This guide serves as a good starting point for anyone trying out the SDK for the first time.

Creating a new React Native app

Let’s begin by creating a fresh React Native application using npx.

  npx react-native@latest init MyVideoConfApp

Once the app has been created, follow these instructions (opens in a new tab) to run the app depending on your platform and device. If the app was successfully built and run, you should be seeing something similar to this.

Get-Started

The react native starter app comes with some of this pre-written UI that you see on the screen, so let’s get rid of that before we dive into our video conferencing functionalities.

Go to your app.tsx file and replace all of the code with this

app.tsx
import React from 'react';
import {SafeAreaView, StyleSheet, Text} from 'react-native';
 
function App(): JSX.Element {
  return (
    <SafeAreaView style={styles.background}>
      <Text style={styles.text}>My Video Conferencing App</Text>
    </SafeAreaView>
  );
}
 
const styles = StyleSheet.create({
  background: {
    backgroundColor: '#222222',
    height: '100%',
    width: '100%',
  },
  text: {
    color: '#ffffff',
  },
});
 
export default App;

That should be everything we need for the initial app config. Let’s move on to installing and setting up the Huddle01 SDK.

Installing and setting up the SDK

In your terminal, install the following packages.

pnpm add @huddle01/react-native react-native-get-random-values react-native-webrtc

This will install the SDK and a few extra packages that we need for it to work as intended.

Important Step: Make sure to add camera and mic permissions to your AndroidManifest.xml file (for Android) (opens in a new tab) and Info.plist file (for iOS) (opens in a new tab).

If you are building for iOS, don’t forget to run pod install inside the ios directory to install the iOS native dependencies.

Now that we have our packages installed and app permissions granted, we need to add a few more lines of code for configuring the SDK. Go to your top-level index.js file and replace all of the code with the following:

index.js
import { AppRegistry } from 'react-native';
import 'react-native-get-random-values';
import { registerGlobals } from 'react-native-webrtc';
import App from './App';
import { name as appName } from './app.json';
 
registerGlobals();
 
AppRegistry.registerComponent(appName, () => App);

That’s all the SDK setup we need to build a full-fledged video conferencing app. Kudos if you made it till here! Let’s move on to the fun part now.

Initializing the SDK

Inside our app.tsx file, we can initialize the SDK by using the useHuddle01() hook.

app.tsx
import React from 'react';
import {SafeAreaView, StyleSheet, Text} from 'react-native';
import {useHuddle01} from '@huddle01/react-native';
 
function App(): JSX.Element {
  const {initialize, isInitialized} = useHuddle01();
 
  return (
    <SafeAreaView style={styles.background}>
      <Text style={styles.text}>My Video Conferencing App</Text>
    </SafeAreaView>
  );
}

We can use the initialize() function returned from the hook to initialize the SDK and interact with it. But before we do that, let’s build out a basic UI for our app.

app.tsx
import {useHuddle01} from '@huddle01/react-native';
import {useMeetingMachine} from '@huddle01/react-native/hooks';
import React from 'react';
import {Button, SafeAreaView, StyleSheet, Text, View} from 'react-native';
 
function App(): JSX.Element {
  const {initialize, isInitialized} = useHuddle01();
  const {state} = useMeetingMachine();
 
  return (
    <SafeAreaView style={styles.background}>
      <Text style={styles.appTitle}>My Video Conferencing App</Text>
 
      <View style={styles.infoSection}>
        <View style={styles.infoTab}>
          <View style={styles.infoKey}>
            <Text style={styles.text}>Room State</Text>
          </View>
          <View style={styles.infoValue}>
            <Text style={styles.text}>{JSON.stringify(state.value)}</Text>
          </View>
        </View>
        <View style={styles.infoTab}>
          <View style={styles.infoKey}>
            <Text style={styles.text}>Me Id</Text>
          </View>
          <View style={styles.infoValue}>
            <Text style={styles.text}>
              {JSON.stringify(state.context.peerId)}
            </Text>
          </View>
        </View>
        <View style={styles.infoTab}>
          <View style={styles.infoKey}>
            <Text style={styles.text}>Peers</Text>
          </View>
          <View style={styles.infoValue}>
            <Text style={styles.text}>
              {JSON.stringify(state.context.peers)}
            </Text>
          </View>
        </View>
        <View style={styles.infoTab}>
          <View style={styles.infoKey}>
            <Text style={styles.text}>Consumers</Text>
          </View>
          <View style={styles.infoValue}>
            <Text style={styles.text}>
              {JSON.stringify(state.context.consumers)}
            </Text>
          </View>
        </View>
      </View>
 
      <View style={styles.controlsSection}>
        <View style={styles.controlsColumn}>
          <View style={styles.controlGroup}>
            <Text style={styles.controlsGroupTitle}>Idle</Text>
            <View style={styles.button}>
              <Button title="INIT" />
            </View>
          </View>
          <View style={styles.controlGroup}>
            <Text style={styles.controlsGroupTitle}>Lobby</Text>
            <View>
              <View style={styles.button}>
                <Button title="ENABLE_CAM" />
              </View>
 
              <View style={styles.button}>
                <Button title="ENABLE_MIC" />
              </View>
 
              <View style={styles.button}>
                <Button title="JOIN_ROOM" />
              </View>
 
              <View style={styles.button}>
                <Button title="LEAVE_LOBBY" />
              </View>
 
              <View style={styles.button}>
                <Button title="DISABLE_CAM" />
              </View>
 
              <View style={styles.button}>
                <Button title="DISABLE_MIC" />
              </View>
            </View>
          </View>
        </View>
 
        <View style={styles.controlsColumn}>
          <View style={styles.controlGroup}>
            <Text style={styles.controlsGroupTitle}>Initialized</Text>
 
            <View style={styles.button}>
              <Button title="JOIN_LOBBY" />
            </View>
          </View>
 
          <View style={styles.controlGroup}>
            <Text style={styles.controlsGroupTitle}>Room</Text>
            <View>
              <View style={styles.button}>
                <Button title="PRODUCE_MIC" />
              </View>
 
              <View style={styles.button}>
                <Button title="PRODUCE_CAM" />
              </View>
 
              <View style={styles.button}>
                <Button title="STOP_PRODUCING_MIC" />
              </View>
 
              <View style={styles.button}>
                <Button title="STOP_PRODUCING_CAM" />
              </View>
 
              <View style={styles.button}>
                <Button title="LEAVE_ROOM" />
              </View>
            </View>
          </View>
        </View>
      </View>
    </SafeAreaView>
  );
}
 
const styles = StyleSheet.create({
  appTitle: {
    color: '#ffffff',
    fontSize: 18,
    textAlign: 'center',
    fontWeight: 'bold',
  },
  background: {
    backgroundColor: '#222222',
    height: '100%',
    width: '100%',
  },
  text: {
    color: '#ffffff',
    fontSize: 18,
  },
  infoSection: {
    borderBottomColor: '#fff',
    borderBottomWidth: 2,
    padding: 10,
  },
  infoTab: {
    width: '100%',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'space-between',
    borderWidth: 2,
    borderColor: '#fff',
    borderRadius: 6,
    marginTop: 4,
  },
  infoKey: {
    borderRightColor: '#fff',
    borderRightWidth: 2,
    padding: 4,
  },
  infoValue: {
    flex: 1,
    padding: 4,
  },
  controlsSection: {
    width: '100%',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'space-between',
    padding: 10,
    borderBottomColor: '#fff',
    borderBottomWidth: 2,
  },
  controlsColumn: {
    display: 'flex',
    alignItems: 'center',
  },
  button: {
    marginTop: 4,
    borderRadius: 8,
    borderWidth: 2,
    borderColor: '#fff',
  },
  controlsGroupTitle: {
    color: '#ffffff',
    fontSize: 18,
    textAlign: 'center',
    textTransform: 'uppercase',
    fontWeight: 'bold',
  },
  controlGroup: {
    marginTop: 4,
    textAlign: 'center',
    display: 'flex',
    alignItems: 'center',
  },
});
 
export default App;
Get-Started

This gives us a basic UI with two main sections. The first section at the top is for general information about the current state of the app, like the app state, your peerID, and other peers in the room. We get this information from the state object returned from the useMeetingMachine() hook. The second section is a control centre which provides us different buttons grouped by the app state they can be pressed in. These buttons will help us interact with the SDK.

Let’s link the buttons with their intended interactions by calling the different SDK methods upon pressing them. We will import these interaction methods at the top, from different hooks available as a part of the SDK.

app.tsx
import {
  useAudio,
  useEventListener,
  useHuddle01,
  useLobby,
  useMeetingMachine,
  usePeers,
  useRoom,
  useVideo,
} from '@huddle01/react-native/hooks';
import React from 'react';
import {Button, SafeAreaView, StyleSheet, Text, View} from 'react-native';
 
function App(): JSX.Element {
    const {initialize, isInitialized} = useHuddle01();
    const {state} = useMeetingMachine();
    const {joinLobby, leaveLobby} = useLobby();
    const {
      fetchAudioStream,
      produceAudio,
      stopAudioStream,
      stopProducingAudio,
      stream: micStream,
    } = useAudio();
    const {
      fetchVideoStream,
      produceVideo,
      stopVideoStream,
      stopProducingVideo,
      stream: camStream,
    } = useVideo();
    const {joinRoom, leaveRoom} = useRoom();
    const {peers} = usePeers();
 
    return (...)
}
 
export default App;

These imported functions can then be called inside the onPress prop function of each of the buttons.

Invoking the different methods returned from hooks

Initialization of project

Head over to API Keys Page and connect your wallet to get your project credentials:

  • API Key
  • projectId

Once done, initialize your project by calling the initialize() method and pass projectId as an argument.

app.tsx
<View style={styles.button}>
  <Button
      title="INIT"
      disabled={!state.matches('Idle')}
      onPress={() => initialize('YOUR_PROJECT_ID')}
  />
</View>

Joining the Lobby

To join a lobby/room, you need to first create the room it using the Create Room API. Once that is done, you will have a unique roomID that you can use to join the lobby/room, using the joinLobby() method and passing roomID as an argument.

app.tsx
<View style={styles.button}>
  <Button
    title="JOIN_LOBBY"
    disabled={!joinLobby.isCallable}
    onPress={() => {
      joinLobby('your_unique_room_id');
    }}
  />
</View>

The isCallable is an attribute available on all methods to check whether the function is callable or not.

Enabling and Disabling Media Devices

⚠️

If cam and mic aren’t enabled in the lobby, video and audio won’t be shareable inside the room.

⚠️

These methods are to be called only when in lobby state.

app.tsx
<View style={styles.button}>
  <Button
    title="ENABLE_CAM"
    disabled={!fetchVideoStream.isCallable}
    onPress={fetchVideoStream}
  />
</View>
 
<View style={styles.button}>
  <Button
    title="ENABLE_MIC"
    disabled={!fetchAudioStream.isCallable}
    onPress={fetchAudioStream}
  />
 
<View style={styles.button}>
  <Button
    title="DISABLE_CAM"
    disabled={!stopVideoStream.isCallable}
    onPress={stopVideoStream}
    />
</View>
 
<View style={styles.button}>
  <Button
    title="DISABLE_MIC"
    disabled={!stopAudioStream.isCallable}
    onPress={stopAudioStream}
    />
</View>

Joining the Room

app.tsx
 <View style={styles.button}>
  <Button
    title="JOIN_ROOM"
    disabled={!joinRoom.isCallable}
    onPress={joinRoom}
  />
</View>

Leaving the lobby

app.tsx
<View style={styles.button}>
  <Button
    title="LEAVE_LOBBY"
    disabled={!state.matches('Initialized.JoinedLobby')}
    onPress={leaveLobby}
  />
</View>

Producing your media streams

As defined earlier inside Concepts, producing is the act of sharing a participant’s media stream with other peers inside a room. When a participant starts sharing their video or audio stream, they become a producer and the other peers in the room become consumers for that particular media stream.

app.tsx
<View style={styles.button}>
  <Button
    title="PRODUCE_MIC"
    disabled={!produceAudio.isCallable}
    onPress={() => produceAudio(micStream)}
  />
</View>
 
<View style={styles.button}>
  <Button
    title="PRODUCE_CAM"
    disabled={!produceVideo.isCallable}
    onPress={() => produceVideo(camStream)}
  />
</View>
 
<View style={styles.button}>
  <Button
    title="STOP_PRODUCING_MIC"
    disabled={!stopProducingAudio.isCallable}
    onPress={() => stopProducingAudio()}
  />
</View>
 
<View style={styles.button}>
  <Button
    title="STOP_PRODUCING_CAM"
    disabled={!stopProducingVideo.isCallable}
    onPress={() => stopProducingVideo()}
  />
</View>

Leaving the Room

app.tsx
 <View style={styles.button}>
  <Button
    title="LEAVE_ROOM"
    disabled={!leaveRoom.isCallable}
    onPress={leaveRoom}
  />
</View>

You’ll be able to see that all the buttons except the first one have been disabled because the app is in “Idle” state right now.

Get-Started

You can refer to the hooks section to see in detail what each of these hooks and the functions returned from them do when they are called.

Rendering media streams

Now let’s add the UI to render the video streams of you and other peers that might join the same room as you. To render our own video stream, we will be using the <RTCView /> component imported from react-native-webrtc.

app.tsx
<View style={styles.videoSection}>
  <Text style={styles.text}>My Video:</Text>
  <View style={styles.myVideo}>
    <RTCView
      mirror={true}
      objectFit={'cover'}
      streamURL={streamURL}
      zOrder={0}
      style={{
        backgroundColor: 'white',
        width: '75%',
        height: '100%',
      }}
    />
  </View>
</View>
 
const styles = StyleSheet.create({
  videoSection: {},
  myVideo: {
    height: 300,
    width: '100%',
    display: 'flex',
    alignItems: 'center',
  },
});

There are a couple of things happening here. Let’s break it down. First, we added an RTCView component to render our own video stream. We passed a prop streamURL={streamURL} to it but streamURL is not defined at the moment. We need to initialize a state variable with that name and set it to our video stream only when we enable our camera. So let’s do that now.

app.tsx
const [streamURL, setStreamURL] = useState('');
 
useEventListener('lobby:cam-on', () => {
  if (camStream) {
    console.log('camStream: ', camStream.toURL());
    setStreamURL(camStream.toURL());
  }
});

The streamURL state is initially set to an empty string. We added an event listener using the useEventListener hook, that sets the streamURL to our video stream when the app state changes to lobby:cam-on, indicating that the camera was enabled in the lobby state.

That’s all we need to render our own camera stream. Now let’s render video streams coming from other peers in the room. To do that, we will use the custom <Video /> component imported from the SDK. This component accepts a track (MediaStreamTrack) and a peerId (string) as it’s props and renders the video stream. You can aslo pass in a style prop for customizing how the video component looks.

Audio and Video components

Using the audio and video components

Importing

app.tsx
import { Video, Audio } from '@huddle01/react-native/components'

There are two ways to use the audio and video components

1. Usage with peerId:

app.tsx
  <Video peerId="PEER_ID" />
  <Audio peerId="PEER_ID" />

2. Usage with track or stream:

The stream value for mic and cam are available on the useAudio and useVideo hooks respectively.

The tracks for peers can be found from the usePeers hook.

🚨

To be used only when you want to make changes to streams from your side otherwise use peerId method!

app.tsx
  <Video peerId="PEER_ID" track={camTrack}  />
  <Audio peerId="PEER_ID" track={micTrack} />
app.tsx
  <Video peerId="PEER_ID" stream={camStream}  />
  <Audio peerId="PEER_ID" stream={micStream} />

The Audio and Video components also accept a style prop using which you can customize the styling of the components.

app.tsx
  <Video
    peerId="PEER_ID"
    style={{
      backgroundColor: 'white',
      width: '75%',
      height: '100%',
    }}
  />
app.tsx
<View style={styles.videoSection}>
  <Text style={styles.text}>My Video:</Text>
  <View style={styles.myVideo}>
    <RTCView
      mirror={true}
      objectFit={"cover"}
      streamURL={streamURL}
      zOrder={0}
      style={{
        backgroundColor: "white",
        width: "75%",
        height: "100%",
      }}
    />
  </View>
  <View>
    {Object.values(peers)
      .filter((peer) => peer.cam)
      .map((peer) => (
        <Video
          key={peer.peerId}
          peerId={peer.peerId}
          track={peer.cam}
          style={{
            backgroundColor: "white",
            width: "75%",
            height: "100%",
          }}
        />
      ))}
  </View>
</View>;

Here we are filtering out peers returned from the usePeer() hook that are producing their camera stream, and then mapping over these peers and rendering an instance of the Video component for each of them by passing the track and peerId as props.

And voila! Our quick little video conferencing app is ready. To test it, play around with the controls and press each button to see what happens.

Get-Started Get-Started

Final app.tsx file

app.tsx
import {Video} from '@huddle01/react-native/components';
import {
  useAudio,
  useEventListener,
  useHuddle01,
  useLobby,
  useMeetingMachine,
  usePeers,
  useRoom,
  useVideo,
} from '@huddle01/react-native/hooks';
import React, {useState} from 'react';
import {Button, ScrollView, StyleSheet, Text, View} from 'react-native';
import {RTCView} from 'react-native-webrtc';
 
function App(): JSX.Element {
  const {state} = useMeetingMachine();
  const {initialize, isInitialized} = useHuddle01();
  const {joinLobby, leaveLobby} = useLobby();
  const {
    fetchAudioStream,
    produceAudio,
    stopAudioStream,
    stopProducingAudio,
    stream: micStream,
  } = useAudio();
  const {
    fetchVideoStream,
    produceVideo,
    stopVideoStream,
    stopProducingVideo,
    stream: camStream,
  } = useVideo();
  const {joinRoom, leaveRoom} = useRoom();
  const {peers} = usePeers();
 
  const [streamURL, setStreamURL] = useState('');
 
  useEventListener('lobby:cam-on', () => {
    if (camStream) {
      console.log('camStream: ', camStream.toURL());
      setStreamURL(camStream.toURL());
    }
  });
 
  return (
    <ScrollView style={styles.background}>
      <Text style={styles.appTitle}>My Video Conferencing App</Text>
 
      <View style={styles.infoSection}>
        <View style={styles.infoTab}>
          <View style={styles.infoKey}>
            <Text style={styles.text}>Room State</Text>
          </View>
          <View style={styles.infoValue}>
            <Text style={styles.text}>{JSON.stringify(state.value)}</Text>
          </View>
        </View>
        <View style={styles.infoTab}>
          <View style={styles.infoKey}>
            <Text style={styles.text}>Me Id</Text>
          </View>
          <View style={styles.infoValue}>
            <Text style={styles.text}>
              {JSON.stringify(state.context.peerId)}
            </Text>
          </View>
        </View>
        <View style={styles.infoTab}>
          <View style={styles.infoKey}>
            <Text style={styles.text}>Peers</Text>
          </View>
          <View style={styles.infoValue}>
            <Text style={styles.text}>
              {JSON.stringify(state.context.peers)}
            </Text>
          </View>
        </View>
        <View style={styles.infoTab}>
          <View style={styles.infoKey}>
            <Text style={styles.text}>Consumers</Text>
          </View>
          <View style={styles.infoValue}>
            <Text style={styles.text}>
              {JSON.stringify(state.context.consumers)}
            </Text>
          </View>
        </View>
      </View>
 
      <View style={styles.controlsSection}>
        <View style={styles.controlsColumn}>
          <View style={styles.controlGroup}>
            <Text style={styles.controlsGroupTitle}>Idle</Text>
            <View style={styles.button}>
              <Button
                title="INIT"
                disabled={!state.matches('Idle')}
                onPress={() => initialize('YOUR_PROJECT_ID')}
              />
            </View>
          </View>
          <View style={styles.controlGroup}>
            <Text style={styles.controlsGroupTitle}>Lobby</Text>
            <View>
              <View style={styles.button}>
                <Button
                  title="ENABLE_CAM"
                  disabled={!fetchVideoStream.isCallable}
                  onPress={fetchVideoStream}
                />
              </View>
 
              <View style={styles.button}>
                <Button
                  title="ENABLE_MIC"
                  disabled={!fetchAudioStream.isCallable}
                  onPress={fetchAudioStream}
                />
              </View>
 
              <View style={styles.button}>
                <Button
                  title="JOIN_ROOM"
                  disabled={!joinRoom.isCallable}
                  onPress={joinRoom}
                />
              </View>
 
              <View style={styles.button}>
                <Button
                  title="LEAVE_LOBBY"
                  disabled={!state.matches('Initialized.JoinedLobby')}
                  onPress={leaveLobby}
                />
              </View>
 
              <View style={styles.button}>
                <Button
                  title="DISABLE_CAM"
                  disabled={!stopVideoStream.isCallable}
                  onPress={stopVideoStream}
                />
              </View>
 
              <View style={styles.button}>
                <Button
                  title="DISABLE_MIC"
                  disabled={!stopAudioStream.isCallable}
                  onPress={stopAudioStream}
                />
              </View>
            </View>
          </View>
        </View>
 
        <View style={styles.controlsColumn}>
          <View style={styles.controlGroup}>
            <Text style={styles.controlsGroupTitle}>Initialized</Text>
 
            <View style={styles.button}>
              <Button
                title="JOIN_LOBBY"
                disabled={!joinLobby.isCallable}
                onPress={() => {
                  joinLobby('your_unique_room_id');
                }}
              />
            </View>
          </View>
 
          <View style={styles.controlGroup}>
            <Text style={styles.controlsGroupTitle}>Room</Text>
            <View>
              <View style={styles.button}>
                <Button
                  title="PRODUCE_MIC"
                  disabled={!produceAudio.isCallable}
                  onPress={() => produceAudio(micStream)}
                />
              </View>
 
              <View style={styles.button}>
                <Button
                  title="PRODUCE_CAM"
                  disabled={!produceVideo.isCallable}
                  onPress={() => produceVideo(camStream)}
                />
              </View>
 
              <View style={styles.button}>
                <Button
                  title="STOP_PRODUCING_MIC"
                  disabled={!stopProducingAudio.isCallable}
                  onPress={() => stopProducingAudio()}
                />
              </View>
 
              <View style={styles.button}>
                <Button
                  title="STOP_PRODUCING_CAM"
                  disabled={!stopProducingVideo.isCallable}
                  onPress={() => stopProducingVideo()}
                />
              </View>
 
              <View style={styles.button}>
                <Button
                  title="LEAVE_ROOM"
                  disabled={!leaveRoom.isCallable}
                  onPress={leaveRoom}
                />
              </View>
            </View>
          </View>
        </View>
      </View>
 
      <View style={styles.videoSection}>
        <Text style={styles.text}>My Video:</Text>
        <View style={styles.myVideo}>
          <RTCView
            mirror={true}
            objectFit={'cover'}
            streamURL={streamURL}
            zOrder={0}
            style={{
              backgroundColor: 'white',
              width: '75%',
              height: '100%',
            }}
          />
        </View>
        <View>
          {Object.values(peers)
            .filter(peer => peer.cam)
            .map(peer => (
              <Video
                key={peer.peerId}
                peerId={peer.peerId}
                track={peer.cam}
                style={{
                  backgroundColor: 'white',
                  width: '75%',
                  height: '100%',
                }}
              />
            ))}
        </View>
      </View>
    </ScrollView>
  );
}
 
const styles = StyleSheet.create({
  appTitle: {
    color: '#ffffff',
    fontSize: 18,
    textAlign: 'center',
    fontWeight: 'bold',
  },
  background: {
    backgroundColor: '#222222',
    height: '100%',
    width: '100%',
    paddingVertical: 50,
  },
  text: {
    color: '#ffffff',
    fontSize: 18,
  },
  infoSection: {
    borderBottomColor: '#fff',
    borderBottomWidth: 2,
    padding: 10,
  },
  infoTab: {
    width: '100%',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'space-between',
    borderWidth: 2,
    borderColor: '#fff',
    borderRadius: 6,
    marginTop: 4,
  },
  infoKey: {
    borderRightColor: '#fff',
    borderRightWidth: 2,
    padding: 4,
  },
  infoValue: {
    flex: 1,
    padding: 4,
  },
  controlsSection: {
    width: '100%',
    display: 'flex',
    flexDirection: 'row',
    justifyContent: 'space-between',
    padding: 10,
    borderBottomColor: '#fff',
    borderBottomWidth: 2,
  },
  controlsColumn: {
    display: 'flex',
    alignItems: 'center',
  },
  button: {
    marginTop: 4,
    borderRadius: 8,
    borderWidth: 2,
    borderColor: '#fff',
  },
  controlsGroupTitle: {
    color: '#ffffff',
    fontSize: 18,
    textAlign: 'center',
    textTransform: 'uppercase',
    fontWeight: 'bold',
  },
  controlGroup: {
    marginTop: 4,
    textAlign: 'center',
    display: 'flex',
    alignItems: 'center',
  },
  videoSection: {},
  myVideo: {
    height: 300,
    width: '100%',
    display: 'flex',
    alignItems: 'center',
  },
});
 
export default App;

GitHub

Link to github repository: https://github.com/Huddle01/react-native-SDK-example (opens in a new tab)

Audio/Video Infrastructure designed for the developers to empower them ship simple yet powerful Audio/Video Apps.
support
company
Copyright © 2022 Graphene 01, Inc. All Rights Reserved.