Exemplo n.º 1
0
    private void OnWSMessage(object sender, MessageEventArgs args)
    {
        var message = JsonUtility.FromJson <SignalingMessage>(args.Data);
        var data    = message.data;

        switch (message.type)
        {
        case "message":
            Debug.Log($"Receive a message from Node: {data.message}");
            break;

        case "offer":
            Debug.Log("Receive an offer from Node");
            var offerDesc = new RTCSessionDescription {
                type = RTCSdpType.Offer,
                sdp  = data.sdp
            };
            onOfferCallBack = state => { StartCoroutine(OnOffer(offerDesc)); };
            mainThreadSyncCtx.Post(onOfferCallBack, null);
            break;

        case "candidate":
            var candidateInfo = new RTCIceCandidateInit {
                candidate     = data.candidate,
                sdpMid        = data.sdpMid,
                sdpMLineIndex = data.sdpMLineIndex
            };
            pc.AddIceCandidate(new RTCIceCandidate(candidateInfo));
            break;

        default:
            Debug.Log($"Receive unknown message type: {message.type}");
            break;
        }
    }
Exemplo n.º 2
0
 public static Webrtc.IceCandidate ToNative(this RTCIceCandidateInit iceCandidateInit) =>
 new Webrtc.IceCandidate(iceCandidateInit.SdpMid, (int)iceCandidateInit.SdpMLineIndex,
                         iceCandidateInit.Candidate)
 {
     //AdapterType = Webrtc.PeerConnection.AdapterType.AdapterTypeAny,
     ////ServerUrl = ???
 };
Exemplo n.º 3
0
 private void AddActionsToSignalingWebSocket()
 {
     signaling.OnMessage += (sender, message) =>
     {
         logger.LogDebug($"Received message: {message.Data}");
         if (message.Data == "{\"data\":{\"getRemoteMedia\":true}}")
         {
             Negotiate();
             return;
         }
         if (message.Data == "{\"data\":{\"candidate\":null}}")
         {
             return;
         }
         string correct_message = ConvertString(message.Data);
         logger.LogDebug($"After nesting:{correct_message}");
         if (RTCIceCandidateInit.TryParse(correct_message, out var IceCandidate))
         {
             logger.LogDebug($"Got remote candidate: {correct_message}");
             pc.addIceCandidate(IceCandidate);
         }
         else if (RTCSessionDescriptionInit.TryParse(correct_message, out var SDP))
         {
             logger.LogDebug($"Setting SDP: {correct_message}");
             var result = pc.setRemoteDescription(SDP);
             if (result != SetDescriptionResultEnum.OK)
             {
                 logger.LogWarning($"Failed to set remote description, {result}.");
                 pc.Close("failed to set remote description");
             }
         }
     };
 }
        public void ToJsonUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            var candidate = RTCIceCandidate.Parse("1390596646 1 udp 1880747346 192.168.11.50 61680 typ host generation 0");

            Assert.NotNull(candidate);
            Assert.Equal(RTCIceCandidateType.host, candidate.type);
            Assert.Equal(RTCIceProtocol.udp, candidate.protocol);

            logger.LogDebug(candidate.toJSON());

            bool parseResult = RTCIceCandidateInit.TryParse(candidate.toJSON(), out var init);

            Assert.True(parseResult);

            Assert.Equal(0, init.sdpMLineIndex);
            Assert.Equal("0", init.sdpMid);

            var initCandidate = RTCIceCandidate.Parse(init.candidate);

            Assert.Equal(RTCIceCandidateType.host, initCandidate.type);
            Assert.Equal(RTCIceProtocol.udp, initCandidate.protocol);
        }
Exemplo n.º 5
0
        public IActionResult AddIceCandidate(string id, [FromBody] RTCIceCandidateInit iceCandidate)
        {
            _logger.LogDebug($"SetIceCandidate {id} {iceCandidate?.candidate}.");

            if (string.IsNullOrWhiteSpace(id))
            {
                return(BadRequest("The id cannot be empty in AddIceCandidate."));
            }
            else if (string.IsNullOrWhiteSpace(iceCandidate?.candidate))
            {
                return(BadRequest("The candidate field cannot be empty in AddIceCandidate."));
            }

            // TODO: Handle .local addresses.
            if (iceCandidate.candidate.Contains(".local"))
            {
                _logger.LogWarning("ICE candidates with .local addresses are currently not supported, coming soon.");
            }
            else
            {
                _webRTCServer.AddIceCandidate(id, iceCandidate);
            }

            return(Ok());
        }
Exemplo n.º 6
0
        public void NonEquivalentCandidateFoundationUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            RTCIceCandidateInit initA = new RTCIceCandidateInit {
                usernameFragment = "abcd"
            };
            var candidateA = new RTCIceCandidate(initA);

            candidateA.SetAddressProperties(RTCIceProtocol.udp, IPAddress.Loopback, 1024, RTCIceCandidateType.host, null, 0);

            RTCIceCandidateInit initB = new RTCIceCandidateInit {
                usernameFragment = "efgh"
            };
            var candidateB = new RTCIceCandidate(initB);

            candidateB.SetAddressProperties(RTCIceProtocol.udp, IPAddress.IPv6Loopback, 1024, RTCIceCandidateType.host, null, 0);

            Assert.NotNull(candidateA);
            Assert.NotNull(candidateB);
            Assert.NotEqual(candidateA.foundation, candidateB.foundation);

            logger.LogDebug(candidateA.ToString());
            logger.LogDebug(candidateB.ToString());
        }
Exemplo n.º 7
0
        public void ConstructWithOption()
        {
            var option = new RTCIceCandidateInit
            {
                sdpMid        = "0",
                sdpMLineIndex = 0,
                candidate     =
                    "candidate:102362043 1 udp 2122262783 240b:10:2fe0:4900:3cbd:7306:63c4:a8e1 50241 typ host generation 0 ufrag DEIG network-id 5"
            };
            var candidate = new RTCIceCandidate(option);

            Assert.IsNotEmpty(candidate.Candidate);
            Assert.AreEqual(candidate.Candidate, option.candidate);
            Assert.AreEqual(candidate.SdpMLineIndex, option.sdpMLineIndex);
            Assert.AreEqual(candidate.SdpMid, option.sdpMid);
            Assert.AreEqual(RTCIceComponent.Rtp, candidate.Component);
            Assert.IsNotEmpty(candidate.Foundation);
            Assert.NotNull(candidate.Port);
            Assert.NotNull(candidate.Priority);
            Assert.IsNotEmpty(candidate.Address);
            Assert.NotNull(candidate.Protocol);
            Assert.IsNotEmpty(candidate.RelatedAddress);
            Assert.NotNull(candidate.RelatedPort);
            Assert.IsNotEmpty(candidate.SdpMid);
            Assert.NotNull(candidate.SdpMLineIndex);
            Assert.NotNull(candidate.Type);
            Assert.Null(candidate.TcpType);
            Assert.IsNotEmpty(candidate.UserNameFragment);
        }
Exemplo n.º 8
0
        public Task AddIceCandidate(RTCIceCandidateInit candidate)
        {
            var x = candidate.ToNative();

            ((Webrtc.RTCPeerConnection)NativeObject).AddIceCandidate(x /*candidate.ToNative()*/);
            return(Task.CompletedTask);
        }
Exemplo n.º 9
0
        public Task AddIceCandidate(RTCIceCandidateInit candidate)
        {
            var x = candidate.ToNative();

            System.Diagnostics.Debug.WriteLine($"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ SET ICE: {x.AdapterType} {x.Sdp} {x.SdpMid} {x.SdpMLineIndex} {x.ServerUrl}");
            ((Webrtc.PeerConnection)NativeObject).AddIceCandidate(x);
            return(Task.CompletedTask);
        }
        public async Task <ActionResult> Ice(string id, [FromBody] RTCIceCandidateInit candidate)
        {
            _logger.LogDebug($"Echo controller posting ice candidate to {_echoTestRestUrl}/ice/{id}.");
            _logger.LogDebug($"Candidate={candidate.candidate}");

            HttpClient client   = new HttpClient();
            var        postResp = await client.PostAsync($"{_echoTestRestUrl}/ice/{id}", new StringContent(candidate.toJSON(), Encoding.UTF8, REST_CONTENT_TYPE));

            _logger.LogDebug($"Echo controller post ice response {postResp.StatusCode}:{postResp.ReasonPhrase}.");

            return(postResp.IsSuccessStatusCode ? Ok() : BadRequest(postResp.ReasonPhrase));
        }
Exemplo n.º 11
0
 public async Task AddIceCandidate(string id, RTCIceCandidateInit iceCandidate)
 {
     if (!_peerConnections.TryGetValue(id, out var pc))
     {
         throw new ApplicationException("No peer connection is available for the specified id.");
     }
     else
     {
         _logger.LogDebug("ICE Candidate: " + iceCandidate.candidate);
         await pc.addIceCandidate(iceCandidate);
     }
 }
Exemplo n.º 12
0
    public RTCIceCandidate toCand()
    {
        var candidateInfo = new RTCIceCandidateInit
        {
            candidate     = candidate,
            sdpMid        = sdpMid,
            sdpMLineIndex = sdpMLineIndex
        };
        var cand = new RTCIceCandidate(candidateInfo);

        return(cand);
    }
Exemplo n.º 13
0
        void OnIceCandidate(ISignaling signaling, CandidateData e)
        {
            if (!m_mapConnectionIdAndPeer.TryGetValue(e.connectionId, out var pc))
            {
                return;
            }

            RTCIceCandidateInit option = new RTCIceCandidateInit
            {
                candidate     = e.candidate,
                sdpMLineIndex = e.sdpMLineIndex,
                sdpMid        = e.sdpMid
            };

            pc.AddIceCandidate(new RTCIceCandidate(option));
        }
Exemplo n.º 14
0
        public IActionResult AddIceCandidate(string id, [FromBody] RTCIceCandidateInit iceCandidate)
        {
            _logger.LogDebug($"SetIceCandidate {id} {iceCandidate?.candidate}.");

            if (string.IsNullOrWhiteSpace(id))
            {
                return(BadRequest("The id cannot be empty in AddIceCandidate."));
            }
            else if (string.IsNullOrWhiteSpace(iceCandidate?.candidate))
            {
                return(BadRequest("The candidate field cannot be empty in AddIceCandidate."));
            }

            _webRTCServer.AddIceCandidate(id, iceCandidate);

            return(Ok());
        }
        void OnIceCandidate(ISignaling signaling, CandidateData e)
        {
            if (!_mapConnectionIdAndPeer.TryGetValue(e.connectionId, out var pc))
            {
                return;
            }

            RTCIceCandidateInit option = new RTCIceCandidateInit
            {
                candidate = e.candidate, sdpMLineIndex = e.sdpMLineIndex, sdpMid = e.sdpMid
            };

            if (!pc.peer.AddIceCandidate(new RTCIceCandidate(option)) && !pc.ignoreOffer)
            {
                Debug.LogWarning($"{pc} this candidate can't accept current signaling state {pc.peer.SignalingState}.");
            }
        }
Exemplo n.º 16
0
        public static bool TryParse(string json, out RTCIceCandidateInit init)
        {
            //init = JsonSerializer.Deserialize< RTCIceCandidateInit>(json);

            init = null;

            if (string.IsNullOrWhiteSpace(json))
            {
                return(false);
            }
            else
            {
                init = TinyJson.JSONParser.FromJson <RTCIceCandidateInit>(json);

                // To qualify as parsed all required fields must be set.
                return(init != null &&
                       init.candidate != null &&
                       init.sdpMid != null);
            }
        }
Exemplo n.º 17
0
        private void DoProcessMessage(Message message)
        {
            switch (message.type)
            {
            case "Offer":
                // var offer = JsonUtility.FromJson<RTCSessionDescriptionInit>(message.args);
                if (RTCSessionDescriptionInit.TryParse(message.args, out RTCSessionDescriptionInit offer))
                {
                    Debug.Log($"Got remote SDP, type {offer.type}");

                    var result = rtcPeerConnection.setRemoteDescription(offer);
                    if (result != SetDescriptionResultEnum.OK)
                    {
                        Debug.Log($"Failed to set remote description, {result}.");
                        rtcPeerConnection.Close("Failed to set remote description");
                    }
                    else
                    {
                        if (rtcPeerConnection.signalingState == RTCSignalingState.have_remote_offer)
                        {
                            var answerSdp = rtcPeerConnection.createAnswer();
                            rtcPeerConnection.setLocalDescription(answerSdp);

                            Debug.Log($"Sending SDP answer");

                            Send("Offer", answerSdp.toJSON());
                        }
                    }
                }
                break;

            case "IceCandidate":
                if (RTCIceCandidateInit.TryParse(message.args, out RTCIceCandidateInit candidate))
                {
                    Debug.Log($"Got remote Ice Candidate, uri {candidate.candidate}");
                    rtcPeerConnection.addIceCandidate(candidate);
                }
                break;
            }
        }
Exemplo n.º 18
0
        public async Task <IActionResult> PutIce(string from, string to, [FromBody] RTCIceCandidateInit ice)
        {
            if (string.IsNullOrEmpty(to) || string.IsNullOrEmpty(from) || ice == null || ice.candidate == null)
            {
                _logger.LogWarning($"WebRTC signal controller PUT ice candidate request had invalid parameters.");
                return(BadRequest());
            }

            WebRTCSignal iceSignal = new WebRTCSignal
            {
                ID         = Guid.NewGuid().ToString(),
                To         = to,
                From       = from,
                SignalType = WebRTCSignalTypesEnum.ice.ToString(),
                Signal     = ice.toJSON(),
                Inserted   = DateTime.UtcNow.ToString("o")
            };

            _context.WebRTCSignals.Add(iceSignal);

            await _context.SaveChangesAsync();

            return(Ok());
        }
Exemplo n.º 19
0
 public async Task SignalIceAsync(string name, Guid peerId, RTCIceCandidateInit e, Guid roomId)
 {
     await _hub.SendAsync("SignalIceCandidate", e.candidate, peerId, name, roomId);
 }
Exemplo n.º 20
0
 public static Webrtc.RTCIceCandidate ToNative(this RTCIceCandidateInit iceCandidateInit) =>
 new Webrtc.RTCIceCandidate(iceCandidateInit.Candidate, (int)iceCandidateInit.SdpMLineIndex,
                            iceCandidateInit.SdpMid)
 {
     ///ServerUrl = ???
 };
Exemplo n.º 21
0
        private static RTCPeerConnection Createpc(WebSocketContext context, RTCIceServer stunServer, bool relayOnly)
        {
            if (_peerConnection != null)
            {
                _peerConnection.Close("normal");
            }

            List <RTCCertificate> presetCertificates = null;

            if (File.Exists(LOCALHOST_CERTIFICATE_PATH))
            {
                var localhostCert = new X509Certificate2(LOCALHOST_CERTIFICATE_PATH, (string)null, X509KeyStorageFlags.Exportable);
                presetCertificates = new List <RTCCertificate> {
                    new RTCCertificate {
                        Certificate = localhostCert
                    }
                };
            }

            RTCConfiguration pcConfiguration = new RTCConfiguration
            {
                certificates = presetCertificates,
                //X_RemoteSignallingAddress = (context != null) ? context.UserEndPoint.Address : null,
                iceServers = stunServer != null ? new List <RTCIceServer> {
                    stunServer
                } : null,
                //iceTransportPolicy = RTCIceTransportPolicy.all,
                iceTransportPolicy = relayOnly ? RTCIceTransportPolicy.relay : RTCIceTransportPolicy.all,
                //X_BindAddress = IPAddress.Any, // NOTE: Not reqd. Using this to filter out IPv6 addresses so can test with Pion.
            };

            _peerConnection = new RTCPeerConnection(pcConfiguration);

            //_peerConnection.GetRtpChannel().MdnsResolve = (hostname) => Task.FromResult(NetServices.InternetDefaultAddress);
            _peerConnection.GetRtpChannel().MdnsResolve = MdnsResolve;
            //_peerConnection.GetRtpChannel().OnStunMessageReceived += (msg, ep, isrelay) => logger.LogDebug($"STUN message received from {ep}, message type {msg.Header.MessageType}.");

            var dc = _peerConnection.createDataChannel(DATA_CHANNEL_LABEL, null);

            dc.onmessage += (msg) => logger.LogDebug($"data channel receive ({dc.label}-{dc.id}): {msg}");

            // Add inactive audio and video tracks.
            //MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List<SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }, MediaStreamStatusEnum.RecvOnly);
            //pc.addTrack(audioTrack);
            //MediaStreamTrack videoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List<SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.VP8) }, MediaStreamStatusEnum.Inactive);
            //pc.addTrack(videoTrack);

            _peerConnection.onicecandidateerror     += (candidate, error) => logger.LogWarning($"Error adding remote ICE candidate. {error} {candidate}");
            _peerConnection.onconnectionstatechange += (state) =>
            {
                logger.LogDebug($"Peer connection state changed to {state}.");

                if (state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed)
                {
                    _peerConnection.Close("remote disconnection");
                }
            };
            _peerConnection.OnReceiveReport += (ep, type, rtcp) => logger.LogDebug($"RTCP {type} report received.");
            _peerConnection.OnRtcpBye       += (reason) => logger.LogDebug($"RTCP BYE receive, reason: {(string.IsNullOrWhiteSpace(reason) ? "<none>" : reason)}.");

            _peerConnection.onicecandidate += (candidate) =>
            {
                if (_peerConnection.signalingState == RTCSignalingState.have_local_offer ||
                    _peerConnection.signalingState == RTCSignalingState.have_remote_offer)
                {
                    if (context != null &&
                        (_iceTypes.Count == 0 || _iceTypes.Any(x => x == candidate.type)))
                    {
                        var candidateInit = new RTCIceCandidateInit
                        {
                            sdpMid           = candidate.sdpMid,
                            sdpMLineIndex    = candidate.sdpMLineIndex,
                            usernameFragment = candidate.usernameFragment,
                            candidate        = "candidate:" + candidate.ToString()
                        };

                        context.WebSocket.Send(JsonConvert.SerializeObject(candidateInit));
                    }
                }
            };

            // Peer ICE connection state changes are for ICE events such as the STUN checks completing.
            _peerConnection.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state change to {state}.");

            _peerConnection.ondatachannel += (dc) =>
            {
                logger.LogDebug($"Data channel opened by remote peer, label {dc.label}, stream ID {dc.id}.");
                dc.onmessage += (msg) =>
                {
                    logger.LogDebug($"data channel ({dc.label}:{dc.id}): {msg}.");
                };
            };

            return(_peerConnection);
        }
Exemplo n.º 22
0
    private async void Start()
    {
        websocket = new WebSocket("ws://localhost:8000/socket");

        Debug.Log("GetSelectedSdpSemantics");
        var configuration = GetSelectedSdpSemantics();

        pc2 = new RTCPeerConnection(ref configuration);
        Debug.Log("Created remote peer connection object pc2");

        pc2.OnIceCandidate        = pc2OnIceCandidate;
        pc2.OnIceConnectionChange = pc2OnIceConnectionChange;
        pc2.OnDataChannel         = onDataChannel;

        RTCDataChannelInit conf = new RTCDataChannelInit();

        dataChannel        = pc2.CreateDataChannel("data", conf);
        dataChannel.OnOpen = onDataChannelOpen;

        //dataChannel.Send("3TEST");

        pc2OnIceConnectionChange = state => { OnIceConnectionChange(pc2, state); };
        pc2OnIceCandidate        = candidate => { OnIceCandidate(pc2, candidate); };

        textReceive.text += "0TEST";

        onDataChannel = channel =>
        {
            Debug.Log("Data Channel works!");
            textReceive.text     += "2TEST";
            dataChannel           = channel;
            dataChannel.OnMessage = onDataChannelMessage;
        };
        onDataChannelMessage = bytes =>
        {
            textReceive.text = System.Text.Encoding.UTF8.GetString(bytes);

            //         var epoint = new Uint8Array(event.data, 92);

            //   if (epoint.byteLength > 93) {

            //var dpoint = MessagePack.decode(epoint);
            //         console.log("Decoded pointarray:", dpoint);

            //var pointbuff = new Int8Array(dpoint);
            //         colate(pointbuff);
        };
        onDataChannelOpen  = () => { textReceive.text += "1TEST"; };
        onDataChannelClose = () => { sendButton.interactable = false; };

        websocket.OnOpen += () =>
        {
            Debug.Log("Connection open!");
        };

        websocket.OnError += (e) =>
        {
            Debug.Log("Error! " + e);
        };

        websocket.OnClose += (e) =>
        {
            Debug.Log("Connection closed!");
        };

        websocket.OnMessage += (bytes) =>
        {
            Debug.Log("Message: " + System.Text.Encoding.UTF8.GetString(bytes));

            Message message = JsonConvert.DeserializeObject <Message>(System.Text.Encoding.UTF8.GetString(bytes));
            Debug.Log("New message: " + message.type);

            switch (message.type)
            {
            case "offer":

                var remoteDesc = new RTCSessionDescription();
                remoteDesc.type = 0;
                remoteDesc.sdp  = message.sdp;
                pc2.SetRemoteDescription(ref remoteDesc);

                var answer = pc2.CreateAnswer(ref AnswerOptions);
                Debug.Log("Answer: " + answer.Desc.sdp);
                Debug.Log("Answer Desc: " + answer.Desc.type);

                var localDesc = new RTCSessionDescription();
                localDesc.type = answer.Desc.type;
                localDesc.sdp  = message.sdp;
                pc2.SetLocalDescription(ref localDesc);

                Message newMessage = new Message();

                newMessage.type = "answer";
                newMessage.sdp  = answer.Desc.sdp;

                string output = JsonConvert.SerializeObject(newMessage);

                websocket.SendText(output);

                break;

            case "candidate":
                RTCIceCandidateInit candidateMessage = new RTCIceCandidateInit();
                candidateMessage.candidate     = message.sdp;
                candidateMessage.sdpMLineIndex = 0;
                candidateMessage.sdpMid        = "";

                RTCIceCandidate candidate = new RTCIceCandidate(candidateMessage);

                pc2.AddIceCandidate(candidate);
                break;

            default:
                Debug.Log("P2: We got something from the signaling server but we don't know what it is!");
                Debug.Log("Take a look for yourself: " + message.type);
                Debug.Log("Take a look for yourself: " + message.sdp);
                break;
            }
        };

        await websocket.Connect();
    }
Exemplo n.º 23
0
 public Task AddIceCandidate(RTCIceCandidateInit candidate) =>
 JsRuntime.CallJsMethodVoidAsync(NativeObject, "addIceCandidate", candidate /*.NativeObject*/).AsTask();
Exemplo n.º 24
0
        public void MessageFromPeerTaskRun(int peerId, string content)
        {
            PeerId = peerId;

            Task.Run(async() =>
            {
                Debug.Assert(_peerId == PeerId || _peerId == -1);
                Debug.Assert(content.Length > 0);

                if (_peerId != PeerId && _peerId != -1)
                {
                    Debug.WriteLine("Received a message from unknown peer " +
                                    "while already in a conversation with a different peer.");

                    return;
                }

                if (!JsonObject.TryParse(content, out JsonObject jMessage))
                {
                    Debug.WriteLine($"Received unknown message: {content}");
                    return;
                }

                string type = jMessage.ContainsKey(NegotiationAtributes.Type)
                       ? jMessage.GetNamedString(NegotiationAtributes.Type)
                       : null;

                if (PeerConnection == null)
                {
                    if (!string.IsNullOrEmpty(type))
                    {
                        // Create the peer connection only when call is
                        // about to get initiated. Otherwise ignore the
                        // message from peers which could be result
                        // of old (but not yet fully closed) connections.
                        if (type == "offer" || type == "answer" || type == "json")
                        {
                            Debug.Assert(_peerId == -1);
                            _peerId = PeerId;

                            if (!CreatePeerConnection())
                            {
                                Debug.WriteLine("Failed to initialize our PeerConnection instance");

                                OnSignedOut.Invoke(this, null);
                                return;
                            }
                            else if (_peerId != PeerId)
                            {
                                Debug.WriteLine("Received a message from unknown peer while already " +
                                                "in a conversation with a different peer.");

                                return;
                            }
                        }
                    }
                    else
                    {
                        Debug.WriteLine("[Warn] Received an untyped message after closing peer connection.");
                        return;
                    }
                }

                if (PeerConnection != null && !string.IsNullOrEmpty(type))
                {
                    if (type == "offer-loopback")
                    {
                        // Loopback not supported
                        Debug.Assert(false);
                    }

                    string sdp = null;

                    sdp = jMessage.ContainsKey(NegotiationAtributes.Sdp)
                          ? jMessage.GetNamedString(NegotiationAtributes.Sdp)
                          : null;

                    if (string.IsNullOrEmpty(sdp))
                    {
                        Debug.WriteLine("[Error] Can't parse received session description message.");
                        return;
                    }

                    Debug.WriteLine($"Received session description:\n{content}");

                    RTCSdpType messageType = RTCSdpType.Offer;
                    switch (type)
                    {
                    case "offer": messageType = RTCSdpType.Offer; break;

                    case "answer": messageType = RTCSdpType.Answer; break;

                    case "pranswer": messageType = RTCSdpType.Pranswer; break;

                    default: Debug.Assert(false, type); break;
                    }

                    var sdpInit     = new RTCSessionDescriptionInit();
                    sdpInit.Sdp     = sdp;
                    sdpInit.Type    = messageType;
                    var description = new RTCSessionDescription(sdpInit);

                    await PeerConnection.SetRemoteDescription(description);

                    if (messageType == RTCSdpType.Offer)
                    {
                        var answerOptions             = new RTCAnswerOptions();
                        IRTCSessionDescription answer = await PeerConnection.CreateAnswer(answerOptions);
                        await PeerConnection.SetLocalDescription(answer);
                        string jsonString = SdpToJsonString(answer);
                        // Send answer
                        OnSendMessageToRemotePeer.Invoke(this, jsonString);
                    }
                }
                else
                {
                    RTCIceCandidate candidate = null;

                    string sdpMid = jMessage.ContainsKey(NegotiationAtributes.SdpMid)
                           ? jMessage.GetNamedString(NegotiationAtributes.SdpMid)
                           : null;

                    double sdpMLineIndex = jMessage.ContainsKey(NegotiationAtributes.SdpMLineIndex)
                           ? jMessage.GetNamedNumber(NegotiationAtributes.SdpMLineIndex)
                           : -1;

                    string sdpCandidate = jMessage.ContainsKey(NegotiationAtributes.Candidate)
                           ? jMessage.GetNamedString(NegotiationAtributes.Candidate)
                           : null;

                    if (string.IsNullOrEmpty(sdpMid) || sdpMLineIndex == -1 || string.IsNullOrEmpty(sdpCandidate))
                    {
                        Debug.WriteLine($"[Error] Can't parse received message.\n{content}");
                        return;
                    }

                    var candidateInit           = new RTCIceCandidateInit();
                    candidateInit.Candidate     = sdpCandidate;
                    candidateInit.SdpMid        = sdpMid;
                    candidateInit.SdpMLineIndex = (ushort)sdpMLineIndex;
                    candidate = new RTCIceCandidate(candidateInit);

                    await PeerConnection.AddIceCandidate(candidate);

                    Debug.WriteLine($"Receiving ice candidate:\n{content}");
                }
            }).Wait();
        }
Exemplo n.º 25
0
        async Task CreateOrDeletePeerConnectionAsync(Guid peerId, string peerName, bool isInitiator, bool isDelete = false)
        {
            try
            {
                PeerContext        peerContext    = null;
                IRTCPeerConnection peerConnection = null;
                IMediaStream       mediaStream    = null;
                IRTCDataChannel    dataChannel    = null;

                if (isDelete)
                {
                    peerContext    = _connectionContext.PeerContexts.Single(context => context.Id.Equals(peerId));
                    peerConnection = peerContext.PeerConnection;

                    peerConnection.OnConnectionStateChanged   -= OnConnectionStateChanged;
                    peerConnection.OnDataChannel              -= OnDataChannel;
                    peerConnection.OnIceCandidate             -= OnIceCandidate;
                    peerConnection.OnIceConnectionStateChange -= OnIceConnectionStateChange;
                    peerConnection.OnIceGatheringStateChange  -= OnIceGatheringStateChange;
                    peerConnection.OnNegotiationNeeded        -= OnNegotiationNeeded;
                    peerConnection.OnSignallingStateChange    -= OnSignallingStateChange;
                    peerConnection.OnTrack -= OnTrack;

                    // Remove local tracks and close.
                    var senders = peerConnection.GetSenders();
                    foreach (var sender in senders)
                    {
                        peerConnection.RemoveTrack(sender);
                    }
                    peerConnection.Close();

                    _connectionContext.PeerContexts.Remove(peerContext);
                }
                else
                {
                    mediaStream = _webRtc.Window(_jsRuntime).MediaStream();
                    RTCIceServer[] iceServers = _connectionContext.IceServers;
                    if (iceServers is null)
                    {
                        var result = await _signalingServerApi.GetIceServersAsync();

                        if (!result.IsOk)
                        {
                            throw new Exception($"{result.ErrorMessage}");
                        }
                        iceServers = result.Value;
                        _connectionContext.IceServers = iceServers;
                    }
                    var configuration = new RTCConfiguration
                    {
                        IceServers = iceServers,
                        //PeerIdentity = peerName
                    };

                    _logger.LogInformation($"################ LIST OF ICE SERVERS ################");
                    foreach (var iceServer in configuration.IceServers)
                    {
                        foreach (var url in iceServer.Urls)
                        {
                            _logger.LogInformation($"\t - {url}");
                        }
                    }
                    _logger.LogInformation($"#####################################################");

                    peerConnection = _webRtc.Window(_jsRuntime).RTCPeerConnection(configuration);
                    peerContext    = new PeerContext
                    {
                        Id             = peerId,
                        Name           = peerName,
                        PeerConnection = peerConnection,
                        IsInitiator    = isInitiator,
                    };
                    _connectionContext.PeerContexts.Add(peerContext);

                    peerConnection.OnConnectionStateChanged   += OnConnectionStateChanged;
                    peerConnection.OnDataChannel              += OnDataChannel;
                    peerConnection.OnIceCandidate             += OnIceCandidate;
                    peerConnection.OnIceConnectionStateChange += OnIceConnectionStateChange;
                    peerConnection.OnIceGatheringStateChange  += OnIceGatheringStateChange;
                    peerConnection.OnNegotiationNeeded        += OnNegotiationNeeded;
                    peerConnection.OnSignallingStateChange    += OnSignallingStateChange;
                    peerConnection.OnTrack += OnTrack;


                    if (_connectionContext.UserContext.DataChannelName is not null && isInitiator)
                    {
                        dataChannel = peerConnection.CreateDataChannel(
                            _connectionContext.UserContext.DataChannelName,
                            new RTCDataChannelInit
                        {
                            Negotiated = false,
                        });
                    }

                    if (_connectionContext.UserContext.LocalStream is not null)
                    {
                        var videoTrack = _connectionContext.UserContext.LocalStream.GetVideoTracks().FirstOrDefault();
                        var audioTrack = _connectionContext.UserContext.LocalStream.GetAudioTracks().FirstOrDefault();
                        if (videoTrack is not null)
                        {
                            peerConnection.AddTrack(videoTrack, _connectionContext.UserContext.LocalStream);
                        }
                        if (audioTrack is not null)
                        {
                            peerConnection.AddTrack(audioTrack, _connectionContext.UserContext.LocalStream);
                        }
                    }
                }

                void OnConnectionStateChanged(object s, EventArgs e)
                {
                    _logger.LogInformation(
                        $"######## OnConnectionStateChanged - room:{_connectionContext.UserContext.Room} " +
                        $"user:{_connectionContext.UserContext.Name} " +
                        $"peerUser:{peerName} " +
                        $"connectionState:{peerConnection.ConnectionState}");
                    if (peerConnection.ConnectionState == RTCPeerConnectionState.Connected)
                    {
                        _connectionContext.Observer.OnNext(new PeerResponse
                        {
                            Type        = PeerResponseType.PeerJoined,
                            Id          = peerId,
                            Name        = peerName,
                            MediaStream = mediaStream,
                            DataChannel = isInitiator ? dataChannel : null
                        });
                    }
                    //// WILL BE HANDLED BY PEER LEFT
                    //else if (peerConnection.ConnectionState == RTCPeerConnectionState.Disconnected)
                    //ConnectionResponseSubject.OnCompleted();
                }
                void OnDataChannel(object s, IRTCDataChannelEvent e)
                {
                    _logger.LogInformation(
                        $"######## OnDataChannel - room:{_connectionContext.UserContext.Room} " +
                        $"user:{_connectionContext.UserContext.Name} " +
                        $"peerUser:{peerName} " +
                        $"state:{e.Channel.ReadyState}");

                    dataChannel?.Close();
                    dataChannel?.Dispose();

                    dataChannel = e.Channel;
                    _connectionContext.Observer.OnNext(new PeerResponse
                    {
                        Type        = PeerResponseType.PeerJoined,
                        Name        = peerName,
                        MediaStream = null,
                        DataChannel = dataChannel
                    });
                }
                async void OnIceCandidate(object s, IRTCPeerConnectionIceEvent e)
                {
                    //_logger.LogInformation(
                    //    $"######## OnIceCandidate - room:{roomName} " +
                    //    $"user:{connectionContext.ConnectionRequestParameters.ConnectionParameters.UserName} " +
                    //    $"peerUser:{peerName}");

                    // 'null' is valid and indicates end of ICE gathering process.
                    if (e.Candidate is not null)
                    {
                        var iceCandidate = new RTCIceCandidateInit
                        {
                            Candidate     = e.Candidate.Candidate,
                            SdpMid        = e.Candidate.SdpMid,
                            SdpMLineIndex = e.Candidate.SdpMLineIndex,
                            //UsernameFragment = ???
                        };
                        var ice = JsonSerializer.Serialize(iceCandidate, JsonHelper.WebRtcJsonSerializerOptions);
                        _logger.LogInformation(
                            $"--------> Sending ICE Candidate - room:{_connectionContext.UserContext.Room} " +
                            $"user:{_connectionContext.UserContext.Name} " +
                            $"peerUser:{peerName} " +
                            $"ice:{ice}");
                        var result = await _signalingServerApi.IceAsync(peerId, ice);

                        if (!result.IsOk)
                        {
                            throw new Exception($"{result.ErrorMessage}");
                        }
                    }
                }
                void OnIceConnectionStateChange(object s, EventArgs e)
                {
                    _logger.LogInformation(
                        $"######## OnIceConnectionStateChange - room:{_connectionContext.UserContext.Room} " +
                        $"user:{_connectionContext.UserContext.Name} " +
                        $"peerUser:{peerName} " +
                        $"iceConnectionState:{peerConnection.IceConnectionState}");
                }
                void OnIceGatheringStateChange(object s, EventArgs e)
                {
                    _logger.LogInformation(
                        $"######## OnIceGatheringStateChange - room:{_connectionContext.UserContext.Room} " +
                        $"user:{_connectionContext.UserContext.Name} " +
                        $"peerUser:{peerName} " +
                        $"iceGatheringState: {peerConnection.IceGatheringState}");
                }
                void OnNegotiationNeeded(object s, EventArgs e)
                {
                    _logger.LogInformation(
                        $"######## OnNegotiationNeeded - room:{_connectionContext.UserContext.Room} " +
                        $"user:{_connectionContext.UserContext.Name} " +
                        $"peerUser:{peerName}");
                    // TODO: WHAT IF Not initiator adds track (which trigggers this event)???
                }
                void OnSignallingStateChange(object s, EventArgs e)
                {
                    _logger.LogInformation(
                        $"######## OnSignallingStateChange - room:{_connectionContext.UserContext.Room} " +
                        $"user:{_connectionContext.UserContext.Name} " +
                        $"peerUser:{peerName}, " +
                        $"signallingState:{ peerConnection.SignalingState }");
                    //RoomEventSubject.OnNext(new RoomEvent
                    //{
                    //    Code = RoomEventCode.PeerJoined,
                    //    RoomName = roomName,
                    //    PeerUserName = peerName,
                    //    MediaStream = mediaStream
                    //});
                }
                void OnTrack(object s, IRTCTrackEvent e)
                {
                    _logger.LogInformation(
                        $"######## OnTrack - room:{_connectionContext.UserContext.Room} " +
                        $"user:{_connectionContext.UserContext.Name} " +
                        $"peerUser:{peerName} " +
                        $"trackType:{e.Track.Kind}");
                    mediaStream.AddTrack(e.Track);
                }
            }
            catch (Exception ex)
            {
                _connectionContext?.Observer.OnNext(new PeerResponse
                {
                    Type         = PeerResponseType.PeerError,
                    Id           = peerId,
                    Name         = peerName,
                    ErrorMessage = ex.Message
                });
            }
        }
Exemplo n.º 26
0
        private static Task <RTCPeerConnection> Createpc(WebSocketContext context, RTCIceServer stunServer, bool relayOnly)
        {
            if (_peerConnection != null)
            {
                _peerConnection.Close("normal");
            }

            List <RTCCertificate> presetCertificates = null;

            if (File.Exists(LOCALHOST_CERTIFICATE_PATH))
            {
                var localhostCert = new X509Certificate2(LOCALHOST_CERTIFICATE_PATH, (string)null, X509KeyStorageFlags.Exportable);
                presetCertificates = new List <RTCCertificate> {
                    new RTCCertificate {
                        Certificate = localhostCert
                    }
                };
            }

            RTCConfiguration pcConfiguration = new RTCConfiguration
            {
                certificates = presetCertificates,
                //X_RemoteSignallingAddress = (context != null) ? context.UserEndPoint.Address : null,
                iceServers = stunServer != null ? new List <RTCIceServer> {
                    stunServer
                } : null,
                //iceTransportPolicy = RTCIceTransportPolicy.all,
                iceTransportPolicy = relayOnly ? RTCIceTransportPolicy.relay : RTCIceTransportPolicy.all,
                //X_BindAddress = IPAddress.Any, // NOTE: Not reqd. Using this to filter out IPv6 addresses so can test with Pion.
            };

            _peerConnection = new RTCPeerConnection(pcConfiguration);

            //_peerConnection.GetRtpChannel().MdnsResolve = (hostname) => Task.FromResult(NetServices.InternetDefaultAddress);
            _peerConnection.GetRtpChannel().MdnsResolve = MdnsResolve;
            //_peerConnection.GetRtpChannel().OnStunMessageReceived += (msg, ep, isrelay) => logger.LogDebug($"STUN message received from {ep}, message type {msg.Header.MessageType}.");

            var dc = _peerConnection.createDataChannel(DATA_CHANNEL_LABEL, null);

            dc.onmessage += (msg) => logger.LogDebug($"data channel receive ({dc.label}-{dc.id}): {msg}");

            // Add a send-only audio track (this doesn't require any native libraries for encoding so is good for x-platform testing).
            AudioExtrasSource audioSource = new AudioExtrasSource(new AudioEncoder(), new AudioSourceOptions {
                AudioSource = AudioSourcesEnum.SineWave
            });

            audioSource.OnAudioSourceEncodedSample += _peerConnection.SendAudio;

            MediaStreamTrack audioTrack = new MediaStreamTrack(audioSource.GetAudioSourceFormats(), MediaStreamStatusEnum.SendOnly);

            _peerConnection.addTrack(audioTrack);

            _peerConnection.OnAudioFormatsNegotiated += (sdpFormat) =>
                                                        audioSource.SetAudioSourceFormat(SDPMediaFormatInfo.GetAudioCodecForSdpFormat(sdpFormat.First().FormatCodec));

            _peerConnection.onicecandidateerror     += (candidate, error) => logger.LogWarning($"Error adding remote ICE candidate. {error} {candidate}");
            _peerConnection.onconnectionstatechange += async(state) =>
            {
                logger.LogDebug($"Peer connection state changed to {state}.");

                if (state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed)
                {
                    _peerConnection.Close("remote disconnection");
                }

                if (state == RTCPeerConnectionState.connected)
                {
                    await audioSource.StartAudio();
                }
                else if (state == RTCPeerConnectionState.closed)
                {
                    await audioSource.CloseAudio();
                }
            };
            _peerConnection.OnReceiveReport += (ep, type, rtcp) => logger.LogDebug($"RTCP {type} report received.");
            _peerConnection.OnRtcpBye       += (reason) => logger.LogDebug($"RTCP BYE receive, reason: {(string.IsNullOrWhiteSpace(reason) ? "<none>" : reason)}.");

            _peerConnection.onicecandidate += (candidate) =>
            {
                if (_peerConnection.signalingState == RTCSignalingState.have_local_offer ||
                    _peerConnection.signalingState == RTCSignalingState.have_remote_offer)
                {
                    if (context != null &&
                        (_iceTypes.Count == 0 || _iceTypes.Any(x => x == candidate.type)))
                    {
                        var candidateInit = new RTCIceCandidateInit
                        {
                            sdpMid           = candidate.sdpMid,
                            sdpMLineIndex    = candidate.sdpMLineIndex,
                            usernameFragment = candidate.usernameFragment,
                            candidate        = "candidate:" + candidate.ToString()
                        };

                        context.WebSocket.Send(JsonConvert.SerializeObject(candidateInit));
                    }
                }
            };

            // Peer ICE connection state changes are for ICE events such as the STUN checks completing.
            _peerConnection.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state change to {state}.");

            _peerConnection.ondatachannel += (dc) =>
            {
                logger.LogDebug($"Data channel opened by remote peer, label {dc.label}, stream ID {dc.id}.");
                dc.onmessage += (msg) =>
                {
                    logger.LogDebug($"data channel ({dc.label}:{dc.id}): {msg}.");
                };
            };

            return(Task.FromResult(_peerConnection));
        }