For AI agents: see the complete documentation index at /llms.txt.
Join multiple channels
Updated
Broadcast or subscribe to multiple channels.
Agora Video SDK enables you to simultaneously join multiple channels. This capability allows you to receive and publish audio and video streams across multiple channels concurrently.
Understand the tech
Video SDK's multi-channel functionality is based on two key components:
-
RtcConnectionThe
RtcConnectionobject identifies a connection. It contains the following information:- Channel name
- User ID of the local user
You create multiple
RtcConnectionobjects, each with a different channel name and user ID. EachRtcConnectioninstance can independently publish multiple audio streams and a single video stream. -
RtcEngineExThe class contains methods tailored for interacting with a designated
RtcConnectionobject.To join multiple channels, you call
joinChannelExmethod in theRtcEngineExclass multiple times, using a differentRtcConnectioninstance each time.
When joining multiple channels:
-
Ensure that the user ID for each
RtcConnectionobject is unique and nonzero. -
Configure publishing and subscribing options for the
RtcConnectionobject injoinChannelEx. -
Pass the
IRtcEngineEventHandlerobject to theeventHandlerparameter when calling thejoinChannelExmethod to receive multiple channel-related event notifications.
Prerequisites
Ensure that you have implemented the SDK quickstart in your project.
Implementation
This section explains how to join a second channel as a host after you have already joined the first channel.
- Declare variables for
RtcEngineExandRtcConnectionobjects.
private RtcEngineEx engine;
private RtcConnection rtcConnection2 = new RtcConnection();private lateinit var engine: RtcEngineEx
private val rtcConnection2 = RtcConnection()- Initialize the engine instance.
engine = (RtcEngineEx) RtcEngine.create(config);engine = RtcEngine.create(config) as RtcEngineEx- Join the channel using a random user ID.
private boolean joinSecondChannel() {
ChannelMediaOptions option = new ChannelMediaOptions();
mediaOptions.autoSubscribeAudio = true;
mediaOptions.autoSubscribeVideo = true;
rtcConnection2.channelId = "channel-2";
rtcConnection2.localUid = new Random().nextInt(512)+512;
int ret = engine.joinChannelEx("your token", rtcConnection2, mediaOptions,
iRtcEngineEventHandler2);
return (ret == 0);
}private fun joinSecondChannel(): Boolean {
val mediaOptions = ChannelMediaOptions().apply {
autoSubscribeAudio = true
autoSubscribeVideo = true
}
rtcConnection2.channelId = "channel-2"
rtcConnection2.localUid = (Random().nextInt(512) + 512)
val ret = engine.joinChannelEx("your token", rtcConnection2, mediaOptions, iRtcEngineEventHandler2)
return ret == 0
}- Listen for events in
rtcConnection2and set up the remote video in theonUserJoinedcallback.
private final IRtcEngineEventHandler iRtcEngineEventHandler2 = new IRtcEngineEventHandler() {
@Override
public void onJoinChannelSuccess(String channel, int uid, int elapsed) {
Log.i(TAG, String.format("channel2 onJoinChannelSuccess channel %s uid %d", channel2, uid));
showLongToast(String.format("onJoinChannelSuccess channel %s uid %d", channel2, uid));
}
@Override
public void onUserJoined(int uid, int elapsed) {
Log.i(TAG, "channel2 onUserJoined->" + uid);
showLongToast(String.format("user %d joined!", uid));
Context context = getContext();
if (context == null) {
return;
}
handler.post(() ->
{
// Display the remote video stream
SurfaceView surfaceView = null;
if (fl_remote2.getChildCount() > 0) {
fl_remote2.removeAllViews();
}
// Create the rendering view through RtcEngine
surfaceView = new SurfaceView(context);
surfaceView.setZOrderMediaOverlay(true);
// Add the view to the remote container
fl_remote2.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// Set the remote view
engine.setupRemoteVideoEx(new VideoCanvas(surfaceView, RENDER_MODE_FIT, uid), rtcConnection2);
});
}
};private val iRtcEngineEventHandler2 = object : IRtcEngineEventHandler() {
override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
Log.i(TAG, String.format("channel2 onJoinChannelSuccess channel %s uid %d", channel2, uid))
showLongToast(String.format("onJoinChannelSuccess channel %s uid %d", channel2, uid))
}
override fun onUserJoined(uid: Int, elapsed: Int) {
Log.i(TAG, "channel2 onUserJoined->$uid")
showLongToast("user $uid joined!")
val context = context
if (context == null) {
return
}
handler.post {
// Display the remote video stream
var surfaceView: SurfaceView? = null
if (fl_remote2.childCount > 0) {
fl_remote2.removeAllViews()
}
// Create the rendering view through RtcEngine
surfaceView = SurfaceView(context)
surfaceView?.zOrderMediaOverlay = true
// Add the view to the remote container
fl_remote2.addView(surfaceView, FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
// Set the remote view
engine.setupRemoteVideoEx(VideoCanvas(surfaceView, RENDER_MODE_FIT, uid), rtcConnection2)
}
}
}Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
Sample project
Agora provides the JoinMultipleChannels open-source sample project for your reference. Download the project or view the source code for a more detailed example.
API reference
Understand the tech
Video SDK's multi-channel functionality is based on two key components:
-
AgoraRtcConnectionThe
AgoraRtcConnectionobject identifies a connection. It contains the following information:- Channel name
- User ID of the local user
You create multiple
AgoraRtcConnectionobjects, each with a different channel name and user ID. EachAgoraRtcConnectioninstance can independently publish multiple audio streams and a single video stream. -
AgoraRtcEngineKit(Ex)The class contains methods tailored for interacting with a designated
AgoraRtcConnectionobject.To join multiple channels, you call
joinChannelExByTokenmethod in theAgoraRtcEngineKit(Ex)class multiple times, using a differentAgoraRtcConnectioninstance each time.
When joining multiple channels:
-
Ensure that the user ID for each
AgoraRtcConnectionobject is unique and nonzero. -
Configure publishing and subscribing options for the
AgoraRtcConnectionobject injoinChannelExByToken. -
Pass the
AgoraRtcEngineDelegateobject to thedelegateparameter when calling thejoinChannelExByTokenmethod to receive multiple channel-related event notifications.
Prerequisites
Ensure that you have implemented the SDK quickstart in your project.
Implementation
This section explains how to join a second channel as a host after you have already joined the first channel.
-
Define an
AgoraRtcConnectionobject in your project'sViewController.swiftfile.let connection1 = AgoraRtcConnection() -
Join the channel using a random user ID.
var mediaOptions = AgoraRtcChannelMediaOptions() mediaOptions.autoSubscribeVideo = .of(true) mediaOptions.autoSubscribeAudio = .of(true) connection1.channelId = channelName1 connection1.localUid = UInt.random(in: 1001...2000) var result = agoraKit.joinChannelEx(byToken: "your token", connection: connection1, delegate: channel1, mediaOptions: mediaOptions, joinSuccess: nil) channel1.channelId = channelName1 channel1.connectionDelegate = self if result != 0 { self.showAlert(title: "Error", message: "joinChannel1 call failed: \(result), please check your params") } -
Set up the remote video in the
didJoinedOfUidcallback.func rtcEngine(_ engine: AgoraRtcEngineKit, channelId: String, didJoinedOfUid uid: UInt, elapsed: Int) { LogUtils.log(message: "remote user join: \(uid) \(elapsed)ms", level: .info) // In this operation, only one remote video view is available // Here, we check if there's a view tagged with that UID let videoCanvas = AgoraRtcVideoCanvas() videoCanvas.uid = uid // The view to be bound videoCanvas.view = channelId == channelName1 ? channel1RemoteVideo.videoView : channel2RemoteVideo.videoView videoCanvas.renderMode = .hidden let connection = AgoraRtcConnection() agoraKit.setupRemoteVideoEx(videoCanvas, connection: connection) }
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
Sample project
Agora offers the JoinMultiChannels open-source sample project for your reference. Download the project or view the source code for a more detailed example.
API reference
Understand the tech
Video SDK's multi-channel functionality is based on two key components:
-
AgoraRtcConnectionThe
AgoraRtcConnectionobject identifies a connection. It contains the following information:- Channel name
- User ID of the local user
You create multiple
AgoraRtcConnectionobjects, each with a different channel name and user ID. EachAgoraRtcConnectioninstance can independently publish multiple audio streams and a single video stream. -
AgoraRtcEngineKit(Ex)The class contains methods tailored for interacting with a designated
AgoraRtcConnectionobject.To join multiple channels, you call
joinChannelExByTokenmethod in theAgoraRtcEngineKit(Ex)class multiple times, using a differentAgoraRtcConnectioninstance each time.
When joining multiple channels:
-
Ensure that the user ID for each
AgoraRtcConnectionobject is unique and nonzero. -
Configure publishing and subscribing options for the
AgoraRtcConnectionobject injoinChannelExByToken. -
Pass the
AgoraRtcEngineDelegateobject to thedelegateparameter when calling thejoinChannelExByTokenmethod to receive multiple channel-related event notifications.
Prerequisites
Ensure that you have implemented the SDK quickstart in your project.
Implementation
This section explains how to join a second channel as a host after you have already joined the first channel.
-
Define an
AgoraRtcConnectionobject in your project'sViewController.swiftfile.let connection1 = AgoraRtcConnection() -
Join the channel using a random user ID.
var mediaOptions = AgoraRtcChannelMediaOptions() mediaOptions.autoSubscribeVideo = .of(true) mediaOptions.autoSubscribeAudio = .of(true) connection1.channelId = channelName1 connection1.localUid = UInt.random(in: 1001...2000) var result = agoraKit.joinChannelEx(byToken: "your token", connection: connection1, delegate: channel1, mediaOptions: mediaOptions, joinSuccess: nil) channel1.channelId = channelName1 channel1.connectionDelegate = self if result != 0 { self.showAlert(title: "Error", message: "joinChannel1 call failed: \(result), please check your params") } -
Set up the remote video in the
didJoinedOfUidcallback.func rtcEngine(_ engine: AgoraRtcEngineKit, channelId: String, didJoinedOfUid uid: UInt, elapsed: Int) { LogUtils.log(message: "remote user join: \(uid) \(elapsed)ms", level: .info) // In this operation, only one remote video view is available // Here, we check if there's a view tagged with that UID let videoCanvas = AgoraRtcVideoCanvas() videoCanvas.uid = uid // The view to be bound videoCanvas.view = channelId == channelName1 ? channel1RemoteVideo.videoView : channel2RemoteVideo.videoView videoCanvas.renderMode = .hidden let connection = AgoraRtcConnection() agoraKit.setupRemoteVideoEx(videoCanvas, connection: connection) }
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
Sample project
Agora offers the JoinMultiChannels open-source sample project for your reference. Download the project or view the source code for a more detailed example.
API reference
Understand the tech
Video SDK's multi-channel functionality is based on two key components:
-
RtcConnectionThe
RtcConnectionobject identifies a connection. It contains the following information:- Channel name
- User ID of the local user
You create multiple
RtcConnectionobjects, each with a different channel name and user ID. EachRtcConnectioninstance can independently publish multiple audio streams and a single video stream. -
IRtcEngineExThe class contains methods tailored for interacting with a designated
RtcConnectionobject.To join multiple channels, you call
joinChannelExmethod in theIRtcEngineExclass multiple times, using a differentRtcConnectioninstance each time.
When joining multiple channels:
-
Ensure that the user ID for each
RtcConnectionobject is unique and nonzero. -
Configure publishing and subscribing options for the
RtcConnectionobject injoinChannelEx. -
Pass the
IRtcEngineEventHandlerobject to theeventHandlerparameter when calling thejoinChannelExmethod to receive multiple channel-related event notifications.
Prerequisites
Ensure that you have implemented the SDK quickstart in your project.
Implementation
This section explains how to join a second channel as a host after you have already joined the first channel.
-
Define a connection and join the channel using a random user ID.
agora::rtc::ChannelMediaOptions options2; options2.autoSubscribeAudio = false; options2.autoSubscribeVideo = false; options2.publishAudioTrack = false; options2.publishCameraTrack = false; options2.publishSecondaryCameraTrack = true; options2.clientRoleType = CLIENT_ROLE_BROADCASTER; // Define the connection connection.localUid = generateUid(); connection.channelId = szChannelId.data(); // Join the channel int ret = m_rtcEngine->joinChannelEx(APP_TOKEN, connection, options2, &m_camera2EventHandler); -
Listen for the
onUserJoinedcallback and set up the remote video.// Listen for the onUserJoined callback void CAGEngineEventHandler::onUserJoined(uid_t uid, int elapsed) { LPAGE_USER_JOINED lpData = new AGE_USER_JOINED; lpData->uid = uid; lpData->elapsed = elapsed; if (m_hMainWnd != NULL) ::PostMessage(m_hMainWnd, WM_MSGID(EID_USER_JOINED), (WPARAM)lpData, 0); } -
Set up the remote video
VideoCanvas canvas; canvas.renderMode = RENDER_MODE_FIT; canvas.uid = 123; canvas.view = videoWnd.GetSafeHwnd(); connection.localUid = 123; connection.channelId = "channel123"; // Set up the remote video rtcEngine->setupRemoteVideoEx(canvas, connection);
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
Sample project
Agora provides the MultiChannels open-source sample project for your reference. Download the project or view the source code for a more detailed example.
API reference
Understand the tech
Video SDK's multi-channel functionality is based on two key components:
-
RtcConnectionThe
RtcConnectionobject identifies a connection. It contains the following information:- Channel name
- User ID of the local user
You create multiple
RtcConnectionobjects, each with a different channel name and user ID. EachRtcConnectioninstance can independently publish multiple audio streams and a single video stream. -
IRtcEngineExThe class contains methods tailored for interacting with a designated
RtcConnectionobject.To join multiple channels, you call
joinChannelExmethod in theIRtcEngineExclass multiple times, using a differentRtcConnectioninstance each time.
When joining multiple channels:
-
Ensure that the user ID for each
RtcConnectionobject is unique and nonzero. -
Configure publishing and subscribing options for the
RtcConnectionobject injoinChannelEx. -
Use the
connectionparameter in each callback to identify the corresponding channel.
Prerequisites
Ensure that you have implemented the SDK quickstart in your project.
Implementation
This section explains how to join a second channel as a host after you have already joined the first channel.
- Join the second channel
Call joinChannelEx to join a secondary channel by specifying the RtcConnection object and using a random user ID.
let token2 = "token2";
const channelId2 = "channel2";
let uid2 = 321;
// Join multiple channels
rtcEngine.joinChannelEx(
token2,
// RtcConnection
{
channelId: channelId2,
localUid: uid2,
},
// ChannelMediaOptions
{
clientRoleType: ClientRoleType.ClientRoleBroadcaster,
publishMicrophoneTrack: false,
publishCameraTrack: false,
autoSubscribeAudio: false,
autoSubscribeVideo: false,
publishSecondaryCameraTrack: true,
}
);- Set up remote user video
Listen to the onUserJoined callback and set up the remote video.
const EventHandles = {
// Monitor remote user joining channel events
onUserJoined: ({ channelId, localUid }, remoteUid, elapsed) => {
console.log('Remote user ' + remoteUid + ' joined');
// After the remote user joins the channel, set the remote video window
rtcEngine.setupRemoteVideoEx(
{
sourceType: VideoSourceType.VideoSourceRemote,
uid: remoteUid,
view: remoteVideoContainer,
setupMode: VideoViewSetupMode.VideoViewSetupAdd,
},
{
channelId: channelId2,
localUid: uid2,
},
);
},
// Other event callbacks
};
// Register event callback
rtcEngine.registerEventHandler(EventHandles);Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
Sample project
Agora provides the JoinMultipleChannel open-source sample project for your reference. Download the project or view the source code for a more detailed example.
API reference
Understand the tech
Video SDK's multi-channel functionality is based on two key components:
-
RtcConnectionThe
RtcConnectionobject identifies a connection. It contains the following information:- Channel name
- User ID of the local user
You create multiple
RtcConnectionobjects, each with a different channel name and user ID. EachRtcConnectioninstance can independently publish multiple audio streams and a single video stream. -
IRtcEngineExThe class contains methods tailored for interacting with a designated
RtcConnectionobject.To join multiple channels, you call
joinChannelExmethod in theIRtcEngineExclass multiple times, using a differentRtcConnectioninstance each time.
When joining multiple channels:
-
Ensure that the user ID for each
RtcConnectionobject is unique and nonzero. -
Configure publishing and subscribing options for the
RtcConnectionobject injoinChannelEx. -
Use the
connectionparameter in each callback to identify the corresponding channel.
Prerequisites
Ensure that you have implemented the SDK quickstart in your project.
Implementation
This section explains how to join a second channel as a host after you have already joined the first channel.
- Join the second channel
Call joinChannelEx to join a secondary channel by specifying the RtcConnection object and using a random user ID.
let token2 = "token2";
const channelId2 = "channel2";
let uid2 = 321;
// Join multiple channels
rtcEngine.joinChannelEx(
token2,
// RtcConnection
{
channelId: channelId2,
localUid: uid2,
},
// ChannelMediaOptions
{
clientRoleType: ClientRoleType.ClientRoleBroadcaster,
publishMicrophoneTrack: false,
publishCameraTrack: false,
autoSubscribeAudio: false,
autoSubscribeVideo: false,
publishSecondaryCameraTrack: true,
}
);- Set up remote user video
Listen to the onUserJoined callback and set the remote video.
// Register callback for when a remote user joins the current channel
agoraEngine.registerEventHandler(
{
onUserJoined: (_connection: RtcConnection, uid: number) => {
showMessage("remote user " + uid + " added");
setRemoteUid(uid);
},
}
);
// Creating a Remote View Using RtcSurfaceView
<RtcSurfaceView
canvas={
{
uid: remoteUid,
sourceType: VideoSourceType.VideoSourceRemote,
}
}
style={styles.videoView}
/>;Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
Sample project
Agora provides the JoinMultipleChannel open-source sample project for your reference. Download the project or view the source code for a more detailed example.
API reference
Understand the tech
Video SDK's multi-channel functionality is based on two key components:
-
RtcConnectionThe
RtcConnectionobject identifies a connection. It contains the following information:- Channel name
- User ID of the local user
You create multiple
RtcConnectionobjects, each with a different channel name and user ID. EachRtcConnectioninstance can independently publish multiple audio streams and a single video stream. -
IRtcEngineExThe class contains methods tailored for interacting with a designated
RtcConnectionobject.To join multiple channels, you call
JoinChannelExmethod in theIRtcEngineExclass multiple times, using a differentRtcConnectioninstance each time.
When joining multiple channels:
-
Ensure that the user ID for each
RtcConnectionobject is unique and nonzero. -
Configure publishing and subscribing options for the
RtcConnectionobject inJoinChannelEx. -
Use the
connectionparameter in each callback to identify the corresponding channel.
Prerequisites
Ensure that you have implemented the SDK quickstart in your project.
Implementation
This section explains how to join a second channel as a host after you have already joined the first channel.
- Join the second channel
Call JoinChannelEx to join the second channel:
ChannelMediaOptions options2 = new ChannelMediaOptions();
// Automatically subscribe to remote audio
options2.autoSubscribeAudio.SetValue(true);
// Automatically subscribe to remote video
options2.autoSubscribeVideo.SetValue(true);
// Publish audio from the microphone
options2.publishMicrophoneTrack.SetValue(true);
// Publish video from the camera
options2.publishCameraTrack.SetValue(true);
// Set the user role to broadcaster
options2.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
// Join the second channel with a unique user ID
ret = RtcEngine.JoinChannelEx(_token, new RtcConnection(_channelName, UID2), options2);
Debug.Log("JoinChannelEx returns: " + ret);- Set up remote user video
Listen for the OnUserJoined callback and call SetForUser to set up video rendering for the remote user:
public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed)
{
Debug.Log(string.Format("OnUserJoined uid: {0}, elapsed: {1}", uid, elapsed));
// Create a view for the remote user
var videoSurface = MakeImageSurface(uid.ToString());
// Set up video rendering
videoSurface.SetForUser(uid, connection.channelId, VIDEO_SOURCE_TYPE.VIDEO_SOURCE_REMOTE);
// Callback when the Texture size changes
videoSurface.OnTextureSizeModify += (int width, int height) =>
{
var transform = videoSurface.GetComponent<RectTransform>();
if (transform)
{
// If rendering in a RawImage, set the RawImage size
transform.sizeDelta = new Vector2(width / 2, height / 2);
transform.localScale = Vector3.one;
}
else
{
// If rendering in a MeshRenderer, set the MeshRenderer size
float scale = (float)height / (float)width;
videoSurface.transform.localScale = new Vector3(-1, 1, scale);
}
Debug.Log("OnTextureSizeModify: " + width + " " + height);
};
}
private static VideoSurface MakeImageSurface(string goName)
{
GameObject go = new GameObject { name = goName };
// Add RawImage for rendering
go.AddComponent<RawImage>();
// Make the object draggable
go.AddComponent<UIElementDrag>();
var canvas = GameObject.Find("VideoCanvas");
if (canvas != null)
{
go.transform.parent = canvas.transform;
Debug.Log("Video view added");
}
else
{
Debug.Log("Canvas is null, video view not added");
}
// Set transform properties
go.transform.Rotate(0f, 0f, 180f);
go.transform.localPosition = Vector3.zero;
go.transform.localScale = new Vector3(2f, 3f, 1f);
// Add VideoSurface component
var videoSurface = go.AddComponent<VideoSurface>();
return videoSurface;
}Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
