コード例 #1
0
        /// <summary>
        /// Runs a DTLS handshake test between two threads on a loopback address. The main motivation for
        /// this test was that the first DTLS handshake between this application and a client browser
        /// was often substantially slower and occasionally failed. By doing a loopback test the idea
        /// is that the internal OpenSSL state is initialised.
        /// </summary>
        private static void DoDtlsHandshakeLoopbackTest()
        {
            IPAddress testAddr = IPAddress.Loopback;

            //var dtlsFingerprint = DTLS_CERTIFICATE_FINGERPRINT.Substring(DTLS_CERTIFICATE_FINGERPRINT.Length + 1).Trim().Replace(":", "").Replace(" ", "");
            //var dtlsFingerprintBuffer = SIPSorcery.Sys.ByteBufferInfo.ParseHexStr(dtlsFingerprint);

            Socket svrSock = new Socket(testAddr.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

            svrSock.Bind(new IPEndPoint(testAddr, 9000));
            int           svrPort      = ((IPEndPoint)svrSock.LocalEndPoint).Port;
            DtlsHandshake svrHandshake = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

            svrHandshake.Debug = true;
            byte[] clientFingerprint = null;
            var    svrTask           = Task.Run(() => svrHandshake.DoHandshakeAsServer((ulong)svrSock.Handle, ref clientFingerprint));

            Socket cliSock = new Socket(testAddr.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

            cliSock.Bind(new IPEndPoint(testAddr, 0));
            cliSock.Connect(testAddr, svrPort);
            DtlsHandshake cliHandshake = new DtlsHandshake();

            cliHandshake.Debug = true;
            byte[] serverFingerprint = null;
            var    cliTask           = Task.Run(() => cliHandshake.DoHandshakeAsClient((ulong)cliSock.Handle, (short)testAddr.AddressFamily, testAddr.GetAddressBytes(), (ushort)svrPort, ref serverFingerprint));

            bool result = Task.WaitAll(new Task[] { svrTask, cliTask }, TEST_DTLS_HANDSHAKE_TIMEOUT);

            cliHandshake.Shutdown();
            svrHandshake.Shutdown();
            cliSock.Close();
            svrSock.Close();
        }
コード例 #2
0
ファイル: Program.cs プロジェクト: bleissem/sipsorcery
        /// <summary>
        /// Hands the socket handle to the DTLS context and waits for the handshake to complete.
        /// </summary>
        /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param>
        /// <returns>True if the handshake completes successfully. False if not.</returns>
        private static bool DoDtlsHandshake(WebRtcSession webRtcSession)
        {
            Console.WriteLine("DoDtlsHandshake started.");

            var dtls = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

            webRtcSession.OnClose += (reason) => dtls.Shutdown();

            int res = dtls.DoHandshakeAsServer((ulong)webRtcSession.RtpSession.RtpChannel.RtpSocket.Handle);

            Console.WriteLine("DtlsContext initialisation result=" + res);

            if (dtls.IsHandshakeComplete())
            {
                Console.WriteLine("DTLS negotiation complete.");

                // TODO fix race condition!!! First RTP packet is not getting decrypted.
                var srtpSendContext    = new Srtp(dtls, false);
                var srtpReceiveContext = new Srtp(dtls, true);

                webRtcSession.RtpSession.SetSecurityContext(
                    srtpSendContext.ProtectRTP,
                    srtpReceiveContext.UnprotectRTP,
                    srtpSendContext.ProtectRTCP,
                    srtpReceiveContext.UnprotectRTCP);

                return(true);
            }
            else
            {
                return(false);
            }
        }
コード例 #3
0
ファイル: Program.cs プロジェクト: lordrak007/sipsorcery
        /// <summary>
        /// Hands the socket handle to the DTLS context and waits for the handshake to complete.
        /// </summary>
        /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param>
        /// <returns>True if the handshake completed successfully or false otherwise.</returns>
        private static bool DoDtlsHandshake(RTCPeerConnection peerConnection)
        {
            logger.LogDebug("DoDtlsHandshake started.");

            var dtls = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

            int res = dtls.DoHandshakeAsServer((ulong)peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle);

            logger.LogDebug("DtlsContext initialisation result=" + res);

            if (dtls.IsHandshakeComplete())
            {
                logger.LogDebug("DTLS negotiation complete.");

                var srtpSendContext    = new Srtp(dtls, false);
                var srtpReceiveContext = new Srtp(dtls, true);

                peerConnection.SetSecurityContext(
                    srtpSendContext.ProtectRTP,
                    srtpReceiveContext.UnprotectRTP,
                    srtpSendContext.ProtectRTCP,
                    srtpReceiveContext.UnprotectRTCP);

                dtls.Shutdown();
                return(true);
            }
            else
            {
                dtls.Shutdown();
                return(false);
            }
        }
コード例 #4
0
        /// <summary>
        /// Runs a DTLS handshake test between two threads on a loopback address. The main motivation for
        /// this test was that the first DTLS handshake between this application and a client browser
        /// was often substantially slower and occasionally failed. By doing a loopback test the idea
        /// is that the internal OpenSSL state is initialised.
        /// </summary>
        private void DoDtlsHandshakeLoopbackTest()
        {
            IPAddress testAddr = IPAddress.Loopback;

            Socket svrSock = new Socket(testAddr.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

            svrSock.Bind(new IPEndPoint(testAddr, 9000));
            int           svrPort      = ((IPEndPoint)svrSock.LocalEndPoint).Port;
            DtlsHandshake svrHandshake = new DtlsHandshake(_dtlsCertificatePath, _dtlsKeyPath);
            //svrHandshake.Debug = true;
            var svrTask = Task.Run(() => svrHandshake.DoHandshakeAsServer((ulong)svrSock.Handle));

            Socket cliSock = new Socket(testAddr.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

            cliSock.Bind(new IPEndPoint(testAddr, 0));
            cliSock.Connect(testAddr, svrPort);
            DtlsHandshake cliHandshake = new DtlsHandshake();
            //cliHandshake.Debug = true;
            var cliTask = Task.Run(() => cliHandshake.DoHandshakeAsClient((ulong)cliSock.Handle, (short)testAddr.AddressFamily, testAddr.GetAddressBytes(), (ushort)svrPort));

            bool result = Task.WaitAll(new Task[] { svrTask, cliTask }, TEST_DTLS_HANDSHAKE_TIMEOUT);

            cliHandshake.Shutdown();
            svrHandshake.Shutdown();
            cliSock.Close();
            svrSock.Close();
        }
コード例 #5
0
        static void Main()
        {
            Console.WriteLine("WebRTC Server Sample Program");
            Console.WriteLine("Press ctrl-c to exit.");

            // 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();

            if (!File.Exists(MP4_FILE_PATH))
            {
                throw new ApplicationException($"The media file at does not exist at {MP4_FILE_PATH}.");
            }

            // Initialise OpenSSL & libsrtp, saves a couple of seconds for the first client connection.
            Console.WriteLine("Initialising OpenSSL and libsrtp...");
            DtlsHandshake.InitialiseOpenSSL();
            Srtp.InitialiseLibSrtp();

            Task.Run(DoDtlsHandshakeLoopbackTest).Wait();

            Console.WriteLine("Test DTLS handshake complete.");

            _mediaSource = new MediaSource();
            _mediaSource.Init(MP4_FILE_PATH, true);
            //_mediaSource.Init(0, 0, VideoSubTypesEnum.I420, 640, 480);

            // Start web socket.
            Console.WriteLine("Starting web socket server...");
            _webSocketServer = new WebSocketServer(IPAddress.Any, WEBSOCKET_PORT, true);
            _webSocketServer.SslConfiguration.ServerCertificate          = new System.Security.Cryptography.X509Certificates.X509Certificate2(WEBSOCKET_CERTIFICATE_PATH);
            _webSocketServer.SslConfiguration.CheckCertificateRevocation = false;
            //_webSocketServer.Log.Level = WebSocketSharp.LogLevel.Debug;
            _webSocketServer.AddWebSocketService <SDPExchange>("/", (sdpExchanger) =>
            {
                sdpExchanger.WebSocketOpened   += SendSDPOffer;
                sdpExchanger.SDPAnswerReceived += SDPAnswerReceived;
            });
            _webSocketServer.Start();

            Console.WriteLine($"Waiting for browser web socket connection to {_webSocketServer.Address}:{_webSocketServer.Port}...");

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

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

            _mediaSource.Shutdown();
            _webSocketServer.Stop();
        }
コード例 #6
0
        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogDebug("WebRTCHostedService StartAsync.");

            // Initialise OpenSSL & libsrtp, saves a couple of seconds for the first client connection.
            _logger.LogDebug("Initialising OpenSSL and libsrtp...");
            DtlsHandshake.InitialiseOpenSSL();
            Srtp.InitialiseLibSrtp();

            return(Task.CompletedTask);
        }
コード例 #7
0
        /// <summary>
        /// Hands the socket handle to the DTLS context and waits for the handshake to complete.
        /// </summary>
        /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param>
        private static bool DoDtlsHandshake(RTCPeerConnection peerConnection)
        {
            Log.LogDebug("DoDtlsHandshake started.");

            if (!File.Exists(DTLS_CERTIFICATE_PATH))
            {
                throw new ApplicationException($"The DTLS certificate file could not be found at {DTLS_CERTIFICATE_PATH}.");
            }
            else if (!File.Exists(DTLS_KEY_PATH))
            {
                throw new ApplicationException($"The DTLS key file could not be found at {DTLS_KEY_PATH}.");
            }

            var dtls = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

            peerConnection.onconnectionstatechange += (state) =>
            {
                if (state == RTCPeerConnectionState.closed)
                {
                    dtls.Shutdown();
                }
            };

            byte[] clientFingerprint = null;
            int    res = dtls.DoHandshakeAsServer((ulong)peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle, ref clientFingerprint);

            Log.LogDebug("DtlsContext initialisation result=" + res);

            if (dtls.IsHandshakeComplete())
            {
                // TODO: Check client fingerprint matches one supplied in the SDP.

                Log.LogDebug("DTLS negotiation complete.");

                var srtpSendContext    = new Srtp(dtls, false);
                var srtpReceiveContext = new Srtp(dtls, true);

                peerConnection.SetSecurityContext(
                    srtpSendContext.ProtectRTP,
                    srtpReceiveContext.UnprotectRTP,
                    srtpSendContext.ProtectRTCP,
                    srtpReceiveContext.UnprotectRTCP);

                return(true);
            }
            else
            {
                return(false);
            }
        }
コード例 #8
0
        /// <summary>
        /// Hands the socket handle to the DTLS context and waits for the handshake to complete.
        /// </summary>
        /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param>
        /// <returns>True if the handshake completed successfully or false otherwise.</returns>
        private bool DoDtlsHandshake(WebRtcSession webRtcSession)
        {
            try
            {
                logger.LogDebug("DoDtlsHandshake started.");

                if (!File.Exists(_dtlsCertificatePath))
                {
                    throw new ApplicationException($"The DTLS certificate file could not be found at {_dtlsCertificatePath}.");
                }
                else if (!File.Exists(_dtlsKeyPath))
                {
                    throw new ApplicationException($"The DTLS key file could not be found at {_dtlsKeyPath}.");
                }

                var dtls = new DtlsHandshake(_dtlsCertificatePath, _dtlsKeyPath);
                webRtcSession.OnClose += (reason) => dtls.Shutdown();

                int res = dtls.DoHandshakeAsServer((ulong)webRtcSession.RtpSession.RtpChannel.RtpSocket.Handle);

                logger.LogDebug("DtlsContext initialisation result=" + res);

                if (dtls.IsHandshakeComplete())
                {
                    logger.LogDebug("DTLS negotiation complete.");

                    var srtpSendContext    = new Srtp(dtls, false);
                    var srtpReceiveContext = new Srtp(dtls, true);

                    webRtcSession.RtpSession.SetSecurityContext(
                        srtpSendContext.ProtectRTP,
                        srtpReceiveContext.UnprotectRTP,
                        srtpSendContext.ProtectRTCP,
                        srtpReceiveContext.UnprotectRTCP);

                    webRtcSession.IsDtlsNegotiationComplete = true;

                    return(true);
                }
                else
                {
                    return(false);
                }
            }
            catch (Exception excp)
            {
                logger.LogWarning($"Exception DoDtlsHandshake. {excp}");
                return(false);
            }
        }
コード例 #9
0
        /// <summary>
        /// Hands the socket handle to the DTLS context and waits for the handshake to complete.
        /// </summary>
        /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param>
        private static async Task <bool> DoDtlsHandshake(RTCPeerConnection peerConnection)
        {
            logger.LogDebug("DoDtlsHandshake started.");

            if (!File.Exists(DTLS_CERTIFICATE_PATH))
            {
                throw new ApplicationException($"The DTLS certificate file could not be found at {DTLS_CERTIFICATE_PATH}.");
            }
            else if (!File.Exists(DTLS_KEY_PATH))
            {
                throw new ApplicationException($"The DTLS key file could not be found at {DTLS_KEY_PATH}.");
            }

            var dtls = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

            byte[] clientFingerprint = null;
            var    dtlsResult        = await Task.Run(() => dtls.DoHandshakeAsServer((ulong)peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle, ref clientFingerprint));

            logger.LogDebug("DtlsContext initialisation result=" + dtlsResult);

            if (dtls.IsHandshakeComplete())
            {
                logger.LogDebug("DTLS handshake succeeded.");

                // TODO: Check client fingerprint matches one supplied in the SDP.

                var srtpSendContext    = new Srtp(dtls, false);
                var srtpReceiveContext = new Srtp(dtls, true);

                peerConnection.SetSecurityContext(
                    srtpSendContext.ProtectRTP,
                    srtpReceiveContext.UnprotectRTP,
                    srtpSendContext.ProtectRTCP,
                    srtpReceiveContext.UnprotectRTCP);

                return(true);
            }
            else
            {
                logger.LogWarning("DTLS handshake failed.");

                dtls.Shutdown();
                return(false);
            }
        }
コード例 #10
0
        static void Main()
        {
            Console.WriteLine("WebRTC Server Sample Program");
            Console.WriteLine("Press ctrl-c to exit.");

            // 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();

            // Initialise OpenSSL & libsrtp, saves a couple of seconds for the first client connection.
            Console.WriteLine("Initialising OpenSSL and libsrtp...");
            DtlsHandshake.InitialiseOpenSSL();
            Srtp.InitialiseLibSrtp();

            InitialiseTestPattern();

            // Start web socket.
            Console.WriteLine("Starting web socket server...");
            _webSocketServer = new WebSocketServer(IPAddress.Any, WEBSOCKET_PORT, true);
            _webSocketServer.SslConfiguration.ServerCertificate          = new System.Security.Cryptography.X509Certificates.X509Certificate2(WEBSOCKET_CERTIFICATE_PATH);
            _webSocketServer.SslConfiguration.CheckCertificateRevocation = false;
            //_webSocketServer.Log.Level = WebSocketSharp.LogLevel.Debug;
            _webSocketServer.AddWebSocketService <SDPExchange>("/", (sdpExchanger) =>
            {
                sdpExchanger.WebSocketOpened   += SendSDPOffer;
                sdpExchanger.OnMessageReceived += WebSocketMessageReceived;
            });
            _webSocketServer.Start();

            Console.WriteLine($"Waiting for browser web socket connection to {_webSocketServer.Address}:{_webSocketServer.Port}...");

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

            // Wait for a signal saying the call failed, was cancelled with ctrl-c or completed.
            exitMre.WaitOne();
        }
コード例 #11
0
        /// <summary>
        /// Hands the socket handle to the DTLS context and waits for the handshake to complete.
        /// </summary>
        /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param>
        private static int DoDtlsHandshake(WebRtcSession webRtcSession)
        {
            logger.LogDebug("DoDtlsHandshake started.");

            if (!File.Exists(DTLS_CERTIFICATE_PATH))
            {
                throw new ApplicationException($"The DTLS certificate file could not be found at {DTLS_CERTIFICATE_PATH}.");
            }
            else if (!File.Exists(DTLS_KEY_PATH))
            {
                throw new ApplicationException($"The DTLS key file could not be found at {DTLS_KEY_PATH}.");
            }

            var dtls = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

            webRtcSession.OnClose += (reason) => dtls.Shutdown();

            int res = dtls.DoHandshakeAsServer((ulong)webRtcSession.RtpSession.RtpChannel.RtpSocket.Handle);

            logger.LogDebug("DtlsContext initialisation result=" + res);

            if (dtls.IsHandshakeComplete())
            {
                logger.LogDebug("DTLS negotiation complete.");

                var srtpSendContext    = new Srtp(dtls, false);
                var srtpReceiveContext = new Srtp(dtls, true);

                webRtcSession.RtpSession.SetSecurityContext(
                    srtpSendContext.ProtectRTP,
                    srtpReceiveContext.UnprotectRTP,
                    srtpSendContext.ProtectRTCP,
                    srtpReceiveContext.UnprotectRTCP);

                if (!_isSampling)
                {
                    Task.Run(StartMedia);
                }
            }

            return(res);
        }
コード例 #12
0
ファイル: Program.cs プロジェクト: wangscript007/sipsorcery
        /// <summary>
        /// Hands the socket handle to the DTLS context and waits for the handshake to complete.
        /// </summary>
        /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param>
        private static bool DoDtlsHandshake(RTCPeerConnection peerConnection, DtlsHandshake dtls)
        {
            Log.LogDebug("DoDtlsHandshake started.");

            if (!File.Exists(DTLS_CERTIFICATE_PATH))
            {
                throw new ApplicationException($"The DTLS certificate file could not be found at {DTLS_CERTIFICATE_PATH}.");
            }
            else if (!File.Exists(DTLS_KEY_PATH))
            {
                throw new ApplicationException($"The DTLS key file could not be found at {DTLS_KEY_PATH}.");
            }

            byte[] clientFingerprint = null;
            var    dtlsResult        = dtls.DoHandshakeAsServer((ulong)peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle, ref clientFingerprint);

            Log.LogDebug($"DtlsContext initialisation result {dtlsResult}.");

            if (dtls.IsHandshakeComplete())
            {
                Log.LogDebug("DTLS negotiation complete.");

                var srtpSendContext    = new Srtp(dtls, false);
                var srtpReceiveContext = new Srtp(dtls, true);

                peerConnection.SetSecurityContext(
                    srtpSendContext.ProtectRTP,
                    srtpReceiveContext.UnprotectRTP,
                    srtpSendContext.ProtectRTCP,
                    srtpReceiveContext.UnprotectRTCP);

                return(true);
            }
            else
            {
                Log.LogWarning("DTLS handshake failed.");

                dtls.Shutdown();
                return(false);
            }
        }
コード例 #13
0
        private static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context)
        {
            logger.LogDebug($"Web socket client connection from {context.UserEndPoint}.");

            RTCConfiguration pcConfiguration = new RTCConfiguration
            {
                certificates = new List <RTCCertificate>
                {
                    new RTCCertificate
                    {
                        X_CertificatePath = DTLS_CERTIFICATE_PATH,
                        X_KeyPath         = DTLS_KEY_PATH,
                        X_Fingerprint     = DTLS_CERTIFICATE_FINGERPRINT
                    }
                }
            };

            var peerConnection = new RTCPeerConnection(pcConfiguration);
            var dtls           = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

            MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> {
                new SDPMediaFormat(SDPMediaFormatsEnum.PCMU)
            });

            peerConnection.addTrack(audioTrack);
            MediaStreamTrack videoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> {
                new SDPMediaFormat(SDPMediaFormatsEnum.VP8)
            });

            peerConnection.addTrack(videoTrack);

            peerConnection.OnReceiveReport += RtpSession_OnReceiveReport;
            peerConnection.OnSendReport    += RtpSession_OnSendReport;

            peerConnection.OnTimeout += (mediaType) =>
            {
                peerConnection.Close("remote timeout");
                dtls.Shutdown();
            };

            peerConnection.onconnectionstatechange += (state) =>
            {
                if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed)
                {
                    logger.LogDebug($"RTC peer connection was closed.");
                    OnMediaSampleReady             -= peerConnection.SendMedia;
                    peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport;
                    peerConnection.OnSendReport    -= RtpSession_OnSendReport;
                }
                else if (state == RTCPeerConnectionState.connected)
                {
                    logger.LogDebug("Peer connection connected.");
                    OnMediaSampleReady += peerConnection.SendMedia;
                }
            };

            peerConnection.oniceconnectionstatechange += (state) =>
            {
                if (state == RTCIceConnectionState.connected)
                {
                    logger.LogDebug("Starting DTLS handshake task.");

                    bool dtlsResult = false;
                    Task.Run(async() => dtlsResult = await DoDtlsHandshake(peerConnection, dtls, peerConnection.RemotePeerDtlsFingerprint))
                    .ContinueWith((t) =>
                    {
                        logger.LogDebug($"dtls handshake result {dtlsResult}.");

                        if (dtlsResult)
                        {
                            //peerConnection.SetDestination(SDPMediaTypesEnum.audio, peerConnection.IceSession.ConnectedRemoteEndPoint, peerConnection.IceSession.ConnectedRemoteEndPoint);
                        }
                        else
                        {
                            dtls.Shutdown();
                            peerConnection.Close("dtls handshake failed.");
                        }
                    });
                }
            };

            var offerInit = peerConnection.createOffer(null);
            await peerConnection.setLocalDescription(offerInit);

            logger.LogDebug($"Sending SDP offer to client {context.UserEndPoint}.");

            context.WebSocket.Send(offerInit.sdp);

            return(peerConnection);
        }
コード例 #14
0
        public async Task Start(CancellationToken ct)
        {
            try
            {
                AddConsoleLogger();

                logger.LogDebug("WebRTCDaemon starting.");

                var wssCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(_webSocketCertificatePath, _webSocketCertificatePassword);
                logger.LogDebug("Web Socket Server Certificate: " + wssCertificate.Subject + ", have key " + wssCertificate.HasPrivateKey + ", Expires " + wssCertificate.GetExpirationDateString() + ".");
                logger.LogDebug($"DTLS certificate thumbprint {_dtlsCertificateThumbprint}.");
                logger.LogDebug($"Web socket port {_webSocketPort}.");

                if (!File.Exists(_mediaFilePath))
                {
                    throw new ApplicationException($"The media file at does not exist at {_mediaFilePath}.");
                }

                // Initialise OpenSSL & libsrtp, saves a couple of seconds for the first client connection.
                Console.WriteLine("Initialising OpenSSL and libsrtp...");
                DtlsHandshake.InitialiseOpenSSL();
                Srtp.InitialiseLibSrtp();

                Task.Run(DoDtlsHandshakeLoopbackTest).Wait();

                Console.WriteLine("Test DTLS handshake complete.");

                // Configure the web socket and the different end point handlers.
                int webSocketPort = (!String.IsNullOrEmpty(_webSocketPort)) ? Int32.Parse(_webSocketPort) : DEFAULT_WEB_SOCKET_PORT;
                _webSocketServer = new WebSocketServer(IPAddress.Any, webSocketPort, true);
                _webSocketServer.SslConfiguration.ServerCertificate          = wssCertificate;
                _webSocketServer.SslConfiguration.CheckCertificateRevocation = false;

                // Standard encrypted WebRtc stream.
                _webSocketServer.AddWebSocketService <SDPExchange>("/max", (sdpReceiver) =>
                {
                    sdpReceiver.MediaSource        = MediaSourceEnum.Max;
                    sdpReceiver.WebSocketOpened   += WebRtcStartCall;
                    sdpReceiver.SDPAnswerReceived += WebRtcAnswerReceived;
                });

                if (!String.IsNullOrEmpty(_testPatternImagePath) && File.Exists(_testPatternImagePath))
                {
                    _webSocketServer.AddWebSocketService <SDPExchange>("/testpattern", (sdpReceiver) =>
                    {
                        sdpReceiver.MediaSource        = MediaSourceEnum.TestPattern;
                        sdpReceiver.WebSocketOpened   += WebRtcStartCall;
                        sdpReceiver.SDPAnswerReceived += WebRtcAnswerReceived;
                    });
                }

                _webSocketServer.Start();

                Console.WriteLine($"Waiting for browser web socket connection to {_webSocketServer.Address}:{_webSocketServer.Port}...");

                // Initialise the Media Foundation library that will pull the samples from the mp4 file.
                _mediaSource = new MediaSource();
                _mediaSource.Init(_mediaFilePath, true);

                _ = Task.Run(ExpireConnections);

                while (!ct.IsCancellationRequested)
                {
                    await Task.Delay(1000);
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebRTCDaemon.Start. " + excp);
            }
            finally
            {
                Stop();
            }
        }
コード例 #15
0
ファイル: Program.cs プロジェクト: lordrak007/sipsorcery
        static void Main()
        {
            Console.WriteLine("ICE Console Test Program");
            Console.WriteLine("Press ctrl-c to exit.");

            if (!File.Exists(DTLS_CERTIFICATE_PATH))
            {
                throw new ApplicationException($"The DTLS certificate file could not be found at {DTLS_CERTIFICATE_PATH}.");
            }
            else if (!File.Exists(DTLS_KEY_PATH))
            {
                throw new ApplicationException($"The DTLS key file could not be found at {DTLS_KEY_PATH}.");
            }

            // 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();

            // Initialise OpenSSL & libsrtp, saves a couple of seconds for the first client connection.
            Console.WriteLine("Initialising OpenSSL and libsrtp...");
            DtlsHandshake.InitialiseOpenSSL();
            Srtp.InitialiseLibSrtp();

            Task.Run(DoDtlsHandshakeLoopbackTest).Wait();

            Console.WriteLine("Test DTLS handshake complete.");

            // Start web socket.
            Console.WriteLine("Starting web socket server...");
            _webSocketServer = new WebSocketServer(IPAddress.Any, WEBSOCKET_PORT, true);
            _webSocketServer.SslConfiguration.ServerCertificate          = new System.Security.Cryptography.X509Certificates.X509Certificate2(WEBSOCKET_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}...");

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

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

            _webSocketServer.Stop();
        }
コード例 #16
0
ファイル: Program.cs プロジェクト: wangscript007/sipsorcery
        static async Task Main()
        {
            Console.WriteLine("SIPSorcery sip.js Demo");

            AddConsoleLogger();

            var sipTransport = new SIPTransport();

            EnableTraceLogs(sipTransport);

            var sipChannel = new SIPWebSocketChannel(IPAddress.Loopback, 80);

            var wssCertificate   = new System.Security.Cryptography.X509Certificates.X509Certificate2("localhost.pfx");
            var sipChannelSecure = new SIPWebSocketChannel(IPAddress.Loopback, 443, wssCertificate);

            sipTransport.AddSIPChannel(sipChannel);
            sipTransport.AddSIPChannel(sipChannelSecure);

            var userAgent = new SIPUserAgent(sipTransport, null, true);

            userAgent.OnIncomingCall += async(ua, req) =>
            {
                Log.LogDebug($"Auto-answering incoming call from {req.Header.From}.");
                var uas = userAgent.AcceptCall(req);

                RTCConfiguration pcConfiguration = new RTCConfiguration
                {
                    certificates = new List <RTCCertificate>
                    {
                        new RTCCertificate
                        {
                            X_CertificatePath = DTLS_CERTIFICATE_PATH,
                            X_KeyPath         = DTLS_KEY_PATH,
                            X_Fingerprint     = DTLS_CERTIFICATE_FINGERPRINT
                        }
                    },
                    //X_RemoteSignallingAddress = context.UserEndPoint.Address,
                    //iceServers = new List<RTCIceServer> { new RTCIceServer { urls = SIPSORCERY_STUN_SERVER } }
                };

                var peerConnection = new RTCPeerConnection(pcConfiguration);
                var dtls           = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

                peerConnection.OnTimeout += (mediaType) =>
                {
                    peerConnection.Close("remote timeout");
                };

                peerConnection.oniceconnectionstatechange += async(state) =>
                {
                    Log.LogDebug($"ICE connection state change to {state}.");

                    if (state == RTCIceConnectionState.connected)
                    {
                        var remoteEndPoint = peerConnection.AudioDestinationEndPoint;
                        Log.LogInformation($"ICE connected to remote end point {remoteEndPoint}.");

                        await Task.Run(() => DoDtlsHandshake(peerConnection, dtls))
                        .ContinueWith((dtlsResult) =>
                        {
                            Log.LogDebug($"dtls handshake result {dtlsResult.Result}.");

                            if (dtlsResult.Result)
                            {
                                var remoteEP = peerConnection.AudioDestinationEndPoint;
                                peerConnection.SetDestination(SDPMediaTypesEnum.audio, remoteEP, remoteEP);
                            }
                            else
                            {
                                dtls.Shutdown();
                                peerConnection.Close("dtls handshake failed.");
                            }
                        });
                    }
                };

                peerConnection.onconnectionstatechange += (state) =>
                {
                    if (state == RTCPeerConnectionState.connected)
                    {
                        var remoteEP = peerConnection.AudioDestinationEndPoint;

                        Log.LogDebug($"DTLS connected on {remoteEP}.");

                        peerConnection.SetDestination(SDPMediaTypesEnum.audio, remoteEP, remoteEP);
                        peerConnection.SetDestination(SDPMediaTypesEnum.video, remoteEP, remoteEP);

                        peerConnection.OnReceiveReport += RtpSession_OnReceiveReport;
                        peerConnection.OnSendReport    += RtpSession_OnSendReport;
                        // peerConnection.OnRtpPacketReceived += OnRtpPacketReceived;
                    }
                };

                MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> {
                    new SDPMediaFormat(SDPMediaFormatsEnum.PCMU)
                }, MediaStreamStatusEnum.SendRecv);
                peerConnection.addTrack(audioTrack);
                //MediaStreamTrack videoTrack = new MediaStreamTrack("1", SDPMediaTypesEnum.video, false, new List<SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.VP8) }, MediaStreamStatusEnum.Inactive);
                //peerConnection.addTrack(videoTrack);

                var answerResult = await userAgent.Answer(uas, peerConnection);
            };

            Console.Write("press any key to exit...");
            Console.Read();

            sipTransport.Shutdown();
        }
コード例 #17
0
        /// <summary>
        /// Hands the socket handle to the DTLS context and waits for the handshake to complete.
        /// </summary>
        /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param>
        private static async Task <bool> DoDtlsHandshake(RTCPeerConnection peerConnection, DtlsHandshake dtls)
        {
            logger.LogDebug("DoDtlsHandshake started.");

            if (!File.Exists(DTLS_CERTIFICATE_PATH))
            {
                throw new ApplicationException($"The DTLS certificate file could not be found at {DTLS_CERTIFICATE_PATH}.");
            }
            else if (!File.Exists(DTLS_KEY_PATH))
            {
                throw new ApplicationException($"The DTLS key file could not be found at {DTLS_KEY_PATH}.");
            }

            int res = dtls.DoHandshakeAsServer((ulong)peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle);

            logger.LogDebug("DtlsContext initialisation result=" + res);

            if (dtls.IsHandshakeComplete())
            {
                logger.LogDebug("DTLS negotiation complete.");

                var srtpSendContext    = new Srtp(dtls, false);
                var srtpReceiveContext = new Srtp(dtls, true);

                peerConnection.SetSecurityContext(
                    srtpSendContext.ProtectRTP,
                    srtpReceiveContext.UnprotectRTP,
                    srtpSendContext.ProtectRTCP,
                    srtpReceiveContext.UnprotectRTCP);

                await peerConnection.Start();

                //dtls.Shutdown();
                if (_sendTestPatternTimer == null)
                {
                    _sendTestPatternTimer = new Timer(SendTestPattern, null, 0, TEST_PATTERN_SPACING_MILLISECONDS);
                }

                return(true);
            }
            else
            {
                return(false);
            }
        }
コード例 #18
0
        /// <summary>
        /// Hands the socket handle to the DTLS context and waits for the handshake to complete.
        /// </summary>
        /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param>
        /// <returns>True if the handshake completed successfully or false otherwise.</returns>
        private static async Task <bool> DoDtlsHandshake(RTCPeerConnection peerConnection, DtlsHandshake dtls, byte[] sdpFingerprint)
        {
            logger.LogDebug("DoDtlsHandshake started.");

            byte[] clientFingerprint = null;
            int    res = dtls.DoHandshakeAsServer((ulong)peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle, ref clientFingerprint);

            logger.LogDebug("DtlsContext initialisation result=" + res);

            if (dtls.IsHandshakeComplete())
            {
                // TODO: The client fingerprint should be checked against the SDP fingerprint.

                logger.LogDebug("DTLS negotiation complete.");

                var srtpSendContext    = new Srtp(dtls, false);
                var srtpReceiveContext = new Srtp(dtls, true);

                peerConnection.SetSecurityContext(
                    srtpSendContext.ProtectRTP,
                    srtpReceiveContext.UnprotectRTP,
                    srtpSendContext.ProtectRTCP,
                    srtpReceiveContext.UnprotectRTCP);

                await peerConnection.Start();

                if (!_isSampling)
                {
                    _ = Task.Run(StartMedia);
                }

                return(true);
            }
            else
            {
                return(false);
            }
        }
コード例 #19
0
        /// <summary>
        /// Hands the socket handle to the DTLS context and waits for the handshake to complete.
        /// </summary>
        /// <param name="peerConnection">The WebRTC session to perform the DTLS handshake on.</param>
        /// <returns>True if the handshake completes successfully. False if not.</returns>
        private static async Task <bool> DoDtlsHandshake(RTCPeerConnection peerConnection, DtlsHandshake dtls)
        {
            Console.WriteLine("DoDtlsHandshake started.");

            int res = dtls.DoHandshakeAsServer((ulong)peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle);

            Console.WriteLine("DtlsContext initialisation result=" + res);

            if (dtls.IsHandshakeComplete())
            {
                Console.WriteLine("DTLS negotiation complete.");

                // TODO fix race condition!!! First RTP packet is not getting decrypted.
                var srtpSendContext    = new Srtp(dtls, false);
                var srtpReceiveContext = new Srtp(dtls, true);

                peerConnection.SetSecurityContext(
                    srtpSendContext.ProtectRTP,
                    srtpReceiveContext.UnprotectRTP,
                    srtpSendContext.ProtectRTCP,
                    srtpReceiveContext.UnprotectRTCP);

                await peerConnection.Start();

                return(true);
            }
            else
            {
                return(false);
            }
        }
コード例 #20
0
        private static async void WebSocketMessageReceived(WebSocketContext context, RTCPeerConnection peerConnection, string msg)
        {
            if (peerConnection.RemoteDescription != null)
            {
                Console.WriteLine($"ICE Candidate: {msg}.");
                //await _peerConnections[0].addIceCandidate(new RTCIceCandidateInit { candidate = msg });

                //  await peerConnection.addIceCandidate(new RTCIceCandidateInit { candidate = msg });
                Console.WriteLine("add ICE candidate complete.");
            }
            else
            {
                //Console.WriteLine($"websocket recv: {msg}");
                //var offerSDP = SDP.ParseSDPDescription(msg);
                Console.WriteLine($"offer sdp: {msg}");

                var dtls = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

                await peerConnection.setRemoteDescription(new RTCSessionDescriptionInit { sdp = msg, type = RTCSdpType.offer });

                peerConnection.OnReceiveReport         += RtpSession_OnReceiveReport;
                peerConnection.OnSendReport            += RtpSession_OnSendReport;
                peerConnection.onconnectionstatechange += (state) =>
                {
                    if (state == RTCPeerConnectionState.closed)
                    {
                        Console.WriteLine($"webrtc session closed.");
                        //_peerConnections.Remove(peerConnection);
                    }
                };

                peerConnection.oniceconnectionstatechange += (state) =>
                {
                    if (state == RTCIceConnectionState.connected)
                    {
                        Console.WriteLine("Starting DTLS handshake task.");

                        bool dtlsResult = false;
                        Task.Run(async() => dtlsResult = await DoDtlsHandshake(peerConnection, dtls))
                        .ContinueWith((t) =>
                        {
                            Console.WriteLine($"dtls handshake result {dtlsResult}.");

                            if (dtlsResult)
                            {
                                //peerConnection.SetDestination(SDPMediaTypesEnum.audio, peerConnection.IceSession.ConnectedRemoteEndPoint, peerConnection.IceSession.ConnectedRemoteEndPoint);
                                //_peerConnections.Add(peerConnection);
                                peerConnection.OnRtpPacketReceived += RtpSession_OnRtpPacketReceived;
                            }
                            else
                            {
                                dtls.Shutdown();
                                peerConnection.Close("dtls handshake failed.");
                            }
                        });
                    }
                };


                var answerInit = await peerConnection.createAnswer(null);

                await peerConnection.setLocalDescription(answerInit);

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

                context.WebSocket.Send(answerInit.sdp);
            }
        }
コード例 #21
0
        static async Task Main()
        {
            Console.WriteLine("WebRTC No Signalling Server Sample Program");
            Console.WriteLine("Press ctrl-c to exit.");

            // 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();

            // Initialise OpenSSL & libsrtp, saves a couple of seconds for the first client connection.
            Console.WriteLine("Initialising OpenSSL and libsrtp...");
            DtlsHandshake.InitialiseOpenSSL();
            Srtp.InitialiseLibSrtp();

            InitialiseTestPattern();

            Console.WriteLine($"Starting Peer Connection...");

            var pc = await StartPeerConnection();

            // Start the DTLS task before entering the ICE credentials to avoid DTLS client Hello retransmits.
            // Start the DTLS task immediately so that we are ready to receive the client Hello.
            // If it's missed it can take 10 or more seconds for the peer to retry.
            var dtlsTask = DoDtlsHandshake(pc);

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

            Console.WriteLine("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
            Console.WriteLine("THE SDP OFFER ABOVE NEEDS TO BE PASTED INTO YOUR BROWSER");
            Console.WriteLine();

            Console.WriteLine("Press enter when you the SDP answer is available and then enter the ICE username and password...");
            Console.ReadLine();

            Console.Write("Enter the remote peer ICE User (e.g. for 'a=ice-ufrag:tGXy' enter tGXy) => ");
            var remoteIceUser = Console.ReadLine();

            Console.Write("Enter the remote peer ICE Password (e.g. for 'a=ice-pwd:Icew1/BpwUIJLn2dBMbQyYPB' enter Icew1/BpwUIJLn2dBMbQyYPB) => ");
            var remoteIcePassword = Console.ReadLine();

            pc.SetRemoteCredentials(remoteIceUser, remoteIcePassword);

            var dtlsResult = await dtlsTask;

            logger.LogDebug($"dtls handshake result {dtlsResult}.");

            if (dtlsResult)
            {
                var remoteEP = pc.AudioDestinationEndPoint;
                pc.SetDestination(SDPMediaTypesEnum.audio, remoteEP, remoteEP);
            }
            else
            {
                pc.Close("dtls handshake failed.");
            }

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

            pc.Close("normal");
        }
コード例 #22
0
        public async Task <RTCSessionDescriptionInit> GetOffer(string id)
        {
            if (string.IsNullOrWhiteSpace(id))
            {
                throw new ArgumentNullException("id", "A unique ID parameter must be supplied when creating a new peer connection.");
            }
            else if (_peerConnections.ContainsKey(id))
            {
                throw new ArgumentNullException("id", "The specified peer connection ID is already in use.");
            }

            RTCConfiguration pcConfiguration = new RTCConfiguration
            {
                certificates = new List <RTCCertificate>
                {
                    new RTCCertificate
                    {
                        X_CertificatePath = DTLS_CERTIFICATE_PATH,
                        X_KeyPath         = DTLS_KEY_PATH,
                        X_Fingerprint     = DTLS_CERTIFICATE_FINGERPRINT
                    }
                }
            };

            var peerConnection = new RTCPeerConnection(pcConfiguration);
            var dtls           = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

            MediaStreamTrack audioTrack = new MediaStreamTrack("0", SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> {
                new SDPMediaFormat(SDPMediaFormatsEnum.PCMU)
            }, MediaStreamStatusEnum.RecvOnly);

            peerConnection.addTrack(audioTrack);

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

            peerConnection.OnTimeout += (mediaType) =>
            {
                peerConnection.Close("remote timeout");
                dtls.Shutdown();
            };

            peerConnection.onconnectionstatechange += (state) =>
            {
                if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed)
                {
                    //peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport;
                    //peerConnection.OnSendReport -= RtpSession_OnSendReport;
                    _logger.LogDebug($"Peer connection {id} closed.");
                    _peerConnections.TryRemove(id, out _);
                }
                else if (state == RTCPeerConnectionState.connected)
                {
                    _logger.LogDebug("Peer connection connected.");
                }
            };

            _ = Task <bool> .Run(() => Task.FromResult(DoDtlsHandshake(peerConnection, dtls)))
                .ContinueWith((t) =>
            {
                _logger.LogDebug($"dtls handshake result {t.Result}.");

                if (t.Result)
                {
                    var remoteEP = peerConnection.IceSession.ConnectedRemoteEndPoint;
                    peerConnection.SetDestination(SDPMediaTypesEnum.audio, remoteEP, remoteEP);
                }
                else
                {
                    dtls.Shutdown();
                    peerConnection.Close("dtls handshake failed.");
                }
            });

            var offerSdp = await peerConnection.createOffer(null);

            await peerConnection.setLocalDescription(offerSdp);

            _peerConnections.TryAdd(id, peerConnection);

            return(offerSdp);
        }
コード例 #23
0
ファイル: Program.cs プロジェクト: spFly/sipsorcery
        private static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context)
        {
            logger.LogDebug($"Web socket client connection from {context.UserEndPoint}.");

            RTCConfiguration pcConfiguration = new RTCConfiguration
            {
                certificates = new List <RTCCertificate>
                {
                    new RTCCertificate
                    {
                        X_CertificatePath = DTLS_CERTIFICATE_PATH,
                        X_KeyPath         = DTLS_KEY_PATH,
                        X_Fingerprint     = DTLS_CERTIFICATE_FINGERPRINT
                    }
                }
            };

            var peerConnection = new RTCPeerConnection(pcConfiguration);
            var dtls           = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

            MediaStreamTrack audioTrack = new MediaStreamTrack("0", SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> {
                new SDPMediaFormat(SDPMediaFormatsEnum.PCMU)
            }, MediaStreamStatusEnum.RecvOnly);

            peerConnection.addTrack(audioTrack);

            peerConnection.OnReceiveReport += RtpSession_OnReceiveReport;
            peerConnection.OnSendReport    += RtpSession_OnSendReport;

            peerConnection.OnTimeout += (mediaType) =>
            {
                peerConnection.Close("remote timeout");
                dtls.Shutdown();
            };

            peerConnection.onconnectionstatechange += (state) =>
            {
                if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed)
                {
                    peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport;
                    peerConnection.OnSendReport    -= RtpSession_OnSendReport;
                }
                else if (state == RTCPeerConnectionState.connected)
                {
                    logger.LogDebug("Peer connection connected.");
                    peerConnection.OnRtpPacketReceived += (SDPMediaTypesEnum media, RTPPacket rtpPkt) => logger.LogDebug($"RTP {media} pkt received, SSRC {rtpPkt.Header.SyncSource}.");
                }
            };

            peerConnection.oniceconnectionstatechange += (state) =>
            {
                if (state == RTCIceConnectionState.connected)
                {
                    logger.LogDebug("Starting DTLS handshake task.");

                    bool dtlsResult = false;
                    Task.Run(async() => dtlsResult = await DoDtlsHandshake(peerConnection, dtls))
                    .ContinueWith((t) =>
                    {
                        logger.LogDebug($"dtls handshake result {dtlsResult}.");

                        if (dtlsResult)
                        {
                            var remoteEP = peerConnection.IceSession.ConnectedRemoteEndPoint;
                            peerConnection.SetDestination(SDPMediaTypesEnum.audio, remoteEP, remoteEP);
                        }
                        else
                        {
                            dtls.Shutdown();
                            peerConnection.Close("dtls handshake failed.");
                        }
                    });
                }
            };

            var offerSdp = await peerConnection.createOffer(null);

            await peerConnection.setLocalDescription(offerSdp);

            logger.LogDebug($"Sending SDP offer to client {context.UserEndPoint}.");
            logger.LogDebug(offerSdp.sdp);

            context.WebSocket.Send(offerSdp.sdp);

            return(peerConnection);
        }