为方便用户同时加入多个频道,接收多个频道的音视频流,声网 RTC Native SDK 3.0.0 及以上版本支持多频道管理,且频道数量无限制。
该功能可应用于类似超级小班课的场景:将一个互动大班里的学生分到不同的小班,学生可以在小班内进行实时音视频互动。根据场景需要,你还可以给每个小班可以配备一名助教老师。
我们在 GitHub 提供以下实现了多频道功能的开源示例项目,你可以前往下载体验,参考源代码。
SDK 提供 RtcChannel
类和 IRtcChannelEventHandler
类实现多频道控制。
你可以多次调用 createRtcChannel
,通过不同的 channelId
创建多个 RtcChannel
对象(对应多个频道),然后分别调用 RtcChannel
中的 joinChannel
方法加入对应的频道。
实现多频道功能的主要步骤如下:
create
方法,创建并初始化 RtcEngine
。setChannelProfile
方法,将频道场景设置为直播。createRtcChannel
方法,通过 channelId
创建一个 RtcChannel
对象。RtcChannel
类的 setRtcChannelEventHandler
方法,接收该频道的回调通知。RtcChannel
类的 setClientRole
设置用户角色。RtcChannel
类的 joinChannel
方法加入频道。用户加入频道后,默认发布本地流并自动订阅频道内所有其他用户的流。你可以在加入频道时通过ChannelMediaOptions
设置发布和订阅状态,也可以在加入频道后通过 RtcChannel
类的 muteLocal
和 muteRemote
为前缀的方法修改发布和订阅状态。加入多个频道的 API 时序如下:
下面的示例代码演示了如何通过 RtcChannel
类加入多频道,并在加入的第一个频道中发布本地流。
创建并初始化 RtcEngine
对象。
engine = RtcEngine.create(context.getApplicationContext(), getString(R.string.agora_app_id), iRtcEngineEventHandler);
启用视频模块。
engine.enableVideo();
将频道场景设置为直播场景。
engine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);
创建并获取 RtcChannel1
对象,并监听该频道的回调通知。
rtcChannel1 = engine.createRtcChannel(channel1);
rtcChannel1.setRtcChannelEventHandler(new IRtcChannelEventHandler() {
@Override
// 注册 onJoinChannelSuccess 回调。
// 本地用户成功加入频道时,会触发该回调。
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));
}
});
设置用户角色为主播。
rtcChannel1.setClientRole(Constants.CLIENT_ROLE_BROADCASTER);
传入 token
和 channelId
, 加入 RtcChannel1
对象的频道,SDK 会默认发布本地流并自动订阅所有远端流。
ChannelMediaOptions mediaOptions = new ChannelMediaOptions();
mediaOptions.autoSubscribeAudio = true;
mediaOptions.autoSubscribeVideo = true;
mediaOptions.publishLocalAudio = true;
mediaOptions.publishLocalVideo = true;
int ret = rtcChannel1.joinChannel("Your token", "", 0, mediaOptions);
创建并获取 RtcChannel2
对象,并监听该频道的回调通知。
rtcChannel2 = engine.createRtcChannel(channel2);
rtcChannel2.setRtcChannelEventHandler(new IRtcChannelEventHandler() {
@Override
// 注册 onJoinChannelSuccess 回调。
// 本地用户成功加入频道时,会触发该回调。
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));
}
});
设置用户角色为观众,无发流权限。
rtcChannel2.setClientRole(Constants.CLIENT_ROLE_AUDIENCE);
传入 token
和 channelId
, 加入 RtcChannel2
对象的频道。你需要设置 publishLocalAudio
和 publishLocalVideo
为 false
,否则加入频道会失败。
ChannelMediaOptions mediaOptions = new ChannelMediaOptions();
mediaOptions.autoSubscribeAudio = true;
mediaOptions.autoSubscribeVideo = true;
mediaOptions.publishLocalAudio = false;
mediaOptions.publishLocalVideo = false;
int ret = rtcChannel2.joinChannel("Your token", "", 0, mediaOptions);
离开并销毁 RtcChannel2
对象的频道。
rtcChannel2.leaveChannel();
rtcChannel2.destroy();
离开并销毁 RtcChannel1
对象的频道。
rtcChannel1.leaveChannel();
rtcChannel1.destroy();
销毁 RtcEngine
对象。
engine.destroy();
RtcChannel
中的 joinChannel
提供媒体订阅选项设置(autoSubscribeAudio
和 autoSubscribeVideo
),可以控制在加入频道后是否自动订阅音频流和视频流,默认为自动订阅。在加入频道后,你也可以通过 muteRemoteAudioStream
或 muteRemoteVideoStream
修改订阅状态。
如果你需要在加入 RtcChannel
的频道时仅订阅指定用户的音频流或视频流,声网建议使用以下方法:
joinChannel
并在 ChannelMediaOptions
中设置 autoSubscribeAudio = false
或 autoSubscribeVideo = false
不订阅所有远端用户。muteRemoteAudioStream(uid,false)
或 muteRemoteVideoStream(uid,false)
订阅指定的远端用户。在视频场景中,如果远端用户是通过 RtcChannel
加入频道的,那么在设置远端视图时,还需要在 VideoCanvas
中指定该远端用户所在频道的名称,否则会无法渲染出远端视频画面。
// 在远端用户加入频道的回调中设置用户视图。
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();
}
// 创建一个 SurfaceView 对象
surfaceView = RtcEngine.CreateRendererView(context);
surfaceView.setZOrderMediaOverlay(true);
// 将 SurfaceView 添加到远端布局。
fl_remote2.addView(surfaceView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// 初始化远端用户视图。
engine.setupRemoteVideo(new VideoCanvas(surfaceView, RENDER_MODE_FIT, channel2, uid));
});
}
SDK 仅支持用户同一时间在一个频道内发布媒体流。声网推荐以观众角色加入无需发流的频道并在加入频道时设置 ChannelMediaOptions
中的 publishLocalAudio
和 publishLocalVideo
为 false
。
在直播场景中,用户作为主播加入频道一后,SDK 默认发布本地流到频道一。如果用户需要加入频道二,则你需要根据实际场景修改发布状态:
setClientRole(AUDIENCE)
)加入频道二。setClientRole(AUDIENCE)
),再让用户作为主播(setClientRole(BROADCASTER)
)加入频道二。如果用户已在频道一内发流,并且在频道二内调用如下方法,则该方法会调用失败并返回 -5(ERR_REFUSED)
错误码:
joinChannel
方法加入频道二时使用 publishLocalAudio = true
和 publishLocalVideo = true
。muteLocalAudioStream(false)
或 muteLocalVideoStream(false)
。setClientRole(BROADCASTER)
。自 3.4.5 版本起,RtcChannel
类有如下变更:
publish
和 unpublish
,并新增 muteLocalAudioStream
和 muteLocalVideoStream
替代。加入频道后,你可以分别设置音频流和视频流的发布状态。RtcEngine
类和 RtcChannel
类下的 muteLocalAudioStream
和 muteLocalVideoStream
分别控制各自频道的发布状态。ChannelMediaOptions
中新增 publishLocalAudio
和 publishLocalVideo
成员,默认值为 true
。你可以调用 joinChannel
加入频道并设置音视频流的发布状态。如果用户已在一个频道中发流,则不论用户是主播还是观众,都需要在加入其他频道时设置 publishLocalAudio
和 publishLocalVideo
为 false
。否则,加入频道会失败。setClientRole(BROADCASTER)
默认发布本地流,无需再调用 publish
。在 3.4.5 之前版本中:
RtcEngine
类的 muteLocalAudioStream(true)
或 muteLocalVideoStream(true)
对 RtcEngine
频道和 RtcChannel
频道都会生效。muteLocalAudioStream
和 muteLocalVideoStream
在加入频道前后调用均可生效。joinChannel
不可以设置本地流的发布状态。RtcChannel
类的 setClientRole(BROADCASTER)
不会发布本地流。你还需要调用 publish
。