This topic describes how to create group discussions using the audio group API.
Typical scenario
Consider a classroom with a teacher, a teaching assistant, and students. The teacher can divide students into discussion groups. Students in a group can only talk with and hear others in the same group. The teacher and teaching assistant can join any group to talk with the students.
Audio groups and the hall
Audio group (group): An audio group allows channel members to create workgroups. When a user joins a group, they can speak to and listen to audio from that group.
Hall: A user who is not in any group is in the hall. The hall is a special type of group. You indirectly enter or leave the hall by leaving or joining a group. Similar to a regular group, a user in the hall can speak to others and subscribe to audio in the hall.
Permission constraint: A user cannot speak to or listen to audio from a group (including the hall) that they are not in.
Audio subscription constraint: A user cannot subscribe to audio from multiple groups (including the hall) at the same time. This constraint prevents issues with understanding mixed audio that can occur if a user is in multiple groups or if too many audio streams, such as six, are mixed.
Note: Audio groups do not affect video publishing and subscribing. Two users can see each other even if they are not in the same group.
Code implementation
This section shows how to implement group discussions using the group API. The examples use C++ code, but the APIs are consistent across all platforms.
On the teacher client, the business layer notifies each student to join a specific group. For example, Student A joins group1, Student B joins group2, Student C joins group1, and Student D joins group2.
1. Teacher-side program
// The teacher joins all groups.
void discuss_begin()
{
engine.JoinGroup("group1", "");
engine.JoinGroup("group2", "");
// Notify students to join.
// Student A joins group1, Student B joins group2, Student C joins group1, and Student D joins group2.
// At this point, the teacher and students are in the process of joining their groups.
// After all JoinGroup calls succeed (OnAudioGroupJoinResult),
// the teacher can talk with students in any group (discuss_group()).
}
// After everyone has joined their groups, the teacher talks with students in any group.
void discuss_group(string group, bool private_talk)
{
// The teacher has already joined all groups, so there is no need to join again.
// The teacher only needs to perform stream ingest and stream pulling for this group.
// The SDK supports ingesting streams to multiple groups at the same time. If the teacher does not want other groups
// to hear the audio, set private_talk to true.
if (private_talk) {
// Cancel audio stream ingest to other groups.
for (all_groups) {
engine.MixAudioToGroup(true, ith_group);
}
}
// Start bidirectional communication with the specified group.
engine.MixAudioToGroup(true, group);
engine.SwitchSubscriptionToGroup(group);
}
// Stop the discussion.
void discuss_end()
{
// Dismiss all groups. The teacher and all students return to the hall.
engine.DismissGroup("group1");
engine.DismissGroup("group2");
// Note: After you return to the hall, set the target for audio stream ingest and pulling to the hall
// (implement this in OnAudioGroupHallMembers).
}
// When the teacher returns to the hall, change the target for audio stream ingest and pulling to the hall.
void MyEngineEventListerner::OnAudioGroupHallMembers(ding::rtc::RtcEngineAudioGroupMember *hallMembers,
int hallMemberCount)
{
bool iAmInHall = false;
for(int i = 0; i < memberCount; i++) {
if (members[i].usrId is my user ID) {
iAmInHall = true;
break;
}
}
// If in the hall.
if (iAmInHall) {
// Ingest audio stream to the hall.
engine.MixAudioToGroup(true, ding::rtc::RtcEngine::HallID);
// Pull audio stream from the hall.
engine.SwitchSubscriptionToGroup(ding::rtc::RtcEngine::HallID);
}
}
2. Student-side program
// Student-side program. The business layer sends a notification to join a group.
void on_teacher_notice_join_group(string group)
{
// Join the specified group.
engine.JoinGroup(group, "");
// Note: Configure audio stream ingest and pulling only after the OnAudioGroupJoinResult call succeeds.
}
// After a student joins a group, update the target for audio stream ingest and pulling to that group.
void OnAudioGroupJoinResult(int result, const ding::rtc::String& errMsg,
const ding::rtc::String& group,
RtcEngineAudioGroupMember *members,
int memberCount)
{
if (result == 0) { // 0 indicates that the user successfully joined the group.
// Audio stream ingest (assuming the student has already started audio stream ingest).
engine.MixAudioToGroup(true, group);
// Audio stream pulling.
engine.SwitchSubscriptionToGroup(group);
}
else {
// Failed to join the group.
}
}
// When the student returns to the hall, change the target for audio stream ingest and pulling to the hall.
void MyEngineEventListerner::OnAudioGroupHallMembers(ding::rtc::RtcEngineAudioGroupMember *hallMembers,
int hallMemberCount)
{
bool iAmInHall = false;
for(int i = 0; i < memberCount; i++) {
if (members[i].usrId is my user ID) {
iAmInHall = true;
break;
}
}
// If in the hall.
if (iAmInHall) {
// Ingest audio stream to the hall.
engine.MixAudioToGroup(true, ding::rtc::RtcEngine::HallID);
// Pull audio stream from the hall.
engine.SwitchSubscriptionToGroup(ding::rtc::RtcEngine::HallID);
}
}