Esempio n. 1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="dtlsCertificateFingerprint">The SHA256 fingerprint that gets placed in the SDP offer for this WebRTC peer. It must match the certificate being used
        /// in the DTLS negotiation.</param>
        /// <param name="turnServerEndPoint">An optional parameter that can be used include a TURN server in this peer's ICE candidate gathering.</param>
        /// <param name="localAddress">Optional parameter to specify the local IP address to use for STUN/TRP sockets. If null all available interfaces will be used.</param>
        public void Initialise(string dtlsCertificateFingerprint, IPEndPoint turnServerEndPoint, List <RtpMediaTypesEnum> mediaTypes, IPAddress localAddress, bool isEncryptionDisabled)
        {
            MediaTypes            = mediaTypes;
            _localIPAddresses     = new List <IPAddress>();
            _isEncryptionDisabled = isEncryptionDisabled;

            if (localAddress != null)
            {
                _localIPAddresses.Add(localAddress);
            }

            if (dtlsCertificateFingerprint.IsNullOrBlank() && isEncryptionDisabled == false)
            {
                throw new ArgumentNullException("dtlsCertificateFingerprint", "A DTLS certificate fingerprint must be supplied when initialising a new WebRTC peer (to get the fingerprint use: openssl x509 -fingerprint -sha256 -in server-cert.pem).");
            }

            try
            {
                _dtlsCertificateFingerprint = dtlsCertificateFingerprint;
                _turnServerEndPoint         = turnServerEndPoint;

                _iceGatheringMRE = new ManualResetEvent(false);

                DateTime startGatheringTime = DateTime.Now;

                SetIceConnectionState(IceConnectionStatesEnum.Gathering);

                GetIceCandidates(_iceGatheringMRE);

                _iceGatheringMRE.WaitOne(ICE_GATHERING_TIMEOUT_MILLISECONDS, true);

                logger.LogDebug("ICE gathering completed for call " + CallID + " in " + DateTime.Now.Subtract(startGatheringTime).TotalMilliseconds + "ms, number of local sockets " + LocalIceCandidates.Count + ".");

                SetIceConnectionState(IceConnectionStatesEnum.GatheringComplete);

                if (LocalIceCandidates.Count == 0)
                {
                    logger.LogWarning("No local socket candidates were found for WebRTC call " + CallID + ", closing peer.");
                    Close();
                }
                else
                {
                    string localIceCandidateString = null;

                    logger.LogDebug("ICE Candidates for " + CallID + ": ");

                    foreach (var iceCandidate in GetIceCandidatesForMediaType(RtpMediaTypesEnum.None))
                    {
                        localIceCandidateString += iceCandidate.ToString();
                    }

                    var localIceUser      = Crypto.GetRandomString(20);
                    var localIcePassword  = Crypto.GetRandomString(20) + Crypto.GetRandomString(20);
                    var localIceCandidate = GetIceCandidatesForMediaType(RtpMediaTypesEnum.None).First();

                    var offerHeader = String.Format(_sdpOfferTemplate, Crypto.GetRandomInt(10).ToString());

                    string dtlsAttribute         = (_isEncryptionDisabled == false) ? String.Format(_dtlsFingerprint, _dtlsCertificateFingerprint) : null;
                    string rtpSecurityDescriptor = (_isEncryptionDisabled == false) ? RTP_MEDIA_SECURE_DESCRIPTOR : RTP_MEDIA_UNSECURE_DESCRIPTOR;

                    var audioOffer = mediaTypes.Contains(RtpMediaTypesEnum.Audio) ? String.Format(_sdpAudioPcmOfferTemplate,
                                                                                                  localIceCandidate.Port,
                                                                                                  rtpSecurityDescriptor,
                                                                                                  localIceCandidate.LocalAddress,
                                                                                                  localIceCandidateString.TrimEnd(),
                                                                                                  localIceUser,
                                                                                                  localIcePassword,
                                                                                                  dtlsAttribute) : null;

                    var videoOffer = String.Format(_sdpVideoOfferTemplate,
                                                   rtpSecurityDescriptor,
                                                   localIceCandidate.LocalAddress,
                                                   localIceUser,
                                                   localIcePassword,
                                                   dtlsAttribute);

                    string offer = offerHeader + audioOffer + videoOffer;

                    //logger.LogDebug("WebRTC Offer SDP: " + offer);

                    SDP              = offer;
                    LocalIceUser     = localIceUser;
                    LocalIcePassword = localIcePassword;

                    Task.Run(() => { SendStunConnectivityChecks(); });

                    logger.LogDebug("Sending SDP offer for WebRTC call " + CallID + ".");

                    OnSdpOfferReady?.Invoke(offer);
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebRtcPeer.Initialise. " + excp);
                Close();
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Initialises the WebRTC session by carrying out the ICE connectivity steps and when complete
        /// handing the RTP socket off for the DTLS handshake. Once the handshake is complete the session
        /// is ready for to exchange encrypted RTP and RTCP packets.
        /// </summary>
        /// <param name="turnServerEndPoint">An optional parameter that can be used include a TURN
        /// server in this session's ICE candidate gathering.</param>
        public async Task Initialise(DoDtlsHandshakeDelegate doDtlsHandshake, IPEndPoint turnServerEndPoint)
        {
            try
            {
                _doDtlsHandshake    = doDtlsHandshake;
                _turnServerEndPoint = turnServerEndPoint;

                DateTime startGatheringTime = DateTime.Now;

                IceConnectionState = IceConnectionStatesEnum.Gathering;

                await GetIceCandidatesAsync();

                logger.LogDebug($"ICE gathering completed for in {DateTime.Now.Subtract(startGatheringTime).TotalMilliseconds:#}ms, candidate count {LocalIceCandidates.Count}.");

                IceConnectionState = IceConnectionStatesEnum.GatheringComplete;

                if (LocalIceCandidates.Count == 0)
                {
                    logger.LogWarning("No local socket candidates were found for WebRTC call closing.");
                    Close("No local ICE candidates available.");
                }
                else
                {
                    string localIceCandidateString = null;

                    foreach (var iceCandidate in LocalIceCandidates)
                    {
                        localIceCandidateString += iceCandidate.ToString();
                    }

                    LocalIceUser     = LocalIceUser ?? Crypto.GetRandomString(20);
                    LocalIcePassword = LocalIcePassword ?? Crypto.GetRandomString(20) + Crypto.GetRandomString(20);
                    //var localIceCandidate = GetIceCandidatesForMediaType(RtpMediaTypesEnum.None).First();

                    var offerHeader = String.Format(_sdpOfferTemplate, Crypto.GetRandomInt(10).ToString());

                    string dtlsAttribute         = String.Format(_dtlsFingerprint, _dtlsCertificateFingerprint);
                    string rtpSecurityDescriptor = RTP_MEDIA_SECURE_DESCRIPTOR;

                    var audioOffer = String.Format(_sdpAudioPcmOfferTemplate,
                                                   _rtpChannel.RTPPort,
                                                   rtpSecurityDescriptor,
                                                   IPAddress.Loopback,
                                                   localIceCandidateString.TrimEnd(),
                                                   LocalIceUser,
                                                   LocalIcePassword,
                                                   dtlsAttribute);

                    var videoOffer = String.Format(_sdpVideoOfferTemplate,
                                                   rtpSecurityDescriptor,
                                                   IPAddress.Loopback,
                                                   LocalIceUser,
                                                   LocalIcePassword,
                                                   dtlsAttribute);

                    string offer = offerHeader + audioOffer + videoOffer;

                    //logger.LogDebug("WebRTC Offer SDP: " + offer);

                    SDP = offer;

                    OnSdpOfferReady?.Invoke(offer);
                }

                // We may have received some remote candidates from the remote part SDP so perform an immediate STUN check.
                // If there are no remote candidates this call will end up being a NOP.
                SendStunConnectivityChecks(null);

                if (_doDtlsHandshake != null)
                {
                    _ = Task.Run(() =>
                    {
                        int result = _doDtlsHandshake(this, _rtpChannel.m_rtpSocket, out _rtpSession.SrtpProtect, out _rtpSession.RtcpSession.SrtcpProtect);
                        IsDtlsNegotiationComplete = (result == 0);
                    });
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebRtcPeer.Initialise. " + excp);
                Close(excp.Message);
            }
        }
Esempio n. 3
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="dtlsCertificateFingerprint">The SHA256 fingerprint that gets placed in the SDP offer for this WebRTC peer. It must match the certificate being used
        /// in the DTLS negotiation.</param>
        /// <param name="turnServerEndPoint">An optional parameter that can be used include a TURN server in this peer's ICE candidate gathering.</param>
        public void Initialise(string dtlsCertificateFingerprint, IPEndPoint turnServerEndPoint)
        {
            if (dtlsCertificateFingerprint.IsNullOrBlank())
            {
                throw new ArgumentNullException("dtlsCertificateFingerprint", "A DTLS certificate fingerprint must be supplied when initialising a new WebRTC peer (to get the fingerprint use: openssl x509 -fingerprint -sha256 -in server-cert.pem).");
            }

            try
            {
                _dtlsCertificateFingerprint = dtlsCertificateFingerprint;
                _turnServerEndPoint         = turnServerEndPoint;

                _iceGatheringMRE = new ManualResetEvent(false);

                DateTime startGatheringTime = DateTime.Now;

                SetIceConnectionState(IceConnectionStatesEnum.Gathering);

                GetIceCandidates(_iceGatheringMRE);

                _iceGatheringMRE.WaitOne(ICE_GATHERING_TIMEOUT_MILLISECONDS, true);

                logger.Debug("ICE gathering completed for call " + CallID + " in " + DateTime.Now.Subtract(startGatheringTime).TotalMilliseconds + "ms, number of local sockets " + LocalIceCandidates.Count + ".");

                SetIceConnectionState(IceConnectionStatesEnum.GatheringComplete);

                if (LocalIceCandidates.Count == 0)
                {
                    logger.Warn("No local socket candidates were found for WebRTC call " + CallID + ", closing peer.");
                    Close();
                }
                else
                {
                    string iceCandidateString = null;

                    logger.Debug("ICE Candidates for " + CallID + ": ");

                    foreach (var iceCandidate in LocalIceCandidates)
                    {
                        iceCandidateString += iceCandidate.ToString();
                    }

                    var localIceUser     = Crypto.GetRandomString(20);
                    var localIcePassword = Crypto.GetRandomString(20) + Crypto.GetRandomString(20);

                    var offer = String.Format(_sdpOfferTemplate, Crypto.GetRandomInt(10).ToString(), (LocalIceCandidates.First().LocalRtpSocket.LocalEndPoint as IPEndPoint).Port, LocalIceCandidates.First().LocalAddress, iceCandidateString.TrimEnd(), localIceUser, localIcePassword, _dtlsCertificateFingerprint);

                    //logger.Debug("WebRTC Offer SDP: " + offer);

                    SDP              = offer;
                    LocalIceUser     = localIceUser;
                    LocalIcePassword = localIcePassword;
                    SSRC             = Convert.ToUInt32(Crypto.GetRandomInt(8));
                    SequenceNumber   = 1;

                    Task.Run(() => { SendStunConnectivityChecks(); });

                    logger.Debug("Sending SDP offer for WebRTC call " + CallID + ".");

                    OnSdpOfferReady?.Invoke(offer);
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception WebRtcPeer.Initialise. " + excp);
                Close();
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Generates the SDP for an offer or answer.
        /// </summary>
        private async Task <SDP> getSdp(string setupAttribute)
        {
            try
            {
                DateTime startGatheringTime = DateTime.Now;

                IceConnectionState = IceConnectionStatesEnum.Gathering;

                await GetIceCandidatesAsync();

                logger.LogDebug($"ICE gathering completed for in {DateTime.Now.Subtract(startGatheringTime).TotalMilliseconds:#}ms, candidate count {LocalIceCandidates.Count}.");

                IceConnectionState = IceConnectionStatesEnum.GatheringComplete;

                if (LocalIceCandidates.Count == 0)
                {
                    //logger.LogWarning("No local socket candidates were found for WebRTC call closing.");
                    //Close("No local ICE candidates available.");
                    throw new ApplicationException("No local ICE candidates available.");
                }
                else
                {
                    bool haveIceCandidatesBeenAdded = false;

                    string localIceCandidateString = null;

                    foreach (var iceCandidate in LocalIceCandidates)
                    {
                        localIceCandidateString += iceCandidate.ToString();
                    }

                    LocalIceUser     = LocalIceUser ?? Crypto.GetRandomString(20);
                    LocalIcePassword = LocalIcePassword ?? Crypto.GetRandomString(20) + Crypto.GetRandomString(20);

                    SDP offerSdp = new SDP(IPAddress.Loopback);
                    offerSdp.SessionId = Crypto.GetRandomInt(5).ToString();

                    // Add a bundle attribute. Indicates that audio and video sessions will be multiplexed
                    // on a single RTP socket.
                    if (RtpSession.AudioTrack != null && RtpSession.VideoTrack != null)
                    {
                        offerSdp.Group = MEDIA_GROUPING;
                    }

                    // --- Audio announcement ---
                    if (RtpSession.AudioTrack != null)
                    {
                        SDPMediaAnnouncement audioAnnouncement = new SDPMediaAnnouncement(
                            SDPMediaTypesEnum.audio,
                            _rtpChannel.RTPPort,
                            RtpSession.AudioTrack.Capabilties);

                        audioAnnouncement.Transport = RTP_MEDIA_PROFILE;
                        if (!haveIceCandidatesBeenAdded)
                        {
                            audioAnnouncement.IceCandidates = LocalIceCandidates;
                            haveIceCandidatesBeenAdded      = true;
                        }

                        audioAnnouncement.Connection      = new SDPConnectionInformation(IPAddress.Any);
                        audioAnnouncement.IceUfrag        = LocalIceUser;
                        audioAnnouncement.IcePwd          = LocalIcePassword;
                        audioAnnouncement.DtlsFingerprint = _dtlsCertificateFingerprint;
                        audioAnnouncement.AddExtra(RTCP_MUX_ATTRIBUTE);
                        audioAnnouncement.AddExtra(setupAttribute);
                        audioAnnouncement.MediaStreamStatus = RtpSession.AudioTrack.Transceiver.Direction;
                        audioAnnouncement.MediaID           = RtpSession.AudioTrack.Transceiver.MID;

                        offerSdp.Media.Add(audioAnnouncement);
                    }

                    // --- Video announcement ---
                    if (RtpSession.VideoTrack != null)
                    {
                        SDPMediaAnnouncement videoAnnouncement = new SDPMediaAnnouncement(
                            SDPMediaTypesEnum.video,
                            _rtpChannel.RTPPort,
                            RtpSession.VideoTrack.Capabilties);

                        videoAnnouncement.Transport = RTP_MEDIA_PROFILE;
                        if (!haveIceCandidatesBeenAdded)
                        {
                            videoAnnouncement.IceCandidates = LocalIceCandidates;
                            haveIceCandidatesBeenAdded      = true;
                        }

                        videoAnnouncement.Connection      = new SDPConnectionInformation(IPAddress.Any);
                        videoAnnouncement.IceUfrag        = LocalIceUser;
                        videoAnnouncement.IcePwd          = LocalIcePassword;
                        videoAnnouncement.DtlsFingerprint = _dtlsCertificateFingerprint;
                        videoAnnouncement.AddExtra(RTCP_MUX_ATTRIBUTE);
                        videoAnnouncement.AddExtra(setupAttribute);
                        videoAnnouncement.MediaStreamStatus = RtpSession.VideoTrack.Transceiver.Direction;
                        videoAnnouncement.MediaID           = RtpSession.VideoTrack.Transceiver.MID;;

                        offerSdp.Media.Add(videoAnnouncement);
                    }

                    OnSdpOfferReady?.Invoke(offerSdp);

                    return(offerSdp);
                }

                // We may have received some remote candidates from the remote part SDP so perform an immediate STUN check.
                // If there are no remote candidates this call will end up being a NOP.
                //SendStunConnectivityChecks(null);

                //if (_doDtlsHandshake != null)
                //{
                //    _ = Task.Run(() =>
                //    {
                //        int result = _doDtlsHandshake(this);
                //        IsDtlsNegotiationComplete = (result == 0);
                //    });
                //}
            }
            catch (Exception excp)
            {
                logger.LogError("Exception getSdp. " + excp);
                Close(excp.Message);

                throw;
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Initialises the WebRTC session by carrying out the ICE connectivity steps and when complete
        /// handing the RTP socket off for the DTLS handshake. Once the handshake is complete the session
        /// is ready for to exchange encrypted RTP and RTCP packets.
        /// </summary>
        /// <param name="turnServerEndPoint">An optional parameter that can be used include a TURN
        /// server in this session's ICE candidate gathering.</param>
        public async Task Initialise(DoDtlsHandshakeDelegate doDtlsHandshake, IPEndPoint turnServerEndPoint)
        {
            try
            {
                _doDtlsHandshake    = doDtlsHandshake;
                _turnServerEndPoint = turnServerEndPoint;

                DateTime startGatheringTime = DateTime.Now;

                IceConnectionState = IceConnectionStatesEnum.Gathering;

                await GetIceCandidatesAsync();

                logger.LogDebug($"ICE gathering completed for in {DateTime.Now.Subtract(startGatheringTime).TotalMilliseconds:#}ms, candidate count {LocalIceCandidates.Count}.");

                IceConnectionState = IceConnectionStatesEnum.GatheringComplete;

                if (LocalIceCandidates.Count == 0)
                {
                    logger.LogWarning("No local socket candidates were found for WebRTC call closing.");
                    Close("No local ICE candidates available.");
                }
                else
                {
                    bool includeAudioOffer          = _supportedAudioFormats?.Count() > 0;
                    bool includeVideoOffer          = _supportedVideoFormats?.Count() > 0;
                    bool haveIceCandidatesBeenAdded = false;
                    bool isMediaBundle = includeAudioOffer && includeVideoOffer;    // Is this SDP offer bundling audio and video on the same RTP connection.

                    string localIceCandidateString = null;

                    foreach (var iceCandidate in LocalIceCandidates)
                    {
                        localIceCandidateString += iceCandidate.ToString();
                    }

                    LocalIceUser     = LocalIceUser ?? Crypto.GetRandomString(20);
                    LocalIcePassword = LocalIcePassword ?? Crypto.GetRandomString(20) + Crypto.GetRandomString(20);

                    SDP offerSdp = new SDP(IPAddress.Loopback);
                    offerSdp.SessionId = Crypto.GetRandomInt(5).ToString();

                    // Add a bundle attribute. Indicates that audio and video sessions will be multiplexed
                    // on a single RTP socket.
                    if (isMediaBundle)
                    {
                        offerSdp.Group = MEDIA_GROUPING;
                    }

                    if (includeAudioOffer)
                    {
                        SDPMediaAnnouncement audioAnnouncement = new SDPMediaAnnouncement(
                            SDPMediaTypesEnum.audio,
                            _rtpChannel.RTPPort,
                            _supportedAudioFormats);

                        audioAnnouncement.Transport = RTP_MEDIA_PROFILE;
                        if (!haveIceCandidatesBeenAdded)
                        {
                            audioAnnouncement.IceCandidates = LocalIceCandidates;
                            haveIceCandidatesBeenAdded      = true;
                        }

                        audioAnnouncement.Connection      = new SDPConnectionInformation(IPAddress.Any);
                        audioAnnouncement.IceUfrag        = LocalIceUser;
                        audioAnnouncement.IcePwd          = LocalIcePassword;
                        audioAnnouncement.DtlsFingerprint = _dtlsCertificateFingerprint;
                        audioAnnouncement.AddExtra(RTCP_MUX_ATTRIBUTE);
                        audioAnnouncement.AddExtra(SETUP_ATTRIBUTE);
                        audioAnnouncement.MediaStreamStatus = AudioStreamStatus;

                        if (isMediaBundle)
                        {
                            audioAnnouncement.MediaID = AUDIO_MEDIA_ID;
                        }

                        offerSdp.Media.Add(audioAnnouncement);
                    }

                    if (includeVideoOffer)
                    {
                        SDPMediaAnnouncement videoAnnouncement = new SDPMediaAnnouncement(
                            SDPMediaTypesEnum.video,
                            _rtpChannel.RTPPort,
                            _supportedVideoFormats);

                        videoAnnouncement.Transport = RTP_MEDIA_PROFILE;
                        if (!haveIceCandidatesBeenAdded)
                        {
                            videoAnnouncement.IceCandidates = LocalIceCandidates;
                            haveIceCandidatesBeenAdded      = true;
                        }

                        videoAnnouncement.Connection      = new SDPConnectionInformation(IPAddress.Any);
                        videoAnnouncement.IceUfrag        = LocalIceUser;
                        videoAnnouncement.IcePwd          = LocalIcePassword;
                        videoAnnouncement.DtlsFingerprint = _dtlsCertificateFingerprint;
                        videoAnnouncement.AddExtra(RTCP_MUX_ATTRIBUTE);
                        videoAnnouncement.AddExtra(SETUP_ATTRIBUTE);
                        videoAnnouncement.MediaStreamStatus = VideoStreamStatus;

                        if (isMediaBundle)
                        {
                            videoAnnouncement.MediaID = VIDEO_MEDIA_ID;
                        }

                        offerSdp.Media.Add(videoAnnouncement);
                    }

                    SDP = offerSdp;

                    OnSdpOfferReady?.Invoke(SDP);
                }

                // We may have received some remote candidates from the remote part SDP so perform an immediate STUN check.
                // If there are no remote candidates this call will end up being a NOP.
                SendStunConnectivityChecks(null);

                if (_doDtlsHandshake != null)
                {
                    _ = Task.Run(() =>
                    {
                        int result = _doDtlsHandshake(this);
                        IsDtlsNegotiationComplete = (result == 0);
                    });
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebRtcPeer.Initialise. " + excp);
                Close(excp.Message);
            }
        }