Janus VideoCall

uniapp 跟app 的流程图

6049da73a04d44476da246c04d3da84a.jpg




以下是具体app实现videoCall的具体

  • wss的open、之后create session,然后在create handlerid   这个流程基本上可以说是所有的janus的插件必备的流程

  • 进行register信令注册,这个注册无论是主叫还是被叫都是需要的

  1. ["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

  1. ["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的前置处理

  1. 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
    }()
  2.     /// 对音频处理
        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

  1.     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)
    }
}


Powered By Z-BlogPHP 1.7.4

Copyright 粤ICP备2024347557号 Rights Reserved.