1: Многие WebRTC в Swift - PullRequest
       39

1: Многие WebRTC в Swift

0 голосов
/ 24 октября 2018

Мой код работает 1: 1, но не работает с 1: Многие у меня есть свой собственный внутренний сервер (swift) с WebSocket, а также мой пользователь iS I для поддержки WebRtc

Вот WebRtcМодель и VideoStreamViewController

import UIKit

class WebRTC: NSObject, RTCPeerConnectionDelegate, RTCEAGLVideoViewDelegate {

private var didReceiveRemoteStream:( () -> Void )?
private var onCreatedLocalSdp:((_ localSdp: NSDictionary) -> Void )?

private let factory = RTCPeerConnectionFactory()

private var localStream: RTCMediaStream?
private var localRenderView = RTCEAGLVideoView()
private let _localView = UIView(frame:CGRect(x:0, y:0, width: windowWidth(), height: windowHeight()))

private var remoteStream: RTCMediaStream?
private var remoteRenderView = RTCEAGLVideoView()
private let _remoteView = UIView(frame: CGRect(x: 0, y: 0, width: windowWidth(), height: windowWidth()))

private var peerConnections: [String: RTCPeerConnection] = [String: RTCPeerConnection]()
//    private var peerConnections = [String: RTCPeerConnection]()
private var peerConnectionUserList = [String]()
let configuration = RTCConfiguration()

static let sharedInstance = WebRTC()

private override init() {
    super.init()
    configuration.iceServers = [RTCIceServer(urlStrings: ["stun:stun.l.google.com:19302"])]
}

deinit {
    for peerConnection in peerConnections {
        if let stream = peerConnection.value.localStreams.first {
            peerConnection.value.remove(stream)
        }
    }
}

// MARK: inerface

func localView() -> UIView {
    return _localView
}

func remoteView() -> UIView {
    return _remoteView
}

func setup() {
    setupLocalStream()
}

func connect(userID: String, isliveStreamUser: Bool = false, onCreatedLocalSdp: @escaping ((_ localSdp:NSDictionary) -> Void), didReceiveRemoteStream: @escaping ( () -> Void ) ) {
    self.onCreatedLocalSdp = onCreatedLocalSdp

    if isliveStreamUser == true {
        self.didReceiveRemoteStream = didReceiveRemoteStream
    }

    peerConnectionUserList.append(userID)
    peerConnections[userID] = factory.peerConnection(with: configuration, constraints: WebRTCUtil.peerConnectionConstraints(), delegate: self)
    peerConnections[userID]?.add(localStream!)
}

func diconnect(userID: String, iceServerUrlList: [String], onCreatedLocalSdp: @escaping ((_ localSdp:NSDictionary) -> Void), didReceiveRemoteStream: @escaping ( () -> Void ) ) {

}

func flipCamera() {

    if let mysource = localStream?.videoTracks.first?.source as? RTCAVFoundationVideoSource {

        mysource.useBackCamera = !mysource.useBackCamera

    }
}

func enableVideo() {

    if let mysvideo = localStream?.videoTracks.first {

        mysvideo.isEnabled = !mysvideo.isEnabled
    }
}

func enableAudio() {

    if let myAudio = localStream?.audioTracks.first {

        myAudio.isEnabled = !myAudio.isEnabled
    }
}

private func getPeerConnection(_ id: String) -> RTCPeerConnection? {
    return peerConnections[id]
}

// Answer
func receiveAnswer(hostID: String, remoteSdp: NSDictionary) {
    _receiveAnswer(hostID: hostID, remoteSdp: remoteSdp)
}

// Offer
func receiveOffer(joinUID: String, remoteSdp: NSDictionary) {
    _receiveOffer(joinUID: joinUID, remoteSdp: remoteSdp)
}

// Offer
func createOffer(hostID: String) {
    _createOffer(hostID: hostID)
}

// MARK: implements

private func _receiveAnswer(hostID: String, remoteSdp: NSDictionary) {

    guard let sdpContents = remoteSdp.object(forKey: "sdp") as? String else {
        print("noSDp")
        return
    }

    let sdp = RTCSessionDescription(type: .answer, sdp: sdpContents)

    // 1. remote SDP
    peerConnections[hostID]?.setRemoteDescription(sdp, completionHandler: { (error) in print(105, error?.localizedDescription as Any) })
}

private func _receiveOffer(joinUID: String, remoteSdp: NSDictionary) {

    guard let sdpContents = remoteSdp.object(forKey: "sdp") as? String else {
        print("noSDp")
        return
    }

    // 1. remote SDP
    let remoteSdp = RTCSessionDescription(type: .offer, sdp: sdpContents)
    peerConnections[joinUID]?.setRemoteDescription(remoteSdp, completionHandler: { (error) in

        // if any user offer me i commend it for one user
        // 2. answer
        self.peerConnections[joinUID]?.answer(for: WebRTCUtil.answerConstraints(), completionHandler: { (sdp, error) in

            guard let sdp = sdp else {
                print("can not create sdp")
                return
            }

            print(128, error?.localizedDescription as Any)

            // 3.SDP
            self.peerConnections[joinUID]?.setLocalDescription(sdp, completionHandler: { (error) in print(131, error?.localizedDescription as Any) })

            // 4. peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState)
            //    complete  Answer
        })
    })
}

private func getPeerId(_ peerConnection: RTCPeerConnection) -> String? {
    for peerC in peerConnections {
        if peerConnection == peerC.value {
            return peerC.key
        }
    }
    return nil
}

private func _createOffer(hostID: String) {

    guard let peerConnection = getPeerConnection(hostID) else {
        print("missing peer connection")
        return
    }

    // 1. offer
    print("hostUID", hostID)
    peerConnection.offer(for: WebRTCUtil.mediaStreamConstraints(), completionHandler: { [weak self] (description, error) in

        guard let description = description else {
            print("----- no description ----")
            return
        }

        print(111, error?.localizedDescription as Any)

        // 2.SDP
        peerConnection.setLocalDescription(description, completionHandler: { (error) in
            guard self != nil else {
                print("missin self")
                return
            }

            print(185, error?.localizedDescription as Any)
        })

        print("createOffer 188 webrtc")

        // 3. peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState)
        // complete offer
    })
}

private func setupLocalStream() {

    let streamId = WebRTCUtil.idWithPrefix(prefix: "stream")
    localStream = factory.mediaStream(withStreamId: streamId)

    setupView()

    setupLocalVideoTrack()
    setupLocalAudioTrack()
}

private func setupView() {

    localRenderView.delegate = self
    _localView.backgroundColor = UIColor.white
    _localView.frame.origin = CGPoint(x: 20, y: _remoteView.frame.size.height - (_localView.frame.size.height / 2))
    _localView.addSubview(localRenderView)

    remoteRenderView.delegate = self
    _remoteView.backgroundColor = UIColor.lightGray
    _remoteView.addSubview(remoteRenderView)
}

private func setupLocalVideoTrack() {
    let localVideoSource = factory.avFoundationVideoSource(with: WebRTCUtil.mediaStreamConstraints())
    let localVideoTrack = factory.videoTrack(with: localVideoSource, trackId: WebRTCUtil.idWithPrefix(prefix: "video"))

    if let avSource = localVideoTrack.source as? RTCAVFoundationVideoSource {
        avSource.useBackCamera = false
    }

    localVideoTrack.add(localRenderView)
    localStream?.addVideoTrack(localVideoTrack)
}

private func setupLocalAudioTrack() {
    let localAudioTrack = factory.audioTrack(withTrackId: WebRTCUtil.idWithPrefix(prefix: "audio"))
    localStream?.addAudioTrack(localAudioTrack)
}

// MARK: RTCPeerConnectionDelegate

// いったんスルー
public func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) {
    print("peerConnectionShouldNegotiate")
}

public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) {
    print("stream", printLog, stream)
}

public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) {
    print("didChange newState", printLog, newState)

    switch newState {
    case .checking:
        print("[peerConnection didChangeConnectionState]:checking)")

    case .closed:
        print("[peerConnection didChangeConnectionState]:closed)")

    case .completed:
        print("[peerConnection didChangeConnectionState]:completed)")

    case .connected:
        print("[peerConnection didChangeConnectionState]:connected)")

    case .disconnected:
        print("[peerConnection didChangeConnectionState]:disconnected)")

    case .failed:
        print("[peerConnection didChangeConnectionState]:failed)")

    default:
        print("default")
    }

}

public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) {
    print("didGenerate", printLog, candidate)
}

public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) {
    print("didRemove", printLog, candidates)
}

public func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) {
    print("dataChannel 269", printLog, dataChannel)
}

public func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) {
    print("didChange stateChanged haveLocalOffer", stateChanged == .haveLocalOffer)
    print("didChange stateChanged haveLocalPrAnswer", stateChanged == .haveLocalPrAnswer)
    print("didChange stateChanged haveRemoteOffer", stateChanged == .haveRemoteOffer)
    print("didChange stateChanged haveRemotePrAnswer", stateChanged == .haveRemotePrAnswer)
    print("didChange stateChanged stable", stateChanged == .stable)
    print("didChange stateChanged closed", stateChanged == .closed)
}

public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {
    print("peerConnection didAdd stream:")

    if stream == localStream {
        return
    }

    self.remoteStream = stream

    if let remoteVideoTrack = stream.videoTracks.first {
        DispatchQueue.main.async {
            remoteVideoTrack.add(self.remoteRenderView)
        }
    }

    if let callback = self.didReceiveRemoteStream {
        DispatchQueue.main.async {
            callback()
        }
        self.didReceiveRemoteStream = nil
    }
}

public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) {
    print("peerConnection didChange newState: RTCIceGatheringState, \(newState)")

    if newState != .complete {
        return
    }

    print("1. userList", peerConnectionUserList)

    for user in peerConnectionUserList {

        print("2. user, self.peerConnection[user]", user, self.peerConnections[user] as Any)

        if self.peerConnections[user] == peerConnection {
            print("peerConnection connected", peerConnection)
            guard let callback = self.onCreatedLocalSdp, let localDescription = WebRTCUtil.jsonFromDescription(description: self.peerConnections[user]?.localDescription) else {
                print("no localDescription")
                return
            }

            callback(localDescription)
            self.onCreatedLocalSdp = nil

            print("3. peerConnection didChange")
        }

    }

}

// MARK: RTCEAGLVideoViewDelegate

func videoView(_ videoView: RTCEAGLVideoView, didChangeVideoSize size: CGSize) {
    print("---- didChangeVideoSize -----")

    let ratio:CGFloat = size.width / size.height

    if videoView == localRenderView {
        let parentWidth = _localView.frame.size.width
        _ = parentWidth * ratio
        // localRenderView.frame = CGRect(x: (parentWidth - width) / 2, y: 2, width: width, height: _localView.frame.size.height-4)
        localRenderView.frame = CGRect(x: 0, y: 0, width: size.width, height: _localView.frame.size.height)
    } else if videoView == remoteRenderView {
        let parentWidth = _remoteView.frame.size.width
        _ = parentWidth * ratio
        // remoteRenderView.frame = CGRect(x: (parentWidth - width) / 2, y: 0, width: width, height: _remoteView.frame.size.height)
        remoteRenderView.frame = CGRect(x: 0, y: 0, width: size.width, height: _remoteView.frame.size.height)
    }
}

А вот мой VideoStreamVc.swift

import Foundation
import UIKit

class VideoStremingVC: UIViewController, WebSocketEventsListener {

    private let webRTC = WebRTC.sharedInstance
    private let controlButton = UIButton()
    let username = PerfectLocalAuth.defaults.value(forKey: "username") as? String ?? ""
    let avatar = PerfectLocalAuth.defaults.value(forKey: "profileImage") as? String ?? ""

    @IBOutlet var btnStartLiveVideo: RoundButton!

//    private let flipCameraButton = UIButton()
    @IBOutlet weak var flipCameraButton: UIButton!

    private var typeOffer: Bool = false

    @IBOutlet weak var remoteView: UIView!
    @IBOutlet weak var localView: UIView!

    let socket = WebSocketTracker.shared

    var testDP: NSDictionary!
    var joinID: String?

    @IBOutlet var startView: UIView!
    @IBOutlet var topView: UIView!
    @IBOutlet var bottomView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad(); printTypeOf(self)
        // Do any additional setup after loading the view, typically from a nib.

        webRTC.setup()
        localView.addSubview(webRTC.localView())
        remoteView.addSubview(webRTC.remoteView())
        self.tabBarController?.tabBar.isHidden = true

//        view.addSubview(flipCameraButton)

        socket.addListener(self)
        socket.connect()

    }

    override func viewDidAppear(_ animated: Bool) {

        if #available(iOS 11.0, *) {
            //To change iOS 11 navigationBar largeTitle color
            self.navigationController?.navigationBar.isHidden = true
            self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey(rawValue: NSAttributedStringKey.foregroundColor.rawValue): UIColor.white]

        } else {
            // for default navigation bar title color
            self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey(rawValue: NSAttributedStringKey.foregroundColor.rawValue): UIColor.white]
        }

        var newframe: CGRect = webRTC.localView().frame
        newframe.size.width = localView.frame.size.width
        newframe.size.height = localView.frame.size.height
        newframe.origin.x = 0
        newframe.origin.y = 0
        webRTC.localView().frame = newframe

        newframe = webRTC.remoteView().frame
        newframe.size.width = remoteView.frame.size.width
        newframe.size.height = remoteView.frame.size.height
        newframe.origin.x = 0
        newframe.origin.y = 0
        webRTC.remoteView().frame = newframe

        super.viewDidAppear(animated)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        socket.removeListener(self)
    }

    override var prefersStatusBarHidden: Bool {
        return true
    }

    deinit {
        socket.disconnect()

        self.webRTC.diconnect(userID: PerfectLocalAuth.userid, iceServerUrlList: ["stun:stun.l.google.com:19302"], onCreatedLocalSdp: { (localSdp) in

            let jsonString = HostRequest(type: "leave", hostID: PerfectLocalAuth.userid)
            self.socket.socket.write(string: jsonString.jsonString ?? "")
            print("inside self.webRTC.diconnect")

        }, didReceiveRemoteStream: { () in })

        let jsonString = HostRequest(type: "leave", hostID: PerfectLocalAuth.userid)
        self.socket.socket.write(string: jsonString.jsonString ?? "")

        print("deinit VideoStremingVC")
    }

    func dictToString(dict:NSDictionary) -> String? {

        let jsonData = try? JSONSerialization.data(withJSONObject: dict, options: [])
        let jsonString = String(data: jsonData!, encoding: .utf8)

        return jsonString
    }

    func stringToDict(str:String) -> [String: Any]? {

        let jsonData = str.data(using: .utf8)
        let dictionary = try? JSONSerialization.jsonObject(with: jsonData!, options: .mutableLeaves)
        print(dictionary!)

        return dictionary as? [String : Any]
    }

    func dictToData(dict:NSDictionary) -> Data? {

        let jsonData = try? JSONSerialization.data(withJSONObject: dict, options: [])
        return jsonData
    }

    func dataToDict(data:Data) -> [String: Any]? {

        let dictionary = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
        return dictionary as? [String : Any]
    }

    private func connect() {

        webRTC.connect(userID: PerfectLocalAuth.userid, isliveStreamUser: false, onCreatedLocalSdp: { (localSdp) in

            print("connected webrtc")

//        {
//            hostID:"123",
//            joinuserID:"567",
//            type:"offer", // offer duplicate so we can remove stream and change it to type
//            sdp:"asdfasfgafgdfgfgasuysdfb78832jhbasd"
//        }

            guard let sdp = localSdp["sdp"] as? String else { return }

            let jsonStringOffer = Stream(
                id: "",
                type: "offer",
                username: "",
                hostID: PerfectLocalAuth.userid,
                joinUID: self.joinID,
                avatarImageURL: "",
                sdp: sdp
            )

            self.socket.socket.write(string: jsonStringOffer.jsonString ?? "")
            print("create offer 164 VC")

        }, didReceiveRemoteStream: { () in
            print("connect didReceiveRemoteStream")
        })

        if typeOffer {
            // webRTC.createOffer()
            let jsonString = HostRequest(type: "host", hostID: PerfectLocalAuth.userid)
            self.socket.socket.write(string: jsonString.jsonString ?? "")

            APIClient.createVideoStreams(
                id: nil,
                hostID: PerfectLocalAuth.userid,
                username: self.username,
                avatarImageURL: self.avatar,
                sdp: "",
                type: "",
                active: true
                ).execute(onSuccess: { videoStreamCreateResponse in

                    print("videoStreamCreateResponse", videoStreamCreateResponse)

                }, onFailure: { videoStreamErrorResponse in

                    print("videoStreamErrorResponse", videoStreamErrorResponse)
                })

        } else {
            // self.webRTC.receiveOffer(remoteSdp: self.testDP)
        }
    }

    func connectFromHeventsVC(dict: NSDictionary) {

        guard let hostID = dict["host_id"] as? String else {
                print("dict missing");  printLine()
                return
        }

        print("1 connectFromHeventsVC")

        // print("connectFromHeventsVC dict offer from 183 line type", type)

        webRTC.connect(userID: PerfectLocalAuth.userid, isliveStreamUser: true, onCreatedLocalSdp: { (localSdp) in

            guard let sdp = localSdp["sdp"] as? String else { return }

            print("connectFromHeventsVC localSdp", localSdp)

            let jsonStringOffer = Stream(
                id: "",
                type: "answer",
                username: "",
                hostID: hostID,
                joinUID: PerfectLocalAuth.userid,
                avatarImageURL: "",
                sdp: sdp
            )

            print("2 answer")
            self.socket.socket.write(string: jsonStringOffer.jsonString ?? "")

        }, didReceiveRemoteStream: { () in
            print("connectFromHeventsVC didReceiveRemoteStream")
        })

//        {
//            type:"approveme",
//            hostID:"123",
//            joinuserID:"567"
//        }

        let jsonStringOffer = Stream(
            id: "",
            type: "approveme",
            username: "",
            hostID: hostID,
            joinUID: PerfectLocalAuth.userid,
            avatarImageURL: "",
            sdp: ""
        )

        print("3 approveme")
        self.socket.socket.write(string: jsonStringOffer.jsonString ?? "")

    }

    // MARK: UIEventswe

    @IBAction func flipCameraButton(_ sender: Any) {
        self.webRTC.flipCamera()
    }
    // MARK: states

    @IBAction func dissmissVC(_ sender: UIButton) {
        self.dismiss(animated: true, completion: nil)
    }

    @IBAction func btnStartLiveClicked(_ sender: Any) {
        self.startView.isHidden = true
        self.remoteView.isHidden = true
        typeOffer = true
        connect()
    }

    @IBAction func btnJoinLiveClicked(_ sender: Any) {
//        self.localView.isHidden = true
//        self.startView.isHidden = true
//        typeOffer = false
//        connect()
    }

    // MARK: Websocket Delegate Methods.

    func webSocketEventsListenerDidConnect(_ webSocketTrackerService: WebSocketTracker) {
        print("websocket is connected")
        let jsonString = HostRequest(type: "connect", hostID: PerfectLocalAuth.userid)
        self.socket.socket.write(string: jsonString.jsonString ?? "")
    }

    func webSocketEventsListenerDidDisconnect(_ webSocketTrackerService: WebSocketTracker, didDisconnectError: Error?) {
        print("didDisconnect Error", didDisconnectError?.localizedDescription as Any)
    }

    func webSocketEventsListenerDidRecieveError(_ webSocketTrackerService: WebSocketTracker, didRecieveError: Error) {
        print("didRecieveError", didRecieveError.localizedDescription)
    }

    func webSocketEventsListenerDidReceiveMessage(_ webSocketTrackerService: WebSocketTracker, didRecieveMessage text: String) {
        print("Received text: \(text)")

        guard let stream = Stream.from(json: text) else {
            print("videoStreamingResponse json text error")
            return
        }

        let hostID = stream.hostID ?? ""
        let joinUID = stream.joinUID ?? ""
        let sdp = stream.sdp ?? ""
        let type = stream.type ?? ""

        switch stream.type {
        case "connect":
            print("connect")
        case "host":
            print("steam type host")

        case "approveme":
            print("approveme hostID", hostID)
            // create
            webRTC.createOffer(hostID: hostID)
            self.joinID = joinUID

        case "offer":
            print("steam type offer")

            let offer: [String: String] = [
                "type": type,
                "sdp": sdp
            ]

            webRTC.receiveOffer(joinUID: joinUID, remoteSdp: offer as NSDictionary)

        case "answer":
            print("answer")
            guard let answer = ["sdp": sdp, "type": type] as? NSDictionary else { return }
            webRTC.receiveAnswer(hostID: hostID, remoteSdp: answer)

        case "leave":
            print("leave")
        case "end":
            print("end")
        default:
            print("stream type default")
        }
    }

    func webSocketEventsListenerDidReceiveData(_ webSocketTrackerService: WebSocketTracker, didRecieveMessage data: Data) {
//        print("Received data count: \(data.count)")
//        print("Received data: \(data)")
//        print("onReceiveOffer")
//        let dict = self.dataToDict(data: data)! as NSDictionary
//
//        if dict["type"] as? String == "answer" {
//
//            if !self.typeOffer { return }
//            print("onReceiveAnswer")
//            self.webRTC.receiveAnswer(remoteSdp: dict)
//
//        } else {
//
//            if self.typeOffer { return }
//            self.testDP = dict
////            self.webRTC.receiveOffer(remoteSdp: dict)
//        }
    }

    func sendMessage(_ message: String) {
        print("message", message)
    }

    func register(_ response: String) {
        print("respose", response)
    }

    // MARK: Write Text Action
    @IBAction func writeText(_ sender: UIBarButtonItem) {
    // socket.write(string: "hello there!") need to rewrite
    }

}
  1. Пользователь в прямом эфире
  2. 1-й пользователь может присоединиться и смотреть только пользователя в прямом эфире
  3. 2-й пользователь присоединяется, но после создания предложения он не отправляет предложение присоединиться к пользователю через WebSocket

Пользователь прямой трансляции создает предложение, но не отправляет, поскольку его невозможно подключить, возможно, сwebRTC - кто-нибудь может помочь пройти через мой код и проверить, что не так в моем коде, где я делаю неправильно?спасибо, Advance

У меня есть несколько журналов, чтобы проверить, что происходит, когда я запускаю свой Xcode, работающий с iPhone (который сейчас делится своим живым видео)

Журналы присоединения 1-го пользователя

введите описание ссылки здесь

журналы регистрации второго пользователя

введите описание ссылки здесь

...