As of v3.0.0, the Agora RTC Native SDK enables users to join an unlimited number of channels at a time and to receive the audio and video streams of all the channels.
Agora provides the following open-source sample projects on GitHub. You can download them and refer to the source code.
The SDK uses the RtcChannel and IRtcChannelEventHandler classes to support the multi-channel function.
You can call createRtcChannel multiple times to create multiple RtcChannel objects with different channelId, and then call joinChannel in RtcChannel to join the channel.
The following are the major steps of implementing the multi-channel function:
create to create and initialize RtcEngine.setChannelProfile to set the channel profile as live streaming.createRtcChannel to create an RtcChannel object with a channelId.setRtcChannelEventHandler of the RtcChannel class to receive callbacks in this channel.setClientRole of the RtcChannel class to set the user role.joinChannel of the RtcChannel class to join the channel. After joining a channel, the user publishes the local streams and automatically subscribes to the streams of all the other users in the channel by default. You can set the publishing and subscribing states in ChannelMediaOptions when joining a channel, and you can change the publishing and subscribing states in the methods with prefixes muteLocaland muteRemote of the RtcChannel class after joining a channel.Refer to the following API sequence to join multiple channels:
The following code demonstrates how to join an RtcChannel channel, and then publish the local streams in the first channel.
Create and initialize RtcEngine.
engine = RtcEngine.create(context.getApplicationContext(), getString(R.string.agora_app_id), iRtcEngineEventHandler);
Enable the video module.
engine.enableVideo();
Set the channel profile as interactive live streaming.
engine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);
Create an RtcChannel1 object, and listen for events in this channel.
rtcChannel1 = engine.createRtcChannel(channel1);
rtcChannel1.setRtcChannelEventHandler(new IRtcChannelEventHandler() {
@Override
// Listen for the onJoinChannelSuccess callback.
// This callback occurs when the local user successfully joins the channel.
public void onJoinChannelSuccess(RtcChannel rtcChannel1, int uid, int elapsed) {
super.onJoinChannelSuccess(rtcChannel1, uid, elapsed);
Log.i(TAG, String.format("onJoinChannelSuccess channel %s uid %d", channel1, uid));
}
});
Set the user role as a host.
rtcChannel1.setClientRole(Constants.CLIENT_ROLE_BROADCASTER);
Pass in token and channelId to join the RtcChannel1 channel. The SDK publishes the local streams and automatically subscribes to the streams of all the other users in the channel by default.
ChannelMediaOptions mediaOptions = new ChannelMediaOptions();
mediaOptions.autoSubscribeAudio = true;
mediaOptions.autoSubscribeVideo = true;
mediaOptions.publishLocalAudio = true;
mediaOptions.publishLocalVideo = true;
int ret = rtcChannel1.joinChannel("Your token", "", 0, mediaOptions);
Create an RtcChannel2 object, and listen for events in this channel.
rtcChannel2 = engine.createRtcChannel(channel2);
rtcChannel2.setRtcChannelEventHandler(new IRtcChannelEventHandler() {
@Override
// Listen for the onJoinChannelSuccess callback.
// This callback occurs when the local user successfully joins the channel.
public void onJoinChannelSuccess(RtcChannel rtcChannel2, int uid, int elapsed) {
super.onJoinChannelSuccess(rtcChannel2, uid, elapsed);
Log.i(TAG, String.format("onJoinChannelSuccess channel %s uid %d", channel2, uid));
}
});
Set the user role as an audience member. Note that audience members cannot publish local streams.
rtcChannel2.setClientRole(Constants.CLIENT_ROLE_AUDIENCE);
Pass in token and channelId to join the RtcChannel2 channel. You need to set publishLocalAudio and publishLocalVideo to false; otherwise, the user fails to join the channel.
ChannelMediaOptions mediaOptions = new ChannelMediaOptions();
mediaOptions.autoSubscribeAudio = true;
mediaOptions.autoSubscribeVideo = true;
mediaOptions.publishLocalAudio = false;
mediaOptions.publishLocalVideo = false;
int ret = rtcChannel2.joinChannel("Your token", "", 0, mediaOptions);
Leave and destroy the RtcChannel2 channel.
rtcChannel2.leaveChannel();
rtcChannel2.destroy();
Leave and destroy the RtcChannel1 channel.
rtcChannel1.leaveChannel();
rtcChannel1.destroy();
Destroy the RtcEngine object.
engine.destroy();
joinChannel in the RtcChannel class provides the media-subscription options (autoSubscribeAudio and autoSubscribeVideo) that determine whether to automatically subscribe to the remote streams after joining the channel. The default setting is to subscribe to all the streams automatically. After a user joins a channel, you can change the subscribing state by calling muteRemoteAudioStream or muteRemoteVideoStream.
If you need to subscribe to the streams of specified users after joining an RtcChannel channel, refer to the following steps:
joinChannel and set autoSubscribeAudio = false or autoSubscribeVideo = false of ChannelMediaOptions to unsubscribe from all remote users.muteRemoteAudioStream (uid,false) or muteRemoteVideoStream(uid,false) to subscribe to specified remote users.In video scenarios, if the remote user joins the channel using RtcChannel, ensure that you specify the channel name of the remote user in VideoCanvas when setting the remote video view.
// Set the remote user's view in the onUserJoined callback.
public void onUserJoined(RtcChannel rtcChannel, int uid, int elapsed) {
super.onUserJoined(rtcChannel, uid, elapsed);
Log.i(TAG, "onUserJoined->" + uid);
showLongToast(String.format("user %d joined!", uid));
Context context = getContext();
if (context == null) {
return;
}
handler.post(() ->
{
SurfaceView surfaceView = null;
if (fl_remote2.getChildCount() > 0) {
fl_remote2.removeAllViews();
}
// Create a SurfaceView object.
surfaceView = RtcEngine.CreateRendererView(context);
surfaceView.setZOrderMediaOverlay(true);
// Add the SurfaceView object to the layout of remote users.
fl_remote2.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// Set the remote video view.
engine.setupRemoteVideo(new VideoCanvas(surfaceView, RENDER_MODE_FIT, channel2, uid));
});
}
The SDK supports publishing local streams to only one channel at a time. Agora recommends setting the user role as an audience when joining a channel where the user do not need to publish streams, and setting publishLocalAudio and publishLocalVideo in ChannelMediaOptions to false when joining the channel.
In the interactive live streaming profile, if a user joins the first channel as a host, the SDK publishes local streams in the first channel by default. If the user needs to join a second channel, you need to change the publishing state according to your actual scenario:
setClientRole(AUDIENCE)).setClientRole(AUDIENCE)) in the first channel and joining the second channel as a host (setClientRole(BROADCASTER)).If a user publishes local streams in a channel and you call the following methods in a second channel, the method call fails, and the SDK returns -5(ERR_REFUSED):
publishLocalAudio = true or publishLocalVideo = true when joining the second channel.muteLocalAudioStream(false) or muteLocalVideoStream(false) after joining the second channel.setClientRole(BROADCASTER).As of v3.4.5, the RtcChannel class changes as follows:
publish and unpublish, and adds muteLocalAudioStream and muteLocalVideoStream instead. After joining a channel, you can set the publishing state of the audio stream and video stream separately. muteLocalAudioStream and muteLocalVideoStream in the RtcEngine and RtcChannel classes control the publishing state of each channel in their respective classes only.publishLocalAudio and publishLocalVideo members in ChannelMediaOptions. The default value is true. You can call joinChannel to join a channel and set the publishing state. If a user publishes streams in a channel, regardless of whether the user is a host or an audience member, they need to set publishLocalAudio and publishLocalVideo to false when joining other channels; otherwise, they fail to join the channel.setClientRole(BROADCASTER) of the RtcChannel class, the local user publishes audio and video streams by default. You no longer need to call publish.Earlier than v3.4.5:
muteLocalAudioStream(true) or muteLocalVideoStream(true) in RtcEngine takes effect in channels created by both the RtcEngine and RtcChannel classes. muteLocalAudioStream and muteLocalVideoStream take effect when they are called before or after a user joins a channel.joinChannel cannot set the publishing state of local streams.setClientRole(BROADCASTER) of the RtcChannel class does not publish local streams. You also need to call publish.