简介
通过本篇教程, 您将学习到如何实现 语音房内 麦位上嘉宾说话时的水波纹效果.
1] 加入声网频道前的参数设置 :
在调用 joinChannel 之前, 需要进行如下配置 :
/**
* 启用用户音量提示。
*
* 该方法允许 SDK 定期向 app 报告本地发流用户和瞬时音量最高的远端用户(最多 3 位)的音量相关信息。
* 启用该方法后,只要频道内有发流用户,SDK 会在加入频道后按设置的时间间隔触发 onAudioVolumeIndication 回调。
*
* 参数
* interval 指定音量提示的时间间隔:
* ≤ 0:禁用音量提示功能。
* > 0:返回音量提示的间隔,单位为毫秒。建议设置到大于 200 毫秒。最小不得少于 10 毫秒,否则会收不到 onAudioVolumeIndication 回调。
* smooth 平滑系数,指定音量提示的灵敏度。取值范围为 [0, 10],建议值为 3,数字越大,波动越灵敏;数字越小,波动越平滑。
* report_vad 是否开启人声检测
* true: 开启本地人声检测功能。开启后,onAudioVolumeIndication 回调的 vad 参数会报告是否在本地检测到人声。
* false: (默认)关闭本地人声检测功能。除引擎自动进行本地人声检测的场景外,onAudioVolumeIndication 回调的 vad 参数不会报告是否在本地检测到人声。
* 返回
* 0: 方法调用成功。
* < 0: 方法调用失败。
*
*
* 实测发现enableAudioVolumeIndication interval设置的间隔时间。
* 我们在间隔时间开始内说话。是没有给我们回调的。
* 比如说:设置回调是10s,如果我们在3s的时间节点说一句话,在10s前说话结束,那我们是没有收到onAudioVolumeIndication 回调的。
*/
resultCode = rtcEngine.enableAudioVolumeIndication(500, 3, false);
2] 接收水波纹的声网回调(其实是用户音量回调) :
/**
* 用户音量提示回调
* 该回调默认禁用。可以通过 enableAudioVolumeIndication 方法开启。
* 开启后,只要频道内有发流用户,SDK 会在加入频道后按 enableAudioVolumeIndication 中设置的时间间隔触发 onAudioVolumeIndication 回调。
* 每次会触发两个 onAudioVolumeIndication 回调,一个报告本地发流用户的音量相关信息,另一个报告瞬时音量最高的远端用户(最多 3 位)的音量相关信息。
* <p>
* 注解
* 启用该功能后,如果有用户将自己静音(调用了 muteLocalAudioStream),SDK 行为会受如下影响:
* 1] 本地用户静音后,SDK 立即停止报告本地用户的音量提示回调。
* 2] 瞬时音量最高的远端用户静音后 20 秒,远端的音量提示回调中将不再包含该用户;如果远端所有用户都将自己静音,20 秒后 SDK 停止报告远端用户的音量提示回调。
* <p>
* 参数
*
* @param speakerArray 用户音量信息,详见 AudioVolumeInfo 数组。如果 speakers 为空,则表示远端用户不发流或没有远端用户。
* @param totalVolume 混音后的总音量,取值范围为 [0,255]。
* 1] 在本地用户的回调中,totalVolume 为本地发流用户的音量。
* 2] 在远端用户的回调中,totalVolume 为瞬时音量最高的远端用户(最多 3 位)混音后的总音量。
* 如果用户调用了 startAudioMixing,则 totalVolume 为音乐文件和用户声音的总音量。
*/
@Override
public void onAudioVolumeIndication(final AudioVolumeInfo[] speakerArray, final int totalVolume)
经验积累 :
1] 本端 和 远端 都通过此回调返回;
2] 在用户设置 "静音" 之后, 就不会回调了; 注意 : 不会在点击 "静音" 之后, 给客户端一个回调, 告知客户端本端没声音了.
3] 本端用户只要没设置 "静音", 就会一直根据配置的回调周期, 进行回调;
4] 他端如果没有人说话时, speakerInfos 数组会返回 "空数组", 注意 : 当返回 "空数组" 时, 一定是他端没人说话, 本端不说话时, 也会执行本回调, 并且 speakerInfos 不为空., 注意: 他端没人说话时, 会一直有 speakerInfos 为空的回调返回;
5] 最多支持 返回3个正在说话的嘉宾;
6] 如果使用 [频道转发技术] 来实现跨房PK, 那么对方主播的说话时的水波纹, 也通过此回调来实现;
7] 注意 : 这个回调在子线程中运行
核心代码演示 :
@Override
public void onAudioVolumeIndication(final AudioVolumeInfo[] speakerArray, final int totalVolume) {
handler.post(new Runnable() {
@Override
public void run() {
// 注意 : 要先切换到主线程
// 数组不好操作, 转成 List
final List<AudioVolumeInfo> speakerList = new ArrayList<>();
if (speakerArray != null && speakerArray.length > 0) {
Collections.addAll(speakerList, speakerArray);
}
final List<Integer> speakingSeatIndexList = new ArrayList<>();
final int localSeatIndex = vcrSdk.getSeatIndexForCurrentLoginUser();// 获取当前登录用户的麦位索引(包括 主播位), 如果当前登录用户不在麦位上, 就返回 -1
boolean isLocalSeat = false;
if (speakerList.size() == 1 && speakerList.get(0).uid == 0) {
// "本端”
// getVoiceChatRoomUserRoleType() 获取语音房用户角色(主播 / 嘉宾 / 观众)
if (vcrSdk.getVoiceChatRoomUserRoleType() == GlobalConstant.VoiceChatRoomUserRoleType.AUDIENCE) {
// 本端用户是 "观众", 不在麦上, 直接返回.
return;
}
isLocalSeat = true;
// 本端用户在麦上
if (speakerList.get(0).volume >= 40) {
// 产品需求声音音量小于一个数量值,不用提示水波纹。
// 声网推荐的是这个回调音量80~100,是比较正常的过滤音量
// 80有点大,在操作的办公室。水波纹偶现。
// 最终实测得到的经验值是 40
speakingSeatIndexList.add(localSeatIndex);
}
} else {
// "他端"
isLocalSeat = false;
// 标识 [单人 - 跨房PK] 对方主播 是否正在说话中...
boolean isSingleDifferentRoomPKOtherAnchorSpeaking = false;
for (AudioVolumeInfo speakerInfo : speakerList) {
if (speakerInfo.volume < 40) {
continue;
}
if (vcrSdk.isSingleDifferentRoomPKStarting()) {
// 单人跨房PK正在进行中…
// getSingleDifferentRoomPKOtherAnchorInfo() : 获取 [单人 - 跨房PK] 对方主播信息
if (vcrSdk.getSingleDifferentRoomPKOtherAnchorInfo().getAgoraId() == speakerInfo.uid) {
isSingleDifferentRoomPKOtherAnchorSpeaking = true;
continue;
}
}
// vcrSdk.seatCache 麦位列表缓存(全部麦位, 包括主播麦位, 主播麦位索引定死是 0 )
for (Map.Entry<Integer, VoiceChatRoomSeatInfo> entry : vcrSdk.seatCache.entrySet()) {
final VoiceChatRoomSeatInfo seatInfo = entry.getValue();
if (seatInfo.getSeatStatus() != GlobalConstant.VoiceChatRoomSeatStatus.ON) {
// 麦位上没人
continue;
}
if (seatInfo.getSeatOwner().getAgoraId() == speakerInfo.uid) {
speakingSeatIndexList.add(seatInfo.getSeatIndex());
// 找到说话人所在的麦位索引
break;
}
}
}
if (vcrSdk.isSingleDifferentRoomPKStarting()) {
// 单人跨房PK正在进行中…
try {
/**
* [单人-跨房PK] 对方主播是否正在在说话
*
* @param isSpeaking true : 在说话, false : 没有说话
*/
vcrSdk.voiceChatRoomSdkCallback.onSingleDifferentRoomPKOtherAnchorSpeaking(isSingleDifferentRoomPKOtherAnchorSpeaking);
} catch (Exception e) {
e.printStackTrace();
}
}
}
try {
/**
* 有麦主(包括 主播 和 嘉宾)说话时的回调, "本端" 和 "他端" 都通过此回调通知业务层
*
* @param speakingSeatIndexList 正在说话的麦位索引列表
* 如果 isLocalSeat 等于 true, 如果 "本端" 用户在说话, speakingSeatIndexList中就包含了 "本端" 用户所在麦位索引, 如果 "本端" 用户没在说话, speakingSeatIndexList 为空.
* 如果 isLocalSeat 等于 false, speakingSeatIndexList 里面存放着, 他端正在说话的麦位索引
* @param localSeatIndex 本地登录用户所在麦位的索引, 只有本地登录用户在麦时, 这个参数才有效果, 否则返回 -1
* @param isLocalSeat 本次数据变化, 属于 "本端" 还是 "他端" , true : 本端, false : 他端
*/
vcrSdk.voiceChatRoomSdkCallback.onSeatOwnerSpeaking(speakingSeatIndexList, localSeatIndex, isLocalSeat);
} catch (Exception e) {
e.printStackTrace();
}
}
}