For AI agents: see the complete documentation index at /llms.txt.
Quickstart
Updated
Rapidly develop and easily enhance your social, work, and educational apps with face-to-face interaction.
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
-
Android Studio 4.2 or higher.
-
Android SDK API Level 21 or higher.
-
Two mobile devices running Android 5.0 or higher.
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your Android project and install the Agora Video SDK.
Create a new project
-
Create a new project.
-
Open Android Studio and select File > New > New Project....
-
Select Phone and Tablet > Empty Activity and click Next.
-
Set the project name and storage path.
-
Select Java or Kotlin as the language, and click Finish to create the Android project.
After you create a project, Android Studio automatically starts gradle sync. Ensure that the synchronization is successful before proceeding to the next step.
-
Add to an existing project
-
Add a new activity to your project.
- Open your project in Android Studio.
- Right-click on the
app/src/main/java/<your.package.name>folder. - Select New → Activity → Empty Activity.
- Enter an activity name and click Finish.
This guide uses
MainActivityas the activity name in the sample code. Replace it with your activity name where required.
-
Add a layout file for your activity.
Set up two container elements in your activity to display local and remote video streams. Refer to Create a user interface to get a bare bones sample layout.
Install the SDK
Use either of the following methods to add Video SDK to your project.
Maven Central
-
Open the
settings.gradlefile in the project's root directory and add the Maven Central dependency, if it doesn't already exist:repositories { mavenCentral() }
If your Android project uses dependencyResolutionManagement, the method of adding the Maven Central dependency may differ.
-
To integrate the Video SDK into your Android project, add the following to the
dependenciesblock in your project modulebuild.gradlefile:-
Groovy
build.gradleimplementation 'io.agora.rtc:full-sdk:x.y.z' -
Kotlin
build.gradle.ktsimplementation("io.agora.rtc:full-sdk:x.y.z")
Replace
x.y.zwith the specific SDK version number, such as4.5.0. -
To get the latest version number, check the Release notes. To integrate the Lite SDK, use io.agora.rtc:lite-sdk instead.
- Prevent code obfuscation
Open the /app/proguard-rules.pro file and add the following lines to prevent the Video SDK code from being obfuscated:
-keep class io.agora.** { *; }
-dontwarn io.agora.**Manual integration
-
Download the latest version of Video SDK from the SDKs page and unzip it.
-
Open the unzipped file and copy the following files or subfolders to your project path.
| File or folder | Project path |
|---|---|
agora-rtc-sdk.jar file | /app/libs/ |
arm64-v8a folder | /app/src/main/jniLibs/ |
armeabi-v7a folder | /app/src/main/jniLibs/ |
x86 folder | /app/src/main/jniLibs/ |
x86_64 folder | /app/src/main/jniLibs/ |
high_level_api in include folder | /app/src/main/jniLibs/ |
-
Select the file
/app/libs/agora-rtc-sdk.jarin the left navigation bar of Android Studio project files, right-click, and select add as a library from the drop-down menu. -
Prevent code obfuscation
Open the /app/proguard-rules.pro file and add the following lines to prevent the Video SDK code from being obfuscated:
-keep class io.agora.** { *; }
-dontwarn io.agora.**Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your app.
The following figure illustrates the essential steps:
This guide includes complete sample code that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps and use the code in your MainActivity file.
Import Agora classes
Import the relevant Agora classes and interfaces:
import io.agora.rtc2.Constants;
import io.agora.rtc2.IRtcEngineEventHandler;
import io.agora.rtc2.RtcEngine;
import io.agora.rtc2.RtcEngineConfig;
import io.agora.rtc2.video.VideoCanvas;
import io.agora.rtc2.ChannelMediaOptions;import io.agora.rtc2.Constants
import io.agora.rtc2.IRtcEngineEventHandler
import io.agora.rtc2.RtcEngine
import io.agora.rtc2.RtcEngineConfig
import io.agora.rtc2.video.VideoCanvas
import io.agora.rtc2.ChannelMediaOptionsInitialize the engine
For real-time communication, initialize an RtcEngine instance and set up event handlers to manage user interactions within the channel. Use RtcEngineConfig to specify the application context, App ID, and custom event handler, then call RtcEngine.create(config) to initialize the engine, enabling further channel operations. In your MainActivity file, add the following code:
// Fill in the app ID from Agora Console
private String myAppId = "<Your app ID>";
private RtcEngine mRtcEngine;
private void initializeAgoraVideoSDK() {
try {
RtcEngineConfig config = new RtcEngineConfig();
config.mContext = getBaseContext();
config.mAppId = myAppId;
config.mEventHandler = mRtcEventHandler;
mRtcEngine = RtcEngine.create(config);
} catch (Exception e) {
throw new RuntimeException("Error initializing RTC engine: " + e.getMessage());
}
}// Fill in the App ID obtained from the Agora Console
private val myAppId = "<Your app ID>"
private var mRtcEngine: RtcEngine? = null
private fun initializeRtcEngine() {
try {
val config = RtcEngineConfig().apply {
mContext = applicationContext
mAppId = myAppId
mEventHandler = mRtcEventHandler
}
mRtcEngine = RtcEngine.create(config)
} catch (e: Exception) {
throw RuntimeException("Error initializing RTC engine: ${e.message}")
}
}Join a channel
To join a channel, call joinChannel with the following parameters:
-
Channel name: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.
-
Authentication token: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a token server in your security infrastructure. For the purpose of this guide Generate a temporary token.
-
User ID: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to
0when joining a channel, the SDK generates a random number for the user ID and returns the value in theonJoinChannelSuccesscallback. -
Channel media options: Configure
ChannelMediaOptionsto define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.
For Broadcast Streaming, set the channelProfile to CHANNEL_PROFILE_LIVE_BROADCASTING, the clientRoleType to CLIENT_ROLE_BROADCASTER (host) or CLIENT_ROLE_AUDIENCE, and the audienceLatencyLevel to AUDIENCE_LATENCY_LEVEL_LOW_LATENCY.
// Fill in the channel name
private String channelName = "<Your channel name>";
// Fill in the temporary token generated from Agora Console
private String token = "<Your token>";
private void joinChannel() {
// Create an instance of ChannelMediaOptions and configure it
ChannelMediaOptions options = new ChannelMediaOptions();
// Set the user role to BROADCASTER or AUDIENCE according to the use-case
options.clientRoleType = Constants.CLIENT_ROLE_BROADCASTER;
// In the broadcast streaming use-case, set the channelProfile to BROADCASTING
options.channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING;
// Set the latency level for audience
options.audienceLatencyLevel = Constants.AUDIENCE_LATENCY_LEVEL_LOW_LATENCY;
// Publish local media
options.publishCameraTrack = true;
options.publishMicrophoneTrack = true;
mRtcEngine.joinChannel(token, channelName, 0, options);
}// Fill in the channel name
private val channelName = "<Your channel name>"
// Fill in the temporary token generated from Agora Console
private val token = "<Your token>"
private fun joinChannel() {
// Create an instance of ChannelMediaOptions and configure it
val options = ChannelMediaOptions().apply {
// Set the user role to BROADCASTER or AUDIENCE according to the use-case
clientRoleType = Constants.CLIENT_ROLE_BROADCASTER
// In the broadcast streaming use-case, set the channelProfile to BROADCASTING
channelProfile = Constants.CHANNEL_PROFILE_LIVE_BROADCASTING
// Set the latency level for audience
audienceLatencyLevel = Constants.AUDIENCE_LATENCY_LEVEL_LOW_LATENCY
// Publish local media
publishMicrophoneTrack = true
publishCameraTrack = true
}
mRtcEngine?.joinChannel(token, channelName, 0, options)
}Subscribe to Video SDK events
The Video SDK provides an interface for subscribing to channel events. To use it, create an instance of IRtcEngineEventHandler and implement the event methods you want to handle.
To ensure that you receive all Video SDK events, set the Agora Engine event handler before joining a channel.
private final IRtcEngineEventHandler mRtcEventHandler = new IRtcEngineEventHandler() {
// Triggered when the local user successfully joins the specified channel.
@Override
public void onJoinChannelSuccess(String channel, int uid, int elapsed) {
super.onJoinChannelSuccess(channel, uid, elapsed);
showToast("Joined channel " + channel);
}
// Triggered when a remote user/host joins the channel.
@Override
public void onUserJoined(int uid, int elapsed) {
super.onUserJoined(uid, elapsed);
runOnUiThread(() -> {
// Initialize and display remote video view for the new user.
setupRemoteVideo(uid);
showToast("User joined: " + uid);
});
}
// Triggered when a remote user/host leaves the channel.
@Override
public void onUserOffline(int uid, int reason) {
super.onUserOffline(uid, reason);
runOnUiThread(() -> {
showToast("User offline: " + uid);
});
}
};private val mRtcEventHandler = object : IRtcEngineEventHandler() {
override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
super.onJoinChannelSuccess(channel, uid, elapsed)
runOnUiThread {
showToast("Joined channel $channel")
}
}
override fun onUserJoined(uid: Int, elapsed: Int) {
runOnUiThread {
showToast("User joined: $uid")
}
}
override fun onUserOffline(uid: Int, reason: Int) {
super.onUserOffline(uid, reason)
runOnUiThread {
showToast("User offline: $uid")
}
}
}Enable the video module
Follow these steps to enable the video module:
- Call
enableVideoto enable the video module. - Call
startPreviewto enable local video preview.
private void enableVideo() {
mRtcEngine.enableVideo();
mRtcEngine.startPreview();
}private fun enableVideo() {
mRtcEngine?.apply {
enableVideo()
startPreview()
}
}Display the local video
Call setupLocalVideo to initialize the local view and set the local video display properties.
private void setupLocalVideo() {
FrameLayout container = findViewById(R.id.local_video_view_container);
SurfaceView surfaceView = new SurfaceView(getBaseContext());
container.addView(surfaceView);
mRtcEngine.setupLocalVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0));
}/**
* Initializes the local video view and sets the display properties.
* This method adds a SurfaceView to the local video container and configures it.
*/
private fun setupLocalVideo() {
val container: FrameLayout = findViewById(R.id.local_video_view_container)
val surfaceView = SurfaceView(baseContext)
container.addView(surfaceView)
mRtcEngine.setupLocalVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, 0))
}Display remote video
When a remote user joins the channel, call setupRemoteVideo and pass in the remote user's uid, obtained from the onUserJoined callback, to display the remote video.
private void setupRemoteVideo(int uid) {
FrameLayout container = findViewById(R.id.remote_video_view_container);
SurfaceView surfaceView = new SurfaceView(getBaseContext());
surfaceView.setZOrderMediaOverlay(true);
container.addView(surfaceView);
mRtcEngine.setupRemoteVideo(new VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid));
}private fun setupRemoteVideo(uid: Int) {
val container = findViewById<FrameLayout>(R.id.remote_video_view_container)
val surfaceView = SurfaceView(baseContext).apply {
setZOrderMediaOverlay(true)
}
container.addView(surfaceView)
mRtcEngine.setupRemoteVideo(VideoCanvas(surfaceView, VideoCanvas.RENDER_MODE_FIT, uid))
}Handle permissions
To access the camera and microphone on Android devices, declare the necessary permissions in the app's manifest and ensure that the user grants these permissions when the app starts.
-
Open your project's
AndroidManifest.xmlfile and add the following permissions before<application>:<uses-feature android:name="android.hardware.camera" android:required="false" /> <!--Required permissions--> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <!--Optional permissions--> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.BLUETOOTH"/> <!-- For devices running Android 12 (API level 32) or higher and integrating Agora Video SDK version v4.1.0 or lower, you also need to add the following permissions --> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/> <!-- For Android 12.0 or higher, the following permissions are also required --> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/> -
Use the following code to handle runtime permissions in your Android app. The logic ensures that the necessary permissions are granted before starting Broadcast Streaming. In your
MainActivityfile, add the following code:
private boolean checkPermissions() {
for (String permission : getRequiredPermissions()) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
private String[] getRequiredPermissions() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
return new String[]{
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.BLUETOOTH_CONNECT
};
} else {
return new String[]{
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA
};
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSION_REQ_ID && checkPermissions()) {
startBroadcastStreaming();
}
}private val PERMISSION_REQ_ID = 22
private fun requestPermissions() {
ActivityCompat.requestPermissions(this, getRequiredPermissions(), PERMISSION_REQ_ID)
}
private fun checkPermissions(): Boolean {
for (permission in getRequiredPermissions()) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
return false
}
}
return true
}
private fun getRequiredPermissions(): Array<String> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.BLUETOOTH_CONNECT
)
} else {
arrayOf(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSION_REQ_ID && checkPermissions()) {
startBroadcastStreaming()
}
}Start and close the app
When a user launches your app, start real-time interaction. When a user closes the app, stop the interaction.
- In the
onCreatecallback, check whether the app has been granted the required permissions. If the permissions have not been granted, request the required permissions from the user. If permissions are granted, initializeRtcEngineand join a channel.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (checkPermissions()) {
startBroadcastStreaming();
} else {
requestPermissions();
}
}override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (checkPermissions()) {
startBroadcastStreaming()
} else {
requestPermissions()
}
}- When a user closes the app, or switches the app to the background, call
stopPreviewto stop the video preview and then callleaveChannelto leave the current channel and release all session-related resources.
private void cleanupAgoraEngine() {
if (mRtcEngine != null) {
mRtcEngine.stopPreview();
mRtcEngine.leaveChannel();
mRtcEngine = null;
}
}private fun cleanupAgoraEngine() {
mRtcEngine?.apply {
stopPreview()
leaveChannel()
}
mRtcEngine = null
}Complete sample code
A complete code sample demonstrating the basic process of real-time interaction is provided for your reference. To use the sample code, copy the following lines into the MainActivity file in your project. Then, replace <projectname> in package com.example.<projectname> with your project's name.
For the myAppId and token variables, replace the placeholders with the values you obtained from Agora Console. Ensure you enter the same channelName you used when generating the temporary token.
Create a user interface
To connect the sample code to your existing UI, ensure that your XML layout includes the container UI element IDs used to Display the local video and Display remote video.
Alternatively, use the following sample code to generate a basic user interface. Replace the existing content in /app/src/main/res/layout/activity_main.xml with this code.
Sample code to create the user interface
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/local_video_view_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white" />
<FrameLayout
android:id="@+id/remote_video_view_container"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"
android:background="@android:color/darker_gray"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>Test the sample code
Take the following steps to test the sample code:
-
In
MainActivityupdate the values formyAppId, andtokenwith values from Agora Console. Fill in the samechannelNameyou used to generate the token. -
Enable developer options on your Android test device. Turn on USB debugging, connect the Android device to your development machine through a USB cable, and check that your device appears in the Android device options.
-
In Android Studio, click Sync Project with Gradle Files to resolve project dependencies and update the configuration.
-
After synchronization is successful, click Run app. Android Studio starts compilation. After a few moments, the app is installed on your Android device.
-
Launch the App, grant recording and camera permissions. If you set the user role to host, you will see yourself in the local view.
-
On a second Android device, repeat the previous steps to install and launch the app. Alternatively, use the Web demo to join the same channel and test the following use-cases:
- If users on both devices join the channel as hosts, they can see and hear each other.
- If one user joins as host and the other as audience, the host can see themselves in the local video window; the audience can see the host in the remote video window and hear the host.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
Agora provides open source sample projects on GitHub for your reference. Download or view the JoinChannelVideo project for a more detailed example.
API reference
Frequently asked questions
See also
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
-
Xcode 13.0 or higher.
-
An Apple developer account.
-
If you need to use CocoaPods integrated SDK, make sure CocoaPods is installed.
-
Two devices running iOS 14.0 or higher.
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your iOS project and install the Agora Video SDK.
Create a new project Follow these steps to create a project in Xcode:
-
Refer to Create a project. Under Application select App. Use Storyboard for the user interface and choose Swift as the programming language.
If you have not added the development team information, you see the Add account... button. Click the button and follow the on-screen prompts to log in to your Apple ID. Once login is complete, click Next, and choose your Apple account as the development team.
-
Set up automatic signing for your projects.
-
Set the target devices where your app will be deployed.
-
Create a user interface for your app. Refer to Create a user interface to create a bare bones UI.
Add to an existing project Follow these steps to add Broadcast Streaming to your Xcode project:
-
Open your project in Xcode.
-
Set the target devices where your app will be deployed.
-
Create a user interface for your app. Refer to Create a user interface to create a bare bones UI.
Install the SDK
Use one of the following methods to install Video SDK.
Swift Package Manager
-
In Xcode, go to File > Add Package Dependencies.
-
In the search bar, past the following URL:
{`https://github.com/AgoraIO/AgoraRtcEngine_iOS.git`} -
Click Add Package, select the latest version, and click Next.
-
For basic Broadcast Streaming, select RtcBasic.
If needed, also select:
SpatialAudiofor spatial audio effects.VirtualBackgroundfor virtual background.
-
Under Add to Target, select your project and click Add Package.
For further information, refer to Apple's official documentation.
CocoaPods
-
Go to the project root directory in the terminal and run the
pod initcommand. A text file namedPodfileis generated in the project folder. -
Open
Podfileand modify the content as follows. ReplaceYour Appwith your target name.platform :ios, '9.0' target 'Your App' do # For x.y.z fill in the specific SDK version number, such as 4.4.0. # Integrate the Full SDK pod 'AgoraRtcEngine_iOS', 'x.y.z' # To integrate the Lite SDK, use the following line instead # pod 'AgoraLite_iOS', '4.4.0' endObtain the latest version number from the release notes.
-
Run the
pod installcommand in the terminal to install the Video SDK. After successful installation, the terminal shows Pod installation complete!. -
After successful installation, a file with the suffix
.xcworkspaceis generated in the project folder. Open the file through Xcode for subsequent operations.
Manual integration
-
Download the latest version of the SDK from SDKs download and extract the contents.
-
Copy the files in the
libsfolder of the SDK package to your project directory. -
Open Xcode and add the corresponding dynamic library. Make sure the Embed property of the added dynamic library is set to Embed & Sign.
Agora SDK uses
libc++(LLVM) by default. If you need to uselibstdc++(GNU), please contact support@agora.io. The library provided by the SDK is a FAT Image, which includes 32/64-bit simulator and 32/64-bit real machine versions.
The privacy updates for App Store submissions released by Apple, require developers to declare approved reasons for using a set of APIs in their app’s privacy manifest. Agora provides a PrivacyInfo.xcprivacy file that you can include in your project.
Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your app.
The following figure illustrates the essential steps:
This guide includes complete sample code that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps.
Import Agora framework
Add the following import to your swift file:
import AgoraRtcKitInitialize the engine
Call sharedEngine(withAppId:delegate:) to create and initialize an AgoraRtcEngineKit instance. Provide your App ID and an AgoraRtcEngineDelegate implementation to handle SDK events.
var agoraKit: AgoraRtcEngineKit!
let appId = "YOUR_AGORA_APP_ID"
// Initialize the Agora engine
func initializeAgoraVideoSDK() {
agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: appId, delegate: self)
}Join a channel
To join a channel, call joinChannel with the following parameters:
-
Channel name: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.
-
Authentication token: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a token server in your security infrastructure. For the purpose of this guide Generate a temporary token.
-
User ID: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to
0when joining a channel, the SDK generates a random number for the user ID and returns the value in thedidJoinChannelcallback. -
Channel media options: Configure
AgoraRtcChannelMediaOptionsto define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.
For Broadcast Streaming, set the channelProfile to .liveBroadcasting, the clientRoleType to .broadcaster (host) or .audience, and the audienceLatencyLevel to lowLatency.
let channelName = "demo" // Replace with your actual channel name
let token = "<Authentication token>" // Replace with your token
// Join the channel with specified options
func joinChannel() {
let options = AgoraRtcChannelMediaOptions()
// In a live streaming use-case, set the channel use-case to liveBroadcasting
options.channelProfile = .liveBroadcasting
// Set the user role as broadcaster (default is audience)
options.clientRoleType = .broadcaster
// Publish audio captured by microphone
options.publishMicrophoneTrack = true
// Publish video captured by camera
options.publishCameraTrack = true
// Auto subscribe to all audio streams
options.autoSubscribeAudio = true
// Auto subscribe to all video streams
options.autoSubscribeVideo = true
// Set the audience latency level
options.audienceLatencyLevel = .lowLatency
// Use a temporary Token to join the channel
agoraKit.joinChannel(
byToken: token,
channelId: channelName,
uid: 0,
mediaOptions: options
)
}You can also set the latency level after joining a channel by calling the setClientRole method with role set to AgoraClientRole.audience and options.audienceLatencyLevel set to lowLatency.
// Set the user role to audience
let role = AgoraClientRole.audience
let options = AgoraClientRoleOptions()
// Set the low latency level
options.audienceLatencyLevel = .lowLatency
agoraKit.setClientRole(role, options: options)Subscribe to Video SDK events
The Video SDK provides a delegate for handling channel events. To use it, conform to the AgoraRtcEngineDelegate protocol in your class and implement the event methods you want to handle. The following code implements the didJoinChannel, didOfflineOfUid, and didJoinedOfUid callbacks:
To ensure that you receive all Video SDK events, set the Agora Engine event handler before joining a channel.
// Extension for handling Agora SDK callbacks
extension ViewController: AgoraRtcEngineDelegate {
// Triggered when the local user successfully joins a channel
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) {
print("Successfully joined channel: \(channel) with UID: \(uid)")
}
// Triggered when a remote user joins the channel
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
setupRemoteVideo(uid: uid, view: remoteView)
}
// Triggered when a remote user leaves the channel
func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
setupRemoteVideo(uid: uid, view: nil)
}
}To learn about the other SDK events, see AgoraRtcEngineDelegate.
Enable the video module
Follow the steps below to set up the video module.
-
To enable the video module, call
enableVideo. -
To enable local video preview, call
startPreview.// Enable video functionality (audio is enabled by default) agoraKit.enableVideo() // Enable local video preview agoraKit.startPreview()
Display the local video
Call setupLocalVideo to initialize the local view and set the local video display properties.
// Configures and starts displaying the local video feed
func setupLocalVideo() {
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.view = localView
videoCanvas.uid = 0 // UID 0 is assigned to the local user
videoCanvas.renderMode = .hidden
agoraKit.setupLocalVideo(videoCanvas)
}Display remote video
To initialize the remote user view, call setupRemoteVideo and set the local display properties for the remote user. Use the didJoinedOfUid callback to get the UID of the remote user.
func setupRemoteVideo(uid: UInt, view: UIView?) {
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.uid = uid
videoCanvas.view = view // Assign view for joining, set to nil for leaving
videoCanvas.renderMode = .hidden
agoraKit.setupRemoteVideo(videoCanvas)
}Handle permissions
To access the camera and microphone on iOS devices, add the required permissions for real-time interaction. Open the info.plist file from the project navigation bar, edit the property list, to add the required permissions. These permissions are optional. However, if you do not add these permissions, you will not be able to use the corresponding devices.
| Key | Type | Value |
|---|---|---|
| Privacy - Microphone Usage Description | String | For the purpose of using the microphone. For example, for a call or live interactive streaming session. |
| Privacy - Camera Usage Description | String | For the purpose of using the camera. For example, for a call or live interactive streaming session. |
- If your project depends on third-party plugins or libraries, such as a third-party camera library, and the signature of the plug-in or library is inconsistent with the signature of the project, check the Hardened Runtime settings. Specifically, review and potentially disable Runtime Exceptions and Library Validation in the project configuration.
- For further information, refer to Preparing your app for distribution.
Start and close the app
When the user launches the app, it joins the channel and starts Broadcast Streaming. When the user closes the app, it leaves the channel and ends Broadcast Streaming.
-
To start Broadcast Streaming, call the following methods:
// Initialize the Agora engine initializeAgoraVideoSDK() // Start the local video preview setupLocalVideo() // Join an Agora channel joinChannel() -
To leave the channel and release SDK resources when the app is closed, call the following methods:
// Stop local video preview agoraKit.stopPreview() // Leave the channel and release session-related resources agoraKit.leaveChannel(nil) // Release all resources used by the Agora SDK AgoraRtcEngineKit.destroy()
After destroying the engine, you can no longer use SDK methods and callbacks. To use the real-time interaction functions again, create a new engine. See Initialize the engine for details.
Complete sample code
A complete code sample demonstrating the basic process of real-time interaction is provided for your reference. Copy the following code into your ViewController.swift file:
Create a user interface
To connect the sample code to your existing UI, ensure that your ViewController.swift file includes the UIViews used to Display the local video and Display remote video.
Alternatively, use the following sample code to generate a basic user interface. To use this interface, replace the contents of the ViewController.swift file with the following code:
Sample code to create the user interface
// ViewController.swift
import UIKit
class ViewController: UIViewController {
// UI view for displaying the local video stream
var localView: UIView!
// UI view for displaying the remote video stream
var remoteView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Set up the user interface
setupUI()
}
// Sets up the UI layout for local and remote video views
func setupUI() {
// Create the local video view covering the full screen
localView = UIView(frame: UIScreen.main.bounds)
// Create the remote video view positioned in the top-right corner
remoteView = UIView(frame: CGRect(x: self.view.bounds.width - 135, y: 50, width: 135, height: 240))
// Add video views to the main view
self.view.addSubview(localView)
self.view.addSubview(remoteView)
}
}Test the sample code
Take the following steps to test the sample code:
-
In your code update the
appIdandtoken, with the app ID and temporary token you obtained from Agora Console. Use the samechannelNameyou filled in when generating the temporary token. -
Connect your iOS device to your computer.
-
Click Build to run your project and wait a few seconds for the app installation to complete.
-
Allow the app to access the device's microphone and camera.
-
If an untrusted developer prompt pops up on the device, click Cancel to close the prompt, then open Settings > General > VPN and Device Management on the iOS device, and choose to trust the developer in the Developer APP.
-
On a second iOS device, repeat the previous steps to install and launch the app. Alternatively, use the Web demo to join the same channel and test the following use-cases:
- If users on both devices join the channel as hosts, they can see and hear each other.
- If one user joins as host and the other as audience, the host can see themselves in the local video window; the audience can see the host in the remote video window and hear the host.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
Agora provides an open source sample project on GitHub for your reference. Download it or view the source code for a more detailed example.
Add a privacy manifest file
The Agora Video SDK for iOS provides the PrivacyInfo.xcprivacy file that contains the required reasons for the APIs used by the SDK. To add the privacy manifest to your app in Xcode, follow these steps:
-
Create a privacy manifest in your app project:
-
Choose File > New File.
-
Scroll down to the Resource section and select App Privacy File type.
-
Click Next.
-
Check your app in the Targets list.
-
Click Create.
The default file name is
PrivacyInfo.xcprivacywhich is also the required file name for bundled privacy manifests. -
Add the items in
PrivacyInfo.xcprivacyfile of the Video SDK toPrivacyInfo.xcprivacyof the app using the following source code:<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>NSPrivacyTracking</key> <false/> <key>NSPrivacyCollectedDataTypes</key> <array/> <key>NSPrivacyAccessedAPITypes</key> <array> <dict> <key>NSPrivacyAccessedAPIType</key> <string>NSPrivacyAccessedAPICategorySystemBootTime</string> <key>NSPrivacyAccessedAPITypeReasons</key> <array> <string>35F9.1</string> </array> </dict> <dict> <key>NSPrivacyAccessedAPIType</key> <string>NSPrivacyAccessedAPICategoryFileTimestamp</string> <key>NSPrivacyAccessedAPITypeReasons</key> <array> <string>DDA9.1</string> </array> </dict> <dict> <key>NSPrivacyAccessedAPIType</key> <string>NSPrivacyAccessedAPICategoryDiskSpace</string> <key>NSPrivacyAccessedAPITypeReasons</key> <array> <string>E174.1</string> </array> </dict> </array> </dict> </plist>
API reference
Frequently asked questions
See also
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
-
Xcode 13.0 or higher.
-
An Apple developer account.
-
If you need to use CocoaPods integrated SDK, make sure CocoaPods is installed.
-
Two devices running macOS 10.10 or higher.
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your macOS project and install the Agora Video SDK.
Create a new project Follow these steps to create a project in Xcode:
-
Refer to Create a project. Under Application select App. Use Storyboard for the user interface and choose Swift as the programming language.
If you have not added the development team information, you see the Add account... button. Click the button and follow the on-screen prompts to log in to your Apple ID. Once login is complete, click Next, and choose your Apple account as the development team.
-
Set up automatic signing for your projects.
-
Set the target devices where your app will be deployed.
-
Create a user interface for your app. Refer to Create a user interface to create a bare bones UI.
Add to an existing project Follow these steps to add Broadcast Streaming to your Xcode project:
-
Open your project in Xcode.
-
Set the target devices where your app will be deployed.
-
Create a user interface for your app. Refer to Create a user interface to create a bare bones UI.
Install the SDK
Use one of the following methods to install Video SDK.
Swift Package Manager
-
In Xcode, go to File > Add Package Dependencies.
-
In the search bar, past the following URL:
{`https://github.com/AgoraIO/AgoraRtcEngine_macOS.git`} -
Click Add Package, select the latest version, and click Next.
-
For basic Broadcast Streaming, select RtcBasic.
If needed, also select:
SpatialAudiofor spatial audio effects.VirtualBackgroundfor virtual background.
-
Under Add to Target, select your project and click Add Package.
For further information, refer to Apple's official documentation.
CocoaPods
-
Go to the project root directory in the terminal and run the
pod initcommand. A text file namedPodfileis generated in the project folder. -
Open
Podfileand modify the content as follows. ReplaceYour Appwith your target name.platform :macos, '10.11' target 'Your App' do # For x.y.z fill in the specific SDK version number, such as 4.4.0. pod 'AgoraRtcEngine_macOS', 'x.y.z' endObtain the latest version number from the release notes.
-
Run the
pod installcommand in the terminal to install the Video SDK. After successful installation, the terminal shows Pod installation complete!. -
After successful installation, a file with the suffix
.xcworkspaceis generated in the project folder. Open the file through Xcode for subsequent operations.
Manual integration
-
Download the latest version of the SDK from SDKs download and extract the contents.
-
Copy the files in the
libsfolder of the SDK package to your project directory. -
Open Xcode and add the corresponding dynamic library. Make sure the Embed property of the added dynamic library is set to Embed & Sign.
Agora SDK uses
libc++(LLVM) by default. If you need to uselibstdc++(GNU), please contact support@agora.io. The library provided by the SDK is a FAT Image, which includes 32/64-bit simulator and 32/64-bit real machine versions.
The privacy updates for App Store submissions released by Apple, require developers to declare approved reasons for using a set of APIs in their app’s privacy manifest. Agora provides a PrivacyInfo.xcprivacy file that you can include in your project.
Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your app.
The following figure illustrates the essential steps:
This guide includes complete sample code that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps.
Import Agora framework
Add the following import to your swift file:
import AgoraRtcKitInitialize the engine
Call sharedEngine(withAppId:delegate:) to create and initialize an AgoraRtcEngineKit instance. Provide your App ID and an AgoraRtcEngineDelegate implementation to handle SDK events.
var agoraKit: AgoraRtcEngineKit!
let appId = "YOUR_AGORA_APP_ID"
// Initialize the Agora engine
func initializeAgoraVideoSDK() {
agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: appId, delegate: self)
}Join a channel
To join a channel, call joinChannel with the following parameters:
-
Channel name: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.
-
Authentication token: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a token server in your security infrastructure. For the purpose of this guide Generate a temporary token.
-
User ID: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to
0when joining a channel, the SDK generates a random number for the user ID and returns the value in thedidJoinChannelcallback. -
Channel media options: Configure
AgoraRtcChannelMediaOptionsto define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.
For Broadcast Streaming, set the channelProfile to .liveBroadcasting, the clientRoleType to .broadcaster (host) or .audience, and the audienceLatencyLevel to lowLatency.
let channelName = "demo" // Replace with your actual channel name
let token = "<Authentication token>" // Replace with your token
// Join the channel with specified options
func joinChannel() {
let options = AgoraRtcChannelMediaOptions()
// In a live streaming use-case, set the channel use-case to liveBroadcasting
options.channelProfile = .liveBroadcasting
// Set the user role as broadcaster (default is audience)
options.clientRoleType = .broadcaster
// Publish audio captured by microphone
options.publishMicrophoneTrack = true
// Publish video captured by camera
options.publishCameraTrack = true
// Auto subscribe to all audio streams
options.autoSubscribeAudio = true
// Auto subscribe to all video streams
options.autoSubscribeVideo = true
// Set the audience latency level
options.audienceLatencyLevel = .lowLatency
// Use a temporary Token to join the channel
agoraKit.joinChannel(
byToken: token,
channelId: channelName,
uid: 0,
mediaOptions: options
)
}You can also set the latency level after joining a channel by calling the setClientRole method with role set to AgoraClientRole.audience and options.audienceLatencyLevel set to lowLatency.
// Set the user role to audience
let role = AgoraClientRole.audience
let options = AgoraClientRoleOptions()
// Set the low latency level
options.audienceLatencyLevel = .lowLatency
agoraKit.setClientRole(role, options: options)Subscribe to Video SDK events
The Video SDK provides a delegate for handling channel events. To use it, conform to the AgoraRtcEngineDelegate protocol in your class and implement the event methods you want to handle. The following code implements the didJoinChannel, didOfflineOfUid, and didJoinedOfUid callbacks:
To ensure that you receive all Video SDK events, set the Agora Engine event handler before joining a channel.
// Extension for handling Agora SDK callbacks
extension ViewController: AgoraRtcEngineDelegate {
// Triggered when the local user successfully joins a channel
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinChannel channel: String, withUid uid: UInt, elapsed: Int) {
print("Successfully joined channel: \(channel) with UID: \(uid)")
}
// Triggered when a remote user joins the channel
func rtcEngine(_ engine: AgoraRtcEngineKit, didJoinedOfUid uid: UInt, elapsed: Int) {
setupRemoteVideo(uid: uid, view: remoteView)
}
// Triggered when a remote user leaves the channel
func rtcEngine(_ engine: AgoraRtcEngineKit, didOfflineOfUid uid: UInt, reason: AgoraUserOfflineReason) {
setupRemoteVideo(uid: uid, view: nil)
}
}To learn about the other SDK events, see AgoraRtcEngineDelegate.
Enable the video module
Follow the steps below to set up the video module.
-
To enable the video module, call
enableVideo. -
To enable local video preview, call
startPreview.// Enable video functionality (audio is enabled by default) agoraKit.enableVideo() // Enable local video preview agoraKit.startPreview()
Display the local video
Call setupLocalVideo to initialize the local view and set the local video display properties.
// Configures and starts displaying the local video feed
func setupLocalVideo() {
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.view = localView
videoCanvas.uid = 0 // UID 0 is assigned to the local user
videoCanvas.renderMode = .hidden
agoraKit.setupLocalVideo(videoCanvas)
}Display remote video
To initialize the remote user view, call setupRemoteVideo and set the local display properties for the remote user. Use the didJoinedOfUid callback to get the UID of the remote user.
func setupRemoteVideo(uid: UInt, view: UIView?) {
let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.uid = uid
videoCanvas.view = view // Assign view for joining, set to nil for leaving
videoCanvas.renderMode = .hidden
agoraKit.setupRemoteVideo(videoCanvas)
}Handle permissions
To access the camera and microphone on macOS devices, add the required permissions for real-time interaction. Open the info.plist file from the project navigation bar, edit the property list, to add the required permissions. These permissions are optional. However, if you do not add these permissions, you will not be able to use the corresponding devices.
| Key | Type | Value |
|---|---|---|
| Privacy - Microphone Usage Description | String | For the purpose of using the microphone. For example, for a call or live interactive streaming session. |
| Privacy - Camera Usage Description | String | For the purpose of using the camera. For example, for a call or live interactive streaming session. |
- If your project depends on third-party plugins or libraries, such as a third-party camera library, and the signature of the plug-in or library is inconsistent with the signature of the project, check the Hardened Runtime settings. Specifically, review and potentially disable Runtime Exceptions and Library Validation in the project configuration.
- For further information, refer to Preparing your app for distribution.
Configure your macOS project settings by navigating to TARGETS > Project Name > Signing & Capabilities. Enable App Sandbox and Hardened Runtime, and add the necessary permissions as follows:
| Capability | Category | Permission |
|---|---|---|
| App Sandbox | Network |
|
| App Sandbox | Hardware |
|
| Hardened Runtime | Resource Access |
|
Start and close the app
When the user launches the app, it joins the channel and starts Broadcast Streaming. When the user closes the app, it leaves the channel and ends Broadcast Streaming.
-
To start Broadcast Streaming, call the following methods:
// Initialize the Agora engine initializeAgoraVideoSDK() // Start the local video preview setupLocalVideo() // Join an Agora channel joinChannel() -
To leave the channel and release SDK resources when the app is closed, call the following methods:
// Stop local video preview agoraKit.stopPreview() // Leave the channel and release session-related resources agoraKit.leaveChannel(nil) // Release all resources used by the Agora SDK AgoraRtcEngineKit.destroy()
After destroying the engine, you can no longer use SDK methods and callbacks. To use the real-time interaction functions again, create a new engine. See Initialize the engine for details.
Complete sample code
A complete code sample demonstrating the basic process of real-time interaction is provided for your reference. Copy the following code into your ViewController.swift file:
Create a user interface
To connect the sample code to your existing UI, ensure that your ViewController.swift file includes the UIViews used to Display the local video and Display remote video.
Alternatively, use the following sample code to generate a basic user interface. To use this interface, replace the contents of the ViewController.swift file with the following code:
Sample code to create the user interface
// ViewController.swift
import Cocoa
import AgoraRtcKit
class ViewController: NSViewController {
// UI view for displaying the local video stream
var localView: NSView!
// UI view for displaying the remote video stream
var remoteView: NSView!
override func viewDidLoad() {
super.viewDidLoad()
// Set up the user interface
setupUI()
}
func setupUI() {
guard let screenFrame = NSScreen.main?.frame else { return } // Ensure screen is available
// Create the local video view covering the full screen
localView = NSView(frame: screenFrame)
// Create the remote video view positioned in the top-right corner
remoteView = NSView(frame: CGRect(x: self.view.bounds.width - 135, y: 50, width: 250, height: 250))
// Add video views to the main view
self.view.addSubview(localView)
self.view.addSubview(remoteView)
}
}Test the sample code
Take the following steps to test the sample code:
-
In your code update the
appIdandtoken, with the app ID and temporary token you obtained from Agora Console. Use the samechannelNameyou filled in when generating the temporary token. -
Click Build to run your project and wait a few seconds for the app installation to complete.
-
Allow the app to access the device's microphone and camera.
-
On a second macOS device, repeat the previous steps to install and launch the app. Alternatively, use the Web demo to join the same channel and test the following use-cases:
- If users on both devices join the channel as hosts, they can see and hear each other.
- If one user joins as host and the other as audience, the host can see themselves in the local video window; the audience can see the host in the remote video window and hear the host.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
Agora provides open source sample projects on GitHub for your reference. Download or view the JoinChannelVideo project for a more detailed example.
API reference
See also
Explore sample implementations to quickly integrate Conversational AI.
- RTC SDK API examples: Sample projects for the Agora RTC Web SDK 4.x, covering basic, advanced examples for Vue and React.
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
-
A supported browser. Agora strongly recommends using the latest stable version of Google Chrome.
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your Web project and install the Agora Video SDK.
Create a new project To initialize a new Vite project, take the following steps:
-
Open a terminal and run the following command:
npm create vite@latest agora_web_quickstart -- --template vanilla
This creates a new folder named agora_web_quickstart and initializes a Vite project inside it using the vanilla JavaScript template.
-
Navigate to the newly created folder. Download and set up dependencies for your project:
cd agora_web_quickstart npm install -
Create a user interface for your project. The UI consists of buttons to join and leave a channel. Refer to Create a user interface to get a bare bones html layout.
Add to an existing project To add Broadcast Streaming to your existing project, take the following steps:
-
Create a JavaScript file in your project's
srcfolder to addAgoraRTCClientcode that implements specific application logic. -
Add an HTML file to your project to create a user interface. The UI consists of buttons to join and leave a channel. Refer to Create a user interface to get a bare bones HTML layout.
-
Include the JavaScript file in your HTML file.
<script type="module" src="/src/main.js"></script>
Install the SDK
Add the Video SDK to your project:
npm install agora-rtc-sdk-ngImplement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your app.
This guide includes complete sample code that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps.
Import the AgoraRTC SDK
import AgoraRTC from "agora-rtc-sdk-ng";Initialize an instance of AgoraRTCClient
Call createClient to initialize an AgoraRTCClient object.
Set the Channel mode and Video encoding format based on your use case.
For Broadcast Streaming Set mode to live.
// RTC client instance
let client = null;
// Initialize the AgoraRTC client
function initializeClient() {
client = AgoraRTC.createClient({ mode: "live", codec: "vp8", role: "host" });
setupEventListeners();
}Join a channel
To join a channel, call join with the following parameters:
-
App ID: The App ID for your project.
-
Channel name: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.
-
Authentication token: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a token server in your security infrastructure. For the purpose of this guide Generate a temporary token.
-
User ID: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to
0when joining a channel, the SDK generates a random number for the user ID.
When you join as a host, you can publish local media streams in the channel; you can also subscribe to streams published by other hosts. As an audience member, you may only subscribe to media streams published by hosts.
-
Join as a host
// Join as a host async function joinChannel() { await client.join(appId, channel, token, uid); // A host can both publish tracks and subscribe to tracks client.setClientRole("host", clientRoleOptions); // Create and publish local tracks await createLocalMediaTracks(); await publishLocalTracks(); displayLocalVideo(); } -
Join as audience
To join a channel as an audience member, first call
join, then usesetClientRoleto set the role. Listen for theuser-publishedcallback to play the tracks published in the channel.// Join as audience async function joinAsAudience() { await client.join(appId, channel, token, uid); // Low audience latency level for broadcast streaming let clientRoleOptions = { level: 1 }; // Audience can only subscribe to tracks client.setClientRole("audience", clientRoleOptions); }
Create local media tracks
To set up the necessary local media tracks:
- Call
createMicrophoneAudioTrackto create a local audio track. - Call
createCameraVideoTrackto create a local Video track.
// Declare variables for local tracks
let localAudioTrack = null;
let localVideoTrack = null;
// Create local audio and video tracks
async function createLocalMediaTracks() {
localAudioTrack = await AgoraRTC.createMicrophoneAudioTrack();
localVideoTrack = await AgoraRTC.createCameraVideoTrack();
}Publish local media tracks
To make the created audio and video tracks available for other users in the channel, use the publish method.
async function publishLocalTracks() {
await client.publish([localAudioTrack, localVideoTrack]);
}See Local audio and video tracks to learn more about local tracks.
Set up event listeners
Use the on method to register event listeners for SDK events. The SDK triggers the user-published event when a user publishes an audio track in the channel. Similarly, it triggers the user-unpublished event when a user leaves the channel, goes offline, or unpublishes a media track.
// Handle client events
function setupEventListeners() {
// Declare event handler for "user-published"
client.on("user-published", async (user, mediaType) => {
// Subscribe to media streams
await client.subscribe(user, mediaType);
if (mediaType === "video") {
// Specify the ID of the DOM element or pass a DOM object.
user.videoTrack.play("<Specify a DOM element>");
}
if (mediaType === "audio") {
user.audioTrack.play();
}
});
// Handle the "user-unpublished" event to unsubscribe from the user's media tracks
client.on("user-unpublished", async (user) => {
const remotePlayerContainer = document.getElementById(user.uid);
remotePlayerContainer && remotePlayerContainer.remove();
});
}- After successfully unsubscribing, the SDK releases the corresponding
RemoteTrackobject. This automatically removes the video playback element and stops audio playback. - If a remote user actively stops publishing, the local user receives the
user-unpublishedcallback. Upon receiving this callback, the SDK automatically releases the correspondingRemoteTrackobject, so you do not need to callunsubscribeagain. - The
unsubscribemethod is asynchronous and should be used withPromiseorasync/await.
To ensure that you receive all Video SDK events, set up event listeners before joining a channel.
For more information about other AgoraRTCClient events, refer to the API reference.
Display the local video
To play the local video, use the play method of the local video track. Pass an element ID or a DOM element from your UI where you want to render the video.
// Display local video
function displayLocalVideo() {
const localPlayerContainer = document.createElement("div");
localPlayerContainer.id = uid;
localPlayerContainer.textContent = `Local user ${uid}`;
localPlayerContainer.style.width = "640px";
localPlayerContainer.style.height = "480px";
document.body.append(localPlayerContainer);
localVideoTrack.play(localPlayerContainer);
}Display remote video
To display the remote video, call the play method of the remote user's videoTrack and pass in either the element ID or a DOM element from your UI where you want to render the video.
// Display remote user's video
function displayRemoteVideo(user) {
const remotePlayerContainer = document.createElement("div");
remotePlayerContainer.id = user.uid.toString();
remotePlayerContainer.textContent = `Remote user ${user.uid}`;
remotePlayerContainer.style.width = "640px";
remotePlayerContainer.style.height = "480px";
document.body.append(remotePlayerContainer);
user.videoTrack.play(remotePlayerContainer);
}Leave the channel
To exit the channel, close local audio and video tracks and call leave.
// Leave the channel and clean up
async function leaveChannel() {
// Stop the local media tracks to release the microphone and camera resources
if (localAudioTrack) {
localAudioTrack.close();
localAudioTrack = null;
}
if (localVideoTrack) {
localVideoTrack.close();
localVideoTrack = null;
}
// Leave the channel
await client.leave();
}Complete sample code
A complete code sample demonstrating the basic process of real-time interaction is provided here for your reference. To use the sample, copy the following code to the JavaScript file in the project's src folder.
Create a user interface
Open index.html in the project's root folder and replace the contents with the following code to implement a basic client user interface:
Sample code to create the user interface
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Agora Web SDK Quickstart</title>
</head>
<body>
<h2 class="left-align">Agora Web SDK Quickstart</h2>
<div class="row">
<div>
<button type="button" id="host-join">Join as host</button>
<button type="button" id="audience-join">Join as audience</button>
<button type="button" id="leave">Leave</button>
</div>
</div>
<!-- Include the JavaScript file -->
<script type="module" src="/src/main.js"></script>
</body>
</html>Test the sample code
Take the following steps to run and test the sample code:
-
In your
.jsfile, update the values forappId, andtokenwith values from Agora Console. Use the samechannelname you used to generate the token. -
To start the development server, use the following command:
npm run dev -
Open your browser and navigate to the URL displayed in your terminal, for example,
http://localhost:5173.
You see the following page:
-
On a second device, repeat the previous steps to install and launch the app. Alternatively, use the Web demo or clone the sample project on Github to join the same channel and test the following use-cases:
- If users on both devices join the channel as hosts, they can see and hear each other.
- If one user joins as host and the other as audience, the host can see themselves; the audience can see the host in the remote video view and hear the host.
- Run the web app on a local server (localhost) for testing purposes only. When deploying to a production environment, use the HTTPS protocol.
- Due to browser security policies that restrict HTTP addresses to 127.0.0.1, the Agora Web SDK only supports HTTPS protocol and
http://localhost(http://127.0.0.1). Please do not use the HTTP protocol to access your project, except forhttp://localhost(http://127.0.0.1).
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
-
Agora provides open source sample projects on GitHub for your reference. Download or view the basicVideoCall project for a more detailed example.
API reference
AgoraRTC.createClientIAgoraRTCRemoteUserplayjoincreateMicrophoneAudioTrackcreateCameraVideoTrackpublish- AgoraRTCClient events
Channel modes
The Video SDK supports the following channel modes:
rtc: Communication use-caselive: Live broadcast use-case
Communication use-case
It is suitable for use-cases where all users in the channel need to communicate with each other and the total number of users is not too large, such as multi-person conferences and online chats.
Live broadcast use-case
It is suitable for use-cases where there are few publishers but many subscribers. In this use-case, the SDK defines two user roles: audience (default) and host. Hosts can send and receive audio and video, but audience cannot send and may only receive audio and video. You can specify user roles by setting the parameters createClient of role, or you can call setClientRole to dynamically modify user roles.
Video encoding formats
You can set the codec parameter in the createClient method to the following video encoding formats:
vp8(VP8)h264(H.264)vp9(VP9)
This setting only affects the video encoding format of the host. For the audience, as long as their device and browser support the decoding of this format, the subscription can be completed normally.
Support for these formats may vary across browsers and devices. The following table lists the codec formats supported by different browsers for reference:
| Browser | VP8 | H.264 | VP9 |
|---|---|---|---|
| Desktop Chrome 58+ | ✔ | ✔ | ✔ |
| Firefox 56+ | ✔ | ✔ | ✔(Requires Firefox 69+) |
| Safari 12.1+ | ✔ | ✔ | ✔(Requires Safari 16+) |
| Safari < 12.1 | ✘ | ✔ | ✘ |
| AndroidChrome 58+ | ✔ | No clear information | ✔(Requires Chrome 68+) |
- H.264 support on Firefox depends on the
OpenH264video codec plug-in from Cisco Systems, Inc. - For Chrome, support for H.264 on Android devices varies based on device hardware compatibility with hardware codecs mandated by Chrome.
Local audio and video tracks
The SDK uses a hierarchy where all local track objects derive from the LocalTrack base class. This class defines the common behavior for all local tracks. Specific track types, such as LocalAudioTrack and LocalVideoTrack inherit from LocalTrack and extend its functionality.
To publish a local track, you call the publish method of the client with the LocalTrack object as an input parameter. This approach makes publishing a track independent of how you create your local track.
There are two main types of local tracks: LocalAudioTrack and LocalVideoTrack for publishing audio and video, respectively. Each type of LocalTrack comes with its own set of tools. For example, LocalAudioTrack lets you control the volume, while LocalVideoTrack has functions for customizing video.
The SDK includes more specific classes based on LocalAudioTrack and LocalVideoTrack. For example, the CameraVideoTrack, is a type of LocalVideoTrack that you can use to publish video from your camera. It comes with extra features for controlling the camera and adjusting video quality.
The following diagram shows the relationship between the LocalTrack classes:
Other integration methods
When you use npm to install the Web SDK, you can enable tree shaking to reduce the size of the app after integration. For details, see Using tree shaking.
In addition to using npm to install the Web SDK, you can also use the following methods:
-
In the project HTML file, add the following tag to obtain the SDK from CDN:
<script src="https://download.agora.io/sdk/release/AgoraRTC_N-4.24.5.js"></script> -
Download the Video SDK package locally, save the
.jsfiles in the SDK package to the project directory, and then add the following tag to the project HTML file:<script src="./AgoraRTC_N-4.24.5.js"></script>
Visit the Download page to obtain the link for the latest SDK version.
Frequently asked questions
Why do I get a digital envelope routines::unsupported error when running the quickstart project locally?
This issue arises in projects configured with webpack for local execution due to changes in Node.js 16 and above. The modifications in Node.js, particularly its dependency on OpenSSL (detailed in the node issue), impact the local development environment dependencies of the project. Refer to the webpack issue for details.
Use one of the following solutions to resolve the issue:
-
Run the following command to set a temporary environment variable (Recommended):
export NODE_OPTIONS=--openssl-legacy-provider -
Temporarily switch to a lower version of Node.js.
See also
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
-
A device running Windows 7 or higher.
-
Microsoft Visual Studio 2017 or higher with C++ desktop development support. C++ 11 or above.
-
If you use C# for development, you also need the .NET framework.
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your Windows project and install the Agora Video SDK.
Create a new project The following steps outline the process to set up a new Visual Studio 2022 project on Windows 11 for implementing real-time audio and video interaction functions.
-
In Visual Studio, select File > New > Project to create a new project. In the pop-up window, select MFC application as the project template, click Next, update the project name to
AgoraQuickStart, set the project storage location, and then click Create. -
In the pop-up MFC application window, set the application type to Dialog-based , and set Use MFC to Use MFC in a shared DLL. Enter the generated class, set the generated class to Dlg, set the base class to CDialog, and click Finish.
Add to an existing project To integrate real-time audio and video interaction into your project:
-
Launch Visual Studio 2022 and open your existing project by selecting File > Open > Project/Solution.
-
Navigate to your project directory and open the
.slnfile. -
Create a user-interface for your app based on your application use-case.
A basic UI consists of the following controls:
- A Picture Control for displaying local video
- A Picture Control for displaying remote video
- An Edit Control for entering a channel name
- Buttons to join and leave a channel
Refer to Create a user interface to get a bare bones sample layout.
Install the SDK
Install the Agora Video SDK:
-
Download the latest Windows SDK.
-
Unzip and open the downloaded SDK. Copy all subfolders in
sdk/to your solution folder. Make sure these subfolders are in the same directory as your.slnfile.
Configure the project
In the Solution Explorer window, right-click the project name and click Properties to configure the following:
- Go to the C/C++ > General > Additional Include Directory menu, click Edit, and in the pop-up window enter
$(SolutionDir)sdk\high_level_api\include. - Go to the Linker > General > Additional Library Directory menu, click Edit, and in the pop-up window:
- for 64 bit Windows, enter
$(SolutionDir)sdk\x86_64. - for x86 Windows, enter
$(SolutionDir)sdk\x86.
- for 64 bit Windows, enter
- Go to the Linker > Input > Additional Dependencies menu, click Edit, and in the pop-up window:
- for 64 bit Windows, enter
$(SolutionDir)sdk\x86_64\agora_rtc_sdk.dll.lib. - for x86 Windows, enter
$(SolutionDir)sdk\x86\agora_rtc_sdk.dll.lib.
- for 64 bit Windows, enter
- Enter the Advanced menu, and in Advanced Properties, set Copy contents to OutDir and Copy C++ runtime to output directory to
Yes. - Go to the Build Events > Post-Build Events > Command Line menu and enter
copy $(SolutionDir)sdk\x86_64\*.dll $(SolutionDir)$(Platform)\$(Configuration). - Click Apply to save the configuration.
Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your app.
The following figure illustrates the essential steps:
This guide includes complete sample code that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps.
Initialize the engine
For real-time communication, initialize an IRtcEngine instance and set up event handlers to manage user interactions within the channel. Use RtcEngineContext to specify the App ID, and custom event handler, then call initialize to initialize the engine, enabling further channel operations. Add the following code to your header and .cpp files:
// Declare the required variables
IRtcEngine* m_rtcEngine = nullptr; // RTC engine instance
CAgoraQuickStartRtcEngineEventHandler m_eventHandler; // IRtcEngineEventHandlervoid CAgoraQuickStartDlg::initializeAgoraEngine() {
// Create IRtcEngine object
m_rtcEngine = createAgoraRtcEngine();
// Create IRtcEngine context object
RtcEngineContext context;
// Input your App ID. You can obtain your project's App ID from the Agora Console
context.appId = APP_ID;
// Add event handler for callbacks and events
context.eventHandler = &m_eventHandler;
// Initialize
int ret = m_rtcEngine->initialize(context);
m_initialize = (ret == 0);
if (m_initialize) {
// Enable the video module
m_rtcEngine->enableVideo();
} else {
AfxMessageBox(_T("Failed to initialize Agora RTC engine"));
}
}Join a channel
To join a channel, call joinChannel[2/2] with the following parameters:
-
Channel name: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.
-
Authentication token: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a token server in your security infrastructure. For the purpose of this guide Generate a temporary token.
-
User ID: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to
0when joining a channel, the SDK generates a random number for the user ID and returns the value in theonJoinChannelSuccesscallback. -
Channel media options: Configure
ChannelMediaOptionsto define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.
For Broadcast Streaming, set the channelProfile to CHANNEL_PROFILE_LIVE_BROADCASTING, the clientRoleType to CLIENT_ROLE_BROADCASTER (host) or CLIENT_ROLE_AUDIENCE, and the audienceLatencyLevel to AUDIENCE_LATENCY_LEVEL_LOW_LATENCY.
void CAgoraQuickStartDlg::joinChannel(const char* token, const char* channelName) {
ChannelMediaOptions options;
// Set channel profile to live broadcasting
options.channelProfile = CHANNEL_PROFILE_LIVE_BROADCASTING;
// Set user role to broadcaster; keep default value if setting user role to audience
options.clientRoleType = CLIENT_ROLE_BROADCASTER;
// Publish the audio stream captured by the microphone
options.publishMicrophoneTrack = true;
// Publish the camera track
options.publishCameraTrack = true;
// Automatically subscribe to all audio streams
options.autoSubscribeAudio = true;
// Automatically subscribe to all video streams
options.autoSubscribeVideo = true;
// Specify the audio latency level
options.audienceLatencyLevel = AUDIENCE_LATENCY_LEVEL_LOW_LATENCY;
// Join the channel using the token and channel name (both are const char*)
m_rtcEngine->joinChannel(token, channelName, 0, options);
}Subscribe to Video SDK events
The Video SDK provides an interface for subscribing to channel events. To use it, create a custom event handler class by inheriting from IRtcEngineEventHandler and override its methods to handle real-time interaction events. Add callbacks to receive notification of users joining and leaving the channel.
To ensure that you receive all Video SDK events, set the Agora Engine event handler before joining a channel.
// Define the CAgoraQuickStartRtcEngineEventHandler class to handle callback events such as users joining and leaving the channel
class CAgoraQuickStartRtcEngineEventHandler
: public IRtcEngineEventHandler {
public:
CAgoraQuickStartRtcEngineEventHandler()
: m_hMsgHandler(nullptr) {
}
// Set the handle of the message receiving window
void SetMsgReceiver(HWND hWnd) {
m_hMsgHandler = hWnd;
}
// Register onJoinChannelSuccess callback
// This callback is triggered when a local user successfully joins a channel
virtual void onJoinChannelSuccess(const char* channel, uid_t uid, int elapsed) {
if (m_hMsgHandler) {
::PostMessage(m_hMsgHandler, WM_MSGID(EID_JOIN_CHANNEL_SUCCESS), uid, 0);
}
}
// Register onUserJoined callback
// This callback is triggered when the remote host successfully joins the channel
virtual void onUserJoined(uid_t uid, int elapsed) {
if (m_hMsgHandler) {
::PostMessage(m_hMsgHandler, WM_MSGID(EID_USER_JOINED), uid, 0);
}
}
// Register onUserOffline callback
// This callback is triggered when the remote host leaves the channel or is offline
virtual void onUserOffline(uid_t uid, USER_OFFLINE_REASON_TYPE reason) {
if (m_hMsgHandler) {
::PostMessage(m_hMsgHandler, WM_MSGID(EID_USER_OFFLINE), uid, 0);
}
}
private:
HWND m_hMsgHandler;
};Implement the callback functions.
LRESULT CAgoraQuickStartDlg::OnEIDJoinChannelSuccess(WPARAM wParam, LPARAM lParam) {
// Join channel success callback
uid_t localUid = wParam;
return 0;
}
LRESULT CAgoraQuickStartDlg::OnEIDUserJoined(WPARAM wParam, LPARAM lParam) {
// Remote user joined callback
uid_t remoteUid = wParam;
if (m_remoteRender) {
return 0;
}
// Render remote view
VideoCanvas canvas;
canvas.renderMode = RENDER_MODE_TYPE::RENDER_MODE_HIDDEN;
canvas.uid = remoteUid;
canvas.view = m_staRemote.GetSafeHwnd();
m_rtcEngine->setupRemoteVideo(canvas);
m_remoteRender = true;
return 0;
}
LRESULT CAgoraQuickStartDlg::OnEIDUserOffline(WPARAM wParam, LPARAM lParam) {
// Remote user left callback
uid_t remoteUid = wParam;
if (!m_remoteRender) {
return 0;
}
// Clear remote view
VideoCanvas canvas;
canvas.uid = remoteUid;
m_rtcEngine->setupRemoteVideo(canvas);
m_remoteRender = false;
return 0;
}Enable the video module
Call the enableVideo method to enable the video module.
// Enable the video module
m_rtcEngine->enableVideo();Display the local video
Follow these steps to set up and start the local video preview:
-
Create a
VideoCanvasinstance and configure its properties: -
Set the video rendering mode.
-
Specify the user ID (
uid). -
Define the display window.
-
Call the
setupLocalVideomethod to apply theVideoCanvasconfiguration. -
Call the
startPreviewmethod to start the local video preview.void CAgoraQuickStartDlg::setupLocalVideo() { // Set local video display properties VideoCanvas canvas; // Set video to be scaled proportionally canvas.renderMode = RENDER_MODE_TYPE::RENDER_MODE_HIDDEN; // User ID canvas.uid = 0; // Video display window canvas.view = m_staLocal.GetSafeHwnd(); m_rtcEngine->setupLocalVideo(canvas); // Preview the local video m_rtcEngine->startPreview(); }
Display remote video
To display the remote user's video:
-
Define the video display properties using
VideoCanvas. -
Call
setupRemoteVideoto render the video.void CAgoraQuickStartDlg::setupRemoteVideo(uid_t remoteUid) { // Set remote video display properties VideoCanvas canvas; // Set video size to be proportionally scaled canvas.renderMode = RENDER_MODE_TYPE::RENDER_MODE_HIDDEN; // Remote user ID canvas.uid = remoteUid; // You can only choose to set either view or surfaceTexture. If both are set, only the settings in view take effect. canvas.view = m_staRemote.GetSafeHwnd(); m_rtcEngine->setupRemoteVideo(canvas); m_remoteRender = true; return 0; }
Leave the channel
When a user ends a call, or closes the app call leaveChannel to exit the current channel.
To stop the local video preview and clear the view:
-
Call
stopPreviewto stop playing the local video. -
Call
setupLocalVideo, passing an emptyVideoCanvasto reset the view.void CAgoraQuickStartDlg::LeaveChannel() { if (m_rtcEngine) { // Stop local video preview m_rtcEngine->stopPreview(); // Leave the channel m_rtcEngine->leaveChannel(); // Clear local view VideoCanvas canvas; canvas.uid = 0; m_rtcEngine->setupLocalVideo(canvas); m_remoteRender = false; } }
Release resources
To destroy the engine instance, call release :
// Release resources when the object is destroyed
m_rtcEngine->release(true);
m_rtcEngine = NULL;After you call Dispose, you can no longer use any SDK methods or callbacks. To use real-time Broadcast Streaming again, you must create a new engine. For more information, see Initialize the Engine.
Complete sample code
A complete code sample that implements the basic process of real-time interaction is presented here for your reference. Copy the sample code into your project to quickly implement the basic functions of real-time interaction.
AgoraQuickStartDlg.h
Create a user interface
To connect the sample code to your existing user interface, ensure that your UI includes the controls used to Display the local video and Display remote video.
Alternatively, follow these steps to create a bare-bones UI for your project.
Steps to create a minimalistic UI
-
Switch the project to resource view in the right menu bar, and then open the
.Dialogfile. -
From View > Toolbox, select Add Picture Control, and in Properties > Miscellaneous, set the ID of the control to
IDC_STATIC_REMOTE. -
From View > Toolbox , select Add Picture Control , and in Properties > Miscellaneous , set the control's ID to
IDC_STATIC_LOCAL. -
To set up an input box for entering the channel name, from View > Toolbox , select add Static Text control, and change the description text to
Channel namein the properties. Add an Edit Control as an input box, and in Properties > Miscellaneous, set the control's ID toIDC_EDIT_CHANNEL. -
To add join and leave channel buttons, open View > Toolbox and add two Button controls. In Properties > Miscellaneous , set the IDs to
ID_BTN_JOINandID_BTN_LEAVE, and set the description text to Join and Leave respectively. Your user interface looks similar to the following:
Follow these steps to create a bare-bones UI for your project.
Test the sample code
To test your app, follow these steps:
-
In Visual Studio, select local Windows debugger to start compiling the application.
-
Enter the name of the channel you want to join in the input box and click the Join button to join the channel.
You see yourself in the local view.
-
Use the Web demo to join the same channel and test the following use-cases:
- If users on both devices join the channel as hosts, they can see and hear each other.
- If one user joins as host and the other as audience, the host can see themselves in the local video window; the audience can see the host in the remote video window and hear the host.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
Agora provides open source sample projects on GitHub for your reference.
- Download or view the sample project for a more detailed example.
- For a Windows C# implementation, see this sample project.
API Reference
Frequently asked questions
See also
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
-
Node.js 14 or higher.
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your Electron project and install the Agora Video SDK.
-
Create a new directory for your Electron project in a local folder. Add the following files to the root of the project directory:
package.json: Manages project dependencies and scripts.index.html: Defines the app's user interface.main.js: The main process entry point.renderer.js: The renderer process script, responsible for interacting with the Agora Electron SDK.
-
Create a user interface for your app based on your use-case.
A basic user interface consists of an element for local video and an element for remote video. Refer to Create a user interface to get a bare bones sample layout.
Install the SDK
npm integration
Add the Video SDK to your project using npm:
-
Configure
package.json-
macOS
{ "name": "electron-demo-app", "version": "0.1.0", "author": "your name", "description": "My Electron app", "main": "main.js", "scripts": { "start": "electron ." }, "agora_electron": { "platform": "darwin", "prebuilt": true }, "dependencies": { "agora-electron-sdk": "latest" }, "devDependencies": { "electron": "latest" } } -
Windows
{ "name": "electron-demo-app", "version": "0.1.0", "author": "your name", "description": "My Electron app", "main": "main.js", "scripts": { "start": "electron ." }, "agora_electron": { "platform": "win32", "prebuilt": true, "arch": "ia32" }, "dependencies": { "agora-electron-sdk": "latest" }, "devDependencies": {"electron": "latest" } }Configuration parameters
agora-electron-sdk: Version number of the Agora Electron SDK. Set to latest for the most recent version, or specify a version number. Seeagora-electron-sdkon npm for available versions.electron: Electron version number. Versions 5.0.0 and above are supported. For macOS devices with M1 chips, use version 11.0.0 or higher.platform(Optional): Target platform. Set todarwinfor macOS orwin32for Windows.prebuilt(Optional): Set totrueby default to prevent compatibility issues between Electron, Node.js, and the SDK.arch(Optional): Target architecture. Defaults to your system architecture.
Different versions of Electron have different environment requirements. If you see errors due to a mismatched environment when running the project, refer to the official Electron documentation to choose the appropriate Electron and Node.js versions.
-
-
To install the dependencies, open a terminal in your project root directory and run the following commands::
-
macOS
npm install -
Windows
npm install -D --arch=ia32 electron npm install
-
- On Windows, you must first install the 32-bit version of Electron by running
npm install -D --arch=ia32 electron, and then runnpm install. Otherwise, you may encounter the error: “Not a valid Win32 application.” - If a
node_modulesfolder exists in the project root directory, delete it before runningnpm installto avoid potential errors.
Manual Integration Compile the latest SDK from the Electron SDK repository and integrate it into your application.
Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your app.
The following figure illustrates the essential steps:
This guide includes complete sample code that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps.
Set up the main process
The main.js file is the entry point for your Electron application. It defines the main process, which is responsible for creating and managing browser windows. This script initializes the app, loads the UI from index.html, and ensures proper behavior across platforms.
const { app, BrowserWindow } = require("electron");
const path = require("path");
// If using Electron 9.x or later, set allowRendererProcessReuse to false
app.allowRendererProcessReuse = false;
function createWindow() {
// Create the browser window
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
// preload: path.join(__dirname, "renderer.js"),
// Enable Node integration and disable context isolation
nodeIntegration: true,
contextIsolation: false,
},
});
// Load the contents of the index.html file
mainWindow.loadFile("./index.html");
// Open the Developer Tools
mainWindow.webContents.openDevTools();
}
// Manage the browser window for the Electron app
app.whenReady().then(() => {
createWindow();
// On macOS, create a new window if none are open
app.on("activate", function () {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
// Quit the Electron app when all windows are closed (except on macOS)
app.on("window-all-closed", function () {
if (process.platform !== "darwin") app.quit();
});Handle permissions
Perform this step only when the target platform is macOS. Starting with macOS v10.14, you must check and obtain permission before accessing the camera or microphone. Use Electron’s askForMediaAccess method to request these permissions from the user.
Add the following code to the main.js file:
// Check and request device permissions
async function checkAndApplyDeviceAccessPrivilege() {
// Check and request camera permission
const cameraPrivilege = systemPreferences.getMediaAccessStatus('camera');
console.log(`Camera privilege before applying: ${cameraPrivilege}`);
if (cameraPrivilege !== 'granted') {
await systemPreferences.askForMediaAccess('camera');
console.log('Requested camera access from user');
}
// Check and request microphone permission
const micPrivilege = systemPreferences.getMediaAccessStatus('microphone');
console.log(`Microphone privilege before applying: ${micPrivilege}`);
if (micPrivilege !== 'granted') {
await systemPreferences.askForMediaAccess('microphone');
console.log('Requested microphone access from user');
}
}
checkAndApplyDeviceAccessPrivilege();Import dependencies
Import the modules and functions required to build the app using Video SDK. Add the following code to renderer.js:
const {
createAgoraRtcEngine,
ChannelProfileType,
ClientRoleType,
VideoSourceType,
VideoViewSetupMode,
AudienceLatencyLevelType
} = require("agora-electron-sdk");Specify connection parameters
Provide the App ID, temporary token, and the channel name you used when generating the token. The engine uses these values to initialize and join the channel.
// Enter your App ID
const APPID = "<-- Insert App Id -->";
// Enter your temporary token
let token = "<-- Insert Token -->";
// Enter the channel name used when generating the token
const channel = "<-- Insert Channel Name -->";
// Specify a unique user ID for this session
let uid = 123;Initialize the engine
For real-time communication, create an IRtcEngine object by calling createAgoraRtcEngine and then call initialize with RtcEngineContext to specify the App ID. Call registerEventHandler to register a custom event handler for managing user interactions within the channel.
let rtcEngine;
const os = require("os");
const path = require("path");
const sdkLogPath = path.resolve(os.homedir(), "./test.log");
// Create RtcEngine instance
rtcEngine = createAgoraRtcEngine();
// Initialize RtcEngine instance
rtcEngine.initialize({
appId: APPID,
logConfig: { filePath: sdkLogPath }
});Enable the video module
Call the enableVideo method to enable the video module, and then call startPreview to start the local video preview.
// Enable the video module
rtcEngine.enableVideo();
// Start the local video preview
rtcEngine.startPreview();Join a channel
To join a channel, call joinChannel with the following parameters:
-
Channel name: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.
-
Authentication token: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a token server in your security infrastructure. For the purpose of this guide Generate a temporary token.
-
User ID: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to
0when joining a channel, the SDK generates a random number for the user ID and returns the value in theonJoinChannelSuccesscallback. -
Channel media options: Configure
ChannelMediaOptionsto define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.
For Broadcast Streaming, set the channelProfile to ChannelProfileLiveBroadcasting, the clientRoleType to ClientRoleBroadcaster (host) or ClientRoleAudience, and the audienceLatencyLevelType to AudienceLatencyLevelLowLatency.
// Join the channel using a temporary token
rtcEngine.joinChannel(token, channel, uid, {
// Set the channel profile to live broadcasting
channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,
// Set the user role to broadcaster; keep the default value for audience role
clientRoleType: ClientRoleType.ClientRoleBroadcaster,
// Publish audio collected from the microphone
publishMicrophoneTrack: true,
// Publish video collected from the camera
publishCameraTrack: true,
// Automatically subscribe to all audio streams
autoSubscribeAudio: true,
// Automatically subscribe to all video streams
autoSubscribeVideo: true,
// For live streaming use-case (This setting takes effect only when the user role is set to ClientRoleAudience )
audienceLatencyLevelType: AudienceLatencyLevelType.AudienceLatencyLevelLowLatency,
});Register event handlers
Call the registerEventHandler method to register the following callback events:
onJoinChannelSuccess: Triggered when the local user successfully joins a channel. After joining, callsetupLocalVideoto configure the local video window.onUserJoined: Triggered when a remote user joins the current channel. After the remote user joins, callsetupRemoteVideoto configure the remote video window.onUserOffline: Triggered when a remote user leaves the current channel. After the remote user leaves, callsetupRemoteVideoto close the remote video window.
const EventHandles = {
// Listen for the local user joining the channel
onJoinChannelSuccess: ({ channelId, localUid }, elapsed) => {
console.log('Successfully joined channel: ' + channelId);
// After the local user joins the channel, set up the local video view
rtcEngine.setupLocalVideo({
sourceType: VideoSourceType.VideoSourceCameraPrimary,
uid: uid,
view: localVideoContainer,
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
});
},
// Listen for remote users joining the channel
onUserJoined: ({ channelId, localUid }, remoteUid, elapsed) => {
console.log('Remote user ' + remoteUid + ' joined');
// After a remote user joins the channel, set up the remote video view
rtcEngine.setupRemoteVideo(
{
sourceType: VideoSourceType.VideoSourceRemote,
uid: remoteUid,
view: remoteVideoContainer,
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
},
{ channelId },
);
},
// Listen for users leaving the channel
onUserOffline: ( { channelId, localUid }, remoteUid, reason ) => {
console.log('Remote user ' + remoteUid + ' left the channel');
// After a remote user leaves the channel, remove the remote video view
rtcEngine.setupRemoteVideo(
{
sourceType: VideoSourceType.VideoSourceRemote,
uid: remoteUid,
view: remoteVideoContainer,
setupMode: VideoViewSetupMode.VideoViewSetupRemove,
},
);
},
};
// Register the event handler
rtcEngine.registerEventHandler(EventHandles);To ensure that you receive all Video SDK events, set the Agora Engine event handler before joining a channel.
Display the local video
To display the local video:
// Set up the local video view
rtcEngine.setupLocalVideo({
sourceType: VideoSourceType.VideoSourceCameraPrimary,
uid: uid,
view: localVideoContainer,
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
});Display remote video
To display the remote video, call setupRemoteVideo to configure the remote video feed.
// Set up the remote video view
rtcEngine.setupRemoteVideo(
{
sourceType: VideoSourceType.VideoSourceRemote,
uid: remoteUid,
view: remoteVideoContainer,
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
},
{ channelId },
);
// Remove the remote video view
rtcEngine.setupRemoteVideo(
{
sourceType: VideoSourceType.VideoSourceRemote,
uid: remoteUid,
view: remoteVideoContainer,
setupMode: VideoViewSetupMode.VideoViewSetupRemove,
},
);Call this method when the app receives the onUserJoined callback.
Start and close the app
When a user launches your app, start Broadcast Streaming. When a user closes the app, stop Broadcast Streaming and release resources.
-
To start Broadcast Streaming, initialize the engine, display the local video and join a channel.
-
When Broadcast Streaming ends, leave the channel and clean up resources:
// Stop the preview rtcEngine.stopPreview(); // Leave the channel rtcEngine.leaveChannel();
When you no longer need to interact, unregister the event handler and call release to release engine resources.
// Unregister the event handler
rtcEngine.unregisterEventHandler(EventHandles);
// Release the engine
rtcEngine.release();After destroying the engine, you can no longer use SDK methods and callbacks. To use the real-time interaction functions again, create a new engine instance. See Initialize the engine for details.
Complete sample code
A complete code sample demonstrating the basic process of real-time interaction is provided for your reference. Replace the entire contents of the main.js and renderer.js with the following code samples:
main.js
- In
renderer.js, replace the values forAPPID,token, andchannelwith your App ID, the temporary token from Agora Console, and the channel name you used to generate the token. - If you're targeting macOS v10.14 or later, add the device permission code to
main.js. For more details, see Get device permissions.
To test the complete code, see Test the sample code.
Create a user interface
To connect the sample code to your existing UI, ensure that your index.html file includes the HTML elements used to Display the local video and Display remote video.
Alternatively, use the following sample to create a minimal interface. Add the following code to index.html:
Sample code to create the user interface
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Electron Quickstart</title>
</head>
<body>
<h1>Electron Quickstart</h1>
<!-- Add the local video view to the interface -->
<div
id="join-channel-local-video"
style="width: 300px; height: 300px; float: left"
></div>
<!-- Add the remote video view to the interface -->
<div
id="join-channel-remote-video"
style="width: 300px; height: 300px; float: left"
></div>
</body>
<script src="./renderer.js"></script>
</html>Test the sample code
Take the following steps to test the sample code:
-
Update the
APPID,token, andchannelparameter values in your code. -
To run the app, execute the following command in the project root directory:
npm start
You see a window pop up showing the local video.
-
Invite a friend to run the demo app on a second device. Alternatively, use the Web demo to join the same channel.
- If users on both devices join the channel as hosts, they can see and hear each other.
- If one user joins as host and the other as audience, the host can see themselves in the local video window; the audience can see the host in the remote video window and hear the host.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
Agora provides open source sample projects on GitHub for your reference. Download or view the JoinChannelVideo project for a more detailed example.
API Reference
Frequently asked questions
See also
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
- Flutter 2.10.5 or higher with Dart 2.14.0 or higher.
- Android Studio, IntelliJ, VS Code, or any other IDE that supports Flutter. See Set up an editor.
- Prepare your development and testing environment according to your target platform.
Run the flutter doctor command to confirm that your development environment is set up correctly for Flutter development.
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your Flutter project and install the Agora Video SDK.
Create a new project
From the Terminal, run the following commands to create a new project named agora_project, or follow the steps for your IDE:
flutter create agora_project
cd agora_projectAdd to an existing project To add Broadcast Streaming to your existing project:
- Open your Flutter project and navigate to the
libfolder. - Add a new file to the
libfolder and name itagora_logic.dart.
Install the SDK
Install the Agora Video SDK and other dependencies.
-
Add the latest version of Agora Video SDK to your Flutter project:
flutter pub add agora_rtc_engine -
Add the permission processing package:
flutter pub add permission_handler
The dependencies in your pubspec.yaml file should look like the following:
dependencies:
flutter:
sdk: flutter
agora_rtc_engine: ^6.5.0 # Agora Flutter SDK, please use the latest version
permission_handler: ^11.3.1 # Package for managing runtime permissions
cupertino_icons: ^1.0.8-
Install the dependencies.
Execute the following command in the project path:
flutter pub get
Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your app.
The following figure illustrates the essential steps:
This guide includes complete sample code that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps.
Import packages
Import the following packages in your dart file.
import 'dart:async';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';Initialize the engine
For real-time communication, initialize an RtcEngine instance. Use RtcEngineContext to specify the App ID, and other configuration parameters. In your dart file, add the following code:
// Set up the Agora RTC engine instance
Future<void> _initializeAgoraVideoSDK() async {
_engine = createAgoraRtcEngine();
await _engine.initialize(const RtcEngineContext(
appId: appId,
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
));
}Join a channel
To join a channel, call joinChannel with the following parameters:
-
Channel name: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.
-
Authentication token: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a token server in your security infrastructure. For the purpose of this guide Generate a temporary token.
-
User ID: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to
0when joining a channel, the SDK generates a random number for the user ID and returns the value in theonJoinChannelSuccesscallback. -
Channel media options: Configure
ChannelMediaOptionsto define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.
For Broadcast Streaming, set the clientRoleType to clientRoleBroadcaster (host) or clientRoleAudience, and the audienceLatencyLevel to audienceLatencyLevelLowLatency.
// Join a channel
Future<void> _joinChannel() async {
await _engine.joinChannel(
token: token,
channelId: channel,
options: const ChannelMediaOptions(
autoSubscribeVideo: true, // Automatically subscribe to all video streams
autoSubscribeAudio: true, // Automatically subscribe to all audio streams
publishCameraTrack: true, // Publish camera-captured video
publishMicrophoneTrack: true, // Publish microphone-captured audio
// Use clientRoleBroadcaster to act as a host or clientRoleAudience for audience
clientRoleType: ClientRoleType.clientRoleBroadcaster,
// Set the audience latency level
audienceLatencyLevel: AudienceLatencyLevelType.audienceLatencyLevelLowLatency
),
uid: 0,
);
}Subscribe to Video SDK events
The SDK provides the RtcEngineEventHandler for subscribing to channel events. To use it, pass an instance of RtcEngineEventHandler to registerEventHandler and implement the event methods you want to handle.
Call registerEventHandler to bind the event handler to the SDK.
// Register an event handler for Agora RTC
void _setupEventHandlers() {
_engine.registerEventHandler(
RtcEngineEventHandler(
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
debugPrint("Local user ${connection.localUid} joined");
setState(() => _localUserJoined = true);
},
onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
debugPrint("Remote user $remoteUid joined");
setState(() => _remoteUid = remoteUid);
},
onUserOffline: (RtcConnection connection, int remoteUid, UserOfflineReasonType reason) {
debugPrint("Remote user $remoteUid left");
setState(() => _remoteUid = null);
},
),
);
}When a remote user joins the channel, the onUserJoined callback is triggered. Use the remote user's uid returned in the callback, to create an AgoraVideoView control for displaying the video stream from the remote user.
To ensure that you receive all Video SDK events, register the event handler before joining a channel.
Display the local video
To display the local video, enable the video module by calling enableVideo, then start the local video preview with startPreview.
Future<void> _setupLocalVideo() async {
// The video module and preview are disabled by default.
await _engine.enableVideo();
await _engine.startPreview();
}To render the local video, add the following widget inside your UI’s widget tree, such as in the build method of your StatefulWidget:
// Displays the local user's video view using the Agora engine.
Widget _localVideo() {
return AgoraVideoView(
controller: VideoViewController(
rtcEngine: _engine, // Uses the Agora engine instance
canvas: const VideoCanvas(
uid: 0, // Specifies the local user
renderMode: RenderModeType.renderModeHidden, // Sets the video rendering mode
),
),
);
}Display remote video
To render a remote video, add the following widget inside your UI’s widget tree, such as in the build method of your StatefulWidget:
// If a remote user has joined, render their video, else display a waiting message
Widget _remoteVideo() {
if (_remoteUid != null) {
return AgoraVideoView(
controller: VideoViewController.remote(
rtcEngine: _engine, // Uses the Agora engine instance
canvas: VideoCanvas(uid: _remoteUid), // Binds the remote user's video
connection: const RtcConnection(channelId: channel), // Specifies the channel
),
);
} else {
return const Text(
'Waiting for remote user to join...',
textAlign: TextAlign.center,
);
}
}Handle permissions
Request microphone and camera permissions for Broadcast Streaming.
Future<void> _requestPermissions() async {
await [Permission.microphone, Permission.camera].request();
}If your target platform is iOS or macOS, add the microphone and camera permission declarations required for real-time interaction to Info.plist.
| Device | Key | Value |
|---|---|---|
| Microphone | Privacy - Microphone Usage Description | for audio calls |
| Camera | Privacy - Camera Usage Description | for video calls |
Start and close the app
To start Broadcast Streaming, request microphone and camera permissions, initialize the Agora SDK instance, set up event handlers, join a channel, and display the local video.
await _requestPermissions();
await _initializeAgoraVideoSDK();
await _setupLocalVideo();
_setupEventHandlers();
await _joinChannel();To stop Broadcast Streaming, leave the channel and release the engine instance.
// Leaves the channel and releases resources
Future<void> _cleanupAgoraEngine() async {
await _engine.leaveChannel();
await _engine.release();
}After you call release, you no longer have access to the methods and callbacks of the SDK. To use Broadcast Streaming features again, create a new engine instance.
Complete sample code
A complete code sample demonstrating the basic process of real-time interaction is provided for your reference. To use the sample code, copy the following code to replace the entire contents of the .dart file in your project.
In the appId and token fields, enter the corresponding values you obtained from Agora Console. Use the same channel name you filled in when generating the temporary token.
Create a user interface
To connect the sample code to your existing UI, ensure that your widget tree includes the _remoteVideo and _localVideo widgets used to Display the local video and Display remote video.
Alternatively, use the following sample code to generate a basic user interface:
Sample code to create the user interface
// Build UI to display local video and remote video
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Agora Video Call')),
body: Stack(
children: [
Center(child: _remoteVideo()),
Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 100,
height: 150,
child: Center(
child: _localUserJoined
? _localVideo()
: const CircularProgressIndicator(),
),
),
),
],
),
);
}Test the sample code
Take the following steps to test the sample code:
-
In
main.dartupdate the values forappId, andtokenwith values from Agora Console. Fill in the samechannelname you used to generate the token. -
Connect a target device to your development computer.
-
Open Terminal and execute the following command in the project folder to run the sample project:
flutter run -
Launch the App, grant microphone and camera permissions. If you set the user role to host, you see yourself in the local view.
-
On a second target device, repeat the previous steps to install and launch the app. Alternatively, use the Web demo to join the same channel and test the following use-cases:
- If users on both devices join the channel as hosts, they can see and hear each other.
- If one user joins as host and the other as audience, the host can see themselves in the local video window; the audience can see the host in the remote video window and hear the host.
On an Android device, the app UI appears similar to the following:
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
Agora provides open source sample projects on GitHub for your reference. Download or view join_channel_video.dart for a more detailed example.
API reference
Frequently asked questions
See also
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
- React Native 0.60 or later. For more information, see Get Started with React Native.
- Node 16 or later For your target platform, prepare the following:
- A machine running macOS, Windows, or Linux
- Java Development Kit (JDK) 11 or later
- Latest version of Android Studio
- A physical or virtual mobile device running Android 5.0 or later
- A machine running macOS
- Xcode 10 or later
- CocoaPods
- A physical or virtual mobile device running iOS 9.0 or later. If you use React Native 0.63 or later, ensure your iOS version is 10.0 or later.
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your React Native project and install the Agora Video SDK.
Create a project
To create a new application using React Native Community CLI, see Get Started Without a Framework.
Install the SDK
Download and add the Agora Video SDK to your React Native project. Choose either of the following methods:
npm In your project folder, execute the following command:
npm i --save react-native-agorayarn In your project folder, execute the following commands:
# Install yarn
npm install -g yarn
# Use yarn to download Agora React Native SDK
yarn add react-native-agoraReact Native 0.60.0 and later support automatic linking of native modules. Manual linking is not recommended. See Autolinking for details
Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your app.
The following figure illustrates the essential steps:
This guide includes complete sample code that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps and use the code in your App.tsx file.
Import dependencies
Add the required Agora dependencies to your app.
import {
createAgoraRtcEngine,
ChannelProfileType,
ClientRoleType,
AudienceLatencyLevelType,
IRtcEngine,
RtcSurfaceView,
RtcConnection,
IRtcEngineEventHandler,
} from 'react-native-agora';Initialize the engine
For real-time communication, call createAgoraRtcEngine to create an RtcEngine instance, and then initialize the engine using RtcEngineContext to specify the App ID.
const appId = '<-- Insert app ID -->';
const setupVideoSDKEngine = async () => {
if (Platform.OS === 'android') { await getPermission(); }
agoraEngineRef.current = createAgoraRtcEngine();
const agoraEngine = agoraEngineRef.current;
await agoraEngine.initialize({ appId: appId });
};Join a channel
To join a channel, call joinChannel with the following parameters:
-
Channel name: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.
-
Authentication token: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a token server in your security infrastructure. For the purpose of this guide Generate a temporary token.
-
User ID: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to
0when joining a channel, the SDK generates a random number for the user ID and returns the value in theonJoinChannelSuccesscallback. -
Channel media options: Configure
ChannelMediaOptionsto define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.
For Broadcast Streaming, set the channelProfile to ChannelProfileLiveBroadcasting, the clientRoleType to ClientRoleBroadcaster (host) or ClientRoleAudience, and the audienceLatencyLevel to audienceLatencyLevelLowLatency.
const token = '<-- Insert token -->';
const channelName = '<-- Insert channel name -->';
const localUid = 0; // Local user UID, no need to modify
// Define the join method called after clicking the join channel button
const join = async () => {
if (isJoined) {
return;
}
if (isHost) {
// Join the channel as a broadcaster
agoraEngineRef.current?.joinChannel(token, channelName, localUid, {
// Set channel profile to live broadcast
channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,
// Set user role to broadcaster
clientRoleType: ClientRoleType.ClientRoleBroadcaster,
// Publish audio collected by the microphone
publishMicrophoneTrack: true,
// Publish video collected by the camera
publishCameraTrack: true,
// Automatically subscribe to all audio streams
autoSubscribeAudio: true,
// Automatically subscribe to all video streams
autoSubscribeVideo: true,
});
} else {
// Join the channel as an audience
agoraEngineRef.current?.joinChannel(token, channelName, localUid, {
// Set channel profile to live broadcast
channelProfile: ChannelProfileType.ChannelProfileLiveBroadcasting,
// Set user role to audience
clientRoleType: ClientRoleType.ClientRoleAudience,
// Do not publish audio collected by the microphone
publishMicrophoneTrack: false,
// Do not publish video collected by the camera
publishCameraTrack: false,
// Automatically subscribe to all audio streams
autoSubscribeAudio: true,
// Automatically subscribe to all video streams
autoSubscribeVideo: true,',
// Change the delay level of the audience to achieve ultra-fast live broadcast
audienceLatencyLevel: AudienceLatencyLevelType.AudienceLatencyLevelLowLatency,
});
}
};Since the Agora Engine runs locally, reloading the app using the Metro bundler may cause the engine reference to be lost, preventing channel joining. To resolve this issue, restart the app.
Subscribe to Video SDK events
The Video SDK provides an interface for subscribing to channel events. To use it, create an instance of IRtcEngineEventHandler and implement the event methods you want to handle. Call registerEventHandler to register your custom event handler.
const [remoteUid, setRsemoteUid] = useState(0); // Uid of the remote user
const setupEventHandler = () => {
eventHandler.current = {
// Triggered when the local user successfully joins a channel
onJoinChannelSuccess: () => {
setMessage('Successfully joined channel: ' + channelName);
setupLocalVideo();
setIsJoined(true);
},
// Triggered when a remote user joins the channel
onUserJoined: (_connection: RtcConnection, uid: number) => {
setMessage('Remote user ' + uid + ' joined');
setRemoteUid(uid);
},
// Triggered when a remote user leaves the channel
onUserOffline: (_connection: RtcConnection, uid: number) => {
setMessage('Remote user ' + uid + ' left the channel');
setRemoteUid(uid);
},
};
// Register the event handler
agoraEngineRef.current?.registerEventHandler(eventHandler.current);
};To ensure that you receive all Video SDK events, register the event handler before joining a channel.
Enable the video module
Call enableVideo to activate the video module, and use startPreview to enable the local video preview.
const setupLocalVideo = () => {
agoraEngineRef.current?.enableVideo();
agoraEngineRef.current?.startPreview();
};Display the local video
To display the local user's video stream, use the RtcSurfaceView component. Set the uid to 0 and style the video container.
<React.Fragment key={0}>
<RtcSurfaceView canvas={{ uid: 0 }} style={ width: '90%', height: 200 } />
</React.Fragment>Display remote video
To display the remote user's video, use the RtcSurfaceView component. Pass the remoteUid of the remote user to the component and set the desired style for the video container.
<React.Fragment key={remoteUid}>
<RtcSurfaceView
canvas={{ uid: remoteUid }}
style={ width: '90%', height: 200 }
/>
</React.Fragment>Handle permissions
To access the camera and microphone, ensure that the user grants the necessary permissions when the app starts.
On Android devices, pop up a prompt box to obtain permission to use the camera and microphone.
// Import components related to Android device permissions
import {PermissionsAndroid, Platform} from 'react-native';
const getPermission = async () => {
if (Platform.OS === 'android') {
await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
PermissionsAndroid.PERMISSIONS.CAMERA,
]);
}
};In Xcode, open the info.plist file and add the following content to the list on the right to obtain the corresponding device permissions:
| Key | Type | Value |
|---|---|---|
| Privacy - Microphone Usage Description | String | The purpose of using the microphone, for example, for a call or live interactive streaming. |
| Privacy - Camera Usage Description | String | The purpose of using the camera, for example, for a call or live interactive streaming. |
Leave the channel
To exit a Broadcast Streaming channel, call leaveChannel.
const leave = () => {
// Leave the channel
agoraEngineRef.current?.leaveChannel();
setIsJoined(false);
};Clean up resources
Before closing your app, unregister the event handler and call release to free up resources.
const cleanupAgoraEngine = () => {
return () => {
agoraEngineRef.current?.unregisterEventHandler(eventHandler.current!);
agoraEngineRef.current?.release();
};
};After calling release, methods and callbacks of the SDK are no longer available. To use the SDK functions again, create a new engine instance.
Complete sample code
A complete code sample demonstrating the basic process of real-time interaction is provided for your reference. To use the sample code, copy the code into ProjectName/App.tsx to quickly implement the basic functionality.
Create a user interface
Design a user interface for your project based on your application use-case. A basic interface consists of two view frames to display local and remote videos. Add Join channel and Leave channel buttons to enable the user to join and leave a channel. To create such an interface, refer to the following code:
Sample code to create the user interface
import React, { useState } from 'react';
// Import user interface elements
import {
SafeAreaView,
ScrollView,
StyleSheet,
Text,
View,
Switch,
} from 'react-native';
const localUid = 0; // Local user Uid, no need to modify
const App = () => {
const [isJoined, setIsJoined] = useState(false); // Whether the local user has joined the channel
const [isHost, setIsHost] = useState(true); // User role
const [remoteUid, setRemoteUid] = useState(0); // Uid of the remote user
const [message, setMessage] = useState(''); // User prompt message
// Render user interface
return (
<SafeAreaView style={styles.main}>
<Text style={styles.head}>Agora Video SDK Quickstart</Text>
<View style={styles.btnContainer}>
<Text onPress={join} style={styles.button}>
Join Channel
</Text>
<Text onPress={leave} style={styles.button}>
Leave Channel
</Text>
</View>
<View style={styles.btnContainer}>
<Text>Audience</Text>
<Switch
onValueChange={switchValue => {
setIsHost(switchValue);
if (isJoined) {
leave();
}
}}
value={isHost}
/>
<Text>Host</Text>
</View>
<ScrollView
style={styles.scroll}
contentContainerStyle={styles.scrollContainer}>
{isJoined && isHost ? (
<React.Fragment key={localUid}>
<Text>Local user uid: {localUid}</Text>
</React.Fragment>
) : (
<Text>Join a channel</Text>
)}
{isJoined && remoteUid !== 0 ? (
<React.Fragment key={remoteUid}>
<Text>Remote user uid: {remoteUid}</Text>
</React.Fragment>
) : (
<Text>{isJoined && !isHost ? 'Waiting for remote user to join' : ''}</Text>
)}
<Text style={styles.info}>{message}</Text>
</ScrollView>
</SafeAreaView>
);
};
// Define user interface styles
const styles = StyleSheet.create({
button: {
paddingHorizontal: 25,
paddingVertical: 4,
fontWeight: 'bold',
color: '#ffffff',
backgroundColor: '#0055cc',
margin: 5,
},
main: { flex: 1, alignItems: 'center' },
scroll: { flex: 1, backgroundColor: '#ddeeff', width: '100%' },
scrollContainer: { alignItems: 'center' },
videoView: { width: '90%', height: 200 },
btnContainer: { flexDirection: 'row', justifyContent: 'center' },
head: { fontSize: 20 },
info: { backgroundColor: '#ffffe0', paddingHorizontal: 8, color: '#0000ff' },
});
export default App;Test the sample code
Take the following steps to test the sample code:
-
Update the values for
appId,token, andchannelNamein your code. -
Run the app.
Some simulators may not support all the functions of this project. Best practice is to run the project on a physical device.
To run the app on an Android device:
-
Turn on the developer options of the Android device and connect the Android device to the computer through a USB cable.
-
In the project root directory, execute
npx react-native run-android.
To run on the app on an iOS device:
-
Open the
ProjectName/ios/ProjectName.xcworkspacefolder using Xcode. -
Connect the iOS device to your computer through a USB cable.
-
In Xcode, click the Build and Run button.
For detailed steps on running the app on a real Android or iOS device, please refer to Running On Device.
-
Launch the app and click the Join channel button to join a channel.
-
Invite a friend to install and run the app on a second device. Alternatively, use the Web demo to join the same channel. Once your friend joins the channel, you can see and hear each other.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
Agora provides open source sample projects on GitHub for your reference. Download or view the JoinChannelVideo project for a more detailed example.
API reference
Frequently asked questions
See also
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
The React SDK is built on Web SDK 4.x and includes its APIs. For example, useLocalMicrophoneTrack calls the Web SDK createMicrophoneAudioTrack method.
Use the React SDK for basic real-time audio and video features. For advanced or complex scenarios, use the Web SDK. When a feature requires the Web SDK, the React SDK documentation links to the relevant Web SDK API pages.
Prerequisites
-
A JavaScript package manager such as npm.
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your ReactJS project and install the Agora Video SDK.
Create a project
To create a new ReactJS project with the necessary dependencies:
-
Ensure that you have installed Node.js LTS and
npm. -
Open a terminal and execute:
npm create vite@latest agora-sdk-quickstart -- --template react-ts
This creates a new project folder named agora-sdk-quickstart. Open the folder in your preferred IDE.
Install the SDK
Use one of the following methods to install the project dependencies:
NPM Navigate to the project folder and run the following command to install the Video SDK:
npm i agora-rtc-reactCDN To integrate React JS dependencies and the Agora SDK using CDN, add the following code to your HTML file:
<!-- Include the React development libraries (must be introduced in this order) -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- If the above React libraries have already been integrated through npm, skip them and include only the Agora RTC React SDK -->
<script src="https://download.agora.io/sdk/release/agora-rtc-react.2.3.0.js"></script>Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your app.
This guide includes complete sample code that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps.
Import Agora hooks and components
To use Video SDK in your component, include the following imports:
import {
LocalUser, // Plays the microphone audio track and the camera video track
RemoteUser, // Plays the remote user audio and video tracks
useIsConnected, // Returns whether the SDK is connected to Agora's server
useJoin, // Automatically join and leave a channel on mount and unmount
useLocalMicrophoneTrack, // Create a local microphone audio track
useLocalCameraTrack, // Create a local camera video track
usePublish, // Publish the local tracks
useRemoteUsers, // Retrieve the list of remote users
} from "agora-rtc-react";
import AgoraRTC, { AgoraRTCProvider } from "agora-rtc-react";
import { useState } from "react";
import AgoraRTC, { AgoraRTCProvider } from "agora-rtc-react";Initialize the client
Use the createClient method provided by the SDK to create a client object for the <AgoraRTCProvider /> component. Wrap the <Basics /> component with <AgoraRTCProvider /> to enable state management and access operation-related hooks.
For Broadcast Streaming, set the mode property of ClientConfig to "live", the client role to "host" or "audience" and the audience latency level to 1.
const client = AgoraRTC.createClient({ mode: "live", codec: "vp8" });
// Set the client role and latency level
// Join as audience
client.setClientRole("audience", { level: 1 });
// To join as a host, use:
// client.setClientRole("host");
return(
<AgoraRTCProvider client={client}>
<Basics />
</AgoraRTCProvider>
);
}Join a channel
To join a channel, use the useJoin hook. You can specify the following joinOptions:
-
App ID:
appididentifies the project you created in Agora Console. -
Channel name: The name of the
channelto join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins. -
Authentication token: A
tokenis a dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a token server in your security infrastructure. For the purpose of this guide Generate a temporary token. -
User ID:
uidis a 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you do not set a user ID or set it to0when joining a channel, the SDK generates a random number for the user ID and returns the value.
Add the following to Basics:
const [appId, setAppId] = useState("<-- Insert App ID -->");
const [channel, setChannel] = useState("<-- Insert Channel Name -->");
const [token, setToken] = useState("<-- Insert Token -->");
const [calling, setCalling] = useState(false);
useJoin({appid: appId, channel: channel, token: token ? token : null}, calling);Create local audio and video tracks
To create local audio and video tracks, use the useLocalMicrophoneTrack and useLocalCameraTrack hooks. Add the following to Basics:
const { localMicrophoneTrack } = useLocalMicrophoneTrack(micOn);
const { localCameraTrack } = useLocalCameraTrack(cameraOn);Publish tracks in the channel
After joining a channel, publish the local audio and video tracks using the usePublish hook. Add the following to Basics:
const [micOn, setMic] = useState(true);
const [cameraOn, setCamera] = useState(true);
const { localMicrophoneTrack } = useLocalMicrophoneTrack(micOn);
const { localCameraTrack } = useLocalCameraTrack(cameraOn);
usePublish([localMicrophoneTrack, localCameraTrack]);Display the local video
To display the local video, use the LocalUser hook, which provides properties to manage local audio and video tracks. Assign the local video track to videoTrack and the audio track to audioTrack. Use the micOn and cameraOn parameters to toggle audio and video. Include the following code in the markup of your Basics component:
<LocalUser
audioTrack={localMicrophoneTrack}
cameraOn={cameraOn}
micOn={micOn}
videoTrack={localCameraTrack}
style={{width: '50%', height: 300 }}
>Display remote video
To manage and display the list of remote users connected to a channel, use the useRemoteUsers hook. To show each user's video, pass the user object to the RemoteUser component along with the required properties. Follow these steps to implement the logic:
- Retrieve the list of remote users
Use the useRemoteUsers hook to get the current list of remote users:
const remoteUsers = useRemoteUsers();- Render the remoteUser component
Loop through the remoteUsers list and nest the RemoteUser component where you want to display each user's video. Include the following code in the markup of your Basics component:
{remoteUsers.map((user) => (
<div key={user.uid}>
<RemoteUser user={user} style={{ width: '50%', height: 300 }}>
<samp>{user.uid}</samp>
</RemoteUser>
</div>
))}Leave the channel
To leave a channel, destroy the component, close the browser window, or refresh the browser tab. The useJoin hook automatically handles the destruction of the React component.
Complete sample code
A complete code sample demonstrating the basic process of real-time interaction is provided for your reference. To use the complete sample, add the following to your src/App.tsx file.
Test the sample code
Use CodeSandbox, to swiftly run the complete demo code and experience the React Video SDK functionality in just a few simple steps.
To run the demo code in CodeSandbox:
- Fill in the app ID and the temporary token you obtained from Agora Console. Use the same channel name you used to generate the token.
- Click the Join Channel button. You see yourself in the video.
- Ask a friend to open the same demo link in their browser and join the channel using the same app ID, channel name, and temporary token as yours. After successfully joining the channel, you can see and hear each other.
- Click the microphone and camera buttons at the bottom to switch on, or turn off the corresponding devices.
- Click the hang-up button to end the call.
To experiment and learn more, modify the code directly in the editor on the left, and preview the runtime effect on the right.
If CodeSandbox access is slow, try adjusting your network configuration.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
Agora provides an open source sample project on GitHub for your reference. The project demonstrates the use of each component and hook provided by the React SDK, as well as some advanced functions.
API reference
LocalUserRemoteUseruseIsConnected()useJoin()useLocalMicrophoneTrack()usePublish()useRemoteUsers()
See also
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
-
Unity Hub and Unity Editor 2018.4.0 or higher
-
A suitable operating system and compiler for your development platform:
Development platform Operating system version Compiler version Android Android 4.1 or later Android Studio 4.1 or later iOS iOS 10.15 or later Xcode 9.0 or later macOS macOS 10.15 or later Xcode 9.0 or later Windows Windows 7 or later Microsoft Visual Studio 2017 or later -
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your Unity project and install the Agora Video SDK.
Create a new project Refer to the following steps or the Official Unity documentation to create a Unity project.
-
Open Unity and click New.
-
Enter the following details:
- Project name : The name of the project.
- Location : Project storage path.
- Template : The project type. Select 3D.
-
Click Create project.
Add to an existing project To open your existing project:
-
In the Projects window, click the Open button in the top-right corner.
-
Browse your file manager and select the folder of the project you want to open.
-
Confirm your selection to add the project to the Projects window and open it in the Unity Editor.
Install the SDK
-
Go to the Download SDKs page and download the latest version of the Unity SDK.
-
In Unity Editor, navigate to Assets > Import Package > Custom Package, and select the unzipped SDK.
All plugins are selected by default. Deselect any plugins you don't need, then click Import.
Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your game.
The following figure illustrates the essential steps:
This guide includes complete sample code that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps.
Before proceeding, create and set up a script to implement Broadcast Streaming and bind the script to the canvas.
Steps to set up a script
-
Create a new script and import the UI library.
-
In the Project tab, navigate to Assets > Agora-Unity-RTC-SDK > Code > Rtc, right-click and select Create > C# Script. A new file named
NewBehaviourScript.csappears in your Assets. -
Rename the file to
JoinChannel.csand open it. -
Import the Unity namespaces to access UI components by adding the following code at the top of the file:
using UnityEngine; using UnityEngine.UI; -
Bind the script to the canvas.
In Assets/Agora-Unity-RTC-SDK/Code/Rtc , select the JoinChannel.cs file, and drag it to the Canvas. In the Inspector panel, ensure that the file is bound to the Canvas.
Import Agora classes
Import the Agora.Rtc namespace, which contains various classes and interfaces required to implement real-time audio and video functions.
using Agora.Rtc;Initialize the engine
For real-time communication, create an IRtcEngine instance using RtcEngine.CreateAgoraRtcEngine(). Then, configure it using Initialize(context) with an RtcEngineContext, specifying the application context, App ID, and channel profile. In your JoinChannel.cs file, add the following code:
internal IRtcEngine RtcEngine;
// Fill in your app ID
private string _appID= "";
private void SetupVideoSDKEngine()
{
// Create an IRtcEngine instance
RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine();
RtcEngineContext context = new RtcEngineContext();
context.appId = _appID;
context.channelProfile = CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING;
context.audioScenario = AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT;
// Initialize the instance
RtcEngine.Initialize(context);
}Join a channel
To join a channel, call JoinChannel with the following parameters:
-
Channel name: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.
-
Authentication token: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a token server in your security infrastructure. For the purpose of this guide Generate a temporary token.
-
User ID: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to
0when joining a channel, the SDK generates a random number for the user ID and returns the value in theOnJoinChannelSuccesscallback. -
Channel media options: Configure
ChannelMediaOptionsto define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.
For Broadcast Streaming, set the channelProfile to CHANNEL_PROFILE_LIVE_BROADCASTING, the clientRoleType to CLIENT_ROLE_BROADCASTER (host) or CLIENT_ROLE_AUDIENCE, and the audienceLatencyLevel to AUDIENCE_LATENCY_LEVEL_LOW_LATENCY.
// Fill in your channel name
private string _channelName = "";
// Fill in a temporary token
private string _token = "";
public void Join() {
// Set channel media options
ChannelMediaOptions options = new ChannelMediaOptions();
// Start video rendering
LocalView.SetEnable(true);
// Automatically subscribe to all audio streams
options.autoSubscribeAudio.SetValue(true);
// Automatically subscribe to all video streams
options.autoSubscribeVideo.SetValue(true);
// Set the channel profile to live broadcast
options.channelProfile.SetValue(CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING);
//Set the user role as host
options.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
// Set the audience latency level
options.audienceLatencyLevel.SetValue(AUDIENCE_LATENCY_LEVEL_TYPE.AUDIENCE_LATENCY_LEVEL_LOW_LATENCY);
// Join a channel
RtcEngine.JoinChannel(_token, _channelName, 0, options);
}Subscribe to Video SDK events
Create an instance of the UserEventHandler class and set it as the engine event handler. Override the callbacks based on your use-case.
// Implement your own callback class by inheriting from the IRtcEngineEventHandler interface
internal class UserEventHandler : IRtcEngineEventHandler
{
private readonly JoinChannelVideo _videoSample;
internal UserEventHandler(JoinChannelVideo videoSample)
{
_videoSample = videoSample;
}
// Triggered when the local user successfully joins a channel
public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed)
{
}
// Triggered when the SDK receives and successfully decodes the first frame of a remote video
public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed)
{
// Set the display for the remote video
_videoSample.RemoteView.SetForUser(uid, connection.channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE);
// Start video rendering
_videoSample.RemoteView.SetEnable(true);
Debug.Log("Remote user joined");
}
// Triggered when the remote user leaves the channel
public override void OnUserOffline(RtcConnection connection, uint uid, USER_OFFLINE_REASON_TYPE reason)
{
// Stop displaying the remote video
_videoSample.RemoteView.SetEnable(false);
}
}Create an instance of the user callback class and call InitEventHandler to register the event handler.
private void InitEventHandler()
{
UserEventHandler handler = new UserEventHandler(this);
RtcEngine.InitEventHandler(handler);
}To ensure that you receive all Video SDK events, register the event handler before joining a channel.
Display the local video
Use the following code to set up the local video view:
internal VideoSurface LocalView;
private void PreviewSelf()
{
// Enable the video module
RtcEngine.EnableVideo();
// Enable local video preview
RtcEngine.StartPreview();
// Set up local video display
LocalView.SetForUser(0, "");
// Render the video
LocalView.SetEnable(true);
}Display remote video
When a remote user joins the channel, the OnUserJoined callback is triggered. Call SetForUser to set the remote video display and call SetEnable(true) to render the video.
internal VideoSurface RemoteView;
// When the SDK receives the first frame of a remote video stream and successfully decodes it, the OnUserJoined callback is triggered.
public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed) {
// Set the remote video display
_videoSample.RemoteView.SetForUser(uid, connection.channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE);
// Start video rendering
_videoSample.RemoteView.SetEnable(true);
Debug.Log("Remote user joined");
}Leave the channel
Call LeaveChannel to leave the current channel.
public void Leave() {
Debug.Log("Leaving " + _channelName);
// Leave the channel
RtcEngine.LeaveChannel();
// Disable the video module
RtcEngine.DisableVideo();
// Stop remote video rendering0
RemoteView.SetEnable(false);
// Stop local video rendering
LocalView.SetEnable(false);
}Handle permissions
To access the camera and microphone, add device permissions to your project according to your target platform.
Android
Since version 2018.3, Unity does not actively obtain device permissions from the user. Call CheckPermission to check for and obtain the necessary permissions.
-
Include the
UnityEngine.Androidnamespace, which contains Android-specific classes for interacting with Android devices from Unity:#if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID) using UnityEngine.Android; #endif -
Create a list of permissions to be obtained.
#if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID) private ArrayList permissionList = new ArrayList() { Permission.Camera, Permission.Microphone }; #endif -
Check if the required permissions have been granted. If not, prompt the user to grant the necessary permissions.
private void CheckPermissions() { #if (UNITY_2018_3_OR_NEWER && UNITY_ANDROID) foreach (string permission in permissionList) { if (!Permission.HasUserAuthorizedPermission(permission)) { Permission.RequestUserPermission(permission); } } #endif }
iOS and macOS
For iOS and macOS platforms, the Video SDK includes a post-build script named BL_BuildPostProcess.cs. When you build and export your Unity project as an iOS project, this script automatically inserts camera and microphone permission entries into the Info.plist file, eliminating the need for manual updates.
Start and stop your game
-
When the game starts, ensure that device permissions have been granted.
void Update() { CheckPermissions(); } -
To start Broadcast Streaming, initialize the engine and set up the event handler.
void Start() { SetupVideoSDKEngine(); InitEventHandler(); PreviewSelf(); } -
To clean up all session-related resources when a user exits the game, call the
Disposemethod of theIRtcEngine.void OnApplicationQuit() { if (RtcEngine != null) { Leave(); // Destroy IRtcEngine RtcEngine.Dispose(); RtcEngine = null; } }
After calling Dispose, you can no longer use any methods or callbacks of the SDK. To use Broadcast Streaming features again, create a new engine instance.
Complete sample code
A complete code sample demonstrating the basic process of real-time interaction is provided for your reference. To quickly implement the basic functions of real-time Video Calling, copy the following sample code into your project:
Sample code to implement Broadcast Streaming in your game
Create a user interface
Follow these steps to set up a basic UI for your project or to integrate essential UI elements into your existing interface. A basic UI consists of the following components:
- Local view window
- Remote view window
- Buttons to join and leave the channel
Create a basic UI
-
Create buttons to join and leave channel
-
In your Unity project, right-click the Sample Scene and select Game Object > UI > Button. You see a button on the scene canvas.
-
In the Inspector panel, rename the button to
Joinand adjust the position coordinates as needed. For example:- Pos X:
-329 - Pos Y:
-172
- Pos X:
-
Select the Text control of the Join button , and change the text to
Joinin the Inspector panel. -
Repeat the steps to create a Leave button, using the following positions:
- Pos X:
329 - Pos Y:
-172
- Pos X:
-
Create local and remote view windows
-
Right-click the Canvas and select UI > Raw Image.
-
In the Inspector panel, rename
Raw ImagetoLocalViewand adjust its size and position on the canvas. For example:- PosX:
-250 - Pos Y:
0 - Width:
250 - Height:
250
- PosX:
-
Repeat the above steps to create a remote view window, name it
RemoteView, and adjust its position on the canvas:- PosX:
250 - Pos Y:
0 - Width:
250 - Height:
250
Save the changes.
- PosX:
At this point your UI looks similar to the following:
Test the sample code
Take the following steps to test the sample code:
-
Obtain a temporary token from Agora Console.
-
In
JoinChannel.cs, update_appID,_channelName, and_tokenwith the app ID, channel name, and temporary token for your project. -
In Unity Editor, click Play to run your project.
-
Click Join to join a channel.
-
Invite a friend to run the demo game on a second device. Use the same
_appID_,_token, and_channelNameto join. Alternatively, use the Web demo to join the same channel.
After your friend joins successfully, you can hear and see each other.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
Agora provides open source sample projects on GitHub for your reference. Download or view the JoinChannelVideo project for a more detailed example.
API reference
Frequently asked questions
See also
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
-
Unreal Engine 4.27 or higher
-
Prepare your development environment according to your target platform and engine version:
Dev environment requirements Other requirements Android - iOS A valid Apple developer signature. macOS A valid Apple developer signature. Windows 32-bit Windows only supports Unreal Engine 4 and below. To use Unreal Engine with 32-bit Windows, uncomment the code relating to Win32in theAgoraPluginLibrary.Build.csfile. -
Two physical devices for testing
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your Unreal Engine project and install the Agora Video SDK.
Create a new project Refer to the following steps or the Unreal official guide to create a new project. If you already have an Unreal project, skip to the next section.
-
Open Unreal Engine. Select Games under New Project Categories, and click Next.
-
Configure your project as follows:
- Template: Select Blank.
- Project Defaults:
- Language: Select C++.
- Target Platform: Select Desktop.
- Project Location: Enter the project files storage path.
- Project Name: Type a suitable name for your project.
Click Create.
Add to an existing project
-
In the Unreal Project Browser, click on Browse and locate the
.uprojectfile. -
Select the project and click Open.
-
Add the Agora dependency library
In Project/Source/Project/Project.Build.cs, add the AgoraPlugin using PublicDependencyModuleNames.AddRange().
// Add the AgoraPlugin library
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"InputCore",
"AgoraPlugin"
});- Create a new C++ class and generate header and library files
In the Unreal Editor, select Tools > New C++ Class, then select All Classes, find UserWidget and name it AgoraWidget. Click Create Class. A new C++ class is added to your project and you see AgoraWidget.h and AgoraWidget.cpp files.
- Initialize custom Widget
Add the following code to your widget header file:
protected:
// Initialize custom Widget
void NativeConstruct() override;- Associate C++ classes and Widgets
In Unreal Editor, click Content Drawer, select Class Settings > Graph, and set the Parent Class under Class Options to AgoraWidget.
-
Create a user interface for your app. Refer to Create a user interface to create a bare bones UI. A basic user interface consists of the following elements:
- Local user video window
- Remote user video window
- Join and leave channel buttons
Install the SDK
Take the following steps to add the Unreal Engine Video SDK to your project:
- Download the latest version of Agora Unreal Video SDK from Download SDKs and unzip it.
- In your project root folder, create a
Pluginsfolder. - Copy
AgoraPluginfrom the Unreal SDK folder toPlugins.
Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your game.
The following figure illustrates the essential steps:
This guide includes complete sample code that demonstrates implementing basic real-time interaction. To understand the core API calls in the sample code, review the following implementation steps.
Import Agora libraries
To import Agora library, add the following to AgoraWidget.h:
#include "AgoraPluginInterface.h"Initialize the engine
For real-time communication, initialize an IRtcEngine instance and set up an event handler to manage user interactions within the channel. Use RtcEngineContext to specify App ID, and custom event handler, then call RtcEngineProxy->initialize(RtcEngineContext) to initialize the engine, enabling further channel operations.
Add the SetupSDKEngine method declaration and implementation to the following files:
-
AgoraWidget.h// Fill in your app ID FString _appID = ""; // Define a global variable for IRtcEngine agora::rtc::IRtcEngine* RtcEngineProxy; private: // Create and initialize IRtcEngine void SetupSDKEngine(); -
AgoraWidget.cppvoid UAgoraWidget::SetupSDKEngine() { agora::rtc::RtcEngineContext RtcEngineContext; RtcEngineContext.appId = TCHAR_TO_ANSI(*_appID); RtcEngineContext.eventHandler = this; // Create IRtcEngine instance RtcEngineProxy = agora::rtc::ue::createAgoraRtcEngine(); // Initialize IRtcEngine RtcEngineProxy->initialize(RtcEngineContext); }
Join a channel
To join a channel, call joinChannel with the following parameters:
-
Channel name: The name of the channel to join. Clients that pass the same channel name join the same channel. If a channel with the specified name does not exist, it is created when the first user joins.
-
Authentication token: A dynamic key that authenticates a user when the client joins a channel. In a production environment, you obtain a token from a token server in your security infrastructure. For the purpose of this guide Generate a temporary token.
-
User ID: A 32-bit signed integer that identifies a user in the channel. You can specify a unique user ID for each user yourself. If you set the user ID to
0when joining a channel, the SDK generates a random number for the user ID and returns the value in theonJoinChannelSuccesscallback. -
Channel media options: Configure
ChannelMediaOptionsto define publishing and subscription settings, optimize performance for your specific use-case, and set optional parameters.
Add the Join method declaration and implementation to the following files:
-
AgoraWidget.h// Fill in your channel name FString _channelName = ""; // Fill in a valid token FString _token = ""; UFUNCTION(BlueprintCallable) void Join(); UFUNCTION(BlueprintCallable) void Leave(); -
AgoraWidget.cppFor Broadcast Streaming, set the
channelProfiletoCHANNEL_PROFILE_LIVE_BROADCASTING, theclientRoleTypetoCLIENT_ROLE_BROADCASTER(host) orCLIENT_ROLE_AUDIENCE, and theaudienceLatencyLeveltoAUDIENCE_LATENCY_LEVEL_LOW_LATENCY.void UAgoraWidget::Join() { // Enable the video module RtcEngineProxy->enableVideo(); // Set channel media options agora::rtc::ChannelMediaOptions options; // Automatically subscribe to all audio streams options.autoSubscribeAudio = true; // Automatically subscribe to all video streams options.autoSubscribeVideo = true; // Publish video captured by the camera options.publishCameraTrack = true; // Publish audio captured by the microphone options.publishMicrophoneTrack = true; // Set the channel profile to live broadcasting options.channelProfile = agora::CHANNEL_PROFILE_TYPE::CHANNEL_PROFILE_LIVE_BROADCASTING; // Set the user role as host or audience options.clientRoleType = agora::rtc::CLIENT_ROLE_TYPE::CLIENT_ROLE_BROADCASTER; // Set the latency level for optimal experience options.audienceLatencyLevel = agora::rtc::AUDIENCE_LATENCY_LEVEL_TYPE::AUDIENCE_LATENCY_LEVEL_LOW_LATENCY; // Join a channel RtcEngineProxy->joinChannel(TCHAR_TO_ANSI(*_token), TCHAR_TO_ANSI(*_channelName), 0, options); }
Subscribe to Video SDK events
The Video SDK provides an interface for subscribing to channel events. To use it, inherit from the IRtcEngineEventHandler class and override the event handler methods for the events you want to process.
-
AgoraWidget.h// Triggered when the local user leaves a channel void onLeaveChannel(const agora::rtc::RtcStats& stats) override; // Triggered when a remote user joins a channel void onUserJoined(agora::rtc::uid_t uid, int elapsed) override; // Triggered when a remote user leaves a channel void onUserOffline(agora::rtc::uid_t uid, agora::rtc::USER_OFFLINE_REASON_TYPE reason) override; // Triggered when the local user joins a channel void onJoinChannelSuccess(const char* channel, agora::rtc::uid_t uid, int elapsed) override; -
AgoraWidget.cpp// Implement the callback triggered after a remote user joins the channel void UAgoraWidget::onUserJoined(agora::rtc::uid_t uid, int elapsed) { // Set up remote video agora::rtc::VideoCanvas videoCanvas; videoCanvas.view = RemoteVideo; videoCanvas.uid = uid; videoCanvas.sourceType = agora::rtc::VIDEO_SOURCE_TYPE::VIDEO_SOURCE_REMOTE; agora::rtc::RtcConnection connection; connection.channelId = TCHAR_TO_ANSI(*_channelName); ((agora::rtc::IRtcEngineEx*)RtcEngineProxy)->setupRemoteVideoEx(videoCanvas, connection); } // Implement the callback triggered when a remote user leaves the channel void UAgoraWidget::onUserOffline(agora::rtc::uid_t uid, agora::rtc::USER_OFFLINE_REASON_TYPE reason) { // Stop remote video agora::rtc::VideoCanvas videoCanvas; videoCanvas.view = nullptr; videoCanvas.uid = uid; videoCanvas.sourceType = agora::rtc::VIDEO_SOURCE_TYPE::VIDEO_SOURCE_REMOTE; agora::rtc::RtcConnection connection; connection.channelId = TCHAR_TO_ANSI(*_channelName); ((agora::rtc::IRtcEngineEx*)RtcEngineProxy)->setupRemoteVideoEx(videoCanvas, connection); } // Implement the callback triggered when the local user joins the channel void UAgoraWidget::onJoinChannelSuccess(const char* channel, agora::rtc::uid_t uid, int elapsed) { AsyncTask(ENamedThreads::GameThread, [=]() { UE_LOG(LogTemp, Warning, TEXT("JoinChannelSuccess uid: %u"), uid); // Set up local video agora::rtc::VideoCanvas videoCanvas; videoCanvas.view = LocalVideo; videoCanvas.uid = 0; videoCanvas.sourceType = agora::rtc::VIDEO_SOURCE_TYPE::VIDEO_SOURCE_CAMERA; RtcEngineProxy->setupLocalVideo(videoCanvas); }); } // Implement the callback triggered when the local user leaves the channel void UAgoraWidget::onLeaveChannel(const agora::rtc::RtcStats& stats) { AsyncTask(ENamedThreads::GameThread, [=]() { agora::rtc::VideoCanvas videoCanvas; videoCanvas.view = nullptr; videoCanvas.uid = 0; videoCanvas.sourceType = agora::rtc::VIDEO_SOURCE_TYPE::VIDEO_SOURCE_CAMERA; RtcEngineProxy->setupLocalVideo(videoCanvas); }); }
Handle permissions
To access the camera and microphone, follow the steps for your target platform:
Android
Add the AndroidPermission library to obtain device and network permissions:
-
Add the following code to your header file:
#if PLATFORM_ANDROID #include "AndroidPermission/Classes/AndroidPermissionFunctionLibrary.h" #endif -
Add the
AndroidPermissionlibrary to theProject/Source/Project/Project.Build.csfile:if (Target.Platform == UnrealTargetPlatform.Android) { PrivateDependencyModuleNames.AddRange(new string[] { "AndroidPermission" }); } -
To check whether Android permissions have been granted, add the
CheckAndroidPermissionmethod and its implementation to theAgoraWidget.handAgoraWidget.cppfiles.-
AgoraWidget.hprivate: // Get Android permissions void CheckAndroidPermission(); -
AgoraWidget.cppvoid UAgoraWidget::CheckAndroidPermission() { #if PLATFORM_ANDROID FString pathfromName = UGameplayStatics::GetPlatformName(); if (pathfromName == "Android") { TArray<FString> AndroidPermission; AndroidPermission.Add(FString("android.permission.CAMERA")); AndroidPermission.Add(FString("android.permission.RECORD_AUDIO")); AndroidPermission.Add(FString("android.permission.READ_PHONE_STATE")); AndroidPermission.Add(FString("android.permission.WRITE_EXTERNAL_STORAGE")); AndroidPermission.Add(FString("android.permission.ACCESS_WIFI_STATE")); AndroidPermission.Add(FString("android.permission.ACCESS_NETWORK_STATE")); UAndroidPermissionFunctionLibrary::AcquirePermissions(AndroidPermission); } #endif } void UAgoraWidget::NativeConstruct() { Super::NativeConstruct(); #if PLATFORM_ANDROID CheckAndroidPermission() #endif }
-
iOS and macOS
-
Refer to How to add the permissions required for real-time interaction to an Unreal Engine project?
-
In
Project/Source/Project.Target.cs, add the following code:public class unrealstartTarget : TargetRules { public unrealstartTarget( TargetInfo Target) : base(Target) { Type = TargetType.Game; DefaultBuildSettings = BuildSettingsVersion.V2; if (Target.Platform == UnrealTargetPlatform.IOS) { bOverrideBuildEnvironment = true; GlobalDefinitions.Add("FORCE_ANSI_ALLOCATOR=1") } ExtraModuleNames.AddRange( new string[] { "unrealstart" } ); } }
Display the local video
Display the local video.
AsyncTask(ENamedThreads::GameThread, =
{
UE_LOG(LogTemp, Warning, TEXT("JoinChannelSuccess uid: %u"), uid);
agora::rtc::VideoCanvas videoCanvas;
videoCanvas.view = LocalVideo;
videoCanvas.uid = 0;
videoCanvas.sourceType = agora::rtc::VIDEO_SOURCE_TYPE::VIDEO_SOURCE_CAMERA;
RtcEngineProxy->setupLocalVideo(videoCanvas);
});Display remote video
When a remote user joins the channel, display their video.
// Set up remote video
agora::rtc::VideoCanvas videoCanvas;
videoCanvas.view = RemoteVideo;
videoCanvas.uid = uid;
videoCanvas.sourceType = agora::rtc::VIDEO_SOURCE_TYPE::VIDEO_SOURCE_REMOTE;
agora::rtc::RtcConnection connection;
connection.channelId = TCHAR_TO_ANSI(*_channelName);
((agora::rtc::IRtcEngineEx*)RtcEngineProxy)->setupRemoteVideoEx(videoCanvas, connection);Leave a channel
To leave a channel call leaveChannel. Implement the following method:
-
AgoraWidget.hUFUNCTION(BlueprintCallable) void Leave(); -
AgoraWidget.cppvoid UAgoraWidget::Leave() { // Leave the channel RtcEngineProxy->leaveChannel(); }
Release resources
When the local user leaves the channel, or exits the game, release memory by calling the release method of IRtcEngine. Override the NativeDestruct() method and add its implementation as follows:
-
AgoraWidget.hvoid NativeDestruct() override; -
AgoraWidget.cppvoid UAgoraWidget::NativeDestruct() { Super::NativeDestruct(); if (RtcEngineProxy != nullptr) { RtcEngineProxy->unregisterEventHandler(this); RtcEngineProxy->release(); delete RtcEngineProxy; RtcEngineProxy = nullptr; } }
Complete sample code
A complete code sample demonstrating the basic process of real-time interaction is provided for your reference. To use the sample code, copy each of the following code blocks and paste it in the corresponding file.
Project/Source/Project/AgoraWidget.h
Create a user interface
Follow these steps to set up a basic UI for your project or to integrate essential UI elements into your existing interface.
Create a basic UI
- Create Widget Blueprint
In Unreal Editor, click Content Drawer > Content, right-click and select User Interface > Widget Blueprint. Name the new Widget Blueprint AgoraWidget and double-click it to open.
- Create a view canvas.
Select Palette > PANEL > Canvas Panel and drag it to AgoraWidget.
-
Create join and leave channel buttons in Widget Blueprint
-
In AgoraWidget, select COMMON > Button, drag it to the Canvas Panel, and rename it to JoinBtn. Adjust the button's size and position on the canvas or use the following sample settings:
- Position X:
300 - Position Y:
700 - Size X:
240 - Size Y:
120
- Position X:
-
Select COMMON > Text and drag it to JoinBtn. Select the Text control of JoinBtn, and change the text content of Text to Join in the Details panel.
-
Repeat the above steps to create a LeaveBtn. Adjust the button size and position according to your layout design.
-
Create local and remote views in the Widget Blueprint
-
Select COMMON > Image, drag it to the Canvas Panel, rename it LocalVideo. Adjust its position and size on the canvas. Use the following values, or specify as per your own layout design:
- Position X:
350 - Position Y:
150 - Size X:
450 - Size Y:
450
- Position X:
-
Repeat the above steps to create a remote view and name it RemoteVideo. Adjust its position and size on the canvas. Use the following values, or specify as per your own layout design:
- Position X:
1200 - Position Y:
150 - Size X:
450 - Size Y:
450
- Position X:
-
Save the changes. Your user interface in Widget Blueprint looks similar to the following:
-
Create a Level Blueprint and associate it with the created Widget Blueprint
-
In Unreal Editor, click Content Drawer, right-click to select Level, and name it agoraLevel.
-
Double-click to open agoraLevel and click Open Level Blueprint.
-
Right-click and enter Create Widget in the search box. Select the created AgoraWidget, create Event BeginPlay and Add to Viewport in the same way. Connect them as follows:
-
Save your changes and run the project. The UI you have created looks similar to the following:
Test the sample code
Take the following steps to test the sample code:
-
Obtain a temporary token from Agora Console.
-
In
AgoraWidget.h, update_appID,_channelName, and_tokenwith the app ID, channel name, and temporary token for your project. -
In the Unreal Editor, click the play button to run your project, then click Join to join a channel.
-
Invite a friend to run the demo game on a second device. Alternatively, use the Web demo to join the same channel.
After your friend joins successfully, you can hear and see each other.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
Agora provides open source sample projects on GitHub for your reference. Download or view the JoinChannelVideo project for a more detailed example.
To learn about Agora Unreal Blueprint development, refer to the Unreal (Blueprint) Quickstart.
API reference
Frequently asked questions
See also
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
-
Unreal Engine 4.27 or higher
-
Prepare your development environment according to your target platform and engine version:
Dev environment requirements Other requirements Android - iOS A valid Apple developer signature. macOS A valid Apple developer signature. Windows 32-bit Windows only supports Unreal Engine 4 and below. To use Unreal Engine with 32-bit Windows, uncomment the code relating to Win32in theAgoraPluginLibrary.Build.csfile. -
Two physical devices for testing
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your Unreal (Blueprint) project and install the Agora Video SDK.
Create a new project Refer to the following steps or the Unreal official guide to create a new project. If you already have an Unreal project, skip to the next section.
-
Open Unreal Engine. Select Games under New Project Categories, and click Next.
-
Configure your project as follows:
- Template: Select Blank.
- Project Defaults:
- Language: Select Blueprint.
- Target Platform: Pick Desktop.
- Project Location: Enter a project files storage path.
- Project Name: Type a suitable name for your project.
Click Create.
Add to an existing project
-
In the Unreal Project Browser, click on Browse and locate the
.uprojectfile. -
Select the project and click Open.
Install the SDK
Take the following steps to add the Unreal (Blueprint) Video SDK to your project:
- Download the latest version of Agora Unreal Video SDK from Download SDKs and unzip it.
- In your project root folder, create a
Pluginsfolder. - Copy
AgoraPluginfrom the Unreal SDK folder toPlugins.
Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your game.
Create a level
-
In the Content folder of the Content Browser, right-click and select Level to create a Level Blueprint and name it BasicVideoCallScene.
-
Double-click BasicVideoCallScene and click Blueprints > Open Level Blueprint above the editor to open the level blueprint.
Implement basic processes
In the My Blueprint panel, double-click Graphs > EventGraph to open the event graph. You see two event nodes: Event BeginPlay (game starts) and Event End Play (game ends). Create event nodes with the corresponding functions and variables, and connect them as shown in the following figure to implement the Broadcast Streaming logic:
The following table lists the main nodes:
| # | Node | Type | Description |
|---|---|---|---|
| 1 | Set Show Mouse Cursor | Native* | (Optional) Set whether to display the mouse cursor. Check to display it. This node is available on Windows and macOS only. If the node is not retrieved at creation time, uncheck Context Sensitive. |
| 2 | Load Agora Config | Custom** | Loads Agora configuration. Used to verify user identity when creating and joining channels. |
| 3 | Create BP Video Widget | Native | Create user interface:
|
| 4 | Set Basic Video Call Widget | Custom | Set up the user interface:
|
| 5 | BindUIEvent | Custom | Use Bind UI events to handle event logic after clicking the Join Channel and Leave Channel buttons. |
| 6 | Add to Viewport | Native | Add user interface to the viewport. |
| 7 | Check Permission | Custom | (Optional) Check whether you have obtained the system permissions required for real-time audio and video interaction, such as access to the camera and microphone. If your target platform is Android, create this node to check system permissions. |
| 8 | Init Rtc Engine | Custom | Create and initialize the RTC engine. |
| 9 | Un Init Rtc Engine | Custom | Leave the channel and release resources. |
* Native nodes are nodes that come with the blueprint and can be added and called directly.
** Custom nodes are not included in the blueprint. You create a custom function before you can add the corresponding node.
Add channel-related variables
Add variables to create an engine instance and join a channel.
- Create three variables:
Token, ChannelId, and AppId. Select the Variable Type as String.
-
In the Load Agora Config function, add the Sequence node, and then connect Set Token, Set Channel Id, and Set App Id respectively. Fill in the token, channel name, and app ID values obtained from Agora Console.
Initialize RTC engine
-
If your target platform is Android, check whether system permissions have been granted before initializing the RTC engine. Refer to the following figure to create nodes for adding permissions to access the microphone and camera in the CheckPermission function.
If your target platform is macOS or iOS, please refer to How do I add the permissions needed for real-time interaction to my Unreal Engine project?
-
To initialize the RTC engine, in the InitRtcEngine function, create and connect nodes as shown in the following figure:
-
Create
IRtcEngineandIRtcEngineEventHandler.-
To store references to the engine and event-handler interface classes, create
RtcEngineandEventHandlervariables, and set the Variable Type to Agora Rtc Engine and IRtc Engine Event Handler, respectively. -
Add two Construct Object From Class nodes, set Class to Agora Rtc Engine and IRtc Engine Event Handler respectively. Connect to Set Rtc Engine and Set Event Handler, respectively.
-
-
Bind
IRtcEngineEventHandlerclass-related callback functions. -
Create
onJoinChannelSuccess,onLeaveChannel,onUserJoined, andonUserOfflinecallback functions. Refer to the following table to configure the input parameters of the callbacks:
| Callback | Description | Input parameters |
|---|---|---|
FOnJoinChannelSuccess | The local user successfully joined a channel. |
|
FOnLeaveChannel | The local user left the channel. |
|
FOnUserJoined | A remote user joined the current channel. |
|
FOnUserOffline | A remote user left the current channel. |
|
-
Create a Bind Event function. In this function, add a Sequence node, and then bind the
onJoinChannelSuccess,onLeaveChannel,onUserJoined, andonUserOfflinecallback events. -
IRtcEngineinitialization -
Call Initialize to initialize the RTC engine.
-
Connect to the RtcEngineContext configuration
IRtcEngineinstance and select Channel Profile asCHANNEL_PROFILE_LIVE_BROADCASTING.
Bind UI events
To bind UI events:
-
Create and implement the
OnJoinChannelClickedevent callback. -
Call Enable Video and Enable Audio to enable the video and audio modules.
-
Call Join Channel to join the channel.
-
Set the following parameters in Make ChannelMediaOptions:
- Set Publish Camera Track to
AGORA TRUE VALUEto publish the video stream recorded by the camera. - Set Publish Microphone Track to
AGORA TRUE VALUEto publish the audio stream recorded by the microphone. - Set Auto Subscribe Video to
AGORA TRUE VALUEto automatically subscribe to all video streams. - Set Auto Subscribe Audio to
AGORA TRUE VALUEto automatically subscribe to all audio streams. - Check Client Role Type Set Value and set Client Role Type to
CLIENT_ROLE_BROADCASTERorCLIENT_ROLE_AUDIENCE, to set the user role to host or audience. - Check Channel Profile Set Value and set Channel Profile to
CHANNEL_PROFILE_LIVE_BROADCASTING.
- Set Publish Camera Track to
-
Create and implement the
OnLeaveChannelClickedevent callback. When the event is triggered, call Leave Channel to leave the channel. -
In the Bind UIEvent function, refer to the figure below to bind the
OnJoinChannelClickedandOnLeaveChannelClickedcallback functions to the Join Channel and Leave Channel buttons respectively. When the button is clicked, the corresponding event callback is triggered.
You can also bind UI events in Unreal Motion Graphics (UMG). This document only shows binding using the Bind UIEvent Function.
Set up local and remote views
-
Create and implement the MakeVideoView function to load the view when local or remote users join the channel:
-
In this function, create
SavedUID,SavedSourceType,SavedChannelIDlocal variables, set the Variable Type toInteger64,VIDEO_SOURCE_TYPE, andString, respectively. Save the variables for use when loading the view later. -
Create a local view. In the local view, if the UID is 0, a value is randomly assigned by the SDK, and the video source type is
VIDEO_SOURCE_CAMERA_PRIMARY(the first camera). -
Create a remote view. In the remote view, the
uidis the uid sent from the remote end, and the video source type isVIDEO_SOURCE_REMOTE(remote video obtained from the network). -
Create and implement the ReleaseVideoView function to release the view when a local or remote user leaves the channel.
-
In this function, create
SavedUID,SavedSourceType, andSavedChannelIDlocal variables, set the Variable Type toInteger64,VIDEO_SOURCE_TYPE, andString, respectively. Save the variables for use when releasing the view later. -
Release the local view.
-
Release the remote view.
Implement callback function
Configure the previously created onJoinChannelSuccess, onLeaveChannel, onUserJoined, and onUserOffline callback functions as follows:
-
After the local user successfully joins the channel, the
onJoinChannelSuccesscallback is triggered and the local view is created. Theuidis set to0by default, but it is assigned a random value by the SDK. The video source type isVIDEO_SOURCE_CAMERA_PRIMARY(first camera): -
After the local user leaves the channel, the
onLeaveChannelcallback is triggered which releases the local view: -
When a remote user joins the channel, the
onUserJoinedcallback is triggeredd to create a remote view.uidis the uid sent from the remote end, and the video source type isVIDEO_SOURCE_REMOTE(remote video obtained from the network): -
When a remote user leaves the channel, the
onUserOfflinecallback is triggered to release the remote view:
Leave the channel and release resources
To leave the channel, implement the following steps:
- Leave the channel.
- Unregister Agora SDRTN® event callback.
- Destroy
IRtcEngineObjectto release all resources used by Agora SDK.
Refer to the figure below to implement the UnInitRtcEngine function:
Test the sample code
Take the following steps to test the sample code:
-
In the Load Agora Config function, fill in the app ID, channel name, and temporary token for your project.
-
In the Unreal Editor, click Play to run your project. Click JoinChannel to join a channel.
-
Invite a friend to run the demo game on a second device. Use the same app ID, channel name, and token to join. After your friend joins successfully, you can hear and see each other.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
Sample project
Agora provides open source sample projects on GitHub for your reference. Download or view the JoinChannelVideo project for a more detailed example.
Frequently asked questions
- How can I listen for audience joining or leaving a channel?
- How can I fix black screen issues?
- Why can't I turn on the camera?
- How can I solve channel-related issues?
- How can I set the log file?
See also
This page provides a step-by-step guide on how to create a basic Broadcast Streaming app using the Agora Video SDK.
Understand the tech
To start a Broadcast Streaming session, implement the following steps in your app:
-
Initialize the Agora Engine: Before calling other APIs, create and initialize an Agora Engine instance.
-
Join a channel: Call methods to create and join a channel.
-
Join as a host: A live streaming event has one or more hosts. A host publishes audio and video to the channel. Hosts can also subscribe to streams from other hosts.
-
Join as audience: Audience members can only subscribe to streams published by hosts.
-
-
Send and receive audio and video: Hosts publish streams to the channel. Audience members subscribe to audio and video streams published by hosts.
Prerequisites
-
A camera and a microphone
-
A valid Agora account and project. Please refer to Agora account management for details.
Set up your project
This section shows you how to set up your Python project and install the Agora Video SDK.
-
Install the build tools for compiling the SDK.
sudo apt install build-essential python3-dev -
To implement structured, asynchronous event handling in Python, install the
pyeelibrary.pip3 install pyee -
Install the Agora server side Python SDK.
pip3 install agora-python-server-sdk
The Python SDK is a server side SDK.
Implement Broadcast Streaming
This section guides you through the implementation of basic real-time audio and video interaction in your app.
Import Agora classes
Import the relevant Agora SDK classes and interfaces:
from agora.rtc.agora_base import (
AudioScenarioType,
ChannelProfileType,
ClientRoleType,
)
from agora.rtc.agora_service import (
AgoraService,
AgoraServiceConfig,
RTCConnConfig,
)
from agora.rtc.audio_frame_observer import AudioFrame, IAudioFrameObserver
from agora.rtc.audio_pcm_data_sender import PcmAudioFrame
from agora.rtc.local_user import LocalUser
from agora.rtc.local_user_observer import IRTCLocalUserObserver
from agora.rtc.rtc_connection import RTCConnection, RTCConnInfo
from agora.rtc.rtc_connection_observer import IRTCConnectionObserverInitialize the engine
The following code defines the RtcEngine class, which initializes and configures the AgoraService. The class constructor takes an appid as input, configures the Agora service, and initializes it. You use the RtcEngine class to interact with the Agora SDK in this demo.
class RtcEngine:
def __init__(self, appid: str, appcert: str):
self.appid = appid
self.appcert = appcert
if not appid:
raise Exception("App ID is required)")
config = AgoraServiceConfig()
config.audio_scenario = AudioScenarioType.AUDIO_SCENARIO_CHORUS
config.appid = appid
config.log_path = os.path.join(
os.path.dirname(
os.path.dirname(
os.path.dirname(os.path.join(os.path.abspath(__file__)))
)
),
"agorasdk.log",
)
self.agora_service = AgoraService()
self.agora_service.initialize(config)Join a channel
To asynchronously join a channel, implement a Channel class. When you create an instance of the class, the initializer sets up the necessary components for joining a channel. It takes an instance of RtcEngine, a channelId, and a uid as parameters. During initialization, the code creates an event emitter, configures the connection for broadcasting, and registers an event observer for channel events. It also sets up the local user’s audio configuration to enable audio streaming.
UIDs in the Python SDK are set using a string value. Agora recommends using only numerical values for UID strings to ensure compatibility with all Agora products and extensions.
class Channel:
def __init__(self, rtc: "RtcEngine", options: RtcOptions) -> None:
self.loop = asyncio.get_event_loop()
# Create the event emitter
self.emitter = AsyncIOEventEmitter(self.loop)
self.connection_state = 0
self.options = options
self.remote_users = dict[int, Any]()
self.rtc = rtc
self.chat = Chat(self)
self.channelId = options.channel_name
self.uid = options.uid
self.enable_pcm_dump = options.enable_pcm_dump
self.token = options.build_token(rtc.appid, rtc.appcert) if rtc.appcert else ""
conn_config = RTCConnConfig(
client_role_type=ClientRoleType.CLIENT_ROLE_BROADCASTER,
channel_profile=ChannelProfileType.CHANNEL_PROFILE_LIVE_BROADCASTING,
)
self.connection = self.rtc.agora_service.create_rtc_connection(conn_config)
self.channel_event_observer = ChannelEventObserver(
self.emitter,
options=options,
)
self.connection.register_observer(self.channel_event_observer)
self.local_user = self.connection.get_local_user()
self.local_user.set_playback_audio_frame_before_mixing_parameters(
options.channels, options.sample_rate
)
self.local_user.register_local_user_observer(self.channel_event_observer)
self.local_user.register_audio_frame_observer(self.channel_event_observer)
# self.local_user.subscribe_all_audio()
self.media_node_factory = self.rtc.agora_service.create_media_node_factory()
self.audio_pcm_data_sender = (
self.media_node_factory.create_audio_pcm_data_sender()
)
self.audio_track = self.rtc.agora_service.create_custom_audio_track_pcm(
self.audio_pcm_data_sender
)
self.audio_track.set_enabled(1)
self.local_user.publish_audio(self.audio_track)
self.stream_id = self.connection.create_data_stream(False, False)
self.received_chunks = {}
self.waiting_message = None
self.msg_id = ""
self.msg_index = ""
self.on(
"user_joined",
lambda agora_rtc_conn, user_id: self.remote_users.update({user_id: True}),
)
self.on(
"user_left",
lambda agora_rtc_conn, user_id, reason: self.remote_users.pop(
user_id, None
),
)The following code uses the Channel class to join a channel. It sets up a future to handle the connection state and returns a Channel object when the connection is successfully established.
async def connect(self) -> None:
"""
Connects to a channel.
Parameters:
channelId: The channel ID.
uid: The user ID.
Returns:
Channel: The connected channel.
"""
if self.connection_state == 3:
return
future = asyncio.Future()
def callback(agora_rtc_conn: RTCConnection, conn_info: RTCConnInfo, reason):
logger.info(f"Connection state changed: {conn_info.state}")
if conn_info.state == 3: # Connection successful
future.set_result(None)
elif conn_info.state == 5: # Connection failed
future.set_exception(
Exception(f"Connection failed with state: {conn_info.state}")
)
self.on("connection_state_changed", callback)
logger.info(f"Connecting to channel {self.channelId} with token {self.token}")
self.connection.connect(self.token, self.channelId, f"{self.uid}")
if self.enable_pcm_dump:
agora_parameter = self.connection.get_agora_parameter()
agora_parameter.set_parameters("{\"che.audio.frame_dump\":{\"location\":\"all\",\"action\":\"start\",\"max_size_bytes\":\"120000000\",\"uuid\":\"123456789\",\"duration\":\"1200000\"}}")
try:
await future
except Exception as e:
raise Exception(
f"Failed to connect to channel {self.channelId}: {str(e)}"
) from e
finally:
self.off("connection_state_changed", callback)Handle connection and channel events
To listen for channel and connection events, such as users joining or leaving the channel, and connection state changes, implement the ChannelEventObserver class. This class enables you to respond to SDK events.
class ChannelEventObserver(
IRTCConnectionObserver, IRTCLocalUserObserver, IAudioFrameObserver
):
def __init__(self, event_emitter: AsyncIOEventEmitter, options: RtcOptions) -> None:
self.loop = asyncio.get_event_loop()
self.emitter = event_emitter
self.audio_streams = dict[int, AudioStream]()
self.options = options
def emit_event(self, event_name: str, *args):
"""Helper function to emit events."""
self.loop.call_soon_threadsafe(self.emitter.emit, event_name, *args)
def on_connected(
self, agora_rtc_conn: RTCConnection, conn_info: RTCConnInfo, reason
):
logger.info(f"Connected to RTC: {agora_rtc_conn} {conn_info} {reason}")
self.emit_event("connection_state_changed", agora_rtc_conn, conn_info, reason)
def on_disconnected(
self, agora_rtc_conn: RTCConnection, conn_info: RTCConnInfo, reason
):
logger.info(f"Disconnected from RTC: {agora_rtc_conn} {conn_info} {reason}")
self.emit_event("connection_state_changed", agora_rtc_conn, conn_info, reason)
def on_connecting(
self, agora_rtc_conn: RTCConnection, conn_info: RTCConnInfo, reason
):
logger.info(f"Connecting to RTC: {agora_rtc_conn} {conn_info} {reason}")
self.emit_event("connection_state_changed", agora_rtc_conn, conn_info, reason)
def on_connection_failure(self, agora_rtc_conn, conn_info, reason):
logger.error(f"Connection failure: {agora_rtc_conn} {conn_info} {reason}")
self.emit_event("connection_state_changed", agora_rtc_conn, conn_info, reason)
def on_user_joined(self, agora_rtc_conn: RTCConnection, user_id):
logger.info(f"User joined: {agora_rtc_conn} {user_id}")
self.emit_event("user_joined", agora_rtc_conn, user_id)
def on_user_left(self, agora_rtc_conn: RTCConnection, user_id, reason):
logger.info(f"User left: {agora_rtc_conn} {user_id} {reason}")
self.emit_event("user_left", agora_rtc_conn, user_id, reason)
def handle_received_chunk(self, json_chunk):
chunk = json.loads(json_chunk)
msg_id = chunk["msg_id"]
part_idx = chunk["part_idx"]
total_parts = chunk["total_parts"]
if msg_id not in self.received_chunks:
self.received_chunks[msg_id] = {"parts": {}, "total_parts": total_parts}
if (
part_idx not in self.received_chunks[msg_id]["parts"]
and 0 <= part_idx < total_parts
):
self.received_chunks[msg_id]["parts"][part_idx] = chunk
if len(self.received_chunks[msg_id]["parts"]) == total_parts:
# all parts received, now recomposing original message and get rid it from dict
sorted_parts = sorted(
self.received_chunks[msg_id]["parts"].values(),
key=lambda c: c["part_idx"],
)
full_message = "".join(part["content"] for part in sorted_parts)
del self.received_chunks[msg_id]
return full_message, msg_id
return (None, None)
def on_stream_message(
self, agora_local_user: LocalUser, user_id, stream_id, data, length
):
# logger.info(f"Stream message", agora_local_user, user_id, stream_id, length)
(reassembled_message, msg_id) = self.handle_received_chunk(data)
if reassembled_message is not None:
logger.info(f"Reassembled message: {msg_id} {reassembled_message}")
def on_audio_subscribe_state_changed(
self,
agora_local_user,
channel,
user_id,
old_state,
new_state,
elapse_since_last_state,
):
logger.info(
f"Audio subscribe state changed: {user_id} {new_state} {elapse_since_last_state}"
)
self.emit_event(
"audio_subscribe_state_changed",
agora_local_user,
channel,
user_id,
old_state,
new_state,
elapse_since_last_state,
)
def on_playback_audio_frame_before_mixing(
self, agora_local_user: LocalUser, channelId, uid, frame: AudioFrame
):
audio_frame = PcmAudioFrame()
audio_frame.samples_per_channel = frame.samples_per_channel
audio_frame.bytes_per_sample = frame.bytes_per_sample
audio_frame.number_of_channels = frame.channels
audio_frame.sample_rate = self.options.sample_rate
audio_frame.data = frame.buffer
self.loop.call_soon_threadsafe(
self.audio_streams[uid].queue.put_nowait, audio_frame
)
return 0Subscribe to an audio stream
To asynchronously subscribe to audio streams for a specific user identified by their uid, refer to the following code. The method sets up a callback to monitor changes in the audio subscription state and handles the result based on whether the subscription is successfully established.
async def subscribe_audio(self, uid: int) -> None:
"""
Subscribes to the audio of a user.
Parameters:
uid: The user ID to subscribe to.
"""
future = asyncio.Future()
def callback(
agora_local_user,
channel,
user_id,
old_state,
new_state,
elapse_since_last_state,
):
if new_state == 3: # Successfully subscribed
future.set_result(None)
self.on("audio_subscribe_state_changed", callback)
self.local_user.subscribe_audio(uid)
try:
await future
except Exception as e:
raise Exception(
f"Audio subscription failed for user {uid}: {str(e)}"
) from e
finally:
self.off("audio_subscribe_state_changed", callback)Unsubscribe from an audio stream
To unsubscribe from an audio stream, implement an asynchronous method similar to subscribe_audio and use the following code to unsubscribe:
self.local_user.unsubscribe_audio(uid)Disconnect from the service
To leave a channel, disconnect from Agora SDRTN® and release resources, refer to he following code.
async def disconnect(self) -> None:
"""
Disconnects the channel.
"""
if self.connection_state == 1:
return
disconnected_future = asyncio.Future[None]()
def callback(agora_rtc_conn: RTCConnection, conn_info: RTCConnInfo, reason):
self.off("connection_state_changed", callback)
if conn_info.state == 1:
disconnected_future.set_result(None)
self.on("connection_state_changed", callback)
self.connection.disconnect()
await disconnected_futureComplete code
The rtc.py script integrates the code components presented in this section into reusable Python classes that you can extend for your own applications.
Complete code for rtc.py
import asyncio
import json
import logging
import os
from typing import Any, AsyncIterator
from agora.rtc.agora_base import (
AudioScenarioType,
ChannelProfileType,
ClientRoleType,
)
from agora.rtc.agora_service import (
AgoraService,
AgoraServiceConfig,
RTCConnConfig,
)
from agora.rtc.audio_frame_observer import AudioFrame, IAudioFrameObserver
from agora.rtc.audio_pcm_data_sender import PcmAudioFrame
from agora.rtc.local_user import LocalUser
from agora.rtc.local_user_observer import IRTCLocalUserObserver
from agora.rtc.rtc_connection import RTCConnection, RTCConnInfo
from agora.rtc.rtc_connection_observer import IRTCConnectionObserver
from pyee.asyncio import AsyncIOEventEmitter
from .logger import setup_logger
from .token_builder.realtimekit_token_builder import RealtimekitTokenBuilder
# Set up the logger with color and timestamp support
logger = setup_logger(name=__name__, log_level=logging.INFO)
class RtcOptions:
def __init__(
self,
*,
channel_name: str = None,
uid: int = 0,
sample_rate: int = 24000,
channels: int = 1,
enable_pcm_dump: bool = False,
):
self.channel_name = channel_name
self.uid = uid
self.sample_rate = sample_rate
self.channels = channels
self.enable_pcm_dump = enable_pcm_dump
def build_token(self, appid: str, appcert: str) -> str:
return RealtimekitTokenBuilder.build_token(
appid, appcert, self.channel_name, self.uid
)
class AudioStream:
def __init__(self) -> None:
self.queue: asyncio.Queue = asyncio.Queue()
def __aiter__(self) -> AsyncIterator[PcmAudioFrame]:
return self
async def __anext__(self) -> PcmAudioFrame:
item = await self.queue.get()
if item is None:
raise StopAsyncIteration
return item
class ChannelEventObserver(
IRTCConnectionObserver, IRTCLocalUserObserver, IAudioFrameObserver
):
def __init__(self, event_emitter: AsyncIOEventEmitter, options: RtcOptions) -> None:
self.loop = asyncio.get_event_loop()
self.emitter = event_emitter
self.audio_streams = dict[int, AudioStream]()
self.options = options
def emit_event(self, event_name: str, *args):
"""Helper function to emit events."""
self.loop.call_soon_threadsafe(self.emitter.emit, event_name, *args)
def on_connected(
self, agora_rtc_conn: RTCConnection, conn_info: RTCConnInfo, reason
):
logger.info(f"Connected to RTC: {agora_rtc_conn} {conn_info} {reason}")
self.emit_event("connection_state_changed", agora_rtc_conn, conn_info, reason)
def on_disconnected(
self, agora_rtc_conn: RTCConnection, conn_info: RTCConnInfo, reason
):
logger.info(f"Disconnected from RTC: {agora_rtc_conn} {conn_info} {reason}")
self.emit_event("connection_state_changed", agora_rtc_conn, conn_info, reason)
def on_connecting(
self, agora_rtc_conn: RTCConnection, conn_info: RTCConnInfo, reason
):
logger.info(f"Connecting to RTC: {agora_rtc_conn} {conn_info} {reason}")
self.emit_event("connection_state_changed", agora_rtc_conn, conn_info, reason)
def on_connection_failure(self, agora_rtc_conn, conn_info, reason):
logger.error(f"Connection failure: {agora_rtc_conn} {conn_info} {reason}")
self.emit_event("connection_state_changed", agora_rtc_conn, conn_info, reason)
def on_user_joined(self, agora_rtc_conn: RTCConnection, user_id):
logger.info(f"User joined: {agora_rtc_conn} {user_id}")
self.emit_event("user_joined", agora_rtc_conn, user_id)
def on_user_left(self, agora_rtc_conn: RTCConnection, user_id, reason):
logger.info(f"User left: {agora_rtc_conn} {user_id} {reason}")
self.emit_event("user_left", agora_rtc_conn, user_id, reason)
def handle_received_chunk(self, json_chunk):
chunk = json.loads(json_chunk)
msg_id = chunk["msg_id"]
part_idx = chunk["part_idx"]
total_parts = chunk["total_parts"]
if msg_id not in self.received_chunks:
self.received_chunks[msg_id] = {"parts": {}, "total_parts": total_parts}
if (
part_idx not in self.received_chunks[msg_id]["parts"]
and 0 <= part_idx < total_parts
):
self.received_chunks[msg_id]["parts"][part_idx] = chunk
if len(self.received_chunks[msg_id]["parts"]) == total_parts:
# all parts received, now recomposing original message and get rid it from dict
sorted_parts = sorted(
self.received_chunks[msg_id]["parts"].values(),
key=lambda c: c["part_idx"],
)
full_message = "".join(part["content"] for part in sorted_parts)
del self.received_chunks[msg_id]
return full_message, msg_id
return (None, None)
def on_stream_message(
self, agora_local_user: LocalUser, user_id, stream_id, data, length
):
# logger.info(f"Stream message", agora_local_user, user_id, stream_id, length)
(reassembled_message, msg_id) = self.handle_received_chunk(data)
if reassembled_message is not None:
logger.info(f"Reassembled message: {msg_id} {reassembled_message}")
def on_audio_subscribe_state_changed(
self,
agora_local_user,
channel,
user_id,
old_state,
new_state,
elapse_since_last_state,
):
logger.info(
f"Audio subscribe state changed: {user_id} {new_state} {elapse_since_last_state}"
)
self.emit_event(
"audio_subscribe_state_changed",
agora_local_user,
channel,
user_id,
old_state,
new_state,
elapse_since_last_state,
)
def on_playback_audio_frame_before_mixing(
self, agora_local_user: LocalUser, channelId, uid, frame: AudioFrame
):
audio_frame = PcmAudioFrame()
audio_frame.samples_per_channel = frame.samples_per_channel
audio_frame.bytes_per_sample = frame.bytes_per_sample
audio_frame.number_of_channels = frame.channels
audio_frame.sample_rate = self.options.sample_rate
audio_frame.data = frame.buffer
# print(
# "on_playback_audio_frame_before_mixing",
# audio_frame.samples_per_channel,
# audio_frame.bytes_per_sample,
# audio_frame.number_of_channels,
# audio_frame.sample_rate,
# len(audio_frame.data),
# )
self.loop.call_soon_threadsafe(
self.audio_streams[uid].queue.put_nowait, audio_frame
)
return 0
class Channel:
def __init__(self, rtc: "RtcEngine", options: RtcOptions) -> None:
self.loop = asyncio.get_event_loop()
# Create the event emitter
self.emitter = AsyncIOEventEmitter(self.loop)
self.connection_state = 0
self.options = options
self.remote_users = dict[int, Any]()
self.rtc = rtc
self.chat = Chat(self)
self.channelId = options.channel_name
self.uid = options.uid
self.enable_pcm_dump = options.enable_pcm_dump
self.token = options.build_token(rtc.appid, rtc.appcert) if rtc.appcert else ""
conn_config = RTCConnConfig(
client_role_type=ClientRoleType.CLIENT_ROLE_BROADCASTER,
channel_profile=ChannelProfileType.CHANNEL_PROFILE_LIVE_BROADCASTING,
)
self.connection = self.rtc.agora_service.create_rtc_connection(conn_config)
self.channel_event_observer = ChannelEventObserver(
self.emitter,
options=options,
)
self.connection.register_observer(self.channel_event_observer)
self.local_user = self.connection.get_local_user()
self.local_user.set_playback_audio_frame_before_mixing_parameters(
options.channels, options.sample_rate
)
self.local_user.register_local_user_observer(self.channel_event_observer)
self.local_user.register_audio_frame_observer(self.channel_event_observer)
# self.local_user.subscribe_all_audio()
self.media_node_factory = self.rtc.agora_service.create_media_node_factory()
self.audio_pcm_data_sender = (
self.media_node_factory.create_audio_pcm_data_sender()
)
self.audio_track = self.rtc.agora_service.create_custom_audio_track_pcm(
self.audio_pcm_data_sender
)
self.audio_track.set_enabled(1)
self.local_user.publish_audio(self.audio_track)
self.stream_id = self.connection.create_data_stream(False, False)
self.received_chunks = {}
self.waiting_message = None
self.msg_id = ""
self.msg_index = ""
self.on(
"user_joined",
lambda agora_rtc_conn, user_id: self.remote_users.update({user_id: True}),
)
self.on(
"user_left",
lambda agora_rtc_conn, user_id, reason: self.remote_users.pop(
user_id, None
),
)
def handle_audio_subscribe_state_changed(
agora_local_user,
channel,
user_id,
old_state,
new_state,
elapse_since_last_state,
):
if new_state == 3: # Successfully subscribed
self.channel_event_observer.audio_streams.update(
{user_id: AudioStream()}
)
elif new_state == 0:
self.channel_event_observer.audio_streams.pop(user_id, None)
self.on("audio_subscribe_state_changed", handle_audio_subscribe_state_changed)
self.on(
"connection_state_changed",
lambda agora_rtc_conn, conn_info, reason: setattr(
self, "connection_state", conn_info.state
),
)
async def connect(self) -> None:
"""
Connects to a channel.
Parameters:
channelId: The channel ID.
uid: The user ID.
Returns:
Channel: The connected channel.
"""
if self.connection_state == 3:
return
future = asyncio.Future()
def callback(agora_rtc_conn: RTCConnection, conn_info: RTCConnInfo, reason):
logger.info(f"Connection state changed: {conn_info.state}")
if conn_info.state == 3: # Connection successful
future.set_result(None)
elif conn_info.state == 5: # Connection failed
future.set_exception(
Exception(f"Connection failed with state: {conn_info.state}")
)
self.on("connection_state_changed", callback)
logger.info(f"Connecting to channel {self.channelId} with token {self.token}")
self.connection.connect(self.token, self.channelId, f"{self.uid}")
if self.enable_pcm_dump:
agora_parameter = self.connection.get_agora_parameter()
agora_parameter.set_parameters("{"che.audio.frame_dump":{"location":"all","action":"start","max_size_bytes":"120000000","uuid":"123456789","duration":"1200000"}}")
try:
await future
except Exception as e:
raise Exception(
f"Failed to connect to channel {self.channelId}: {str(e)}"
) from e
finally:
self.off("connection_state_changed", callback)
async def disconnect(self) -> None:
"""
Disconnects the channel.
"""
if self.connection_state == 1:
return
disconnected_future = asyncio.Future[None]()
def callback(agora_rtc_conn: RTCConnection, conn_info: RTCConnInfo, reason):
self.off("connection_state_changed", callback)
if conn_info.state == 1:
disconnected_future.set_result(None)
self.on("connection_state_changed", callback)
self.connection.disconnect()
await disconnected_future
def get_audio_frames(self, uid: int) -> AudioStream:
"""
Returns the audio frames from the channel.
Returns:
AudioStream: The audio stream.
"""
return self.channel_event_observer.audio_streams[uid]
async def push_audio_frame(self, frame: bytes) -> None:
"""
Pushes an audio frame to the channel.
Parameters:
frame: The audio frame to push.
"""
audio_frame = PcmAudioFrame()
audio_frame.data = bytearray(frame)
audio_frame.timestamp = 0
audio_frame.bytes_per_sample = 2
audio_frame.number_of_channels = self.options.channels
audio_frame.sample_rate = self.options.sample_rate
audio_frame.samples_per_channel = int(
len(frame) / audio_frame.bytes_per_sample / audio_frame.number_of_channels
)
ret = self.audio_pcm_data_sender.send_audio_pcm_data(audio_frame)
logger.info(f"Pushed audio frame: {ret}, audio frame length: {len(frame)}")
if ret < 0:
raise Exception(f"Failed to send audio frame: {ret}, audio frame length: {len(frame)}")
async def clear_sender_audio_buffer(self) -> None:
"""
Clears the audio buffer which is used to send.
"""
self.audio_track.clear_sender_buffer()
async def subscribe_audio(self, uid: int) -> None:
"""
Subscribes to the audio of a user.
Parameters:
uid: The user ID to subscribe to.
"""
future = asyncio.Future()
def callback(
agora_local_user,
channel,
user_id,
old_state,
new_state,
elapse_since_last_state,
):
if new_state == 3: # Successfully subscribed
future.set_result(None)
# elif new_state == 1: # Subscription failed
# future.set_exception(
# Exception(
# f"Failed to subscribe {user_id} audio: state changed from {old_state} to {new_state}"
# )
# )
self.on("audio_subscribe_state_changed", callback)
self.local_user.subscribe_audio(uid)
try:
await future
except Exception as e:
raise Exception(
f"Audio subscription failed for user {uid}: {str(e)}"
) from e
finally:
self.off("audio_subscribe_state_changed", callback)
async def unsubscribe_audio(self, uid: int) -> None:
"""
Unsubscribes from the audio of a user.
Parameters:
uid: The user ID to unsubscribe from.
"""
future = asyncio.Future()
def callback(
agora_local_user,
channel,
user_id,
old_state,
new_state,
elapse_since_last_state,
):
if new_state == 3: # Successfully unsubscribed
future.set_result(None)
else: # Failed to unsubscribe
future.set_exception(
Exception(
f"Failed to unsubscribe {user_id} audio: state changed from {old_state} to {new_state}"
)
)
self.on("audio_subscribe_state_changed", callback)
self.local_user.unsubscribe_audio(uid)
try:
await future
except Exception as e:
raise Exception(
f"Audio unsubscription failed for user {uid}: {str(e)}"
) from e
finally:
self.off("audio_subscribe_state_changed", callback)
def _split_string_into_chunks(
self, long_string, msg_id, chunk_size=300
) -> list[dict[str:Any]]:
"""
Splits a long string into chunks of a given size.
Parameters:
long_string: The string to split.
msg_id: The message ID.
chunk_size: The size of each chunk.
Returns:
list[dict[str: Any]]: The list of chunks.
"""
total_parts = (len(long_string) + chunk_size - 1) // chunk_size
json_chunks = []
for idx in range(total_parts):
start = idx * chunk_size
end = min(start + chunk_size, len(long_string))
chunk = {
"msg_id": msg_id,
"part_idx": idx,
"total_parts": total_parts,
"content": long_string[start:end],
}
json_chunk = json.dumps(chunk, ensure_ascii=False)
json_chunks.append(json_chunk)
return json_chunks
async def send_stream_message(self, data: str, msg_id: str) -> None:
"""
Sends a stream message to the channel.
Parameters:
data: The data to send.
msg_id: The message ID.
"""
chunks = self._split_string_into_chunks(data, msg_id)
for chunk in chunks:
self.connection.send_stream_message(self.stream_id, chunk)
def on(self, event_name: str, callback):
"""
Allows external components to subscribe to events.
Parameters:
event_name: The name of the event to subscribe to.
callback: The callback to call when the event is emitted.
"""
self.emitter.on(event_name, callback)
def once(self, event_name: str, callback):
"""
Allows external components to subscribe to events once.
Parameters:
event_name: The name of the event to subscribe to.
callback: The callback to call when the event is emitted.
"""
self.emitter.once(event_name, callback)
def off(self, event_name: str, callback):
"""
Allows external components to unsubscribe from events.
Parameters:
event_name: The name of the event to unsubscribe from.
callback: The callback to remove from the event.
"""
self.emitter.remove_listener(event_name, callback)
class ChatMessage:
def __init__(self, message: str, msg_id: str) -> None:
self.message = message
self.msg_id = msg_id
class Chat:
def __init__(self, channel: Channel) -> None:
self.channel = channel
self.loop = self.channel.loop
self.queue = asyncio.Queue()
def log_exception(t: asyncio.Task[Any]) -> None:
if not t.cancelled() and t.exception():
logger.error(
"unhandled exception",
exc_info=t.exception(),
)
asyncio.create_task(self._process_message()).add_done_callback(log_exception)
async def send_message(self, item: ChatMessage) -> None:
"""
Sends a message to the channel.
Parameters:
item: The message to send.
"""
await self.queue.put(item)
# await self.queue.put_nowait(item)
async def _process_message(self) -> None:
"""
Processes messages in the queue.
"""
while True:
item: ChatMessage = await self.queue.get()
await self.channel.send_stream_message(item.message, item.msg_id)
self.queue.task_done()
# await asyncio.sleep(0)
class RtcEngine:
def __init__(self, appid: str, appcert: str):
self.appid = appid
self.appcert = appcert
if not appid:
raise Exception("App ID is required)")
config = AgoraServiceConfig()
config.audio_scenario = AudioScenarioType.AUDIO_SCENARIO_CHORUS
config.appid = appid
config.log_path = os.path.join(
os.path.dirname(
os.path.dirname(
os.path.dirname(os.path.join(os.path.abspath(__file__)))
)
),
"agorasdk.log",
)
self.agora_service = AgoraService()
self.agora_service.initialize(config)
def create_channel(self, options: RtcOptions) -> Channel:
"""
Creates a channel.
Parameters:
channelId: The channel ID.
uid: The user ID.
Returns:
Channel: The created channel.
"""
return Channel(self, options)
def destroy(self) -> None:
"""
Destroys the RTC engine.
"""
self.agora_service.release()Test the sample code
Follow these steps to test the demo code:
-
Create a file named
rtc.pyand paste the complete source code into this file. -
Create a file named
main.pyin the same folder asrtc.pyand copy the following code to the file:import asyncio from rtc import RtcEngine # Import the RtcEngine class from rtc.py async def main(): appid = "<Your app Id>" # Replace with your Agora App ID channelId = "demo" # Replace with your desired channel ID uid = "123" # Replace with your unique user ID rtc_engine = RtcEngine(appid) channel = await rtc_engine.connect(channelId, uid) # Keep the script running to listen for events await asyncio.Event().wait() if __name__ == "__main__": asyncio.run(main()) -
To specify the audio parameters, create a folder named
realtimeapiand add a fileutil.pycontaining the following code:# Number of audio channels (1 for mono, 2 for stereo) CHANNELS = 2 # Sample rate for audio processing (in Hz) SAMPLE_RATE = 44100 # Common sample rates include 8000, 16000, 44100, 48000 -
To run the app, execute the following command in your terminal:
python3 main.py
You see output similar to the following:
Initialization result: 0
LocalUserCB _on_audio_track_publish_start: 123456607304576 123456864576600Congratulations! You have successfully connected to the Agora SDRTN® and joined a channel.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
- If a firewall is deployed in your network environment, refer to Connect with Cloud Proxy to use Agora services normally.
Next steps
After implementing the quickstart sample, read the following documents to learn more:
- To ensure communication security in a test or production environment, best practice is to obtain and use a token from an authentication server. For details, see Secure authentication with tokens.
