简介
通过本系列教程, 您将从零开始学习如何一步步使用声网音视频SDK完成 “语音房-RTC” 的能力提供.
本篇教程讲解2部分 :
1) 声网SDK初始化配置;
2) 如何加入一个声网频道;
1] 声网SDK初始化配置
我们可以设计一个单例类, 用于持有一个 RtcEngine 单例对象, 不需要每次使用RtcEngine能力时, 再去创建对象.
并且在创建RtcEngine对象之前, 要配置好声网log config, 这个很重要, 也是后续声网同学协助定位问题个依据.
void init(String agoraRtcAppKey, Handler handler) throws Exception {
this.agoraRtcAppKey = agoraRtcAppKey;
this.handler = handler;
try {
// 开始时间戳(当前任务)
long startTimestamp = SystemClock.elapsedRealtime();
// 设置初始化方法,没有设置logconfig 默认log地址:
// /storage/emulated/0/Android/data/<package name>/files/agorasdk.log
// TODO : 目前 Agora RTC Native SDK 只支持每个 app 创建一个 RtcEngine 实例。
/**
* context : 安卓活动 (Android Activity) 的上下文。
* appId : Agora 为 app 开发者签发的 App ID,详见获取 App ID。
* 使用同一个 App ID 的 app 才能进入同一个频道进行通话或直播。
* 一个 App ID 只能用于创建一个 RtcEngine。如需更换 App ID,
* 必须先调用 destroy 销毁当前 RtcEngine,并在 destroy 成功返回后,
* 再调用 create 重新创建 RtcEngine。
* handler : io.agora.rtc.IRtcEngineEventHandler 是一个提供了缺省实现的抽象类,
* SDK 通过该抽象类向 app 报告 SDK 运行时的各种事件
*/
// Java
RtcEngineConfig.LogConfig logConfig = new RtcEngineConfig.LogConfig();
// TODO : 不要修改声网日志路径和名字, 否则声网无法在用户在频道期间在线拉取了...
logConfig.level = Constants.LogLevel.getValue(Constants.LogLevel.LOG_LEVEL_INFO);
logConfig.fileSize = 1024 * 4;// TODO : 单位 : K
RtcEngineConfig config = new RtcEngineConfig();
config.mAppId = agoraRtcAppKey;
config.mEventHandler = new MyRtcEngineEventHandler(vcrSdk, handler);
config.mContext = context.getApplicationContext();
config.mLogConfig = logConfig;
rtcEngine = RtcEngine.create(config);
if (DebugLog.logIsOpen) {
DebugLog.e(AppLaunchInitManage.TAG, "RtcEngine.create() 耗时 = " + (SystemClock.elapsedRealtime() - startTimestamp) + ")");
DebugLog.e(vcrSdk.TAG_AGORA, "RtcEngine.create() --> " + (rtcEngine == null ? "创建引擎对象失败!!!" : ("创建引擎对象成功, 当前SDK版本号 = " + RtcEngine.getSdkVersion())));
}
if (rtcEngine == null) {
throw new RuntimeException("RtcEngine.create()返回null.");
}
} catch (Exception e) {
throw new RuntimeException("初始化声网SDK失败, 原因 = " + e.getLocalizedMessage());
}
}
2] 如何加入一个声网频道
2.1] 注意 : 声网不能同时加入 2 个频道, 所以在加入新的频道之前, 建议先调用一下 leaveChannel() 方法.
rtcEngine.leaveChannel();
2.2] 设置声网引擎相关参数
1) 设置频道场景。
/**
* 设置频道场景。
*
* SDK 初始化后默认的频道场景为通信场景,你可以调用该方法设置 Agora 频道的使用场景。
* Agora SDK 会针对不同的使用场景采用不同的优化策略,如通信场景偏好流畅,直播场景偏好画质。
*
* 注解
* 为保证实时音视频质量,我们建议相同频道内的用户必须使用同一种频道场景。
* 该方法必须在加入频道前调用,进入频道后无法再设置频道模式。
* 不同的频道场景下,SDK 的默认音频路由和默认视频编码码率是不同的,
* 详见 setDefaultAudioRoutetoSpeakerphone 和 setVideoEncoderConfiguration 中的说明。
* 参数
* profile 频道使用场景:
* CHANNEL_PROFILE_COMMUNICATION(0):通信场景。该场景下,频道内所有用户都可以发布和接收音、视频流。适用于语音通话、视频群聊等应用场景。
* CHANNEL_PROFILE_LIVE_BROADCASTING(1):直播场景。该场景有主播和观众两种用户角色,可以通过 setClientRole 设置。主播可以发布和接收音视频流,观众直接接收流。适用于语聊房、视频直播、互动大班课等应用场景。
* CHANNEL_PROFILE_GAME(2):Agora 不推荐使用。setParameters("{\"che.audio.opensl\":false }");
* 返回
* 0(ERR_OK): 方法调用成功。
* < 0: 方法调用失败。
* -2(ERR_INVALID_ARGUMENT): 参数无效。
* -7(ERR_NOT_INITIALIZED): SDK 尚未初始化。
*/
resultCode = rtcEngine.setChannelProfile(Constants.CHANNEL_PROFILE_LIVE_BROADCASTING);
DebugLog.e(vcrSdk.TAG_AGORA, "rtcEngine.setChannelProfile(CHANNEL_PROFILE_LIVE_BROADCASTING) --> resultCode = " + resultCode);
if (resultCode < 0) {
VCRTrackTools.trackForCallAgoraApiReturnErrorResultCode("setChannelProfile", resultCode);
}
2)设置音频编码配置。
/**
* 设置音频编码配置。
*
* 注解
* 该方法需要在 joinChannel 之前设置好,joinChannel 后设置不生效。
* 通信和直播场景下,音质(码率)会有网络自适应的调整,通过该方法设置的是一个最高码率。
* 在有高音质需求的场景(例如音乐教学场景)中,建议将 profile 设置为 MUSIC_HIGH_QUALITY (4),Scenario 设置为 GAME_STREAMING (3)。
* 参数
* profile 设置采样率,码率,编码模式和声道数,详见 AudioProfile。
* scenario 设置音频应用场景,详见 AudioScenario。不同的音频场景下,设备的音量类型不同。详见如何区分媒体音量和通话音量。
*/
resultCode = rtcEngine.setAudioProfile(Constants.AUDIO_PROFILE_MUSIC_HIGH_QUALITY, Constants.AUDIO_SCENARIO_GAME_STREAMING);
DebugLog.e(vcrSdk.TAG_AGORA, "rtcEngine.setAudioProfile(AUDIO_PROFILE_MUSIC_HIGH_QUALITY, AUDIO_SCENARIO_GAME_STREAMING) --> resultCode = " + resultCode);
if (resultCode < 0) {
VCRTrackTools.trackForCallAgoraApiReturnErrorResultCode("setAudioProfile", resultCode);
}
3)启用用户音量提示。
/**
* 启用用户音量提示。
*
* 该方法允许 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);
DebugLog.e(vcrSdk.TAG_AGORA, "rtcEngine.enableAudioVolumeIndication(2000, 3, false) --> resultCode = " + resultCode);
if (resultCode < 0) {
VCRTrackTools.trackForCallAgoraApiReturnErrorResultCode("enableAudioVolumeIndication", resultCode);
}
重点说明 :
1) 我们APP的业务场景是 “语音房”, 没有视频场景;
2) 声网API被设计成调用之后, 有同步返回的错误代码, 当返回小于0的错误代码时, 意味着调用API失败,
我们一定要检测这些API调用之后的错误代码, 如果出现问题就不要继续执行了.
否则后续流程也会出现问题, 还不好定位问题出现的时机.
2.3] 设置用户角色
/**
* 设置用户角色。
*
* 详情
* 在加入频道前和加入频道后均可调用该方法设置用户角色。
*
* 如果你在加入频道前调用该方法设置用户角色为主播、并且通过 setupLocalVideo 方法设置了本地视频属性,则用户加入频道时会自动开启本地视频预览。
*
* 如果你在加入频道后调用该方法切换用户角色,调用成功后:
* 本地会触发 onClientRoleChanged 回调。
* 远端会触发 onUserJoined 或 onUserOffline(USER_OFFLINE_BECOME_AUDIENCE) 回调。
* 参数
* role
* 用户的角色:
* CLIENT_ROLE_BROADCASTER (1):主播。
* CLIENT_ROLE_AUDIENCE (2):观众。
* 返回值
* 0: 方法调用成功。
* < 0: 方法调用失败。
* -1: 一般性的错误(未明确归类)。
* -2: 参数无效。
* -7: SDK 尚未初始化。
*/
errorCode = rtcEngine.setClientRole(当前用户的用户角色, 由服务器负责返回);
2.4] 加入声网频道
/**
* 加入频道。
*
* 详情
* 该方法让用户加入通话频道,在同一个频道内的用户可以互相通话,多个用户加入同一个频道,可以群聊。 使用不同 App ID 的 app 不能互通。
*
* 成功调用该方法加入频道后会触发以下回调:
* 本地会触发 onJoinChannelSuccess 和 onConnectionStateChanged 回调。
* 通信场景下的用户和直播场景下的主播加入频道后,远端会触发 onUserJoined 回调。
* 在网络状况不理想的情况下,客户端可能会与声网服务器失去连接;SDK 会自动尝试重连,重连成功后,本地会触发 onRejoinChannelSuccess 回调。
*
* 注意:用户成功加入频道后,默认订阅频道内所有其他用户的音频流和视频流,因此产生用量并影响计费。如果想取消订阅,可以通过调用相应的 mute 方法实现。
* 参数
* token
* 在服务端生成的用于鉴权的动态密钥。详见使用 Token 鉴权。
* channelId
* 频道名。该参数标识用户进行实时音视频互动的频道。App ID 一致的前提下,填入相同频道名的用户会进入同一个频道进行音视频互动。该参数为长度在 64 字节以内的字符串。以下为支持的字符集范围(共 89 个字符):
* 26 个小写英文字母 a~z
* 26 个大写英文字母 A~Z
* 10 个数字 0~9
* 空格
* "!"、"#"、"$"、"%"、"&"、"("、")"、"+"、"-"、":"、";"、"<"、"="、"."、">"、"?"、"@"、"["、"]"、"^"、"_"、"{"、"}"、"|"、"~"、","
* optionalInfo
* (非必选项) 预留参数。
* optionalUid
* 用户 ID。该参数用于标识在实时音视频互动频道中的用户。你需要自行设置和管理用户 ID,
* 并确保同一频道内的每个用户 ID 是唯一的。该参数为 32 位无符号整数。
* 建议设置范围:1 到 232-1。如果不指定(即设为 0),SDK 会自动分配一个,并在 onJoinChannelSuccess 回调中返回,
* 应用层必须记住该返回值并维护,SDK 不对该返回值进行维护。
*/
errorCode = rtcEngine.joinChannel(声网token由服务器负责生成, 频道名由服务器负责生成, "", 声网UID由服务器负责生成);
注意 :
1] 在进入一个语音房之后, 我们会先请求一个 "房间详情接口”, 会在这里返回 当前用户加入声网频道的必要信息.
1) 声网角色(主播 or 观众)
2) 该用户的声网token
3) 声网频道名称
4) 该用户的声网UserId
2] 一个设计细节, 我们APP因为是使用声网SDK提供RTC能力, 使用云信SDK提供IM能力, 而共同拼成我们自己的业务场景 “语音房”,
所以我们把 声网频道ID 和 云信聊天室ID 和 我们自己的业务语音房ID设计成一个ID.
2.5] 加入频道失败的异步回调
我们要自己做一个标志位, 用于标识当前正处于 "进房流程中”,
在 声网回调 IRtcEngineEventHandler 中 onError 回调中.
如果遇到如下错误代码时, 就判定进房失败.
if (errorCode == Constants.ERR_NOT_READY //SDK 初始化失败
|| errorCode == Constants.ERR_NOT_INITIALIZED //SDK 尚未初始化,就调用其 API。请确认在调用 API 之前已创建 RtcEngine 对象并完成初始化。
|| errorCode == Constants.ERR_ALREADY_IN_USE //资源已被占用,不能重复使用。
|| errorCode == Constants.ERR_INVALID_APP_ID //不是有效的 APP ID。请更换有效的 APP ID 重新加入频道。
|| errorCode == Constants.ERR_INVALID_CHANNEL_NAME //不是有效的频道名。请更换有效的频道名重新加入频道。
|| errorCode == Constants.ERR_INVALID_TOKEN //生成的 Token 无效。
|| errorCode == Constants.ERR_LOAD_MEDIA_ENGINE //加载媒体引擎失败。
|| errorCode == Constants.ERR_START_CALL) {// 启动媒体引擎开始通话失败。请尝试重新进入频道。
注意 : 如果不是 “进房流程” 状态下遇到上述错误代码, 意味着, 可以退出语音房了, 因为出现了不可逆的错误;
2.6] 加入频道成功的异步回调
在 声网回调 IRtcEngineEventHandler 中的 onJoinChannelSuccess 意味着加入频道成功.