2017-03-10 3 views
4

Ich möchte ein "eins zu viele" (mit der max 3 Geräte) webrtc Setup erstellen. Ich habe ein Gerät, das mein Hauptgerät ist. Andere Geräte verbinden sich mit diesem Gerät. Du kannst über ein Walky Talky nachdenken. Mit einem Gerät, mit dem sie sich verbinden.Eins zu viele webrtc

Ich habe diesen Code, der mit einer eins zu eins Verbindung funktioniert.

import AVFoundation 
import UIKit 
import WebRTC 
import SocketIO 
import CoreTelephony 
import ReachabilitySwift 

let TAG = "ViewController" 
let AUDIO_TRACK_ID = TAG + "AUDIO" 
let LOCAL_MEDIA_STREAM_ID = TAG + "STREAM" 

class ViewController: UIViewController, RTCPeerConnectionDelegate, RTCDataChannelDelegate { 

    var mediaStream: RTCMediaStream! 
    var localAudioTrack: RTCAudioTrack! 
    var remoteAudioTrack: RTCAudioTrack! 
    var dataChannel: RTCDataChannel! 
    var dataChannelRemote: RTCDataChannel! 

    var roomName: String! 


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

     initWebRTC(); 
     sigConnect(wsUrl: "http://192.168.1.69:3000"); 

     localAudioTrack = peerConnectionFactory.audioTrack(withTrackId: AUDIO_TRACK_ID) 
     mediaStream = peerConnectionFactory.mediaStream(withStreamId: LOCAL_MEDIA_STREAM_ID) 
     mediaStream.addAudioTrack(localAudioTrack) 
    } 

    func getRoomName() -> String { 
     return (roomName == nil || roomName.isEmpty) ? "_defaultroom": roomName; 
    } 

    // webrtc 
    var peerConnectionFactory: RTCPeerConnectionFactory! = nil 
    var peerConnection: RTCPeerConnection! = nil 
    var mediaConstraints: RTCMediaConstraints! = nil 

    var socket: SocketIOClient! = nil 
    var wsServerUrl: String! = nil 
    var peerStarted: Bool = false 

    func initWebRTC() { 
     RTCInitializeSSL() 
     peerConnectionFactory = RTCPeerConnectionFactory() 

     let mandatoryConstraints = ["OfferToReceiveAudio": "true", "OfferToReceiveVideo": "false"] 
     let optionalConstraints = [ "DtlsSrtpKeyAgreement": "true", "RtpDataChannels" : "true", "internalSctpDataChannels" : "true"] 


     mediaConstraints = RTCMediaConstraints.init(mandatoryConstraints: mandatoryConstraints, optionalConstraints: optionalConstraints) 

    } 

    func connect() { 
     if (!peerStarted) { 
      sendOffer() 
      peerStarted = true 
     } 
    } 

    func hangUp() { 
     sendDisconnect() 
     stop() 
    } 

    func stop() { 
     if (peerConnection != nil) { 
      peerConnection.close() 
      peerConnection = nil 
      peerStarted = false 
     } 
    } 

    func prepareNewConnection() -> RTCPeerConnection { 
     var icsServers: [RTCIceServer] = [] 

     icsServers.append(RTCIceServer(urlStrings: ["stun:stun.l.google.com:19302"], username:"",credential: "")) 

     let rtcConfig: RTCConfiguration = RTCConfiguration() 
     rtcConfig.tcpCandidatePolicy = RTCTcpCandidatePolicy.disabled 
     rtcConfig.bundlePolicy = RTCBundlePolicy.maxBundle 
     rtcConfig.rtcpMuxPolicy = RTCRtcpMuxPolicy.require 
     rtcConfig.iceServers = icsServers; 

     peerConnection = peerConnectionFactory.peerConnection(with: rtcConfig, constraints: mediaConstraints, delegate: self) 
     peerConnection.add(mediaStream); 

     let tt = RTCDataChannelConfiguration(); 
     tt.isOrdered = false;   


     self.dataChannel = peerConnection.dataChannel(forLabel: "testt", configuration: tt) 

     self.dataChannel.delegate = self 
     print("Make datachannel") 

     return peerConnection; 
    } 

    // RTCPeerConnectionDelegate - begin [ /////////////////////////////////////////////////////////////////////////////// 


    /** Called when the SignalingState changed. */ 
    public func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState){ 
     print("signal state: \(stateChanged.rawValue)") 
    } 


    /** Called when media is received on a new stream from remote peer. */ 
    public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream){ 
     if (peerConnection == nil) { 
      return 
     } 

     if (stream.audioTracks.count > 1) { 
      print("Weird-looking stream: " + stream.description) 
      return 
     } 
    } 

    /** Called when a remote peer closes a stream. */ 
    public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream){} 


    /** Called when negotiation is needed, for example ICE has restarted. */ 
    public func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection){} 


    /** Called any time the IceConnectionState changes. */ 
    public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState){} 


    /** Called any time the IceGatheringState changes. */ 
    public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState){} 


    /** New ice candidate has been found. */ 
    public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate){ 


      print("iceCandidate: " + candidate.description) 
      let json:[String: AnyObject] = [ 
       "type" : "candidate" as AnyObject, 
       "sdpMLineIndex" : candidate.sdpMLineIndex as AnyObject, 
       "sdpMid" : candidate.sdpMid as AnyObject, 
       "candidate" : candidate.sdp as AnyObject 
      ] 
      sigSendIce(msg: json as NSDictionary) 

    } 


    /** Called when a group of local Ice candidates have been removed. */ 
    public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]){} 


    /** New data channel has been opened. */ 
    public func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel){ 
     print("Datachannel is open, name: \(dataChannel.label)") 
     dataChannel.delegate = self 
     self.dataChannelRemote = dataChannel 
    } 



    // RTCPeerConnectionDelegate - end ]///////////////////////////////////////////////////////////////////////////////// 

    public func dataChannel(_ dataChannel: RTCDataChannel, didReceiveMessageWith buffer: RTCDataBuffer){ 
     print("iets ontvangen"); 
    } 

    public func dataChannelDidChangeState(_ dataChannel: RTCDataChannel){ 
     print("channel.state \(dataChannel.readyState.rawValue)"); 
    } 


    func sendData(message: String) { 
     let newData = message.data(using: String.Encoding.utf8) 
     let dataBuff = RTCDataBuffer(data: newData!, isBinary: false) 
     self.dataChannel.sendData(dataBuff) 
    } 

    func onOffer(sdp:RTCSessionDescription) { 
     print("on offer shizzle") 

     setOffer(sdp: sdp) 
     sendAnswer() 
     peerStarted = true; 
    } 

    func onAnswer(sdp:RTCSessionDescription) { 
     setAnswer(sdp: sdp) 
    } 

    func onCandidate(candidate:RTCIceCandidate) { 
     peerConnection.add(candidate) 
    } 

    func sendSDP(sdp:RTCSessionDescription) { 
     print("Converting sdp...") 
     let json:[String: AnyObject] = [ 
      "type" : sdp.type.rawValue as AnyObject, 
      "sdp" : sdp.sdp.description as AnyObject 
     ] 

     sigSend(msg: json as NSDictionary); 
    } 

    func sendOffer() { 
     peerConnection = prepareNewConnection(); 
     peerConnection.offer(for: mediaConstraints) { (RTCSessionDescription, Error) in 

      if(Error == nil){ 
       print("send offer") 

       self.peerConnection.setLocalDescription(RTCSessionDescription!, completionHandler: { (Error) in 
        print("Sending: SDP") 
        print(RTCSessionDescription as Any) 
        self.sendSDP(sdp: RTCSessionDescription!) 
       }) 
      } else { 
       print("sdp creation error: \(Error)") 
      } 

     } 
    } 


    func setOffer(sdp:RTCSessionDescription) { 
     if (peerConnection != nil) { 
      print("peer connection already exists") 
     } 
     peerConnection = prepareNewConnection(); 
     peerConnection.setRemoteDescription(sdp) { (Error) in 

     } 
    } 

    func sendAnswer() { 
     print("sending Answer. Creating remote session description...") 
     if (peerConnection == nil) { 
      print("peerConnection NOT exist!") 
      return 
     } 

     peerConnection.answer(for: mediaConstraints) { (RTCSessionDescription, Error) in 
      print("ice shizzle") 

      if(Error == nil){ 
       self.peerConnection.setLocalDescription(RTCSessionDescription!, completionHandler: { (Error) in 
        print("Sending: SDP") 
        print(RTCSessionDescription as Any) 
        self.sendSDP(sdp: RTCSessionDescription!) 
       }) 
      } else { 
       print("sdp creation error: \(Error)") 
      } 

     } 
    } 

    func setAnswer(sdp:RTCSessionDescription) { 
     if (peerConnection == nil) { 
      print("peerConnection NOT exist!") 
      return 
     } 

     peerConnection.setRemoteDescription(sdp) { (Error) in 
      print("remote description") 
     } 
    } 

    func sendDisconnect() { 
     let json:[String: AnyObject] = [ 
      "type" : "user disconnected" as AnyObject 
     ] 
     sigSend(msg: json as NSDictionary); 
    } 

    // websocket related operations 
    func sigConnect(wsUrl:String) { 
     wsServerUrl = wsUrl; 

     print("connecting to " + wsServerUrl) 
     socket = SocketIOClient(socketURL: NSURL(string: wsServerUrl)! as URL) 

     socket.on("connect") { data in 
      print("WebSocket connection opened to: " + self.wsServerUrl); 
      self.sigEnter(); 
     } 
     socket.on("disconnect") { data in 
      print("WebSocket connection closed.") 
     } 
     socket.on("message") { (data, emitter) in 
      if (data.count == 0) { 
       return 
      } 

      let json = data[0] as! NSDictionary 
      print("WSS->C: " + json.description); 

      let type = json["type"] as! Int 

      if (type == RTCSdpType.offer.rawValue) { 
       print("Received offer, set offer, sending answer...."); 
       let sdp = RTCSessionDescription(type: RTCSdpType(rawValue: type)!, sdp: json["sdp"] as! String) 
       self.onOffer(sdp: sdp); 
      } else if (type == RTCSdpType.answer.rawValue && self.peerStarted) { 
       print("Received answer, setting answer SDP"); 
       let sdp = RTCSessionDescription(type: RTCSdpType(rawValue: type)!, sdp: json["sdp"] as! String) 
       self.onAnswer(sdp: sdp); 
      } else { 
       print("Unexpected websocket message"); 
      } 
     } 

     socket.on("ice") { (data, emitter) in 
      if (data.count == 0) { 
       return 
      } 

      let json = data[0] as! NSDictionary 
      print("WSS->C: " + json.description); 

      let type = json["type"] as! String 


      if (type == "candidate" && self.peerStarted) { 
       print("Received ICE candidate..."); 
       let candidate = RTCIceCandidate(
        sdp: json["candidate"] as! String, 
        sdpMLineIndex: Int32(json["sdpMLineIndex"] as! Int), 
        sdpMid: json["sdpMid"] as? String) 
        self.onCandidate(candidate: candidate); 
      } else { 
       print("Unexpected websocket message"); 
      } 
     } 

     socket.connect(); 
    } 

    func sigRecoonect() { 
     socket.disconnect(); 
     socket.connect(); 
    } 

    func sigEnter() { 
     let roomName = getRoomName(); 
     print("Entering room: " + roomName); 
     socket.emit("enter", roomName); 
    } 

    func sigSend(msg:NSDictionary) { 
     socket.emit("message", msg) 
    } 

    func sigSendIce(msg:NSDictionary) { 
     socket.emit("ice", msg) 
    } 
} 

Also dachte ich, dass ich ein Array mit den Peers brauche. Und der MediaStream, localAudioTrack und der DataChannel müssen ein Objekt sein, weil das lokale Audio das gleiche ist? Gibt es dafür gute Lösungen? Weil ich nicht weiß, wie ich das richtig umsetzen soll.

Ich untersuche verschiedene Fragen und Projektreferenzierung zu einem Multi-Call-WebRTC-Setup.

Ich sah diese (Website) WebRTC Setup auf GitHub: https://github.com/anoek/webrtc-group-chat-example/blob/master/client.html

ich Ingenieur, dies zu schnellen :) zu umkehren, um versuchen werde. Jede Hilfe wird wirklich geschätzt.

+0

Mögliches Duplikat von [WebRTC mit 3 Benutzer Verbindung] (http://StackOverflow.com/Questions/36885362/Webrc-with-3-users-connection) – jib

+0

@jib nein, meine Frage ist über schnelle. – da1lbi3

+0

Das scheint wie einfache Logistik. Willst du, dass jemand das für dich schreibt? – jib

Antwort

3

Ich würde vorschlagen, gegen eine One-to-Many-Architektur, wo ein einzelnes Gerät seine Medien an alle anderen senden muss. Dies bricht furchtbar schnell (wie nach 2-3 Geräten, die es verbinden muss).

Der Grund dafür ist, dass Uplinks in der Regel in der Kapazität begrenzt sind und selbst wenn sie nicht sind, Geräte sind nicht wirklich darauf ausgerichtet, so viele Daten an viele andere Geräte zu streamen.

Um zu tun, was Sie bei "Skalierung" wollen, verwenden Sie eine Server-Komponente, die Medien zu den anderen Geräten routet. Betrachten Sie https://jitsi.org/ und http://www.kurento.org/ für Startpunkte.