介绍如何通过音频分组接口实现分组讨论的应用场景。
典型场景
一个课堂,有老师,助教和学生。在讨论某个题目的时候,老师可以将学生分成若干个讨论组,每个讨论组中的学生只能在本组中讨论问题,他听不见其他组里面的声音。而老师和助教可以随意加入某个或者多个讨论组,和组内学生语音交流。
引入音频分组,大厅的概念
音频分组(group):允许频道内的成员建立工作组。User加入一个group后,有权向这个group发言,也可以收听这个group内的音频。
大厅(hall):如果一个user当前不在任何一个group中,那么他就留在大厅中。大厅是一个特殊的group,通过离开/加入一个group间接的达到加入/离开大厅的效果。和group类似,如果user在大厅中,那么他有权对大厅中的人发言,也可以订阅大厅内的音频。
权限约束:user无权对不在其中的group (含hall)发言,或者收听音频。
音频订阅约束:user不可以同时订阅多个group (含hall)的音频,这个是为了应对一个user出现在多个group中,以及过量的多路音频(比如6路音频)混音后造成理解困难的问题。
注意:音频分组不影响视频的publish和subscribe,即使2个user不在同一个group,也可以看见对方。
代码实现
下面用分组接口来实现分组讨论。以c++代码为例,其他平台接口是一致的。
老师侧,通过业务层,通知每个学生加入到指定的分组,比如张三加入group1, 李四加入group2, 王五加入group1, 赵六加入group2
1. 老师侧程序
// 老师加入所有的分组
void discuss_begin()
{
engine.JoinGroup("group1", "");
engine.JoinGroup("group2", "");
// 通知学生加入 -
// 张三加入group1, 李四加入group2, 王五加入group1, 赵六加入group2
// 此时老师和学生都还在JoinGroup过程中,
// 等所有的JoinGroup都成功(OnAudioGroupJoinResult)后,可以和任意
// 分组中的学生交流了(discuss_group())。
}
// 当所有人加入分组完成后,老师和任意group中的学生交流
void discuss_group(string group, bool private_talk)
{
// 老师一开始已经提前加入了所有group,无需再次加入group,
// 只需要向这个group推流和拉流
// sdk支持同时向多个group推流,如果老师不想其他group的
// 学生听到,那么private_talk为true
if (private_talk) {
// 取消其他group的语音推流
for (all_groups) {
engine.MixAudioToGroup(true, ith_group);
}
}
// 和指定的group双向交流
engine.MixAudioToGroup(true, group);
engine.SwitchSubscriptionToGroup(group);
}
// 停止讨论
void discuss_end()
{
// 解散所有分组,老师和所有学生都回到大厅
engine.DismissGroup("group1");
engine.DismissGroup("group2");
// 注意:监控到自己回到大厅后,设置音频推流和拉流的目标为大厅
//(在OnAudioGroupHallMembers中实现)
}
// 当老师回到大厅后,改变音频推流和拉流目标为大厅
void MyEngineEventListerner::OnAudioGroupHallMembers(ding::rtc::RtcEngineAudioGroupMember *hallMembers,
int hallMemberCount)
{
bool iAmInHall = false;
for(int i = 0; i < memberCount; i++) {
if (members[i].usrId 是我自己) {
iAmInHall = true;
break;
}
}
// 如果在大厅
if (iAmInHall) {
// 音频推流到大厅
engine.MixAudioToGroup(true, ding::rtc::RtcEngine::HallID);
// 音频大厅拉流
engine.SwitchSubscriptionToGroup(ding::rtc::RtcEngine::HallID);
}
}
2. 学生侧程序
// 学生侧程序,业务层通知,加入某个分组
void on_teacher_notice_join_group(string group)
{
// 加入指定的分组
engine.JoinGroup(group, "");
// 注意:音频推流和拉流的设置,等到OnAudioGroupJoinResult成功后
// 再执行
}
// 当学生加入某个分组后,更新音频推流和拉流目标为该分组
void OnAudioGroupJoinResult(int result, const ding::rtc::String& errMsg,
const ding::rtc::String& group,
RtcEngineAudioGroupMember *members,
int memberCount)
{
if (result == 0) { // 0表示加入分组成功
// 音频推流 (假定学生一开始已经推音频流了)
engine.MixAudioToGroup(true, group);
// 音频拉流
engine.SwitchSubscriptionToGroup(group);
}
else {
// 加入分组失败
}
}
// 当学生回到大厅后,改变音频推流和拉流目标为大厅
void MyEngineEventListerner::OnAudioGroupHallMembers(ding::rtc::RtcEngineAudioGroupMember *hallMembers,
int hallMemberCount)
{
bool iAmInHall = false;
for(int i = 0; i < memberCount; i++) {
if (members[i].usrId 是我自己) {
iAmInHall = true;
break;
}
}
// 如果在大厅
if (iAmInHall) {
// 音频推流到大厅
engine.MixAudioToGroup(true, ding::rtc::RtcEngine::HallID);
// 音频大厅拉流
engine.SwitchSubscriptionToGroup(ding::rtc::RtcEngine::HallID);
}
}
文档内容是否对您有帮助?