文章目录
信令系统,作为音视频聊天中最必不可少的系统,承担着房间管理,用户管理,状态管理,聊天前用户间信息交互等重要功能。没有信令系统,音视频聊天将难以开展。
一般流程
信令系统的作用:
- 管理房间及用户:包括申请及记录当前房间,房间当前状态,用户当前状态(忙时,闲时,不在线等)等信息。
- 对房间内从发起聊天到聊天结束整个过程的指令进行各个用户的转发和通知。
- 保存用户的媒体属性,连接信息等相关SDP数据。
在Webrtc中,网络穿透在调用生成本地媒体约束SDP的时候,或者是接收到邀请方的SDP数据后,对远程SDP数据进行设置,并调用本地本地媒体约束SDP的时候,触发网络穿透的操作。网络穿透成功后,会多次回调不同类型的网络信息,包括若干个内网及外网信息。在正常情况下,应该经过多次的信令系统的调用,及时传递到对方聊天用户进行设置。
同时,当手机网络不稳定,或经过网络切换的情况下,网络穿透也会自动进行处理和回调。保证了在短暂的断开连接状态下,不用再次重新建立新的启动流程。只要相互交互一次Candidate
数据后就可以正常继续使用。这个是经过实践验证的可行流程。
信令的发送和接收
在APP音视频系统架构中, 信令系统的逻辑处理主要由SIPLogicManager来完成,主要负责包括心跳处理,指令发送及信令回调后的逻辑处理的任务。
Cable Messenger中抽象出了信息系统所有情况下的回调规范:
public protocol CUSIPEventListener{
///审请加入房间回复
func onJoinded(chatId:String, roomId:String, users:[CUSipUserInfo], myId:String)
///审请者主动结束
func onCancel(roomId:String)
///收到邀请聊天
func onInvite(roomId:String, users:[CUSipUserInfo], fUid:String, audioOnly:Bool)
///对方忙线
func onBusy(roomId:String, fUid:String)
///对方拒绝
func onReject(roomId:String, fUid:String)
///有新人加入聊天(你的当前状态已经进入聊天室)
func onNewJoin(roomId:String, user:CUSipUserInfo)
///有新人加入聊天(你的当前状态还没进入聊天室)
func onNewMember(roomId:String, user:CUSipUserInfo)
///接收到对方的sdp信息
func onOffer(roomId:String, sdp:String, fUid:String)
///接收到对方回复的sdp信息
func onReceiveCandidate(roomId:String, sdpMid:String, sdpMLineIndex:Int32, candidate:String, fUid:String)
///接收answer
func onAnswer(roomId:String, sdp:String, fUid:String)
///视频状态发生改变
func onVideoStateChange(roomId:String, fUid:String, enable:Bool)
///聊天室成员离开
func onLeave(roomId:String, fUid:String)
///聊天结束
func onFinish(roomId:String)
///同一帐号的其它设置已经进行会话
func onPickup(roomId:String)
}
对于指令的发送,我还是比较推荐以HTTP的方式进行发送。对于指令系统来说,技术稳定可控,实现相对简单对于系统来说是致关重要的。以下为部分指令发送代码,仅供参考:
//MARK: - 用户ID登录
public func idLogin(uid:String, password:String, handler:@escaping (PHResponse<CUSipUserInfo>) ->Void){
var params:[String:Any] = [:]
params["uid"] = uid
params["password"] = password
self.requestPost(requestUrl: Constant.BASE_URL + ID_LOGIN, params: params) {
(result:PHResponse<Any>) in
if result.isSuccess == true, let dictData = result.data as? [String:Any]{
do{
let data:[String:Any] = try JSONTranslater.toDictionary(any: dictData["data"])
if let info:CUSipUserInfo = CUSipUserInfo.parseToObject(data){
handler(PHResponse<CUSipUserInfo>(data:info))
}else{
handler(PHResponse<CUSipUserInfo>(data:nil, result:false, msg:self.launchTranslateError()))
}
}catch{
handler(PHResponse<CUSipUserInfo>(data:nil, result:false, msg:self.launchTranslateError()))
}
}else {
handler(PHResponse<CUSipUserInfo>(msg:result.msg))
}
}
}
//MARK: - 退出登录
public func logout(handler:@escaping (PHResponse<Bool>) ->Void){
let params:[String:Any] = [:]
self.requestPost(requestUrl: Constant.BASE_URL + LOGOUT, params:params) {
(result:PHResponse<Any>) in
if result.isSuccess == true{
handler(PHResponse<Bool>(data:true))
}else {
handler(PHResponse<Bool>(msg:result.msg))
}
}
}
//MARK: - 基础方法
public func getRoomInfo(roomId:String, handler:@escaping (PHResponse<CURoomInfo>) ->Void){
var params:[String:Any] = [:]
params["room_id"] = roomId
self.requestPost(requestUrl: Constant.BASE_URL + GET_ROOM_INFO, params: params) {
(result:PHResponse<Any>) in
if result.isSuccess == true, let dictData = result.data as? [String:Any]{
do{
let data:[String:Any] = try JSONTranslater.toDictionary(any: dictData["data"])
let roomInfo:CURoomInfo = CURoomInfo.parseToObject(data)
handler(PHResponse<CURoomInfo>(data:roomInfo))
}catch{
handler(PHResponse<CURoomInfo>(data:nil, result:false, msg:self.launchTranslateError()))
}
}else {
handler(PHResponse<CURoomInfo>(msg:result.msg))
}
}
}
//MARK: - 发起者创建并进入房间
public func start(chatId:String, deviceId:String, handler:@escaping (PHResponse<[String:Any]>) ->Void){
var params:[String:Any] = [:]
if chatId.count > 0{
params["chat_id"] = chatId
}
params["device_id"] = deviceId
self.postRtcCommon(eventName:"__start", params:params) {
(response:PHResponse<[String : Any]>) in
handler(response)
}
}
//MARK: - 被邀请者进入房间
public func join(roomId:String, deviceId:String, handler:@escaping (PHResponse<[String:Any]>) ->Void){
var params:[String:Any] = [:]
params["room_id"] = roomId
params["device_id"] = deviceId
self.postRtcCommon(eventName:"__join", params:params) {
(response:PHResponse<[String : Any]>) in
handler(response)
}
}
//MARK: - 发起进入房间
public func invite(roomId:String, ids:[String], audioOnly:Int, handler:@escaping (PHResponse<[String