Ejemplo n.º 1
0
        public async Task <string> HandleUpdatedRemoteOffer(string sdp)
        {
            Logger.Debug("PeerChannel", "HandleUpdatedRemoteOffer");

            var sdpInit = new RTCSessionDescriptionInit()
            {
                Type = RTCSdpType.Offer,
                Sdp  = sdp,
            };

            Logger.Debug("PeerChannel", "SetRemoteOffer - SetRemoteDescription");

            await Conn.SetRemoteDescription(new RTCSessionDescription(sdpInit));

            var opts = new RTCAnswerOptions();

            Logger.Debug("PeerChannel", "SetRemoteOffer - CreateAnswer");

            var answer = await Conn.CreateAnswer(opts);

            Logger.Debug("PeerChannel", answer.Sdp);

            await Conn.SetLocalDescription(answer);

            Logger.Debug("PeerChannel", "local description set");

            return(answer.Sdp);
        }
Ejemplo n.º 2
0
        public async Task <IActionResult> Put(string from, string to, [FromBody] RTCSessionDescriptionInit sdp)
        {
            if (string.IsNullOrEmpty(to) || string.IsNullOrEmpty(from) || sdp == null || sdp.sdp == null)
            {
                _logger.LogWarning($"WebRTC signal controller PUT sdp request had invalid parameters.");
                return(BadRequest());
            }

            if (sdp.type == RTCSdpType.offer)
            {
                await ExpireExisting(from, to);
            }

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

            _context.WebRTCSignals.Add(sdpSignal);

            await _context.SaveChangesAsync();

            return(Ok());
        }
Ejemplo 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 JsonRoundtripUnitTest()
        {
            RTCPeerConnection pcSrc = new RTCPeerConnection(null);
            var videoTrackSrc       = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPAudioVideoMediaFormat> {
                new SDPAudioVideoMediaFormat(SDPMediaTypesEnum.video, 96, "VP8", 90000)
            });

            pcSrc.addTrack(videoTrackSrc);

            var offer = pcSrc.createOffer(new RTCOfferOptions());

            Assert.NotNull(offer.toJSON());

            logger.LogDebug($"offer: {offer.toJSON()}");

            var parseResult = RTCSessionDescriptionInit.TryParse(offer.toJSON(), out var init);

            Assert.True(parseResult);

            Assert.Equal(RTCSdpType.offer, init.type);
            Assert.NotNull(init.sdp);

            SDP sdp = SDP.ParseSDPDescription(init.sdp);

            Assert.Equal(0, sdp.Version);
        }
Ejemplo n.º 5
0
        public Task SetRemoteDescription(RTCSessionDescriptionInit sessionDescription)
        {
            var tcs = new TaskCompletionSource <object>();

            ((Webrtc.PeerConnection)NativeObject).SetRemoteDescription(
                new SdpObserverProxy(tcs), sessionDescription.ToNative());
            return(tcs.Task);
        }
Ejemplo n.º 6
0
 internal static WebRTCSessionDescription FromSessionDescription(RTCSessionDescriptionInit sessionDescription)
 {
     return(new WebRTCSessionDescription()
     {
         InternalType = sessionDescription.type,
         Sdp = sessionDescription.sdp
     });
 }
Ejemplo n.º 7
0
        /// <summary>
        /// Helper function. Serializes Session Description.
        /// </summary>
        /// <param name="description"></param>
        /// <returns></returns>
        private string SerializeSessionDescription(RTCSessionDescription description)
        {
            var init = new RTCSessionDescriptionInit()
            {
                Sdp  = description.Sdp,
                Type = description.SdpType
            };

            return(JsonConvert.SerializeObject(init));
        }
        public void ParseJavascriptJsonTest()
        {
            string json = "{\"type\":\"answer\",\"sdp\":\"v=0\r\no=- 3619871509827895381 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\na=msid-semantic: WMS\r\nm=video 9 UDP/TLS/RTP/SAVP 100\r\nc=IN IP4 0.0.0.0\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=ice-ufrag:XqpN\r\na=ice-pwd:VOXKGB0AIf10Kqtv+zcQKgLF\r\na=ice-options:trickle\r\na=fingerprint:sha-256 D4:6E:F7:FD:B3:4F:4D:3D:3A:B3:92:2C:CC:F6:4E:46:88:B7:01:E9:B7:E1:77:03:8E:BB:AA:DC:26:1B:9D:2E\r\na=setup:active\r\na=mid:0\r\na=recvonly\r\na=rtcp-mux\r\na=rtpmap:100 VP8/90000\r\n\"}";

            var parseResult = RTCSessionDescriptionInit.TryParse(json, out var init);

            Assert.True(parseResult);

            Assert.Equal(RTCSdpType.answer, init.type);
            Assert.NotNull(init.sdp);
        }
        public async Task <ActionResult> Answer(string id, [FromBody] RTCSessionDescriptionInit answer)
        {
            _logger.LogDebug($"Echo controller posting answer to {_echoTestRestUrl}/answer/{id}.");
            //_logger.LogDebug($"Answer={answer}");

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

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

            return(postResp.IsSuccessStatusCode ? Ok() : BadRequest(postResp.ReasonPhrase));
        }
Ejemplo n.º 10
0
        public async Task <string> HandleInitialRemoteOffer(string sdp)
        {
            Logger.Debug("PeerChannel", "HandleInitialRemoteOffer");

            if (Conn == null)
            {
                Logger.Debug("PeerChannel", "should initialize beforehand");
                return(string.Empty);
            }

            var sdpInit = new RTCSessionDescriptionInit()
            {
                Type = RTCSdpType.Offer,
                Sdp  = sdp,
            };

            Logger.Debug("PeerChannel", "SetRemoteOffer - SetRemoteDescription");

            await Conn.SetRemoteDescription(new RTCSessionDescription(sdpInit));

            if (mediaOption.VideoUpstreamEnabled)
            {
                Logger.Debug("PeerChannel", "video upstream setting");

                if (screen != null)
                {
                    await screen.StartCaptureAsync();
                }

                SetupVideoTrack();
            }

            if (mediaOption.AudioUpstreamEnabled)
            {
                Logger.Debug("PeerChannel", "audio upstream setting");

                SetupAudioTrack();
            }

            var opts = new RTCAnswerOptions();

            Logger.Debug("PeerChannel", "SetRemoteOffer - CreateAnswer");

            var answer = await Conn.CreateAnswer(opts);

            Logger.Debug("PeerChannel", answer.Sdp);

            await Conn.SetLocalDescription(answer);

            Logger.Debug("PeerChannel", "local description set");

            return(answer.Sdp);
        }
Ejemplo n.º 11
0
 public async Task SetRemoteDescription(string id, RTCSessionDescriptionInit description)
 {
     if (!_peerConnections.TryGetValue(id, out var pc))
     {
         throw new ApplicationException("No peer connection is available for the specified id.");
     }
     else
     {
         _logger.LogDebug("Answer SDP: " + description.sdp);
         await pc.setRemoteDescription(description);
     }
 }
Ejemplo n.º 12
0
        public Task SetRemoteDescription(RTCSessionDescriptionInit sessionDescription)
        {
            var tcs = new TaskCompletionSource <object>();

            ((Webrtc.RTCPeerConnection)NativeObject).SetRemoteDescription(
                sessionDescription.ToNative(),
                (nsError) =>
            {
                if (nsError != null)
                {
                    tcs.SetException(new Exception($"{nsError.LocalizedDescription}"));
                }
                tcs.SetResult(null);
            });
            return(tcs.Task);
        }
Ejemplo n.º 13
0
        public IActionResult SetAnswer(string id, [FromBody] RTCSessionDescriptionInit answer)
        {
            _logger.LogDebug($"SetAnswer {id} {answer?.type} {answer?.sdp}.");

            if (string.IsNullOrWhiteSpace(id))
            {
                return(BadRequest("The id cannot be empty in SetAnswer."));
            }
            else if (string.IsNullOrWhiteSpace(answer?.sdp))
            {
                return(BadRequest("The SDP answer cannot be empty in SetAnswer."));
            }

            _webRTCServer.SetRemoteDescription(id, answer);
            return(Ok());
        }
Ejemplo n.º 14
0
        public async Task <ICallInfo> PlaceCallAsync(CallConfiguration config)
        {
            Debug.Assert(_peerId == -1);

            if (PeerConnection != null)
            {
                Debug.WriteLine("[Error] We only support connection to one peer at a time.");
                return(null);
            }

            if (CreatePeerConnection())
            {
                string selectedAudioCodecName = (string)_localSettings.Values["SelectedAudioCodecName"];
                string selectedVideoCodecName = (string)_localSettings.Values["SelectedVideoCodecName"];

                _peerId = PeerId;

                var offerOptions = new RTCOfferOptions();
                offerOptions.OfferToReceiveAudio = true;
                offerOptions.OfferToReceiveVideo = true;
                IRTCSessionDescription offer = await PeerConnection.CreateOffer(offerOptions);

                // Alter sdp to force usage of selected codecs
                string modifiedSdp = offer.Sdp;
                SdpUtils.SelectCodec(ref modifiedSdp, selectedAudioCodecName, "audio");
                SdpUtils.SelectCodec(ref modifiedSdp, selectedVideoCodecName, "video");
                RTCSessionDescriptionInit sdpInit = new RTCSessionDescriptionInit();
                sdpInit.Sdp  = modifiedSdp;
                sdpInit.Type = offer.SdpType;
                var modifiedOffer = new RTCSessionDescription(sdpInit);
                await PeerConnection.SetLocalDescription(modifiedOffer);

                Debug.WriteLine($"Sending offer: {modifiedOffer.Sdp}");

                string jsonString = SdpToJsonString(modifiedOffer);

                CallInfo callInfo = new CallInfo();
                callInfo.SetCall(new Call());
                callInfo.SetSdp(modifiedSdp);
                callInfo.SetJsonString(jsonString);

                OnSendMessageToRemotePeer.Invoke(this, jsonString);

                return(callInfo);
            }
            return(null);
        }
Ejemplo n.º 15
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;
            }
        }
        public async Task <ActionResult <string> > Offer([FromBody] RTCSessionDescriptionInit offer)
        {
            _logger.LogDebug($"Echo controller posting offer to {_echoTestRestUrl}/offer.");
            _logger.LogDebug($"Offer={offer}");

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

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

            if (postResp.IsSuccessStatusCode)
            {
                var answer = await postResp.Content.ReadAsStringAsync();

                _logger.LogDebug($"Answer: {answer}.");
                return(answer);
            }
            else
            {
                return(BadRequest(postResp.ReasonPhrase));
            }
        }
Ejemplo n.º 17
0
        private async static Task Offer(IHttpContext context, int pcTimeout)
        {
            var offer = await context.GetRequestDataAsync <RTCSessionDescriptionInit>();

            var jsonOptions = new JsonSerializerOptions();

            jsonOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());

            var echoServer = new WebRTCEchoServer(_icePresets);
            var pc         = await echoServer.GotOffer(offer);

            if (pc != null)
            {
                var answer = new RTCSessionDescriptionInit {
                    type = RTCSdpType.answer, sdp = pc.localDescription.sdp.ToString()
                };
                context.Response.ContentType = "application/json";
                using (var responseStm = context.OpenResponseStream(false, false))
                {
                    await JsonSerializer.SerializeAsync(responseStm, answer, jsonOptions);
                }

                if (pcTimeout != 0)
                {
                    logger.LogDebug($"Setting peer connection close timeout to {pcTimeout} seconds.");

                    var timeout = new Timer((state) =>
                    {
                        if (!pc.IsClosed)
                        {
                            logger.LogWarning("Test timed out.");
                            pc.close();
                        }
                    }, null, pcTimeout * 1000, Timeout.Infinite);
                    pc.OnClosed += timeout.Dispose;
                }
            }
        }
Ejemplo n.º 18
0
        /// <summary>
        /// This application spits out a lot of log messages. In an attempt to make command entry slightly more usable
        /// this method attempts to always write the current command input as the bottom line on the console output.
        /// </summary>
        private static async Task ProcessInput(CancellationTokenSource cts)
        {
            // Local function to write the current command in the process of being entered.
            Action <int, string> writeCommandPrompt = (lastPromptRow, cmd) =>
            {
                // The cursor is already at the current row.
                if (Console.CursorTop == lastPromptRow)
                {
                    // The command was corrected. Need to re-write the whole line.
                    Console.SetCursorPosition(0, Console.CursorTop);
                    Console.Write(new string(' ', Console.WindowWidth));
                    Console.SetCursorPosition(0, Console.CursorTop);
                    Console.Write($"{COMMAND_PROMPT}{cmd}");
                }
                else
                {
                    // The cursor row has changed since the last input. Rewrite the prompt and command
                    // on the current line.
                    Console.Write($"{COMMAND_PROMPT}{cmd}");
                }
            };

            string command      = null;
            int    lastInputRow = Console.CursorTop;

            while (!cts.IsCancellationRequested)
            {
                var inKey = Console.ReadKey(true);

                if (inKey.Key == ConsoleKey.Enter)
                {
                    if (command == null)
                    {
                        Console.WriteLine();
                        Console.Write(COMMAND_PROMPT);
                    }
                    else
                    {
                        // Attempt to execute the current command.
                        switch (command.ToLower())
                        {
                        case "c":
                            // Close active peer connection.
                            if (_peerConnection != null)
                            {
                                Console.WriteLine();
                                Console.WriteLine("Closing peer connection");
                                _peerConnection.Close("user initiated");
                            }
                            break;

                        case var x when x.StartsWith("cdc"):
                            // Attempt to create a new data channel.
                            if (_peerConnection != null)
                            {
                                (_, var label) = x.Split(" ", 2, StringSplitOptions.None);
                                if (!string.IsNullOrWhiteSpace(label))
                                {
                                    Console.WriteLine();
                                    Console.WriteLine($"Creating data channel for label {label}.");
                                    var dc = _peerConnection.createDataChannel(label, null);
                                    dc.onmessage += (msg) => logger.LogDebug($" data channel message received on {label}: {msg}");
                                }
                                else
                                {
                                    Console.WriteLine();
                                    Console.WriteLine($"Send message command was in the wrong format. Needs to be: cdc <label>");
                                }
                            }

                            break;

                        case var x when x.StartsWith("ldc"):
                            // List data channels.
                            if (_peerConnection != null)
                            {
                                if (_peerConnection.DataChannels.Count > 0)
                                {
                                    Console.WriteLine();
                                    foreach (var dc in _peerConnection.DataChannels)
                                    {
                                        Console.WriteLine($" data channel: label {dc.label}, stream ID {dc.id}, is open {dc.IsOpened}.");
                                    }
                                }
                                else
                                {
                                    Console.WriteLine();
                                    Console.WriteLine(" no data channels available.");
                                }
                            }

                            break;

                        case var x when x.StartsWith("sdc"):
                            // Send data channel message.
                            if (_peerConnection != null)
                            {
                                (_, var label, var msg) = x.Split(" ", 3, StringSplitOptions.None);
                                if (!string.IsNullOrWhiteSpace(label) && !string.IsNullOrWhiteSpace(msg))
                                {
                                    Console.WriteLine();
                                    Console.WriteLine($"Sending message on channel {label}: {msg}");

                                    var dc = _peerConnection.DataChannels.FirstOrDefault(x => x.label == label && x.IsOpened);
                                    if (dc != null)
                                    {
                                        dc.send(msg);
                                    }
                                    else
                                    {
                                        Console.WriteLine($"No data channel was found for label {label}.");
                                    }
                                }
                                else
                                {
                                    Console.WriteLine();
                                    Console.WriteLine($"Send data channel message command was in the wrong format. Needs to be: sdc <label> <message>");
                                }
                            }

                            break;

                        case "q":
                            // Quit.
                            Console.WriteLine();
                            Console.WriteLine("Quitting...");
                            cts.Cancel();
                            break;

                        case "isalive":
                            // Check responsiveness.
                            Console.WriteLine();
                            Console.WriteLine("yep");
                            Console.Write(COMMAND_PROMPT);
                            break;

                        case var x when x.StartsWith("node"):
                            (_, var sdpType, var myUser, string theirUser) = x.Split(" ", 4, StringSplitOptions.None);

                            if (sdpType == "so")
                            {
                                _peerConnection = Createpc(null, _stunServer);

                                var offerSdp = _peerConnection.createOffer(null);
                                await _peerConnection.setLocalDescription(offerSdp);

                                Console.WriteLine($"Our Offer:\n{offerSdp.sdp}");

                                var offerJson = JsonConvert.SerializeObject(offerSdp, new Newtonsoft.Json.Converters.StringEnumConverter());

                                var content = new StringContent(offerJson, Encoding.UTF8, "application/json");
                                var res     = await _nodeDssclient.PostAsync($"{_nodeDssUri}data/{theirUser}", content);

                                Console.WriteLine($"node-dss POST result {res.StatusCode}.");
                            }
                            else if (sdpType == "go")
                            {
                                var res = await _nodeDssclient.GetAsync($"{_nodeDssUri}data/{myUser}");

                                Console.WriteLine($"node-dss GET result {res.StatusCode}.");

                                if (res.StatusCode == HttpStatusCode.OK)
                                {
                                    var content = await res.Content.ReadAsStringAsync();

                                    RTCSessionDescriptionInit offerInit = JsonConvert.DeserializeObject <RTCSessionDescriptionInit>(content);

                                    Console.WriteLine($"Remote offer:\n{offerInit.sdp}");

                                    _peerConnection = Createpc(null, _stunServer);

                                    var setRes = _peerConnection.setRemoteDescription(offerInit);
                                    if (setRes != SetDescriptionResultEnum.OK)
                                    {
                                        // No point continuing. Something will need to change and then try again.
                                        _peerConnection.Close("failed to set remote sdp offer");
                                    }
                                    else
                                    {
                                        var answer = _peerConnection.createAnswer(null);
                                        await _peerConnection.setLocalDescription(answer);

                                        Console.WriteLine($"Our answer:\n{answer.sdp}");

                                        var answerJson    = JsonConvert.SerializeObject(answer, new Newtonsoft.Json.Converters.StringEnumConverter());
                                        var answerContent = new StringContent(answerJson, Encoding.UTF8, "application/json");
                                        var postRes       = await _nodeDssclient.PostAsync($"{_nodeDssUri}data/{theirUser}", answerContent);

                                        Console.WriteLine($"node-dss POST result {res.StatusCode}.");
                                    }
                                }
                            }
                            else if (sdpType == "ga")
                            {
                                var res = await _nodeDssclient.GetAsync($"{_nodeDssUri}data/{myUser}");

                                Console.WriteLine($"node-dss GET result {res.StatusCode}.");

                                if (res.StatusCode == HttpStatusCode.OK)
                                {
                                    var content = await res.Content.ReadAsStringAsync();

                                    RTCSessionDescriptionInit answerInit = JsonConvert.DeserializeObject <RTCSessionDescriptionInit>(content);

                                    Console.WriteLine($"Remote answer:\n{answerInit.sdp}");

                                    var setRes = _peerConnection.setRemoteDescription(answerInit);
                                    if (setRes != SetDescriptionResultEnum.OK)
                                    {
                                        // No point continuing. Something will need to change and then try again.
                                        _peerConnection.Close("failed to set remote sdp answer");
                                    }
                                }
                            }
                            break;

                        default:
                            // Command not recognised.
                            Console.WriteLine();
                            Console.WriteLine($"Unknown command: {command}");
                            Console.Write(COMMAND_PROMPT);
                            break;
                        }

                        command = null;
                    }
                }
                else if (inKey.Key == ConsoleKey.UpArrow)
                {
                    // Convenience mechanism to get the current input prompt without
                    // needing to change the command being entered.
                    writeCommandPrompt(lastInputRow, command);
                }
                else if (inKey.Key == ConsoleKey.Escape)
                {
                    // Escape key clears the current command.
                    command = null;
                    writeCommandPrompt(lastInputRow, command);
                }
                else if (inKey.Key == ConsoleKey.Backspace)
                {
                    // Backspace removes the last character.
                    command = (command?.Length > 0) ? command.Substring(0, command.Length - 1) : null;
                    writeCommandPrompt(lastInputRow, command);
                }
                else if (!Char.IsControl(inKey.KeyChar))
                {
                    // Non-control character, append to current command.
                    command += inKey.KeyChar;
                    if (Console.CursorTop == lastInputRow)
                    {
                        Console.Write(inKey.KeyChar);
                    }
                    else
                    {
                        writeCommandPrompt(lastInputRow, command);
                    }
                }

                lastInputRow = Console.CursorTop;
            }
        }
Ejemplo n.º 19
0
        private async void OnConnectionStateChanged(RTCPeerConnectionState state)
        {
            if (state == RTCPeerConnectionState.failed)
            {
                if (!_connectionCompleted)
                {
                    if (!_rolesReversed)
                    {
                        Logger.LogInformation("First connection attempt failed. Reversing roles...");

                        _rolesReversed        = true;
                        _canSendIceCandidates = false;

                        DisposeConnection();
                        InitConnection();

                        if (Role == WebRTCAdapterRole.Accept)
                        {
                            try
                            {
                                Logger.LogInformation("Creating offer...");

                                RTCSessionDescriptionInit offer = _connection.createOffer(new RTCOfferOptions());

                                Logger.LogInformation("Setting local description...");
                                await _connection.setLocalDescription(offer);

                                Logger.LogInformation("Sending offer request...");

                                var response = await _signalingTransporter.SendRequestAsync <WebRTCOfferRequest, WebRTCOfferResponse>(new WebRTCOfferRequest()
                                {
                                    ChannelName = ChannelName,
                                    Offer       = WebRTCSessionDescription.FromSessionDescription(offer)
                                }, new ResonanceRequestConfig()
                                {
                                    Timeout = TimeSpan.FromSeconds(10)
                                });

                                if (response.Answer.InternalType == RTCSdpType.answer)
                                {
                                    var result = _connection.setRemoteDescription(response.Answer.ToSessionDescription());

                                    if (result != SetDescriptionResultEnum.OK)
                                    {
                                        throw new Exception("Error setting the remote description.");
                                    }
                                }
                                else
                                {
                                    Logger.LogError($"Invalid answer type received '{response.Answer.InternalType}'.");
                                }

                                FlushIceCandidates();
                            }
                            catch (Exception ex)
                            {
                                FailConnection(ex);
                            }
                        }
                    }
                    else
                    {
                        FailConnection(new Exception("Second connection attempt failed."));
                    }
                }
                else
                {
                    OnFailed(new ResonanceWebRTCChannelClosedException("The WebRTC connection has failed."));
                }
            }
        }
Ejemplo n.º 20
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();
        }
Ejemplo n.º 21
0
        public async Task <RTCSessionDescriptionInit> GotOffer(RTCSessionDescriptionInit offer)
        {
            _logger.LogDebug($"SDP offer received.");
            _logger.LogTrace($"Offer SDP:\n{offer.sdp}");

            var pc = new RTCPeerConnection();

            if (_publicIPv4 != null)
            {
                var rtpPort             = pc.GetRtpChannel().RTPPort;
                var publicIPv4Candidate = new RTCIceCandidate(RTCIceProtocol.udp, _publicIPv4, (ushort)rtpPort, RTCIceCandidateType.host);
                pc.addLocalIceCandidate(publicIPv4Candidate);
            }

            if (_publicIPv6 != null)
            {
                var rtpPort             = pc.GetRtpChannel().RTPPort;
                var publicIPv6Candidate = new RTCIceCandidate(RTCIceProtocol.udp, _publicIPv6, (ushort)rtpPort, RTCIceCandidateType.host);
                pc.addLocalIceCandidate(publicIPv6Candidate);
            }

            MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false,
                                                               new List <SDPAudioVideoMediaFormat> {
                new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU)
            }, MediaStreamStatusEnum.SendRecv);

            pc.addTrack(audioTrack);
            MediaStreamTrack videoTrack = new MediaStreamTrack(new VideoFormat(VideoCodecsEnum.VP8, VP8_PAYLOAD_ID), MediaStreamStatusEnum.SendRecv);

            pc.addTrack(videoTrack);

            pc.OnRtpPacketReceived += (IPEndPoint rep, SDPMediaTypesEnum media, RTPPacket rtpPkt) =>
            {
                pc.SendRtpRaw(media, rtpPkt.Payload, rtpPkt.Header.Timestamp, rtpPkt.Header.MarkerBit, rtpPkt.Header.PayloadType);
                //_logger.LogDebug($"RTP {media} pkt received, SSRC {rtpPkt.Header.SyncSource}, SeqNum {rtpPkt.Header.SequenceNumber}.");
            };
            //peerConnection.OnReceiveReport += RtpSession_OnReceiveReport;
            //peerConnection.OnSendReport += RtpSession_OnSendReport;

            pc.OnTimeout += (mediaType) => _logger.LogWarning($"Timeout for {mediaType}.");
            pc.onconnectionstatechange += (state) =>
            {
                _logger.LogDebug($"Peer connection state changed to {state}.");

                if (state == RTCPeerConnectionState.failed)
                {
                    pc.Close("ice failure");
                }
            };

            var setResult = pc.setRemoteDescription(offer);

            if (setResult == SetDescriptionResultEnum.OK)
            {
                var offerSdp = pc.createOffer(null);
                await pc.setLocalDescription(offerSdp);

                var answer = pc.createAnswer(null);
                await pc.setLocalDescription(answer);

                _logger.LogTrace($"Answer SDP:\n{answer.sdp}");

                return(answer);
            }
            else
            {
                return(null);
            }
        }
Ejemplo n.º 22
0
 public Task SetRemoteDescription(RTCSessionDescriptionInit sessionDescription) =>
 JsRuntime.CallJsMethodVoidAsync(NativeObject, "setRemoteDescription", sessionDescription)
 .AsTask();
Ejemplo n.º 23
0
        private async static Task OfferAsync(IHttpContext context)
        {
            var offer = await context.GetRequestDataAsync <RTCSessionDescriptionInit>();

            logger.LogDebug($"SDP Offer={offer.sdp}");

            var jsonOptions = new JsonSerializerOptions();

            jsonOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());

            CancellationTokenSource cts = new CancellationTokenSource();

            var ws = new ClientWebSocket();
            await ws.ConnectAsync(new Uri(KURENTO_JSONRPC_URL), cts.Token);

            logger.LogDebug($"Successfully connected web socket client to {KURENTO_JSONRPC_URL}.");

            try
            {
                using (var jsonRpc = new JsonRpc(new WebSocketMessageHandler(ws)))
                {
                    jsonRpc.AddLocalRpcMethod("ping", new Action(() =>
                    {
                        logger.LogDebug($"Ping received");
                    }
                                                                 ));

                    jsonRpc.AddLocalRpcMethod("onEvent", new Action <KurentoEvent>((evt) =>
                    {
                        logger.LogDebug($"Event received type={evt.type}, source={evt.data.source}");
                    }
                                                                                   ));

                    jsonRpc.StartListening();

                    // Check the server is there.
                    var pingResult = await jsonRpc.InvokeWithParameterObjectAsync <KurentoResult>(
                        KurentoMethodsEnum.ping.ToString(),
                        new { interval = KEEP_ALIVE_INTERVAL_MS },
                        cts.Token);

                    logger.LogDebug($"Ping result={pingResult.value}.");

                    // Create a media pipeline.
                    var createPipelineResult = await jsonRpc.InvokeWithParameterObjectAsync <KurentoResult>(
                        KurentoMethodsEnum.create.ToString(),
                        new { type = "MediaPipeline" },
                        cts.Token);

                    logger.LogDebug($"Create media pipeline result={createPipelineResult.value}, sessionID={createPipelineResult.sessionId}.");

                    var sessionID     = createPipelineResult.sessionId;
                    var mediaPipeline = createPipelineResult.value;

                    // Create a WebRTC end point.
                    var createEndPointResult = await jsonRpc.InvokeWithParameterObjectAsync <KurentoResult>(
                        KurentoMethodsEnum.create.ToString(),
                        new
                    {
                        type = "WebRtcEndpoint",
                        constructorParams = new { mediaPipeline = mediaPipeline },
                        sessionId         = sessionID
                    },
                        cts.Token);

                    logger.LogDebug($"Create WebRTC endpoint result={createEndPointResult.value}.");

                    var webRTCEndPointID = createEndPointResult.value;

                    // Connect the WebRTC end point to itself to create a loopback connection (no result for this operation).
                    await jsonRpc.InvokeWithParameterObjectAsync <KurentoResult>(
                        KurentoMethodsEnum.invoke.ToString(),
                        new
                    {
                        @object         = webRTCEndPointID,
                        operation       = "connect",
                        operationParams = new { sink = webRTCEndPointID },
                        sessionId       = sessionID
                    },
                        cts.Token);

                    // Subscribe for events from the WebRTC end point.
                    var subscribeResult = await jsonRpc.InvokeWithParameterObjectAsync <KurentoResult>(
                        KurentoMethodsEnum.subscribe.ToString(),
                        new
                    {
                        @object   = webRTCEndPointID,
                        type      = "IceCandidateFound",
                        sessionId = sessionID
                    },
                        cts.Token);

                    logger.LogDebug($"Subscribe to WebRTC endpoint subscription ID={subscribeResult.value}.");

                    var subscriptionID = subscribeResult.value;

                    subscribeResult = await jsonRpc.InvokeWithParameterObjectAsync <KurentoResult>(
                        KurentoMethodsEnum.subscribe.ToString(),
                        new
                    {
                        @object   = webRTCEndPointID,
                        type      = "OnIceCandidate",
                        sessionId = sessionID
                    },
                        cts.Token);

                    logger.LogDebug($"Subscribe to WebRTC endpoint subscription ID={subscribeResult.value}.");

                    // Send SDP offer.
                    var processOfferResult = await jsonRpc.InvokeWithParameterObjectAsync <KurentoResult>(
                        KurentoMethodsEnum.invoke.ToString(),
                        new
                    {
                        @object         = webRTCEndPointID,
                        operation       = "processOffer",
                        operationParams = new { offer = offer.sdp },
                        sessionId       = sessionID
                    },
                        cts.Token);

                    logger.LogDebug($"SDP answer={processOfferResult.value}.");

                    RTCSessionDescriptionInit answerInit = new RTCSessionDescriptionInit
                    {
                        type = RTCSdpType.answer,
                        sdp  = processOfferResult.value
                    };

                    context.Response.ContentType = "application/json";
                    using (var responseStm = context.OpenResponseStream(false, false))
                    {
                        await JsonSerializer.SerializeAsync(responseStm, answerInit, jsonOptions);
                    }

                    // Tell Kurento to start ICE.
                    var gatherCandidatesResult = await jsonRpc.InvokeWithParameterObjectAsync <KurentoResult>(
                        KurentoMethodsEnum.invoke.ToString(),
                        new
                    {
                        @object   = webRTCEndPointID,
                        operation = "gatherCandidates",
                        sessionId = sessionID
                    },
                        cts.Token);

                    logger.LogDebug($"Gather candidates result={gatherCandidatesResult.value}.");
                }
            }
            catch (RemoteInvocationException invokeExcp)
            {
                logger.LogError($"JSON RPC invoke exception, error code={invokeExcp.ErrorCode}, msg={invokeExcp.Message}.");
            }
        }
Ejemplo n.º 24
0
 public static Webrtc.RTCSessionDescription ToNative(this RTCSessionDescriptionInit description) =>
 new Webrtc.RTCSessionDescription(description.Type.ToNative(), description.Sdp);
Ejemplo n.º 25
0
        static async Task Main()
        {
            Console.WriteLine("WebRTC Echo Test Client");

            logger = AddConsoleLogger();

            CancellationTokenSource cts = new CancellationTokenSource();

            #region Set up a simple Windows Form with two picture boxes.

            _form          = new Form();
            _form.AutoSize = true;
            _form.BackgroundImageLayout = ImageLayout.Center;
            _sourceVideoPicBox          = new PictureBox
            {
                Size     = new Size(VIDEO_FRAME_WIDTH, VIDEO_FRAME_HEIGHT),
                Location = new Point(0, 0),
                Visible  = true
            };
            _echoVideoPicBox = new PictureBox
            {
                Size     = new Size(VIDEO_FRAME_WIDTH, VIDEO_FRAME_HEIGHT),
                Location = new Point(0, VIDEO_FRAME_HEIGHT),
                Visible  = true
            };
            _form.Controls.Add(_sourceVideoPicBox);
            _form.Controls.Add(_echoVideoPicBox);

            Application.EnableVisualStyles();
            ThreadPool.QueueUserWorkItem(delegate { Application.Run(_form); });
            _form.FormClosing += (sender, e) => _isFormActivated = false;
            _form.Activated   += (sender, e) => _isFormActivated = true;
            //_form.FormClosed += (sender, e) => // TODO.

            #endregion

            // Video sink and source to generate and consume VP8 video streams.
            var testPattern  = new VideoTestPatternSource(new VpxVideoEncoder());
            var vp8VideoSink = new VideoEncoderEndPoint();

            #region Connect the video frames generated from the sink and source to the Windows form.

            testPattern.OnVideoSourceRawSample += (uint durationMilliseconds, int width, int height, byte[] sample, VideoPixelFormatsEnum pixelFormat) =>
            {
                if (_isFormActivated)
                {
                    _form?.BeginInvoke(new Action(() =>
                    {
                        if (_form.Handle != IntPtr.Zero)
                        {
                            unsafe
                            {
                                fixed(byte *s = sample)
                                {
                                    var bmpImage             = new Bitmap(width, height, width * 3, System.Drawing.Imaging.PixelFormat.Format24bppRgb, (IntPtr)s);
                                    _sourceVideoPicBox.Image = bmpImage;
                                }
                            }
                        }
                    }));
                }
            };

            vp8VideoSink.OnVideoSinkDecodedSample += (byte[] bmp, uint width, uint height, int stride, VideoPixelFormatsEnum pixelFormat) =>
            {
                if (_isFormActivated)
                {
                    _form?.BeginInvoke(new Action(() =>
                    {
                        if (_form.Handle != IntPtr.Zero)
                        {
                            unsafe
                            {
                                fixed(byte *s = bmp)
                                {
                                    var bmpImage           = new Bitmap((int)width, (int)height, stride, PixelFormat.Format24bppRgb, (IntPtr)s);
                                    _echoVideoPicBox.Image = bmpImage;
                                }
                            }
                        }
                    }));
                }
            };

            #endregion

            await testPattern.StartVideo().ConfigureAwait(false);

            var pc = await CreatePeerConnection(testPattern, vp8VideoSink).ConfigureAwait(false);

            Console.WriteLine($"Sending offer to {SIGNALING_SERVER}.");

            var signaler = new HttpClient();

            var offer = pc.createOffer(null);
            await pc.setLocalDescription(offer).ConfigureAwait(false);

            var content  = new StringContent(offer.toJSON(), Encoding.UTF8, "application/json");
            var response = await signaler.PostAsync($"{SIGNALING_SERVER}", content).ConfigureAwait(false);

            var answerStr = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

            if (RTCSessionDescriptionInit.TryParse(answerStr, out var answerInit))
            {
                var setAnswerResult = pc.setRemoteDescription(answerInit);
                if (setAnswerResult != SetDescriptionResultEnum.OK)
                {
                    Console.WriteLine($"Set remote description failed {setAnswerResult}.");
                }
            }
            else
            {
                Console.WriteLine("Failed to parse SDP answer from signaling server.");
            }

            Console.WriteLine("Press any key to exit.");
            Console.ReadLine();
        }
Ejemplo n.º 26
0
 public Task SendSdp(string signalingId, string displayName, RTCSessionDescriptionInit sessionDescription)
 {
     return(Clients.Client(signalingId).SendAsync("Sdp", Context.ConnectionId, displayName, sessionDescription));
 }
Ejemplo n.º 27
0
        public async Task <RTCSessionDescriptionInit> GotOffer(RTCSessionDescriptionInit offer)
        {
            logger.LogTrace($"SDP offer received.");
            logger.LogTrace(offer.sdp);

            var pc = new RTCPeerConnection();

            if (_presetIceAddresses != null)
            {
                foreach (var addr in _presetIceAddresses)
                {
                    var rtpPort             = pc.GetRtpChannel().RTPPort;
                    var publicIPv4Candidate = new RTCIceCandidate(RTCIceProtocol.udp, addr, (ushort)rtpPort, RTCIceCandidateType.host);
                    pc.addLocalIceCandidate(publicIPv4Candidate);
                }
            }

            MediaStreamTrack audioTrack = new MediaStreamTrack(SDPWellKnownMediaFormatsEnum.PCMU);

            pc.addTrack(audioTrack);
            MediaStreamTrack videoTrack = new MediaStreamTrack(new VideoFormat(VideoCodecsEnum.VP8, VP8_PAYLOAD_ID));

            pc.addTrack(videoTrack);

            pc.OnRtpPacketReceived += (IPEndPoint rep, SDPMediaTypesEnum media, RTPPacket rtpPkt) =>
            {
                pc.SendRtpRaw(media, rtpPkt.Payload, rtpPkt.Header.Timestamp, rtpPkt.Header.MarkerBit, rtpPkt.Header.PayloadType);
            };

            pc.OnTimeout += (mediaType) => logger.LogWarning($"Timeout for {mediaType}.");
            pc.oniceconnectionstatechange += (state) => logger.LogInformation($"ICE connection state changed to {state}.");
            pc.onsignalingstatechange     += () => logger.LogInformation($"Signaling state changed to {pc.signalingState}.");
            pc.onconnectionstatechange    += (state) =>
            {
                logger.LogInformation($"Peer connection state changed to {state}.");
                if (state == RTCPeerConnectionState.failed)
                {
                    pc.Close("ice failure");
                }
            };

            var setResult = pc.setRemoteDescription(offer);

            if (setResult == SetDescriptionResultEnum.OK)
            {
                var offerSdp = pc.createOffer(null);
                await pc.setLocalDescription(offerSdp);

                var answer = pc.createAnswer(null);

                logger.LogTrace($"SDP answer created.");
                logger.LogTrace(answer.sdp);

                return(answer);
            }
            else
            {
                logger.LogWarning($"Failed to set remote description {setResult}.");
                return(null);
            }
        }
Ejemplo n.º 28
0
        static async Task <int> Main(string[] args)
        {
            Console.WriteLine("Starting webrtc echo test client.");

            string echoServerUrl = DEFAULT_ECHO_SERVER_URL;

            if (args?.Length > 0)
            {
                echoServerUrl = args[0];
            }

            logger = AddConsoleLogger(LogEventLevel.Verbose);

            var pc    = CreatePeerConnection();
            var offer = pc.createOffer(null);
            await pc.setLocalDescription(offer);

            bool didConnect = false;
            TaskCompletionSource <int> connectResult = new TaskCompletionSource <int>();

            pc.onconnectionstatechange += (state) =>
            {
                logger.LogInformation($"Peer connection state changed to {state}.");

                if (state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed)
                {
                    pc.Close("remote disconnection");
                }
                else if (state == RTCPeerConnectionState.connected)
                {
                    didConnect = true;
                    pc.Close("normal");
                }
                else if (state == RTCPeerConnectionState.closed)
                {
                    connectResult.SetResult(didConnect ? SUCCESS_RESULT : FAILURE_RESULT);
                }
            };

            logger.LogInformation($"Posting offer to {echoServerUrl}.");

            var httpClient = new HttpClient();
            var content    = new StringContent(offer.toJSON(), Encoding.UTF8, "application/json");
            var response   = await httpClient.PostAsync(echoServerUrl, content);

            var answerStr = await response.Content.ReadAsStringAsync();

            if (RTCSessionDescriptionInit.TryParse(answerStr, out var answerInit))
            {
                var setAnswerResult = pc.setRemoteDescription(answerInit);
                if (setAnswerResult != SetDescriptionResultEnum.OK)
                {
                    logger.LogWarning($"Set remote description failed {setAnswerResult}.");
                }
            }
            else
            {
                logger.LogWarning("Failed to parse SDP answer from echo server.");
            }

            var result = await connectResult.Task;

            logger.LogInformation($"Connection result {result}.");

            return(result);
        }
Ejemplo n.º 29
0
        private static async Task RunCommand(Options options, bool noOptions)
        {
            // Plumbing code to facilitate a graceful exit.
            CancellationTokenSource exitCts = new CancellationTokenSource(); // Cancellation token to stop the SIP transport and RTP stream.

            //ManualResetEvent exitMre = new ManualResetEvent(false);

            logger = AddConsoleLogger();

            // Start MDNS server.
            var mdnsServer = new ServiceDiscovery();

            if (options.StunServer != null)
            {
                string[] fields = options.StunServer.Split(';');

                _stunServer = new RTCIceServer
                {
                    urls           = fields[0],
                    username       = fields.Length > 1 ? fields[1] : null,
                    credential     = fields.Length > 2 ? fields[2] : null,
                    credentialType = RTCIceCredentialType.password
                };
            }

            _relayOnly = options.RelayOnly;

            if (!string.IsNullOrEmpty(options.IceTypes))
            {
                options.IceTypes.Split().ToList().ForEach(x =>
                {
                    if (Enum.TryParse <RTCIceCandidateType>(x, out var iceType))
                    {
                        _iceTypes.Add(iceType);
                    }
                });

                if (!_iceTypes.Any(x => x == RTCIceCandidateType.host))
                {
                    _offerOptions = new RTCOfferOptions {
                        X_ExcludeIceCandidates = true
                    };
                    _answerOptions = new RTCAnswerOptions {
                        X_ExcludeIceCandidates = true
                    };
                }
            }

            if (!string.IsNullOrEmpty(options.AcceptIceTypes))
            {
                options.AcceptIceTypes.Split().ToList().ForEach(x =>
                {
                    if (Enum.TryParse <RTCIceCandidateType>(x, out var iceType))
                    {
                        _acceptIceTypes.Add(iceType);
                    }
                });
            }

            if (options.UseWebSocket || options.UseSecureWebSocket || noOptions)
            {
                // Start web socket.
                Console.WriteLine("Starting web socket server...");
                _webSocketServer = new WebSocketServer(IPAddress.Any, WEBSOCKET_PORT, options.UseSecureWebSocket);
                if (options.UseSecureWebSocket)
                {
                    _webSocketServer.SslConfiguration.ServerCertificate          = new X509Certificate2(LOCALHOST_CERTIFICATE_PATH);
                    _webSocketServer.SslConfiguration.CheckCertificateRevocation = false;
                }
                _webSocketServer.AddWebSocketService <WebRTCWebSocketPeer>("/", (peer) =>
                {
                    peer.OfferOptions = _offerOptions;
                    if (_acceptIceTypes != null && _acceptIceTypes.Count > 0)
                    {
                        peer.FilterRemoteICECandidates = (init) => _acceptIceTypes.Any(x => x == RTCIceCandidate.Parse(init.candidate).type);
                    }
                    peer.CreatePeerConnection = CreatePeerConnection;
                });
                _webSocketServer.Start();

                Console.WriteLine($"Waiting for browser web socket connection to {_webSocketServer.Address}:{_webSocketServer.Port}...");
            }
            else if (!string.IsNullOrWhiteSpace(options.WebSocketServer))
            {
                // We are the client for a web socket server. The JSON signalling exchange still occurs the same way as when the web socket
                // server option is used except that as the web socket client we receive the SDP offer from the server.
                WebRTCWebSocketClient wsockClient = new WebRTCWebSocketClient(options.WebSocketServer, CreatePeerConnection);
                await wsockClient.Start(exitCts.Token);

                Console.WriteLine("web socket client started.");
            }
            else if (options.CreateJsonOffer)
            {
                var pc = await Createpc(null, _stunServer, _relayOnly);

                var offerSdp = pc.createOffer(null);
                await pc.setLocalDescription(offerSdp);

                Console.WriteLine(offerSdp.sdp);

                var offerJson   = JsonConvert.SerializeObject(offerSdp, new Newtonsoft.Json.Converters.StringEnumConverter());
                var offerBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(offerJson));

                Console.WriteLine(offerBase64);

                string remoteAnswerB64 = null;
                while (string.IsNullOrWhiteSpace(remoteAnswerB64))
                {
                    Console.Write("Remote Answer => ");
                    remoteAnswerB64 = Console.ReadLine();
                }

                string remoteAnswer = Encoding.UTF8.GetString(Convert.FromBase64String(remoteAnswerB64));

                Console.WriteLine(remoteAnswer);

                RTCSessionDescriptionInit answerInit = JsonConvert.DeserializeObject <RTCSessionDescriptionInit>(remoteAnswer);

                Console.WriteLine($"Remote answer: {answerInit.sdp}");

                var res = pc.setRemoteDescription(answerInit);
                if (res != SetDescriptionResultEnum.OK)
                {
                    // No point continuing. Something will need to change and then try again.
                    pc.Close("failed to set remote sdp");
                }
            }
            else if (options.RestServer != null)
            {
                string[] fields = options.RestServer.Split(';');
                if (fields.Length < 3)
                {
                    throw new ArgumentException("The 'rest' option must contain 3 semi-colon separated fields, e.g. --rest=https://localhost:5001/api/webrtcsignal;myid;theirid.");
                }

                var webrtcRestPeer = new WebRTCRestSignalingPeer(fields[0], fields[1], fields[2], CreatePeerConnection);
                webrtcRestPeer.OfferOptions  = _offerOptions;
                webrtcRestPeer.AnswerOptions = _answerOptions;

                if (_acceptIceTypes != null && _acceptIceTypes.Count > 0)
                {
                    webrtcRestPeer.FilterRemoteICECandidates = (init) => _acceptIceTypes.Any(x => x == RTCIceCandidate.Parse(init.candidate).type);
                }
                await webrtcRestPeer.Start(exitCts);
            }

            _ = Task.Run(() => ProcessInput(exitCts));

            // Ctrl-c will gracefully exit the call at any point.
            Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
            {
                e.Cancel = true;
                exitCts.Cancel();
            };

            // Wait for a signal saying the call failed, was cancelled with ctrl-c or completed.
            exitCts.Token.WaitHandle.WaitOne();

            Console.WriteLine();
            Console.WriteLine("Exiting...");

            _peerConnection?.Close("application exit");

            _webSocketServer?.Stop();

            Task.Delay(1000).Wait();
        }
Ejemplo n.º 30
0
        private static async Task RunCommand(Options options, bool noOptions)
        {
            // Plumbing code to facilitate a graceful exit.
            CancellationTokenSource exitCts = new CancellationTokenSource(); // Cancellation token to stop the SIP transport and RTP stream.

            //ManualResetEvent exitMre = new ManualResetEvent(false);

            AddConsoleLogger();

            // Start MDNS server.
            var mdnsServer = new ServiceDiscovery();

            if (options.StunServer != null)
            {
                string[] fields = options.StunServer.Split(';');

                _stunServer = new RTCIceServer
                {
                    urls           = fields[0],
                    username       = fields.Length > 1 ? fields[1] : null,
                    credential     = fields.Length > 2 ? fields[2] : null,
                    credentialType = RTCIceCredentialType.password
                };
            }

            if (options.UseWebSocket || options.UseSecureWebSocket || noOptions)
            {
                // Start web socket.
                Console.WriteLine("Starting web socket server...");
                _webSocketServer = new WebSocketServer(IPAddress.Any, WEBSOCKET_PORT, options.UseSecureWebSocket);

                if (options.UseSecureWebSocket)
                {
                    _webSocketServer.SslConfiguration.ServerCertificate          = new System.Security.Cryptography.X509Certificates.X509Certificate2(LOCALHOST_CERTIFICATE_PATH);
                    _webSocketServer.SslConfiguration.CheckCertificateRevocation = false;
                }

                //_webSocketServer.Log.Level = WebSocketSharp.LogLevel.Debug;
                _webSocketServer.AddWebSocketService <WebRtcClient>("/sendoffer", (client) =>
                {
                    client.WebSocketOpened   += SendOffer;
                    client.OnMessageReceived += WebSocketMessageReceived;
                });
                _webSocketServer.AddWebSocketService <WebRtcClient>("/receiveoffer", (client) =>
                {
                    client.WebSocketOpened   += ReceiveOffer;
                    client.OnMessageReceived += WebSocketMessageReceived;
                });
                _webSocketServer.Start();

                Console.WriteLine($"Waiting for browser web socket connection to {_webSocketServer.Address}:{_webSocketServer.Port}...");
            }
            else if (options.CreateJsonOffer)
            {
                var pc = Createpc(null, _stunServer);

                var offerSdp = pc.createOffer(null);
                await pc.setLocalDescription(offerSdp);

                Console.WriteLine(offerSdp.sdp);

                var offerJson   = JsonConvert.SerializeObject(offerSdp, new Newtonsoft.Json.Converters.StringEnumConverter());
                var offerBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(offerJson));

                Console.WriteLine(offerBase64);

                string remoteAnswerB64 = null;
                while (string.IsNullOrWhiteSpace(remoteAnswerB64))
                {
                    Console.Write("Remote Answer => ");
                    remoteAnswerB64 = Console.ReadLine();
                }

                string remoteAnswer = Encoding.UTF8.GetString(Convert.FromBase64String(remoteAnswerB64));

                Console.WriteLine(remoteAnswer);

                RTCSessionDescriptionInit answerInit = JsonConvert.DeserializeObject <RTCSessionDescriptionInit>(remoteAnswer);

                Console.WriteLine($"Remote answer: {answerInit.sdp}");

                var res = pc.setRemoteDescription(answerInit);
                if (res != SetDescriptionResultEnum.OK)
                {
                    // No point continuing. Something will need to change and then try again.
                    pc.Close("failed to set remote sdp");
                }
            }
            else if (options.NodeDssServer != null)
            {
                _nodeDssUri    = new Uri(options.NodeDssServer);
                _nodeDssclient = new HttpClient();

                Console.WriteLine($"node-dss server successfully set to {_nodeDssUri}.");
            }

            _ = Task.Run(() => ProcessInput(exitCts));

            // Ctrl-c will gracefully exit the call at any point.
            Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e)
            {
                e.Cancel = true;
                exitCts.Cancel();
            };

            // Wait for a signal saying the call failed, was cancelled with ctrl-c or completed.
            exitCts.Token.WaitHandle.WaitOne();

            Console.WriteLine();
            Console.WriteLine("Exiting...");

            _peerConnection?.Close("application exit");

            _webSocketServer?.Stop();

            Task.Delay(1000).Wait();
        }