React Native Audio Room Tutorial
This tutorial aims to guide you through the process of creating an audio room experience reminiscent of platforms such as Twitter Spaces or Clubhouse. The outcome, as depicted in the image below, will encompass the following functionalities:
- Backstage Mode: Initiate the call with your co-hosts, allowing for pre-live discussions.
- Global Edge Network: Calls operate on Stream's worldwide edge network, ensuring low latency and scalability.
- No Listener Limit: Rooms can accommodate an unlimited number of listeners.
- Raised Hand Feature: Listeners can indicate their desire to speak by raising their hand and subsequently being invited by the host.
- Enhanced Audio Reliability: Audio tracks are transmitted multiple times to enhance overall reliability.
Step 1 - Setup a new React Native app
Confused about "Step 1 - Setup a new React Native app"?
Let us know how we can improve our documentation:
Create a new React Native app using the official template,
Step 2 - Install the SDK and declare permissions
Confused about "Step 2 - Install the SDK and declare permissions"?
Let us know how we can improve our documentation:
In order to install the Stream Video React Native SDK, run the following command in your terminal of choice:
The SDK requires installing some peer dependencies. You can run the following command to install them:
Declare permissions
- Android
- iOS
In AndroidManifest.xml
add the following permissions before the application
section.
If you plan to also support Bluetooth devices then also add the following.
Android Specific installation
In android/build.gradle
add the following inside the buildscript
section:
In android/app/build.gradle
add the following inside the android
section:
In android/gradle.properties
add the following:
Run the app
To ensure the best possible experience, we highly recommend running the app on a physical device. This is due to the limitations in audio and video device support on emulators. You can refer to the React Native documentation for guidance on running the app on a physical device.
However, if you still prefer to use an emulator, execute the following command:
Step 3 - Understand the basics
Confused about "Step 3 - Understand the basics"?
Let us know how we can improve our documentation:
Before we dive deep into writing code, there are two concepts you should be familiar with - StreamVideoClient
and Call
.
StreamVideoClient
is the low level JavaScript client used by the SDK to communicate with the Stream service.
It provides all the necessary methods to connect user Stream service, query calls, create call etc.
And a call
refers to an instance of the Call
class and is utilized for performing call-specific actions, such as joining a call, muting participants, leaving a call, and more.
In this example
apiKey
is the API key of your Stream Video application available on dashboarduser
is the user object{ id: "john_smith", "name": "John Smith" }
- and
token
is the user token generated by your server-side API. For development purpose, you can use the token generated by the Token Generator. You can read more information about client authentication on the Client & Authentication guide.
A call of type audio_room
can exist in either of two states: backstage and live.
- During the backstage mode, only the host and designated speakers have permission to join the call.
- In the live mode, anyone can join the call.
From the dashboard you can disable backstage mode or fine tune the permissions for backstage mode.
Client instance and call instance are made accessible to all the video components from SDK via StreamVideo
component and StreamCall
component respectively.
In your app you need to wrap your component tree with StreamVideo
component and provide the client
instance to it as a prop.
client creation would normally take place during sign-in stage of the application.
And similarly you need to wrap your call specific UI components with StreamCall
component and provide the call
instance to it as a prop.
StreamVideo
and StreamCall
components are basically context providers, and they enable you to consume hooks provided by the SDK.
These hooks do all the heavy lifting around call management and provide you with the necessary state and methods to build your UI.
Step 4 - Setup Starter UI
Confused about "Step 4 - Setup Starter UI"?
Let us know how we can improve our documentation:
Lets begin by creating a basic UI for our audio room. Normally you would use a navigation library like React Navigation to navigate between screens.
But for this tutorial we'll keep it simple and mock the navigation using a state variable - activeScreen
.
Within your tutorial app, create a folder named src
and create the following files within it:
src/HomeScreen.tsx
src/CallScreen.tsx
src/AudioRoomUI.tsx
Now copy the following content into the respective files (as mentioned in header):
- App.tsx
- src/AudioRoomUI.tsx
- src/CallScreen.tsx
- src/HomeScreen.tsx
In App.tsx
we have hardcoded the placeholders of apiKey
, userId
, token
and callId
for simplicity of tutorial.
To actually run this sample we need a valid user token.
The user token is typically generated by your server side API.
When a user logs in to your app you return the user token that gives them access to the call.
To make this tutorial easier to follow we'll generate a user token for you:
Please update REPLACE_WITH_API_KEY, REPLACE_WITH_USER_ID, REPLACE_WITH_TOKEN and REPLACE_WITH_CALL_ID with the actual values shown below:
In actual application you will want to store apiKey in a secure storage or in environment file.
Here are credentials to try out the app with:
Property | Value |
---|---|
API Key | Waiting for an API key ... |
Token | Token is generated ... |
User ID | Loading ... |
Call ID | Creating random call ID ... |
Hit save and you should see on your emulator or device the following UI.
Preview of the Home Screen | Preview of empty audio room |
---|---|
Step 5 - Setup Video Client
Confused about "Step 5 - Setup Video Client"?
Let us know how we can improve our documentation:
Within this configuration, we will establish a StreamVideoClient
instance and facilitate the user's connection to the Stream service.
In real application, client creation should be encapsulated within a useEffect
hook and during unmount you should call client.disconnectUser()
to avoid creating multiple websockets.
Client instance needs to be provided to StreamVideo
component and it will provide the client instance to all the child components using React Context.
It needs to go at the top of the component tree.
You wouldn't see any change in the UI at this point, since we haven't joined the call yet.
Step 6 - Create & Join a call
Confused about "Step 6 - Create & Join a call"?
Let us know how we can improve our documentation:
In this step we will create and join a call. Call will be stored in a state variable call
and it needs to be
provided to StreamCall
component. As explained earlier, StreamCall
component is provided by the SDK and it provides all the necessary hooks for configuring UI around audio room.
We will explore these hooks later in the tutorial.
Open up src/CallScreen.tsx
and replace it with this code:
Also as explained earlier in Understand the Basics section, call can be created or accessed using client.call(...)
method.
Thus we need access to client
inside CallScreen
component. We will use the useStreamVideoContext
hook to get access to the client instance.
We will put the joining logic inside useEffect hook, so we automatically join the call when user goes to CallScreen
.
Please note the difference between call.join
and call.goLive
methods.
When a call is created, it is in backstage mode by default, where only the host and speakers are allowed to join.
User needs to have permission to join the backstage mode, which we will explore later in the tutorial.
Normal user or audience of the audio room will be able to join the call when host calls call.goLive()
.
This makes it easy to try out your room and talk to your co-hosts before going live. You can enable or disable backstage mode in the dashboard.
For now we will call call.goLive()
immediately after joining the call, so that we can test the audio room.
To enhance the interactivity of this tutorial moving forward, kindly follow these steps:
- Give the app a refresh, then tap the "Join Audio Room" button within your mobile tutorial app.
- Access the web version of the audio room on your browser by clicking the "Join Call" link provided below, and subsequently, hit the "Join" button.
Currently, you won't have the ability to speak, as we haven't set up the user interface for the audio room. However, you will be able to view the current user listed among the participants on the web application.
Step 7 - Configure UI for Audio Room
Confused about "Step 7 - Configure UI for Audio Room"?
Let us know how we can improve our documentation:
Up until now, we've effectively established and entered the call. Moving forward, our focus will be on setting up the audio room's user interface. This involves presenting a participant list, indicating their speaking status, offering controls to enable or disable both live mode and audio transmission, and more.
As previously mentioned, all essential call information can be accessed through the SDK's provided hooks, facilitated by the StreamCall
component.
You can conveniently access a range of hooks related to the call's state by utilizing the useCallStateHooks
hook as demonstrated below.
By utilizing these hooks, you can confidently depend on the most up-to-date state information.
For a comprehensive list of all the available hooks, kindly consult the Call and Participant State guide.
Now, we'll establish a basic structure for the UI by crafting the subsequent components within the AudioRoomUI
component:
AudioRoomDescription
: This component will display the call's title and participant count.AudioRoomParticipants
: Here, you'll showcase the list of participants along with their speaking status.AudioRoomControlsPanel
: This component will house controls to activate or deactivate both live mode and audio transmission.
So please go ahead and create the following files:
- AudioRoomDescription
- AudioRoomParticipants
- AudioRoomControlsPanel
And lets add these components to AudioRoomUI
:
If you refresh the app, you should see following UI:
Audio Room Description
We will use the useCallCustomData
to access custom data which we set as data.custom
property in client.call(..)
method which creating the call.
And participant count can be accessed using useParticipants
hook.
Upon refreshing the application, you will notice the title and participant count displayed in the UI. You can also experiment by joining and leaving the audio room using the web app, observing how the participant count dynamically changes on the mobile interface.
Audio Room Participants
We will use the useParticipants
hook to access list of participants and their speaking status.
:::info
In practical applications, depending on business requirements you may want to dynamically sort the participants list based on certain criteria for example dominant speaker first or alphabetical etc.
You can pass comparator function to useParticipants
hook to sort the participants list.
Please take a look at Participants Sorting guide for the details and various sorting options. :::
Typically, audio room applications incorporate an indicator to denote whether a participant is currently speaking.
We can achieve this effect by utilizing the isSpeaking
boolean property associated with each participant
.
This property can be used to dynamically apply a border around the user avatar.
If you refresh the app, you should see the participant list as following
Audio Room Controls Panel
Let's expand the ControlPanel and add a button that controls the backstage of the room. In Create and Join Call step, we made the call live right after creating/joining it. We will move that logic to a toggle button where host or speakers can toggle live mode of the call.
Please create a file named ToggleLiveButton.tsx
in src
directory and add the following code.
Also make the following changes to src/AudioRoomControlsPanel.tsx
and src/CallScreen.tsx
file.
- ToggleLiveButton
- AudioRoomControlsPanel
- CallScreen
Next we will add a button to mute or unmute the microphone button.
Handling audio and video devices in a your application means working with MediaStream, MediaDeviceInfo and other WebRTC API objects.
To make this simpler, we hide all the complexity inside the SDK and export utility functions and states.
You can toggle the state of microphone using call.microphone.toggle()
function and you can access the state of microphone using useMicrophoneState
hook.
Please check Camera and Microphone guide for more details.
Please create a file named ToggleMicButton.tsx
in src
directory as following and add it to AudioRoomControlsPanel
component.
- ToggleMicButton
- AudioRoomControlsPanel
:::info However, if you wish for the app to seamlessly switch between the earphone and speaker, you can use the react-native-incall-manager. Please follow the usage docs of the library for this purpose and our Speaker management docs.
By default, the audio source is set to the device's speaker if the mode
is video
and device's earpiece if the mode
is audio
.
:::
If you refresh the app, you should be able to toggle live mode and mute/unmute the microphone.
Step 8 - Requesting permission to speak
Confused about "Step 8 - Requesting permission to speak"?
Let us know how we can improve our documentation:
In real audio room applications, a participant may need to request permission to speak from the host or speakers.
And host or speakers can grant or deny the request using call.grantPermissions
or call.revokePermissions
methods.
They can subscribe to an event call.permission_requested
to get notified when a participant requests permission to speak.
Let's first have a quick look at how the SDK call object exposes this:
- Request Permission
- Grant or Reject Permission
Now, let's begin by first developing the logic to initiate a permission request for speaking when the user taps on the microphone button.
Let's proceed to add an event listener for the event call.permissions_updated
.
This will keep the current user informed about any changes in permissions, whether they are granted or rejected by hosts or speakers.
It's worth noting that the current user in the app will already possess permission to speak, as the default permission configuration allows the call creator to transmit audio.
To handle the things host side, let's add another component that shows the last incoming request as well as the buttons to grant / reject it
Please create a file PermissionsRequestsPanel.tsx
inside src
directory and make the following changes. The code is pretty much self explanatory.
And now we will add this component to AudioRoomUI
component.
Now, for the purpose of testing:
- Navigate to the web app.
- Join the audio room.
- Click on the raised hand (✋) button located in the top-right corner to request permission to speak.
- You should observe the request being displayed in the mobile app as illustrated below:
Step 9 - Leave the call
Confused about "Step 9 - Leave the call"?
Let us know how we can improve our documentation:
User can leave the call by calling call.leave()
method.
Lets add this functionality to "Leave Audio Room" button.
Other built-in features
Confused about "Other built-in features"?
Let us know how we can improve our documentation:
There are a few more exciting features that you can use to build audio rooms
- Query Calls:: You can query calls to easily show upcoming calls, calls that recently finished as well as call previews.
- Reactions & Custom events: Reactions and custom events are supported.
- Chat: Stream's chat SDKs are fully featured and you can integrate them in the call
- Moderation: Moderation capabilities are built-in to the product
- Transcriptions: Transcriptions aren't available yet, but they are due to launch soon
Recap
Confused about "Recap"?
Let us know how we can improve our documentation:
It was fun to see just how quickly you can build an audio-room for your app. Please do let us know if you ran into any issues. Our team is also happy to review your UI designs and offer recommendations on how to achieve it with Stream.
To recap what we've learned:
- You set up a call with
const call = client.call('audio_room', '123')
- The call type
audio_room
controls which features are enabled and how permissions are set up - The
audio_room
by default enablesbackstage
mode, and only allows admins and the creator of the call to join before the call goes live - When you join a call, real-time communication is set up for audio & video calling:
await call.join()
- Call state
call.state
and helper state access hooks make it easy to build your own UI - Calls run on Stream's global edge network of video servers. Being closer to your users improves the latency and reliability of calls. For audio rooms we use Opus RED and Opus DTX for optimal audio quality.
The SDKs enable you to build audio rooms, video calling and live-streaming in days.
We hope you've enjoyed this tutorial and please do feel free to reach out if you have any suggestions or questions.
Final Thoughts
In this video app tutorial we built a fully functioning React Native messaging app with our React Native SDK component library. We also showed how easy it is to customize the behavior and the style of the React Native video app components with minimal code changes.
Both the video SDK for React Native and the API have plenty more features available to support more advanced use-cases.
Give us Feedback!
Did you find this tutorial helpful in getting you up and running with React Native for adding video to your project? Either good or bad, we’re looking for your honest feedback so we can improve.