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);
        }
示例#2
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 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);
        }
示例#4
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;
            }
        }
示例#5
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();
        }
示例#6
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);
        }
示例#7
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);
            }
            else if (options.EchoServer != null)
            {
                // Create offer and send to echo server.
                var pc = await Createpc(null, _stunServer, _relayOnly);

                var signaler = new HttpClient();

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

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

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

                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 echo server.");
                }
            }

            _ = 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();
        }
示例#8
0
        static async Task <int> Main(string[] args)
        {
            Console.WriteLine("Starting webrtc echo test client.");

            WebRTCTestTypes testType      = WebRTCTestTypes.PeerConnection;
            string          echoServerUrl = Options.DEFAULT_ECHO_SERVER_URL;
            LogEventLevel   verbosity     = Options.DEFAULT_VERBOSITY;
            int             pcTimeout     = Options.TEST_TIMEOUT_SECONDS;

            if (args?.Length == 1)
            {
                echoServerUrl = args[0];
            }
            else if (args?.Length > 1)
            {
                Options opts        = null;
                var     parseResult = Parser.Default.ParseArguments <Options>(args)
                                      .WithParsed(o => opts = o);

                testType      = opts != null ? opts.TestType : testType;
                echoServerUrl = opts != null && !string.IsNullOrEmpty(opts.ServerUrl) ? opts.ServerUrl : echoServerUrl;
                verbosity     = opts != null ? opts.Verbosity : verbosity;
                pcTimeout     = opts != null ? opts.TestTimeoutSeconds : pcTimeout;
            }

            logger = AddConsoleLogger(verbosity);

            logger.LogDebug($"Performing test {testType} to {echoServerUrl}.");

            var pc = await CreatePeerConnection();

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

            bool success = false;
            ManualResetEventSlim testComplete = new ManualResetEventSlim();

            var dc = pc.DataChannels.FirstOrDefault();

            if (dc != null)
            {
                var pseudo = Crypto.GetRandomString(5);

                dc.onopen += () =>
                {
                    logger.LogInformation($"Data channel {dc.label}, stream ID {dc.id} opened.");
                    dc.send(pseudo);
                };

                dc.onmessage += (dc, proto, data) =>
                {
                    string echoMsg = Encoding.UTF8.GetString(data);
                    logger.LogDebug($"data channel onmessage {proto}: {echoMsg}.");

                    if (echoMsg == pseudo)
                    {
                        logger.LogInformation($"Data channel echo test success.");

                        if (testType == WebRTCTestTypes.DataChannelEcho)
                        {
                            success = true;
                            testComplete.Set();
                        }
                    }
                    else
                    {
                        logger.LogWarning($"Data channel echo test failed, echoed message of {echoMsg} did not match original of {pseudo}.");
                    }
                };
            }

            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 &&
                         testType == WebRTCTestTypes.PeerConnection)
                {
                    success = true;
                    testComplete.Set();
                }
            };

            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))
            {
                logger.LogDebug($"SDP answer: {answerInit.sdp}");

                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: {answerStr}.");
            }

            pc.OnClosed += testComplete.Set;

            logger.LogDebug($"Test timeout is {pcTimeout} seconds.");
            testComplete.Wait(pcTimeout * 1000);

            if (!pc.IsClosed)
            {
                pc.close();
                await Task.Delay(1000);
            }

            logger.LogInformation($"{testType} test success {success }.");

            return((success) ? SUCCESS_RESULT : FAILURE_RESULT);
        }