介紹如何通過音頻分組接口實現分組討論的應用場景。
典型場景
一個課堂,有老師,助教和學生。在討論某個題目的時候,老師可以將學生分成若干個討論組,每個討論組中的學生只能在本組中討論問題,他聽不見其他組里面的聲音。而老師和助教可以隨意加入某個或者多個討論組,和組內學生語音交流。
引入音頻分組,大廳的概念
音頻分組(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);
}
}
文檔內容是否對您有幫助?