进阶集成指引
在集成 1v1 私密房方案过程中,你可以参考本文对 API 的调用时机、逻辑、参数设置等进行调优。
使用已初始化过的 rtmClient
声网 1v1 私密房的 CallAPI
中已经实现了对所需 RTM 服务的封装。如果在集成 1v1 私密房之前,你的项目中已经有 RTM 实例 rtmClient
了,则可以直接使用初始化过的 RTM 实例,然后调用相关功能。
如果你使用自己创建的 rtmClient
实例,则可以自行维持 RTM 的登录状态;你也可以通过我们提供的 CallRtmManager
中的 login
和 logout
方法管理登录和登出。
val rtmConfig = RtmConfig.Builder(appId, userId.toString()).build()
val rtmClient = RtmClient.create(rtmConfig)
rtmClient.login(token, object : ResultCallback<Void?> {
override fun onSuccess(p0: Void?) {
// 登录成功即可初始化 CallRtmManager、CallRtmSignalClient、CallApi
}
override fun onFailure(p0: ErrorInfo?) {
// 登录失败
}
})
切换被叫的发布和订阅时机以节省费用
在收到呼叫后,被叫的发布和订阅时机有两种:
- 收到呼叫后自动发布音视频流和订阅视频流,这是默认行为。
- 收到呼叫后,需要被叫接受后再发布音视频流、订阅视频流。
你可以通过 ICallApiListener
中的可选回调 canJoinRtcOnCalling
来设置发布和订阅时机:
- 如果返回
true
,或不实现该回调方法,则使用默认的推流策略,即收到呼叫就发布音视频流和订阅视频流 - 如果返回
false
,则使用接受呼叫后再发布音视频流、订阅视频流的策略
/**
* 当呼叫时判断是否可以加入Rtc
* @param eventInfo 收到呼叫时的扩展信息
* @return true: 可以加入 false: 不可以加入
*/
fun canJoinRtcOnCalling(eventInfo: Map<String, Any>) : Boolean?
手动开启和关闭音视频流发布
由于 CallAPI
内部会在通话时开启、结束通话时关闭采集音视频,因此如果在结束通话后外部需要手动开启音视频采集,例如当 onCallStateChanged
返回 (state: prepared)
时,可以开启采集。
rtcEngine.enableLocalAudio(true)
rtcEngine.enableLocalVideo(true)
消息中携带自定义数据结构
通过在 PrepareConfig
的 userExtension
属性中设置参数,您可以在发送消息给对端时(例如呼叫/取消呼叫/同意/拒绝等)附加额外的用户扩展信息。对端可以通过回调消息接收到这个userExtension
,以便在处理消息时获取相关的附加信息。
override fun onCallStateChanged(
state: CallStateType,
stateReason: CallStateReason,
eventReason: String,
eventInfo: Map<String, Any>
) {
val userExtension = eventInfo[CallApiImpl.kFromUserExtension] as? Map<String, Any>
...
}
通话异常定位
在双端连接过程中(state
为 calling
/connecting
/connected
时),可以通过 getCallId
方法获取当次通话双端的呼叫 ID。
你还可以通过 CallAPI
内部的日志上报,在声网后台查询到当次通话的各个节点耗时。如果需要使用,可以联系 sales@shengwang.cn 申请开通声网自定义数据上报和分析服务。
监听通话频道的 RTC 回调
为了更好地了解当前通话频道的状态和事件,你还可以监听 RTC 频道的回调。由于 CallAPI
里使用的 joinChannelEx
方式加入 RTC 频道,因此不可以使用 rtcEngine.addHandler
方式,需要通过 rtcEngine.addHandlerEx
并指定对应的频道来添加 delegate
:
// 可以在收到呼叫状态时保存呼叫的频道 ID
override fun onCallStateChanged(
state: CallStateType,
stateReason: CallStateReason,
eventReason: String,
eventInfo: Map<String, Any>
) {
when (state) {
CallStateType.Calling -> {
roomId = eventInfo[kFromRoomId] as? String ?: ""
}
else -> {}
}
}
// 在收到 joinRTCStart 事件后通过频道 ID 来设置监听
override fun onCallEventChanged(event: CallEvent, eventReason: String?) {
when(event) {
CallEvent.JoinRTCStart -> {
rtcEngine.addHandlerEx(
object : IRtcEngineEventHandler() {
override fun onJoinChannelSuccess(
channel: String?,
uid: Int,
elapsed: Int
) {
super.onJoinChannelSuccess(channel, uid, elapsed)
// ...
}
},
RtcConnection(roomId, currentUid.toInt()) // demo 为了方便将本端uid的字符串作为了频道名
)
}
else -> {}
}
}
其中,当前 RTC 通话频道的 ID(roomId
参数)可以通过 onCallStateChanged
为 calling
时从 evenInfo
里解析获取。
需要保证加入时已经触发过了 joinRTCStart
事件,在此事件之前调用 rtcEngine.addHandlerEx
会无效。
更新过期 Token
你可以通过如下方式,更新信令和 RTC 的过期 Token。
信令 Token
-
监听 RTM Token 是否过期。通过添加并监听 CallRtmManager 状态回调 监听
ICallRtmManagerListener
的onTokenPrivilegeWillExpire
。Kotlinval listener = object: ICallRtmManagerListener {
override fun onTokenPrivilegeWillExpire(channelName: String) {
// Token 过期,需要重新获取 Token
}
} -
更新 Token。
Kotlin// 更新 RTC Token
this.api.renewToken(rtcToken)
// 更新 RTM Token
this.rtmManager?.renewToken(rtmToken)
// 为确保 RTM 和 RTC Token 同时有效,建议同时更新这两个 Token
RTC Token
-
监听 RTC Token 是否过期。
-
监听进行通话时过期的 Token。
Kotlinval listener = object: ICallApiListener {
override fun tokenPrivilegeWillExpire() {
// Token过期,需要重新获取 Token
}
} -
监听进行通话之前过期的 Token。
Kotlinval listener = ICallApiListener {
override fun onCallError(errorEvent: CallErrorEvent,
errorType: CallErrorCodeType,
errorCode: Int,
message: String?) {
if (errorEvent == CallErrorEvent.RtcOccurError && errorType == CallErrorCodeType.Rtc && errorCode == ErrorCode.TokenExpired.value {
// RTC 加入频道失败,需要取消呼叫,并重新获取 Token
this.api.cancelCall { err -> }
}
}
}
-
-
更新 Token。
Kotlin// 更新 RTC Token
this.api.renewToken(rtcToken)
// 更新 RTM Token
this.rtmManager?.renewToken(rtmToken)
// 为确保 RTM 和 RTC Token 同时有效,建议同时更新这两个 Token
建议给 RTC 和 RTM 配置相同的 Token 过期时间,然后通过信令 Token 方式,借助 RTM 来检测过期并同时更新。