uniapp 跟app 的流程图

以下是具体app实现videoCall的具体
wss的open、之后create session,然后在create handlerid 这个流程基本上可以说是所有的janus的插件必备的流程
进行register信令注册,这个注册无论是主叫还是被叫都是需要的
["handle_id": 3508556780812699, "body": ["pin": 000, "request": register, "muted": 1, "username": "ios999", "display": 12, "room": 599224, "token": 12], "token": "xxxxxxxxxx", "session_id": 3189233037691542, "janus": "message", "transaction": "i12-register-461789"]
注册成功会收到一个event
["plugindata": { data = { result = { event = registered; username = ios999; }; videocall = event; }; plugin = "janus.plugin.videocall"; }, "session_id": 3189233037691542, "sender": 3508556780812699, "transaction": i12-register-461789, "janus": event]
此时就可以进行主叫了,但是需要一些对peer的前置处理
public var rtcFactory:RTCPeerConnectionFactory = { let oo = RTCPeerConnectionFactoryOptions() // RTCPeerConnectionFactory(encoderFactory: nil, decoderFactory: nil, audioDevice: (any RTCAudioDevice)?) // RTCPeerConnectionFactory(encoderFactory: nil, decoderFactory: nil, audioDevice: CustomAudioDevice()) // 有用到video的话,解码跟编码不能为空 let encoderFactory = RTCDefaultVideoEncoderFactory() let decoderFactory = RTCDefaultVideoDecoderFactory() let aaa = RTCPeerConnectionFactory(encoderFactory: encoderFactory, decoderFactory: decoderFactory) return aaa }()/// 对音频处理 func initAudio(){ let audioSource = rtcFactory.audioSource(with: RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)) localAudioTrack = rtcFactory.audioTrack(with: audioSource, trackId: kARDAudioTrackId) let rtpSender = peer?.sender(withKind: kRTCMediaStreamTrackKindAudio, streamId: kARDAudioTrackId) rtpSender?.track = localAudioTrack } /// 对视频处理 override func initVideo() { let videoSource = rtcFactory.videoSource() capturer = RTCCameraVideoCapturer(delegate: videoSource) localVideoTrack = rtcFactory.videoTrack(with: videoSource, trackId: kARDVideoTrackId) let rtpVideoSender = peer?.sender(withKind: kRTCMediaStreamTrackKindVideo, streamId: kARDVideoTrackId) rtpVideoSender?.track = localVideoTrack } /// 开启摄像头 func startCapture(){ guard let capturer = capturer else { return } guard let device = RTCCameraVideoCapturer.captureDevices().first(where: { $0.position == .front }) ?? RTCCameraVideoCapturer.captureDevices().first else { return } let formats = RTCCameraVideoCapturer.supportedFormats(for: device) let targetFormat = formats.sorted { f1, f2 in CMVideoFormatDescriptionGetDimensions(f1.formatDescription).width < CMVideoFormatDescriptionGetDimensions(f2.formatDescription).width }.last let fps = (targetFormat?.videoSupportedFrameRateRanges.first?.maxFrameRate ?? 30) capturer.startCapture(with: device, format: targetFormat!, fps: Int(fps)) { err in if let err = err { print("摄像头启动失败: \(err)") } } }
对audio跟video初始化后,在开启摄像头,然后就可以peer.offer,此时是属于主叫,所以要 peer?.setLocalDescription
override func doOffer() { let aa:[String:String] = ["OfferToReceiveAudio":"true","OfferToReceiveVideo":"true"] let mediaConstraints = RTCMediaConstraints.init(mandatoryConstraints: aa, optionalConstraints: nil) peer?.offer(for: mediaConstraints, completionHandler: { [weak self] sdp, err in guard let this = self else { return } if err != nil { print("peer.offer出问题 \(err!)") }else{ print("peer.offer正常 (sdp!)") // 主叫跟被叫的流程是不同的 if BasePlugin.video().isCall{ this.setLocal(sdp!) }else{ this.localSdp = sdp this.setLocalCb(err) } } }) }
setLocal成功了,就可以发送call信令
sigState = .call let key = sigState.rawValue let jsep:Json = ["type":"offer","sdp":objPeer!.localSdp!.sdp] let msg:Json = ["request":key,"username":calledName!] var pp:[String:Any] = ["janus":"message","body":msg,"jsep":jsep,"transaction":key] objWss.send(msg: &pp)
A 发送call信令后,此时B会收到incoming的时间,这个时候B要去发送accept信令
{ "videocall" : "event", "result" : { "event" : "incomingcall", "username" : "<your username>" } }发送accept信令,注意,是B去发送 sigState = .call let key = sigState.rawValue let jsep:Json = ["type":"offer","sdp":objPeer!.localSdp!.sdp] let msg:Json = ["request":key,"username":calledName!] var pp:[String:Any] = ["janus":"message","body":msg,"jsep":jsep,"transaction":key] objWss.send(msg: &pp)
之后A收到一个accepted的event,然后执行
@objc func setRemote(_ sdp: RTCSessionDescription) { self.peer?.setRemoteDescription(sdp, completionHandler: { err in print("peer.setRemote出问题 \(String(describing: err))") self.remoteSdp = sdp self.setRemoteCb(err) }) } override func setRemoteCb(_ err: (any Error)?) { for vvv in remoteCands { peer?.add(vvv) { error in let msg = error == nil ? "nil" : error?.localizedDescription print("\\(vvv)\n 添加对端ICE--error \(msg!)") } } if !BasePlugin.video().isCall { doAnswer(remoteSdp!) } }
IPeerObj父类
open class IPeerObj:NSObject{
let OPTION = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: ["DtlsSrtpKeyAgreement":"true"])
var errState:RoomError = .none
var peer:RTCPeerConnection?
var localSdp:RTCSessionDescription?
var remoteSdp:RTCSessionDescription?
var localAudioTrack:RTCAudioTrack?
var remoteAudioTrack:RTCAudioTrack?
var remoteCands:[RTCIceCandidate] = []
var subPeerDelegate:RTCPeerConnectionDelegate?
init(peer: RTCPeerConnection? = nil, localSdp: RTCSessionDescription? = nil, remoteSdp: RTCSessionDescription? = nil, localAudioTrack: RTCAudioTrack? = nil, remoteAudioTrack: RTCAudioTrack? = nil, remoteCands: [RTCIceCandidate] = [], subPeerDelegate: RTCPeerConnectionDelegate? = nil) {
self.peer = peer
self.localSdp = localSdp
self.remoteSdp = remoteSdp
self.localAudioTrack = localAudioTrack
self.remoteAudioTrack = remoteAudioTrack
self.remoteCands = remoteCands
self.subPeerDelegate = subPeerDelegate
}
func initPeer(conObj:ConnectObj){
let config = IPeerObj.newRtcConfig(conObj)
self.peer = rtcFactory.peerConnection(with: config, constraints: OPTION, delegate: self)!
print("initpeer \(String(describing: self.peer))")
initAudio()
initVideo()
}
private static func newRtcConfig(_ obj:ConnectObj)->RTCConfiguration {
let config = RTCConfiguration()
config.sdpSemantics = .planB
// config.iceTransportPolicy = .relay
config.offerExtmapAllowMixed = true
config.tcpCandidatePolicy = .disabled
config.maxIPv6Networks = 0
config.iceCandidatePoolSize = 0
config.iceTransportPolicy = .all
let ice1 = RTCIceServer(urlStrings: [obj.stunUrl])
let ice2 = RTCIceServer(urlStrings: [obj.turnUrl], username: obj.turnUsername, credential: obj.turnPassword)
config.iceServers = [ice2,ice1]
//无论这个是那种,都会自动发candidate过去
config.continualGatheringPolicy = .gatherOnce
return config
}
open func initVideo(){
}
/// 对音频处理
func initAudio(){
let audioSource = rtcFactory.audioSource(with: RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil))
localAudioTrack = rtcFactory.audioTrack(with: audioSource, trackId: kARDAudioTrackId)
let rtpSender = peer?.sender(withKind: kRTCMediaStreamTrackKindAudio, streamId: kARDAudioTrackId)
rtpSender?.track = localAudioTrack
}
func resetAll(){
errState = .none
//将所有的都重置,就是不能peer.close()
remoteAudioTrack?.isEnabled = false
localAudioTrack?.isEnabled = false
remoteAudioTrack = nil
localAudioTrack = nil
peer?.delegate = nil
peer?.senders.forEach({ send in
send.track?.isEnabled = false
peer?.removeTrack(send)
})
peer?.remove(remoteCands)
}
func resetPeer(){
}
}
extension IPeerObj:OfferAnser {
@objc func setLocalCb(_ err: Error?) { }
@objc func setRemoteCb(_ err: Error?) {
// if err != nil { return }
// for vvv in remoteCands {
// peer?.add(vvv) { error in
// let msg = err == nil ? "nil" : err?.localizedDescription
// print("\\(vvv)\n 添加对端ICEd \(msg!)")
// }
// }
}
@objc func doOffer() { }
@objc func doAnswer(_ jsep: RTCSessionDescription) { }
@objc func setLocal(_ sdp: RTCSessionDescription) {
peer?.setLocalDescription(sdp) { err in
print("peer.setLocal出问题 \(String(describing: err))")
self.localSdp = sdp
self.setLocalCb(err)
}
}
@objc func setRemote(_ sdp: RTCSessionDescription) {
self.peer?.setRemoteDescription(sdp, completionHandler: { err in
print("peer.setRemote出问题 \(String(describing: err))")
self.remoteSdp = sdp
self.setRemoteCb(err)
})
}
}
extension IPeerObj:RTCPeerConnectionDelegate {
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {
// cbAudioImp?.peerConnection(peerConnection, didChange: stateChanged)
subPeerDelegate?.peerConnection(peerConnection, didChange: stateChanged)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {
// cbAudioImp?.peerConnection(peerConnection, didAdd: stream)
subPeerDelegate?.peerConnection(peerConnection, didAdd: stream)
if let audioTrack = stream.audioTracks.first {
remoteAudioTrack = audioTrack // 保存远程音频轨道
}
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd rtpReceiver: RTCRtpReceiver, streams mediaStreams: [RTCMediaStream]) {
subPeerDelegate?.peerConnection?(peerConnection, didAdd: rtpReceiver, streams: mediaStreams)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) {
// cbAudioImp?.peerConnection(peerConnection, didRemove: stream)
subPeerDelegate?.peerConnection(peerConnection, didRemove: stream)
}
public func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) {
print("peerConnectionShouldNegotiate")
// cbAudioImp?.peerConnectionShouldNegotiate(peerConnection)
subPeerDelegate?.peerConnectionShouldNegotiate(peerConnection)
// doPeerOffer()
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
let objs = ["NEW","CHECKING", "CONNECTED", "COMPLETED", "FAILED", "DISCONNECTED", "CLOSED","COUNT"]
print("cb ==== didChange RTCIceConnectionState \(objs[newState.rawValue])")
// cbAudioImp?.peerConnection(peerConnection, didChange: newState)
subPeerDelegate?.peerConnection(peerConnection, didChange: newState)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) {
let str = ["RTCIceGatheringStateNew","RTCIceGatheringStateGathering","RTCIceGatheringStateComplete",]
print("RTCIceGatheringState = \(str[newState.rawValue])")
subPeerDelegate?.peerConnection(peerConnection, didChange: newState)
// cbAudioImp?.peerConnection(peerConnection, didChange: newState)
if newState == .complete {
// objPeerImp?.signalTrickleCompled()
}
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
subPeerDelegate?.peerConnection(peerConnection, didGenerate: candidate)
// cbAudioImp?.peerConnection(peerConnection, didGenerate: candidate)
// objPeerImp?.signalTrickle(candidate: candidate)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {
subPeerDelegate?.peerConnection(peerConnection, didRemove: candidates)
// cbAudioImp?.peerConnection(peerConnection, didRemove: candidates)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {
subPeerDelegate?.peerConnection(peerConnection, didOpen: dataChannel)
// cbAudioImp?.peerConnection(peerConnection, didOpen: dataChannel)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCPeerConnectionState) {
let objs = ["RTCPeerConnectionStateNew",
"RTCPeerConnectionStateConnecting",
"RTCPeerConnectionStateConnected",
"RTCPeerConnectionStateDisconnected",
"RTCPeerConnectionStateFailed",
"RTCPeerConnectionStateClosed"]
print("-------RTCPeerConnectionState--> \(objs[newState.rawValue])")
if newState == .failed && networkStatus == .notReachable {
//有些时候peer fail可能不是网络原因
WssObject.share().state = .disconnect
}
subPeerDelegate?.peerConnection?(peerConnection, didChange: newState)
// objPeerImp?.peerStateChange(state: newState)
}
}
VideoCallPeerObj 子类
class VideoCallPeerObj:IPeerObj {
var capturer: RTCCameraVideoCapturer?
var localVideoTrack: RTCVideoTrack?
var remoteVideoTrack: RTCVideoTrack?
public var delegateUI:CallViewVcStatusDelegate?
/// 开启摄像头
func startCapture(){
guard let capturer = capturer else { return }
guard let device = RTCCameraVideoCapturer.captureDevices().first(where: { $0.position == .front }) ?? RTCCameraVideoCapturer.captureDevices().first else { return }
let formats = RTCCameraVideoCapturer.supportedFormats(for: device)
let targetFormat = formats.sorted { f1, f2 in
CMVideoFormatDescriptionGetDimensions(f1.formatDescription).width < CMVideoFormatDescriptionGetDimensions(f2.formatDescription).width
}.last
let fps = (targetFormat?.videoSupportedFrameRateRanges.first?.maxFrameRate ?? 30)
capturer.startCapture(with: device, format: targetFormat!, fps: Int(fps)) { err in
if let err = err { print("摄像头启动失败: \(err)") }
}
}
/// 对视频处理
override func initVideo() {
let videoSource = rtcFactory.videoSource()
capturer = RTCCameraVideoCapturer(delegate: videoSource)
localVideoTrack = rtcFactory.videoTrack(with: videoSource, trackId: kARDVideoTrackId)
let rtpVideoSender = peer?.sender(withKind: kRTCMediaStreamTrackKindVideo, streamId: kARDVideoTrackId)
rtpVideoSender?.track = localVideoTrack
}
override func setRemoteCb(_ err: (any Error)?) {
for vvv in remoteCands {
peer?.add(vvv) { error in
let msg = error == nil ? "nil" : error?.localizedDescription
print("\\(vvv)\n 添加对端ICE--error \(msg!)")
}
}
if !BasePlugin.video().isCall {
doAnswer(remoteSdp!)
}
}
override func doAnswer(_ sdp: RTCSessionDescription) {
let aa:[String:String] = ["OfferToReceiveAudio":"true","OfferToReceiveVideo":"true"]
let mediaConstraints = RTCMediaConstraints.init(mandatoryConstraints: aa, optionalConstraints: nil)
peer?.answer(for: mediaConstraints, completionHandler: { s, err in
self.localSdp = s
self.setLocal(self.localSdp!)
})
}
override func doOffer() {
let aa:[String:String] = ["OfferToReceiveAudio":"true","OfferToReceiveVideo":"true"]
let mediaConstraints = RTCMediaConstraints.init(mandatoryConstraints: aa, optionalConstraints: nil)
peer?.offer(for: mediaConstraints, completionHandler: { [weak self] sdp, err in
guard let this = self else { return }
if err != nil {
print("peer.offer出问题 \(err!)")
}else{
print("peer.offer正常 (sdp!)")
// 主叫跟被叫的流程是不同的
if BasePlugin.video().isCall{
this.setLocal(sdp!)
}else{
this.localSdp = sdp
this.setLocalCb(err)
}
}
})
}
override func setLocalCb(_ err: (any Error)?) {
let aaa = err == nil ?
(true,"peer.setLocal正常") :
(false,"peer.setLocal出问题 \(err!)")
if aaa.0 {
let videoP = BasePlugin.video()
if videoP.isCall {
if videoP.isSet {
videoP.set()
}else{
videoP.call()
}
}else{
videoP.accpet()
}
}
}
}BasePlugin父类
open class BasePlugin:NSObject{
lazy var objWss = WssObject.share()
var objPeer:IPeerObj?
var sigState:SignalState = .none
var remoteCands:[RTCIceCandidate] = []
var pluginTag:String {
return ""
}
private static var objBase:BasePlugin!
public static func reSetAllObj(){
WssObject.share().resetAll()
if objBase != nil {
objBase.objPeer?.peer?.delegate = nil
objBase.objPeer = nil
objBase = nil
}
}
public static func shared()->BasePlugin {
return objBase
}
public static func audio()->AudioPlugin {
if objBase == nil {
objBase = factory(0)
}
return objBase as! AudioPlugin
}
public static func video()->VideoCallPlugin {
if objBase == nil {
objBase = factory(1)
}else{
}
return objBase as! VideoCallPlugin
}
public static func factory(_ plugin:Int)->BasePlugin {
if plugin == 0 {
objBase = AudioPlugin()
}else if plugin == 1 {
objBase = VideoCallPlugin()
}
return objBase
}
func saveRemoteCand(sdp:String,_ sdpMLineIndex:Int32,_ sdpMid:String){
let split = sdp.split(separator: " ")
if split.count > 2 {
// 这里实际要考虑video的情况 不知道为啥是0
let iceCan = RTCIceCandidate(sdp: sdp, sdpMLineIndex: sdpMLineIndex, sdpMid: sdpMid)
remoteCands.append(iceCan)
objPeer?.remoteCands.append(iceCan)
}else{
print("saveRemoteCand sdp -> \(sdp) 有问题")
}
}
func createSid() {
sigState = .create
let key = SignalState.create.rawValue
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone.current
dateFormatter.dateFormat = "mmss"
var iii = Int64("3\(personObj.phoneNum)\(dateFormatter.string(from: Date()))")!
if objWss.conObj.sessionId != nil {
iii = objWss.conObj.sessionId!
}
var pp:Json = ["janus":key,"transaction":key,"id":iii]
objWss.send(msg: &pp)
}
func createHid(sid:Int64?) {
objWss.conObj.sessionId = sid
sigState = .attach
let key = sigState.rawValue
var pp:Json = ["transaction":key, "janus":key,"plugin":pluginTag]
// conObj.handerId = nil
objWss.send(msg: &pp)
}
func claim() {
sigState = .claim
var pp: [String : Any] = ["janus":sigState.rawValue,"transaction":SignalState.claim.rawValue]
objWss.send(msg: &pp)
}
func sendCompledTrick() {
var dict:Json = ["janus":"trickle","candidate":["completed":1],"transaction":"trickcompled"]
objWss.send(msg: &dict)
}
func sendTrick(_ dict:Json){
var dict:Json = ["janus":"trickle","candidate":dict,"transaction":"trick"]
objWss.send(msg: &dict)
}
// MARK: 具体需要子类去实现
/// 创建handleid成功
open func createHidSuccess(){ }
/// claim换绑成功
open func claimSuccess(){ }
open func waitNewPeer(err:RoomError,_ reason:String = "",_ event:Json = [:]){ }
open func connectStateCb(type:ConnectStateType, _ add :[String:Any] = [:]){}
/// 收到hangup时间
open func receiveHangUp(_ event:Json){
print("hangup = \(event["reason"] as! String)")
}
open func receiveMedia(_ event:Json){
print("receiveMedia")
}
open func receiveEvent(_ event:Json){
var p_data:Json!
var data:Json!
if let plugindata = event["plugindata"] as? Json,
let obj = plugindata["data"] as? Json{
p_data = plugindata
data = obj
}
if p_data == nil { return }
let audiobridge = data["audiobridge"] as? String
let code = data["error_code"] as? Int
let reason = data["error"] as? String
if let a = code,let b = reason {
//丢给子类去处理以及处理公共的
errorEvent(a, b, event: event)
}
}
open func receiveWebrtcUpEvent(_ event:Json){}
/// 这个才是外层的error 只处理公共的
open func receiveErrorEvent(_ event:Json){
guard let err = event["error"] as? Json else {
print("收到janus的错误,转换出问题")
return
}
let code = RoomError(rawValue: err["code"] as! Int) ?? .none
let reason = err["reason"] as! String
if code == .unHandleRequest {
waitNewPeer(err: .unHandleRequest,reason,event)
}else if code == .noSession {
waitNewPeer(err: .noSession,reason,event)
}else if code == .localIceErr {
waitNewPeer(err: .localIceErr,reason,event)
}else if code == .sessionUse {
claim()
}
}
/// 这个是event的错误,并不是最外层wss的Error的错误
open func errorEvent(_ code:Int,_ reason:String, event:Json = [:]){
let errState = RoomError(rawValue: code) ?? .none
if errState == RoomError.unknownRequest {
print("RoomError.unknownRequest",level: .error)
}
}
}
extension BasePlugin:RTCPeerConnectionDelegate {
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCPeerConnectionState) {
print("cb ==== didChange RTCPeerConnectionState \(newState)")
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd rtpReceiver: RTCRtpReceiver, streams mediaStreams: [RTCMediaStream]) {
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) {
}
public func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) {
objPeer?.doOffer()
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
let objs = ["NEW","CHECKING", "CONNECTED", "COMPLETED", "FAILED", "DISCONNECTED", "CLOSED","COUNT"]
print("cb ==== didChange RTCIceConnectionState \(objs[newState.rawValue])")
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) {
if newState == .complete {
sendCompledTrick()
}
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
let cand = ["candidate":candidate.sdp,"sdpMid":candidate.sdpMid!,"sdpMLineIndex":candidate.sdpMLineIndex] as Json
sendTrick(cand)
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {
}
public func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {
}
}VideoCallPlugin 子类
public class VideoCallPlugin:BasePlugin {
var connectJson:Json!
var callId:String?
var callName:String?
var calledName:String?
/// 默认是拨打
var isCall = true
var isSet = false
var delegateUI:CallViewVcStatusDelegate?
var updateTextUI = PublishSubject<(CallViewVcStatus,String)>()
var startCountDown = PublishSubject<Bool>()
override var pluginTag: String {
return "janus.plugin.videocall"
}
public func setDelegateUI(_ delegate:CallViewVcStatusDelegate?){
delegateUI = delegate
}
public override func createHidSuccess() {
register(name: callName!)
}
public override func claimSuccess() {
isSet = true
objPeer?.peer?.restartIce()
// objPeer?.doOffer()
}
func register(name:String) {
print("发送register")
sigState = .register
let key = sigState.rawValue
objWss.joinRoomBody.request = key
var mmm = modelToDictionary(objWss.joinRoomBody)!
mmm["username"] = name
var pp: [String : Any] = ["body":mmm,"janus":"message","transaction":key]
objWss.send(msg: &pp)
}
func actionCall(){
updateTextUI.onNext((.b_register,"对方注册成功准备呼叫"))
let videoObj = VideoCallPeerObj(remoteCands: remoteCands, subPeerDelegate: self)
objPeer = videoObj
videoObj.initPeer(conObj: objWss.conObj)
videoObj.startCapture()
videoObj.doOffer()
delegateUI?.status(didChangeLocalRender: videoObj.localVideoTrack)
}
func set() {
print("发送set")
sigState = .set
let key = sigState.rawValue
let jsep:Json = ["type":"offer","sdp":objPeer!.localSdp!.sdp]
let msg:Json = ["request":key,"audio":true,"video":true]
var pp:[String:Any] = ["janus":"message","body":msg,"jsep":jsep,"transaction":key]
objWss.send(msg: &pp)
updateTextUI.onNext((.none,"已set"))
}
func call() {
print("发送call")
sigState = .call
let key = sigState.rawValue
let jsep:Json = ["type":"offer","sdp":objPeer!.localSdp!.sdp]
let msg:Json = ["request":key,"username":calledName!]
var pp:[String:Any] = ["janus":"message","body":msg,"jsep":jsep,"transaction":key]
objWss.send(msg: &pp)
updateTextUI.onNext((.b_wait_accept, "已呼叫等待对方接听"))
}
func actionAccpet(){
objPeer?.setRemote(objPeer!.remoteSdp!)
}
func actionHangUp(){
hangup()
}
func accpet() {
print("accepte")
sigState = .accept
let key = sigState.rawValue
let jsep:Json = ["type": "answer", "sdp": objPeer!.localSdp!.sdp]
let msg:Json = ["request":key,"username":calledName!]
var pp:[String:Any] = ["janus":"message","body":msg,"jsep":jsep,"transaction":key]
objWss.send(msg: &pp)
}
func hangup() {
print("hangup")
sigState = .hangup
let key = sigState.rawValue
let msg:Json = ["request":key]
var pp:[String:Any] = ["janus":"message","body":msg,"transaction":key]
objWss.send(msg: &pp)
}
@objc public func connect(json: [String : Any], cb: @escaping WXCallBack) {
let config = json["config"] as! Json
print("connect 收到connect json = \(dict2JsonStr(config))",level: .error)
var obj = ConnectObj()
obj.token = config["token"] as! String
obj.url = config["wssUrl"] as! String
obj.stunUrl = config["stunUrl"] as! String
obj.turnUrl = config["turnUrl"] as! String
obj.turnPassword = config["turnPassword"] as! String
obj.turnUsername = config["turnUsername"] as! String
obj.wssKey = config["wssKey"] as! String
objWss.state = .new
objWss.initSocket(obj: obj)
objWss.noRepeatConnect()
}
public override func receiveMedia(_ event: Json) {
updateTextUI.onNext((.none,"收到Media要开启计时"))
startCountDown.onNext(true)
}
public override func receiveEvent(_ event: Json) {
super.receiveEvent(event)
let trans = event["transaction"] as? String
let plugindata = event["plugindata"] as? Json
if let trans = trans,trans.contains("register") {
updateTextUI.onNext((.b_register,"等待对方注册"))
/*
//无论是拨打还是被拨打,都需要先注册,然后在new
if objPeer?.peer?.connectionState == .disconnected {
set()
}
*/
let result = (plugindata!["data"] as! Json )["result"] as! Json
VideoCallPluginConnectCb?(["type":"registered","id":callId!, "data":result,"msg":"注册成功"],true)
}else if let trans = trans,trans.contains("call") {
print("正在calling \(dict2JsonStr(event)),等到对方接听")
}else if let pp = plugindata,let data = pp["data"] as? Json,let result = data["result"] as? Json,
let type = result["event"] as? String {
let username = result["username"] as? String
if type == "accepted" {
//对方接受的话有username
if let user = username {
print("对方 \(user) 接受了")
updateTextUI.onNext((.b_accept ,"已经接听"))
let jsep = event["jsep"] as! Json
let sdp = RTCSessionDescription(type: .answer, sdp: jsep["sdp"] as! String)
objPeer?.setRemote(sdp)
VideoCallPluginConnectCb?(["type":"accepted","id":callId!, "data":result,"msg":"已接通"],true)
}else {
}
// setremote成功后,估计需要candidate的操作
}else if type == "incomingcall",let user = username {
print("对方 \(user) incomingcall")
self.isCall = false
self.calledName = user
let videoObj = VideoCallPeerObj(remoteCands: remoteCands, subPeerDelegate: self)
objPeer = videoObj
videoObj.initPeer(conObj: objWss.conObj)
videoObj.startCapture()
delegateUI?.status(didChangeLocalRender: videoObj.localVideoTrack)
let jsep = event["jsep"] as! Json
let remoteType = jsep["type"] as! String
let remoteSDP = jsep["sdp"] as! String
let sdp = RTCSessionDescription(type:.offer, sdp: remoteSDP)
objPeer?.remoteSdp = sdp
VideoCallPluginConnectCb?(["type":"incomingcall","id":callId!, "data":result,"msg":"收到来电"],true)
}else if type == "update" {
let jsep = event["jsep"] as! Json
let sdp = RTCSessionDescription(type: .answer, sdp: jsep["sdp"] as! String)
objPeer?.setRemote(sdp)
}else if type == "hangup" {
delegateUI?.status(onHangup: nil)
}
}else{
print("没有处理的再次输出 event = \(dict2JsonStr(event))")
}
}
public override func peerConnection(_ peerConnection: RTCPeerConnection, didAdd rtpReceiver: RTCRtpReceiver, streams mediaStreams: [RTCMediaStream]) {
super.peerConnection(peerConnection, didAdd: rtpReceiver, streams: mediaStreams)
if let track = rtpReceiver.track as? RTCVideoTrack {
print(mediaStreams)
let videoP = objPeer as! VideoCallPeerObj
videoP.remoteVideoTrack = track
delegateUI?.status(didChangeRemoteRender: track)
}
}
public override func receiveHangUp(_ event: Json) {
delegateUI?.status(onHangup: nil)
}
}