React Native
Dolby.io Streaming APIs support using the Web SDK with React Native WebRTC to support creating iOS, Android, AndroidTV, and tvOS applications.
React Native SDK
Before you start, make sure that you use React Native WebRTC plugin 106.0.0 or later.
Getting started
Before we start, you can find more information in the Web SDK Documentation, which contains more examples and descriptions of the available modules.
Initializing your project
Initialize your React Native project using the expo tool:
yarn create expo-app GettingStarted
cd GettingStarted/
# Run expo eject to get all the platform specific folders generated
expo eject
Dependencies
By default, React Native does not contain a native implementation of WebRTC, which is required by the Millicast SDK. The react-native-webrtc project implements all the required WebRTC APIs needed by our SDK to function. A patch has been released that integrates WebRTC's Unified Plan, which is required by the Millicast SDK to function. However, there is still some small setup needed. We need to install the mentioned dependency:
npm install --save react-native-webrtc
npm install @millicast/sdk
# OR if you use yarn:
yarn add react-native-webrtc
yarn add @millicast/sdk
On iOS, install the native Pods that are required for react-native-webrtc to run. Navigate to the ios/
directory in your react-native
app and run the following:
pod install --clean-install
# On M1 architectures, you need to prefix with `arch -x86_64` as follows:
arch -x86_64 pod install --clean-install
Now you should be able to run the expo sample app to verify that all necessary tools have been successfully installed.
Application setup
Before using the Millicast SDK, import a function from react-native-webrtc, which registers the shim implementations of the WebRTC data structures as globals, so that the SDK finds it without having to change anything. Add the following:
import { registerRootComponent } from 'expo';
import { registerGlobals } from 'react-native-webrtc';
// This registers the shim WebRTC data structures, like RTCPeerConnection as global variables so the SDK can find them anywhere.
registerGlobals();
// Here we can import the SDK and use it normally.
// The Millicast Logger will help you debug your app.
import { Logger as MillicastLogger } from '@millicast/sdk'
// Initialize Logger
window.Logger = MillicastLogger
Logger.setLevel(MillicastLogger.DEBUG);
// Use either Viewer or Publisher example as the App.js script
import App from './App';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// The environment is set up appropriately
registerRootComponent(App);
Permissions
iOS permissions
If the application is running natively on iOS, we need to grant the application permissions to use the camera and microphone, which is required by React Native WebRTC. Navigate to the Info.plist
file (by default, it is located in the previously mentioned ios/
directory mentioned) and add the following dictionary values if they do not exist:
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for uploading videos</string>
<key>NSCameraUsageDescription</key>
<string>Need camera access for uploading images</string>
For additional information, see: React Native WebRTC iOS Installation.
Android permissions
Similarly, we need to provide the application with permissions to be able to run it. Add the following to the android/app/src/main/AndroidManifest.xml
before the application section:
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:name="android.hardware.audio.output" />
<uses-feature android:name="android.hardware.microphone" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.INTERNET" />
Add the following to support Bluetooth:
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
For additional information, see the React Native WebRTC Android Installation.
Gradle v3.5
If using Grade v3.5, an additional line is required in android/gradle.properties
to prevent a crash at startup. This is required by react-native-webrtc:
android.enableDexingArtifactTransform.desugaring=false
Examples
Prior to using the viewer and publisher examples, you have to get your tokens. Use the this guide to manage your Dolby.io Real-time Streaming tokens.
In your App.js file, decide whether you want to test a viewer or publisher based on your needs.
Viewer example
The following example widget subscribes to a stream and uses React Native WebRTC's RTCView to view the received remote track.
Since React Native already has a class called View
, when subscribing, you must alias Millicast's View class. For example:
import { Director, View as MillicastView } from '@millicast/sdk'
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, View } from 'react-native';
import React from 'react';
import { RTCView } from 'react-native-webrtc';
import { Director, View as MillicastView } from '@millicast/sdk'
class MillicastWidget extends React.Component {
constructor(props) {
super(props)
this.state = {
streamURL: null
}
this.styles = StyleSheet.create({
video: {
width: 480,
height: 320
}
})
this.subscribe(props.streamName, props.accountID)
}
async subscribe(streamName, accountID) {
console.log(streamName, accountID)
const tokenGenerator = () => Director.getSubscriber({
streamName: streamName,
streamAccountId: accountID
})
// Create a new instance
const millicastView = new MillicastView(streamName, tokenGenerator, null)
// Set track event handler to receive streams from Publisher.
millicastView.on('track', (event) => {
console.log(`My event is ${event}`);
const videoUrl = event.streams[0].toURL()
if (!videoUrl) return null
this.setState({
streamURL: videoUrl
})
})
// Start connection to viewer
try {
await millicastView.connect()
} catch (e) {
console.log('Connection failed. Reason:', e)
}
}
reconnect = () => {
millicastView.reconnect()
}
render() {
return (
<RTCView streamURL={this.state.streamURL} style ={this.styles.video}/>
);
}
};
export default function App() {
return (
<View style={styles.container}>
<StatusBar style="auto" />
<MillicastWidget streamName=<YOUR_STREAM_NAME> accountID=<YOUR_ACCOUNT_ID>/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
}
});
Once the MillicastWidget component is defined, you can embed this widget into your app as follows:
<MillicastWidget streamName=... accountID=.../>
Publisher example
The following is a publisher example:
import {
Button,
SafeAreaView,
StyleSheet,
View,
StatusBar,
} from 'react-native';
import { Colors } from 'react-native/Libraries/NewAppScreen';
import React, { useState } from 'react';
import { mediaDevices, RTCView } from 'react-native-webrtc';
// Import the required classes
import { Director, Publish } from '@millicast/sdk'
class MillicastWidget extends React.Component {
constructor(props) {
super(props)
this.millicastPublish = null
this.state = {
mediaStream: null,
stream: null
}
this.styles = StyleSheet.create({
video: {
width: '100%',
height: '100%'
}
})
}
start = async () => {
console.log('start');
if (!this.state.mediaStream) {
let s;
try {
s = await mediaDevices.getUserMedia({ video: true, audio: true });
this.setState({
mediaStream: s
});
this.publish(this.props.streamName, this.props.token)
} catch (e) {
console.error(e);
}
}
};
stop = () => {
console.log('stop');
this.millicastPublish.stop();
if (this.state.mediaStream) {
this.state.mediaStream.release();
this.setState({
mediaStream: null
});
}
};
async publish(streamName, token) {
const tokenGenerator = () => Director.getPublisher({
token,
streamName
})
// Create a new instance
this.millicastPublish = new Publish(streamName, tokenGenerator)
this.setState({
streamURL: this.state.mediaStream
})
// Publishing Options
const broadcastOptions = {
mediaStream: this.state.mediaStream,
codec: 'vp8'
}
// Start broadcast
try {
await this.millicastPublish.connect(broadcastOptions)
} catch (e) {
console.log('Connection failed, handle error', e)
}
}
render() {
return (
<View style={styles.body}>
{
this.state.mediaStream ?
<RTCView streamURL={this.state.mediaStream.toURL()} style={this.styles.video} objectFit='contain' />
:
null
}
<View style={styles.footer}>
<Button
title="Start"
onPress={this.start} />
<Button
title="Stop"
onPress={this.stop} />
</View>
</View>
)
}
};
export default function App() {
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView style={styles.body}>
<MillicastWidget streamName=<YOUR_STREAM_NAME> token=<YOUR_PUBLISHER_TOKEN> />
</SafeAreaView>
</>
);
}
const styles = StyleSheet.create({
body: {
backgroundColor: Colors.white,
...StyleSheet.absoluteFill
},
stream: {
flex: 1
},
footer: {
backgroundColor: Colors.lighter,
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
},
});
Once the MillicastWidget component is defined, you can embed this widget into your app as follows:
<MillicastWidget streamName=... token=.../>
Build and run the application
If you are using yarn:
yarn run [android|ios]
Windows troubleshooting
If you have issues installing on Windows, we suggest following these steps:
- Move to android directory and run
.\gradlew clean
- Go to
<USERNAME>\AppData\Local\Android\Sdk\ndk
- Delete all the versions of NDK installed on your system.
- When you execute
npm run android
, the correct version of NDK will be installed.
Updated 10 months ago