/// <summary>
        /// Attempts to get a list of local ICE candidates.
        /// </summary>
        private async Task GetIceCandidatesAsync()
        {
            // The media is being multiplexed so the audio and video RTP channel is the same.
            var rtpChannel = GetRtpChannel(SDPMediaTypesEnum.audio);

            if (rtpChannel == null)
            {
                throw new ApplicationException("Cannot start gathering ICE candidates without an RTP channel.");
            }
            else
            {
                var localIPAddresses = _offerAddresses ?? NetServices.LocalIPAddresses;
                IceNegotiationStartedAt = DateTime.Now;
                LocalIceCandidates      = new List <IceCandidate>();

                foreach (var address in localIPAddresses.Where(x => x.AddressFamily == rtpChannel.RTPLocalEndPoint.AddressFamily))
                {
                    var iceCandidate = new IceCandidate(address, rtpChannel.RTPPort);

                    if (_turnServerEndPoint != null)
                    {
                        iceCandidate.TurnServer = new TurnServer()
                        {
                            ServerEndPoint = _turnServerEndPoint
                        };
                        iceCandidate.InitialStunBindingCheck = SendTurnServerBindingRequest(iceCandidate);
                    }

                    LocalIceCandidates.Add(iceCandidate);
                }

                await Task.WhenAll(LocalIceCandidates.Where(x => x.InitialStunBindingCheck != null).Select(x => x.InitialStunBindingCheck)).ConfigureAwait(false);
            }
        }
Exemple #2
0
        private async Task GetIceCandidatesAsync()
        {
            var localIPAddresses = _offerAddresses ?? NetServices.GetAllLocalIPAddresses();

            IceNegotiationStartedAt = DateTime.Now;
            LocalIceCandidates      = new List <IceCandidate>();

            foreach (var address in localIPAddresses.Where(x => x.AddressFamily == _rtpChannel.RTPLocalEndPoint.AddressFamily))
            {
                var iceCandidate = new IceCandidate(address, _rtpChannel.RTPPort);

                if (_turnServerEndPoint != null)
                {
                    iceCandidate.TurnServer = new TurnServer()
                    {
                        ServerEndPoint = _turnServerEndPoint
                    };
                    iceCandidate.InitialStunBindingCheck = SendTurnServerBindingRequest(iceCandidate);
                }

                LocalIceCandidates.Add(iceCandidate);
            }

            await Task.WhenAll(LocalIceCandidates.Where(x => x.InitialStunBindingCheck != null).Select(x => x.InitialStunBindingCheck));
        }
Exemple #3
0
        private void SendInitialStunBindingRequest(IceCandidate iceCandidate, ManualResetEvent iceGatheringCompleteMRE)
        {
            int attempt = 1;

            while (attempt < INITIAL_STUN_BINDING_ATTEMPTS_LIMIT && !IsClosed && !iceCandidate.IsGatheringComplete)
            {
                logger.Debug("Sending STUN binding request " + attempt + " from " + iceCandidate.LocalRtpSocket.LocalEndPoint + " to " + iceCandidate.TurnServer.ServerEndPoint + ".");

                STUNv2Message stunRequest = new STUNv2Message(STUNv2MessageTypesEnum.BindingRequest);
                stunRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray();
                byte[] stunReqBytes = stunRequest.ToByteBuffer(null, false);

                iceCandidate.LocalRtpSocket.SendTo(stunReqBytes, iceCandidate.TurnServer.ServerEndPoint);

                Thread.Sleep(INITIAL_STUN_BINDING_PERIOD_MILLISECONDS);

                attempt++;
            }

            iceCandidate.IsGatheringComplete = true;

            // Potentially save a few seconds if all the ICE candidates are now ready.
            if (LocalIceCandidates.All(x => x.IsGatheringComplete))
            {
                iceGatheringCompleteMRE.Set();
            }
        }
Exemple #4
0
        public void AppendRemoteIceCandidate(IceCandidate remoteIceCandidate)
        {
            IPAddress candidateIPAddress = null;

            //foreach (var iceCandidate in remoteIceCandidates)
            //{
            //    logger.LogDebug("Appending remote ICE candidate " + iceCandidate.NetworkAddress + ":" + iceCandidate.Port + ".");
            //}

            if (remoteIceCandidate.Transport.ToLower() != "udp")
            {
                logger.LogDebug("Omitting remote non-UDP ICE candidate. " + remoteIceCandidate.RawString + ".");
            }
            else if (!IPAddress.TryParse(remoteIceCandidate.NetworkAddress, out candidateIPAddress))
            {
                logger.LogDebug("Omitting ICE candidate with unrecognised IP Address. " + remoteIceCandidate.RawString + ".");
            }
            else if (candidateIPAddress.AddressFamily == AddressFamily.InterNetworkV6)
            {
                logger.LogDebug("Omitting IPv6 ICE candidate. " + remoteIceCandidate.RawString + ".");
            }
            else
            {
                // ToDo: Add srflx and relay endpoints as hosts as well.

                if (!_remoteIceCandidates.Any(x => x.NetworkAddress == remoteIceCandidate.NetworkAddress && x.Port == remoteIceCandidate.Port))
                {
                    logger.LogDebug("Adding remote ICE candidate: " + remoteIceCandidate.CandidateType + " " + remoteIceCandidate.NetworkAddress + ":" + remoteIceCandidate.Port + " (" + remoteIceCandidate.RawString + ").");
                    _remoteIceCandidates.Add(remoteIceCandidate);
                }
            }

            // We now should have a remote ICE candidate to start the STUN dance with.
            SendStunConnectivityChecks(null);
        }
Exemple #5
0
        private void AllocateTurn(IceCandidate iceCandidate)
        {
            try
            {
                if (iceCandidate.TurnAllocateAttempts >= MAXIMUM_TURN_ALLOCATE_ATTEMPTS)
                {
                    logger.LogDebug("TURN allocation for local socket " + iceCandidate.LocalAddress + " failed after " + iceCandidate.TurnAllocateAttempts + " attempts.");

                    iceCandidate.IsGatheringComplete = true;
                }
                else
                {
                    iceCandidate.TurnAllocateAttempts++;

                    //logger.LogDebug("Sending STUN connectivity check to client " + client.SocketAddress + ".");

                    STUNv2Message stunRequest = new STUNv2Message(STUNv2MessageTypesEnum.Allocate);
                    stunRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray();
                    stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.Lifetime, 3600));
                    stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.RequestedTransport, STUNv2AttributeConstants.UdpTransportType));   // UDP
                    byte[] stunReqBytes = stunRequest.ToByteBuffer(null, false);
                    _rtpChannel.SendAsync(RTPChannelSocketsEnum.RTP, iceCandidate.TurnServer.ServerEndPoint, stunReqBytes);
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception AllocateTurn. " + excp);
            }
        }
Exemple #6
0
        private void GetIceCandidates(ManualResetEvent iceGatheringCompleteMRE)
        {
            IceNegotiationStartedAt = DateTime.Now;
            LocalIceCandidates      = new List <IceCandidate>();

            var addresses = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().GetUnicastAddresses()
                            .Where(x =>
                                   x.Address.AddressFamily == AddressFamily.InterNetwork && // Exclude IPv6 at this stage.
                                   IPAddress.IsLoopback(x.Address) == false &&
                                   (x.Address != null && x.Address.ToString().StartsWith(AUTOMATIC_PRIVATE_ADRRESS_PREFIX) == false));

            foreach (var address in addresses)
            {
                logger.Debug("Attempting to create RTP socket with IP address " + address.Address + ".");

                Socket rtpSocket     = null;
                Socket controlSocket = null;

                NetServices.CreateRtpSocket(address.Address, WEBRTC_START_PORT, WEBRTC_END_PORT, false, out rtpSocket, out controlSocket);

                if (rtpSocket != null)
                {
                    logger.Debug("RTP socket successfully created on " + rtpSocket.LocalEndPoint + ".");

                    var iceCandidate = new IceCandidate()
                    {
                        LocalAddress = address.Address, Port = ((IPEndPoint)rtpSocket.LocalEndPoint).Port, LocalRtpSocket = rtpSocket, LocalControlSocket = controlSocket,
                        TurnServer   = (_turnServerEndPoint != null) ? new TurnServer()
                        {
                            ServerEndPoint = _turnServerEndPoint
                        } : null
                    };

                    LocalIceCandidates.Add(iceCandidate);

                    var listenerTask = Task.Run(() => { StartWebRtcRtpListener(iceCandidate); });

                    iceCandidate.RtpListenerTask = listenerTask;

                    if (_turnServerEndPoint != null)
                    {
                        var stunBindingTask = Task.Run(() => { SendInitialStunBindingRequest(iceCandidate, iceGatheringCompleteMRE); });
                    }
                    else
                    {
                        iceCandidate.IsGatheringComplete = true;

                        // Potentially save a few seconds if all the ICE candidates are now ready.
                        if (LocalIceCandidates.All(x => x.IsGatheringComplete))
                        {
                            iceGatheringCompleteMRE.Set();
                        }
                    }
                }
            }
        }
Exemple #7
0
        private async Task SendTurnServerBindingRequest(IceCandidate iceCandidate)
        {
            int attempt = 1;

            while (attempt < INITIAL_STUN_BINDING_ATTEMPTS_LIMIT && !IsConnected && !IsClosed && !iceCandidate.IsGatheringComplete)
            {
                logger.LogDebug($"Sending STUN binding request {attempt} from {_rtpChannel.RTPLocalEndPoint} to {iceCandidate.TurnServer.ServerEndPoint}.");

                STUNv2Message stunRequest = new STUNv2Message(STUNv2MessageTypesEnum.BindingRequest);
                stunRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray();
                byte[] stunReqBytes = stunRequest.ToByteBuffer(null, false);

                _rtpChannel.SendAsync(RTPChannelSocketsEnum.RTP, iceCandidate.TurnServer.ServerEndPoint, stunReqBytes);

                await Task.Delay(INITIAL_STUN_BINDING_PERIOD_MILLISECONDS);

                attempt++;
            }

            iceCandidate.IsGatheringComplete = true;
        }
        public static IceCandidate Parse(string candidateLine)
        {
            IceCandidate candidate = new IceCandidate();

            candidate.RawString = candidateLine;

            string[] candidateFields = candidateLine.Trim().Split(' ');
            candidate.Transport      = candidateFields[2];
            candidate.NetworkAddress = candidateFields[4];
            candidate.Port           = Convert.ToInt32(candidateFields[5]);
            Enum.TryParse <IceCandidateTypesEnum>(candidateFields[7], out candidate.CandidateType);

            if (candidateFields.Length > 8 && candidateFields[8] == REMOTE_ADDRESS_KEY)
            {
                candidate.RemoteAddress = candidateFields[9];
            }

            if (candidateFields.Length > 10 && candidateFields[10] == REMOTE_PORT_KEY)
            {
                candidate.RemotePort = Convert.ToInt32(candidateFields[11]);
            }

            return(candidate);
        }
Exemple #9
0
        private void ProcessStunMessage(IceCandidate iceCandidate, STUNv2Message stunMessage, IPEndPoint remoteEndPoint)
        {
            //logger.Debug("STUN message received from remote " + remoteEndPoint + " " + stunMessage.Header.MessageType + ".");

            if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingRequest)
            {
                STUNv2Message stunResponse = new STUNv2Message(STUNv2MessageTypesEnum.BindingSuccessResponse);
                stunResponse.Header.TransactionId = stunMessage.Header.TransactionId;
                stunResponse.AddXORMappedAddressAttribute(remoteEndPoint.Address, remoteEndPoint.Port);

                // ToDo: Check authentication.

                byte[] stunRespBytes = stunResponse.ToByteBufferStringKey(LocalIcePassword, true);
                iceCandidate.LocalRtpSocket.SendTo(stunRespBytes, remoteEndPoint);

                iceCandidate.LastStunRequestReceivedAt    = DateTime.Now;
                iceCandidate.IsStunRemoteExchangeComplete = true;

                if (_remoteIceCandidates != null && !_remoteIceCandidates.Any(x =>
                                                                              (x.NetworkAddress == remoteEndPoint.Address.ToString() || x.RemoteAddress == remoteEndPoint.Address.ToString()) &&
                                                                              (x.Port == remoteEndPoint.Port || x.RemotePort == remoteEndPoint.Port)))
                {
                    // This STUN request has come from a socket not in the remote ICE candidates list. Add it so we can send our STUN binding request to it.
                    IceCandidate remoteIceCandidate = new IceCandidate()
                    {
                        Transport      = "udp",
                        NetworkAddress = remoteEndPoint.Address.ToString(),
                        Port           = remoteEndPoint.Port,
                        CandidateType  = IceCandidateTypesEnum.host
                    };

                    logger.Debug("Adding missing remote ICE candidate for " + remoteEndPoint + ".");

                    _remoteIceCandidates.Add(remoteIceCandidate);
                }
            }
            else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingSuccessResponse)
            {
                if (_turnServerEndPoint != null && remoteEndPoint.ToString() == _turnServerEndPoint.ToString())
                {
                    if (iceCandidate.IsGatheringComplete == false)
                    {
                        var reflexAddressAttribute = stunMessage.Attributes.FirstOrDefault(y => y.AttributeType == STUNv2AttributeTypesEnum.XORMappedAddress) as STUNv2XORAddressAttribute;

                        if (reflexAddressAttribute != null)
                        {
                            iceCandidate.StunRflxIPEndPoint  = new IPEndPoint(reflexAddressAttribute.Address, reflexAddressAttribute.Port);
                            iceCandidate.IsGatheringComplete = true;

                            logger.Debug("ICE gathering complete for local socket " + iceCandidate.LocalRtpSocket.LocalEndPoint + ", rflx address " + iceCandidate.StunRflxIPEndPoint + ".");
                        }
                        else
                        {
                            iceCandidate.IsGatheringComplete = true;

                            logger.Debug("The STUN binding response received on " + iceCandidate.LocalRtpSocket.LocalEndPoint + " from " + remoteEndPoint + " did not have an XORMappedAddress attribute, rlfx address can not be determined.");
                        }
                    }
                }
                else
                {
                    iceCandidate.LastStunResponseReceivedAt = DateTime.Now;

                    if (iceCandidate.IsStunLocalExchangeComplete == false)
                    {
                        iceCandidate.IsStunLocalExchangeComplete = true;
                        logger.Debug("WebRTC client STUN exchange complete for call " + CallID + ", candidate local socket " + iceCandidate.LocalRtpSocket.LocalEndPoint + ", remote socket " + remoteEndPoint + ".");

                        SetIceConnectionState(IceConnectionStatesEnum.Connected);
                    }
                }
            }
            else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingErrorResponse)
            {
                logger.Warn("A STUN binding error response was received on " + iceCandidate.LocalRtpSocket.LocalEndPoint + " from  " + remoteEndPoint + ".");
            }
            else
            {
                logger.Warn("An unrecognised STUN request was received on " + iceCandidate.LocalRtpSocket.LocalEndPoint + " from " + remoteEndPoint + ".");
            }
        }
Exemple #10
0
        private void StartWebRtcRtpListener(IceCandidate iceCandidate)
        {
            string localEndPoint = "?";

            try
            {
                localEndPoint = iceCandidate.LocalRtpSocket.LocalEndPoint.ToString();

                logger.Debug("Starting WebRTC RTP listener for call " + CallID + " on socket " + localEndPoint + ".");

                IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
                UdpClient  localSocket    = new UdpClient();
                localSocket.Client = iceCandidate.LocalRtpSocket;

                while (!IsClosed)
                {
                    try
                    {
                        //logger.Debug("ListenToReceiverWebRTCClient Receive.");
                        byte[] buffer = localSocket.Receive(ref remoteEndPoint);

                        iceCandidate.LastCommunicationAt = DateTime.Now;

                        //logger.Debug(buffer.Length + " bytes read on Receiver Client media socket from " + remoteEndPoint.ToString() + ".");

                        //if (buffer.Length > 3 && buffer[0] == 0x16 && buffer[1] == 0xfe)
                        if (buffer[0] >= 20 && buffer[0] <= 64)
                        {
                            //OnMediaPacket(iceCandidate, buffer, remoteEndPoint);
                            if (OnDtlsPacket != null)
                            {
                                OnDtlsPacket(iceCandidate, buffer, remoteEndPoint);
                            }
                        }
                        //else if ((buffer[0] & 0x80) == 0)
                        else if (buffer[0] == 0 || buffer[0] == 1)
                        {
                            STUNv2Message stunMessage = STUNv2Message.ParseSTUNMessage(buffer, buffer.Length);
                            ProcessStunMessage(iceCandidate, stunMessage, remoteEndPoint);
                        }
                        else
                        {
                            if (OnMediaPacket != null)
                            {
                                OnMediaPacket(iceCandidate, buffer, remoteEndPoint);
                            }
                        }
                    }
                    catch (Exception sockExcp)
                    {
                        _communicationFailureCount++;

                        logger.Warn("Exception ListenToReceiverWebRTCClient Receive (" + localEndPoint + " and " + remoteEndPoint + ", failure count " + _communicationFailureCount + "). " + sockExcp.Message);

                        // Need to be careful about deciding when the connection has failed. Sometimes the STUN requests we send will arrive before the remote peer is ready and cause a socket exception.
                        // Only shutdown the peer if we are sure all ICE intialisation is complete and the socket exception occurred after the RTP had stated flowing.
                        if (iceCandidate.IsStunLocalExchangeComplete && iceCandidate.IsStunRemoteExchangeComplete &&
                            iceCandidate.RemoteRtpEndPoint != null && remoteEndPoint != null && iceCandidate.RemoteRtpEndPoint.ToString() == remoteEndPoint.ToString() &&
                            DateTime.Now.Subtract(IceNegotiationStartedAt).TotalSeconds > 10)
                        {
                            logger.Warn("WebRtc peer communication failure on call " + CallID + " for local RTP socket " + localEndPoint + " and remote RTP socket " + remoteEndPoint + " .");
                            iceCandidate.DisconnectionMessage = sockExcp.Message;
                            break;
                        }
                        else if (_communicationFailureCount > COMMUNICATION_FAILURE_COUNT_FOR_CLOSE)
                        {
                            logger.Warn("WebRtc peer communication failures on call " + CallID + " exceeded limit of " + COMMUNICATION_FAILURE_COUNT_FOR_CLOSE + " closing peer.");
                            break;
                        }
                        //else if (DateTime.Now.Subtract(peer.IceNegotiationStartedAt).TotalSeconds > ICE_CONNECTION_LIMIT_SECONDS)
                        //{
                        //    logger.Warn("WebRTC peer ICE connection establishment timed out on call " + peer.CallID + " for " + iceCandidate.LocalRtpSocket.LocalEndPoint + ".");
                        //    break;
                        //}
                    }
                }

                Close();
            }
            catch (Exception excp)
            {
                logger.Error("Exception ListenForWebRTCClient (" + localEndPoint + "). " + excp);
            }
        }
Exemple #11
0
        public void AppendRemoteIceCandidate(IceCandidate remoteIceCandidate)
        {
            IPAddress candidateIPAddress = null;

            //foreach (var iceCandidate in remoteIceCandidates)
            //{
            //    logger.Debug("Appending remote ICE candidate " + iceCandidate.NetworkAddress + ":" + iceCandidate.Port + ".");
            //}

            if (remoteIceCandidate.Transport.ToLower() != "udp")
            {
                logger.Debug("Omitting remote non-UDP ICE candidate. " + remoteIceCandidate.RawString + ".");
            }
            else if (!IPAddress.TryParse(remoteIceCandidate.NetworkAddress, out candidateIPAddress))
            {
                logger.Debug("Omitting ICE candidate with unrecognised IP Address. " + remoteIceCandidate.RawString + ".");
            }
            else if (candidateIPAddress.AddressFamily == AddressFamily.InterNetworkV6)
            {
                logger.Debug("Omitting IPv6 ICE candidate. " + remoteIceCandidate.RawString + ".");
            }
            else
            {
                // ToDo: Add srflx and relay endpoints as hosts as well.

                if (!_remoteIceCandidates.Any(x => x.NetworkAddress == remoteIceCandidate.NetworkAddress && x.Port == remoteIceCandidate.Port))
                {
                    logger.Debug("Adding remote ICE candidate: " + remoteIceCandidate.CandidateType + " " + remoteIceCandidate.NetworkAddress + ":" + remoteIceCandidate.Port + " (" + remoteIceCandidate.RawString + ").");
                    _remoteIceCandidates.Add(remoteIceCandidate);
                }
            }
        }
        public void MediaPacketReceived(IceCandidate iceCandidate, byte[] buffer, IPEndPoint remoteEndPoint)
        {
            if ((buffer[0] >= 128) && (buffer[0] <= 191))
            {
                //logger.Debug("A non-STUN packet was received Receiver Client.");

                if (buffer[1] == 0xC8 /* RTCP SR */ || buffer[1] == 0xC9 /* RTCP RR */)
                {
                    // RTCP packet.
                    //webRtcClient.LastSTUNReceiveAt = DateTime.Now;
                }
                else
                {
                    // RTP packet.
                    //int res = peer.SrtpReceiveContext.UnprotectRTP(buffer, buffer.Length);

                    //if (res != 0)
                    //{
                    //    logger.Warn("SRTP unprotect failed, result " + res + ".");
                    //}
                }
            }
            else
            {
                logger.Debug("An unrecognised packet was received on the WebRTC media socket.");
            }
        }
        public void DtlsPacketReceived(IceCandidate iceCandidate, byte[] buffer, IPEndPoint remoteEndPoint)
        {
            logger.Debug("DTLS packet received " + buffer.Length + " bytes from " + remoteEndPoint.ToString() + ".");

            if (DtlsContext == null)
            {
                DtlsContext = new DtlsManaged();
                int res = DtlsContext.Init();
                logger.Debug("DtlsContext initialisation result=" + res);
            }

            int bytesWritten = DtlsContext.Write(buffer, buffer.Length);

            if (bytesWritten != buffer.Length)
            {
                logger.Warn("The required number of bytes were not successfully written to the DTLS context.");
            }
            else
            {
                byte[] dtlsOutBytes = new byte[2048];

                int bytesRead = DtlsContext.Read(dtlsOutBytes, dtlsOutBytes.Length);

                if (bytesRead == 0)
                {
                    logger.Debug("No bytes read from DTLS context :(.");
                }
                else
                {
                    logger.Debug(bytesRead + " bytes read from DTLS context sending to " + remoteEndPoint.ToString() + ".");
                    iceCandidate.LocalRtpSocket.SendTo(dtlsOutBytes, 0, bytesRead, SocketFlags.None, remoteEndPoint);

                    //if (client.DtlsContext.IsHandshakeComplete())
                    if (DtlsContext.GetState() == 3)
                    {
                        logger.Debug("DTLS negotiation complete for " + remoteEndPoint.ToString() + ".");
                        SrtpContext = new SRTPManaged(DtlsContext, false);
                        SrtpReceiveContext = new SRTPManaged(DtlsContext, true);
                        Peer.IsDtlsNegotiationComplete = true;
                        iceCandidate.RemoteRtpEndPoint = remoteEndPoint;
                    }
                }
            }
        }
Exemple #14
0
        private void StartWebRtcRtpListener(IceCandidate iceCandidate)
        {
            string localEndPoint = "?";

            try
            {
                localEndPoint = iceCandidate.LocalRtpSocket.LocalEndPoint.ToString();

                logger.Debug("Starting WebRTC RTP listener for call " + CallID + " on socket " + localEndPoint + ".");

                IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
                UdpClient localSocket = new UdpClient();
                localSocket.Client = iceCandidate.LocalRtpSocket;

                while (!IsClosed)
                {
                    try
                    {
                        //logger.Debug("ListenToReceiverWebRTCClient Receive.");
                        byte[] buffer = localSocket.Receive(ref remoteEndPoint);

                        iceCandidate.LastCommunicationAt = DateTime.Now;

                        //logger.Debug(buffer.Length + " bytes read on Receiver Client media socket from " + remoteEndPoint.ToString() + ".");

                        //if (buffer.Length > 3 && buffer[0] == 0x16 && buffer[1] == 0xfe)
                        if (buffer[0] >= 20 && buffer[0] <= 64)
                        {
                            //OnMediaPacket(iceCandidate, buffer, remoteEndPoint);
                            if (OnDtlsPacket != null)
                            {
                                OnDtlsPacket(iceCandidate, buffer, remoteEndPoint);
                            }
                        }
                        //else if ((buffer[0] & 0x80) == 0)
                        else if (buffer[0] == 0 || buffer[0] == 1)
                        {
                            STUNv2Message stunMessage = STUNv2Message.ParseSTUNMessage(buffer, buffer.Length);
                            ProcessStunMessage(iceCandidate, stunMessage, remoteEndPoint);
                        }
                        else
                        {
                            if (OnMediaPacket != null)
                            {
                                OnMediaPacket(iceCandidate, buffer, remoteEndPoint);
                            }
                        }
                    }
                    catch (Exception sockExcp)
                    {
                        _communicationFailureCount++;

                        logger.Warn("Exception ListenToReceiverWebRTCClient Receive (" + localEndPoint + " and " + remoteEndPoint + ", failure count " + _communicationFailureCount + "). " + sockExcp.Message);

                        // Need to be careful about deciding when the connection has failed. Sometimes the STUN requests we send will arrive before the remote peer is ready and cause a socket exception.
                        // Only shutdown the peer if we are sure all ICE intialisation is complete and the socket exception occurred after the RTP had stated flowing.
                        if (iceCandidate.IsStunLocalExchangeComplete && iceCandidate.IsStunRemoteExchangeComplete &&
                            iceCandidate.RemoteRtpEndPoint != null && remoteEndPoint != null && iceCandidate.RemoteRtpEndPoint.ToString() == remoteEndPoint.ToString() &&
                            DateTime.Now.Subtract(IceNegotiationStartedAt).TotalSeconds > 10)
                        {
                            logger.Warn("WebRtc peer communication failure on call " + CallID + " for local RTP socket " + localEndPoint + " and remote RTP socket " + remoteEndPoint + " .");
                            iceCandidate.DisconnectionMessage = sockExcp.Message;
                            break;
                        }
                        else if (_communicationFailureCount > COMMUNICATION_FAILURE_COUNT_FOR_CLOSE)
                        {
                            logger.Warn("WebRtc peer communication failures on call " + CallID + " exceeded limit of " + COMMUNICATION_FAILURE_COUNT_FOR_CLOSE + " closing peer.");
                            break;
                        }
                        //else if (DateTime.Now.Subtract(peer.IceNegotiationStartedAt).TotalSeconds > ICE_CONNECTION_LIMIT_SECONDS)
                        //{
                        //    logger.Warn("WebRTC peer ICE connection establishment timed out on call " + peer.CallID + " for " + iceCandidate.LocalRtpSocket.LocalEndPoint + ".");
                        //    break;
                        //}
                    }
                }

                Close();
            }
            catch (Exception excp)
            {
                logger.Error("Exception ListenForWebRTCClient (" + localEndPoint + "). " + excp);
            }
        }
Exemple #15
0
        private void SendInitialStunBindingRequest(IceCandidate iceCandidate, ManualResetEvent iceGatheringCompleteMRE)
        {
            int attempt = 1;

            while (attempt < INITIAL_STUN_BINDING_ATTEMPTS_LIMIT && !IsClosed && !iceCandidate.IsGatheringComplete)
            {
                logger.Debug("Sending STUN binding request " + attempt + " from " + iceCandidate.LocalRtpSocket.LocalEndPoint + " to " + iceCandidate.TurnServer.ServerEndPoint + ".");

                STUNv2Message stunRequest = new STUNv2Message(STUNv2MessageTypesEnum.BindingRequest);
                stunRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray();
                byte[] stunReqBytes = stunRequest.ToByteBuffer(null, false);

                iceCandidate.LocalRtpSocket.SendTo(stunReqBytes, iceCandidate.TurnServer.ServerEndPoint);

                Thread.Sleep(INITIAL_STUN_BINDING_PERIOD_MILLISECONDS);

                attempt++;
            }

            iceCandidate.IsGatheringComplete = true;

            // Potentially save a few seconds if all the ICE candidates are now ready.
            if (LocalIceCandidates.All(x => x.IsGatheringComplete))
            {
                iceGatheringCompleteMRE.Set();
            }
        }
Exemple #16
0
        private void ProcessStunMessage(IceCandidate iceCandidate, STUNv2Message stunMessage, IPEndPoint remoteEndPoint)
        {
            //logger.Debug("STUN message received from remote " + remoteEndPoint + " " + stunMessage.Header.MessageType + ".");

            if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingRequest)
            {
                STUNv2Message stunResponse = new STUNv2Message(STUNv2MessageTypesEnum.BindingSuccessResponse);
                stunResponse.Header.TransactionId = stunMessage.Header.TransactionId;
                stunResponse.AddXORMappedAddressAttribute(remoteEndPoint.Address, remoteEndPoint.Port);

                // ToDo: Check authentication.

                byte[] stunRespBytes = stunResponse.ToByteBufferStringKey(LocalIcePassword, true);
                iceCandidate.LocalRtpSocket.SendTo(stunRespBytes, remoteEndPoint);

                iceCandidate.LastStunRequestReceivedAt = DateTime.Now;
                iceCandidate.IsStunRemoteExchangeComplete = true;

                if (_remoteIceCandidates != null && !_remoteIceCandidates.Any(x =>
                     (x.NetworkAddress == remoteEndPoint.Address.ToString() || x.RemoteAddress == remoteEndPoint.Address.ToString()) &&
                     (x.Port == remoteEndPoint.Port || x.RemotePort == remoteEndPoint.Port)))
                {
                    // This STUN request has come from a socket not in the remote ICE candidates list. Add it so we can send our STUN binding request to it.
                    IceCandidate remoteIceCandidate = new IceCandidate()
                    {
                        Transport = "udp",
                        NetworkAddress = remoteEndPoint.Address.ToString(),
                        Port = remoteEndPoint.Port,
                        CandidateType = IceCandidateTypesEnum.host
                    };

                    logger.Debug("Adding missing remote ICE candidate for " + remoteEndPoint + ".");

                    _remoteIceCandidates.Add(remoteIceCandidate);
                }
            }
            else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingSuccessResponse)
            {
                if (_turnServerEndPoint != null && remoteEndPoint.ToString() == _turnServerEndPoint.ToString())
                {
                    if (iceCandidate.IsGatheringComplete == false)
                    {
                        var reflexAddressAttribute = stunMessage.Attributes.FirstOrDefault(y => y.AttributeType == STUNv2AttributeTypesEnum.XORMappedAddress) as STUNv2XORAddressAttribute;

                        if (reflexAddressAttribute != null)
                        {
                            iceCandidate.StunRflxIPEndPoint = new IPEndPoint(reflexAddressAttribute.Address, reflexAddressAttribute.Port);
                            iceCandidate.IsGatheringComplete = true;

                            logger.Debug("ICE gathering complete for local socket " + iceCandidate.LocalRtpSocket.LocalEndPoint + ", rflx address " + iceCandidate.StunRflxIPEndPoint + ".");
                        }
                        else
                        {
                            iceCandidate.IsGatheringComplete = true;

                            logger.Debug("The STUN binding response received on " + iceCandidate.LocalRtpSocket.LocalEndPoint + " from " + remoteEndPoint + " did not have an XORMappedAddress attribute, rlfx address can not be determined.");
                        }
                    }
                }
                else
                {
                    iceCandidate.LastStunResponseReceivedAt = DateTime.Now;

                    if (iceCandidate.IsStunLocalExchangeComplete == false)
                    {
                        iceCandidate.IsStunLocalExchangeComplete = true;
                        logger.Debug("WebRTC client STUN exchange complete for call " + CallID + ", candidate local socket " + iceCandidate.LocalRtpSocket.LocalEndPoint + ", remote socket " + remoteEndPoint + ".");

                        SetIceConnectionState(IceConnectionStatesEnum.Connected);
                    }
                }
            }
            else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingErrorResponse)
            {
                logger.Warn("A STUN binding error response was received on " + iceCandidate.LocalRtpSocket.LocalEndPoint + " from  " + remoteEndPoint + ".");
            }
            else
            {
                logger.Warn("An unrecognised STUN request was received on " + iceCandidate.LocalRtpSocket.LocalEndPoint + " from " + remoteEndPoint + ".");
            }
        }
Exemple #17
0
        private void GetIceCandidates(ManualResetEvent iceGatheringCompleteMRE)
        {
            IceNegotiationStartedAt = DateTime.Now;
            LocalIceCandidates = new List<IceCandidate>();
            List<UnicastIPAddressInformation> addresses = new List<UnicastIPAddressInformation>();

            // CAUTION: GetUnicastAddresses canj take up to 60 seconds to return if the machine has IP addresses in the IpDadStateTentative state,
            // such as DHCP addresses still checking for their lease. More info at: https://msdn.microsoft.com/en-us/library/windows/desktop/aa814507(v=vs.85).aspx
            //var addresses = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().GetUnicastAddresses()
            //    .Where(x =>
            //    x.Address.AddressFamily == AddressFamily.InterNetwork &&    // Exclude IPv6 at this stage.
            //    IPAddress.IsLoopback(x.Address) == false &&
            //    (x.Address != null && x.Address.ToString().StartsWith(AUTOMATIC_PRIVATE_ADRRESS_PREFIX) == false));

            foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
            {
                if (ni.OperationalStatus == OperationalStatus.Up)
                {
                    foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
                    {
                        if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && IPAddress.IsLoopback(ip.Address) == false && ip.IsTransient == false)
                        {
                            //Console.WriteLine(ip.Address.ToString());
                            addresses.Add(ip);
                        }
                    }
                }
            }

            foreach (var address in addresses)
            {
                logger.Debug("Attempting to create RTP socket with IP address " + address.Address + ".");

                Socket rtpSocket = null;
                Socket controlSocket = null;

                NetServices.CreateRtpSocket(address.Address, WEBRTC_START_PORT, WEBRTC_END_PORT, false, out rtpSocket, out controlSocket);

                if (rtpSocket != null)
                {
                    logger.Debug("RTP socket successfully created on " + rtpSocket.LocalEndPoint + ".");

                    var iceCandidate = new IceCandidate() { LocalAddress = address.Address, Port = ((IPEndPoint)rtpSocket.LocalEndPoint).Port, LocalRtpSocket = rtpSocket, LocalControlSocket = controlSocket,
                        TurnServer = (_turnServerEndPoint != null) ? new TurnServer() { ServerEndPoint = _turnServerEndPoint } : null };

                    LocalIceCandidates.Add(iceCandidate);

                    var listenerTask = Task.Run(() => { StartWebRtcRtpListener(iceCandidate); });

                    iceCandidate.RtpListenerTask = listenerTask;

                    if (_turnServerEndPoint != null)
                    {
                        var stunBindingTask = Task.Run(() => { SendInitialStunBindingRequest(iceCandidate, iceGatheringCompleteMRE); });
                    }
                    else
                    {
                        iceCandidate.IsGatheringComplete = true;

                        // Potentially save a few seconds if all the ICE candidates are now ready.
                        if (LocalIceCandidates.All(x => x.IsGatheringComplete))
                        {
                            iceGatheringCompleteMRE.Set();
                        }
                    }
                }
            }
        }
Exemple #18
0
        private void AllocateTurn(IceCandidate iceCandidate)
        {
            try
            {
                if (iceCandidate.TurnAllocateAttempts >= MAXIMUM_TURN_ALLOCATE_ATTEMPTS)
                {
                    logger.Debug("TURN allocation for local socket " + iceCandidate.LocalAddress + " failed after " + iceCandidate.TurnAllocateAttempts + " attempts.");

                    iceCandidate.IsGatheringComplete = true;
                }
                else
                {
                    iceCandidate.TurnAllocateAttempts++;

                    //logger.Debug("Sending STUN connectivity check to client " + client.SocketAddress + ".");

                    STUNv2Message stunRequest = new STUNv2Message(STUNv2MessageTypesEnum.Allocate);
                    stunRequest.Header.TransactionId = Guid.NewGuid().ToByteArray().Take(12).ToArray();
                    stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.Lifetime, 3600));
                    stunRequest.Attributes.Add(new STUNv2Attribute(STUNv2AttributeTypesEnum.RequestedTransport, STUNv2AttributeConstants.UdpTransportType));   // UDP
                    byte[] stunReqBytes = stunRequest.ToByteBuffer(null, false);
                    iceCandidate.LocalRtpSocket.SendTo(stunReqBytes, iceCandidate.TurnServer.ServerEndPoint);
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception AllocateTurn. " + excp);
            }
        }
Exemple #19
0
        private void ProcessStunMessage(STUNv2Message stunMessage, IPEndPoint remoteEndPoint)
        {
            //logger.LogDebug("STUN message received from remote " + remoteEndPoint + " " + stunMessage.Header.MessageType + ".");

            if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingRequest)
            {
                STUNv2Message stunResponse = new STUNv2Message(STUNv2MessageTypesEnum.BindingSuccessResponse);
                stunResponse.Header.TransactionId = stunMessage.Header.TransactionId;
                stunResponse.AddXORMappedAddressAttribute(remoteEndPoint.Address, remoteEndPoint.Port);

                // ToDo: Check authentication.

                string localIcePassword = LocalIcePassword;
                byte[] stunRespBytes    = stunResponse.ToByteBufferStringKey(localIcePassword, true);
                //iceCandidate.LocalRtpSocket.SendTo(stunRespBytes, remoteEndPoint);
                _rtpChannel.SendAsync(RTPChannelSocketsEnum.RTP, remoteEndPoint, stunRespBytes);

                //iceCandidate.LastStunRequestReceivedAt = DateTime.Now;
                //iceCandidate.IsStunRemoteExchangeComplete = true;

                if (RemoteEndPoint == null)
                {
                    RemoteEndPoint = remoteEndPoint;
                    _rtpSession.DestinationEndPoint = RemoteEndPoint;
                    _rtpSession.RtcpSession.ControlDestinationEndPoint = RemoteEndPoint;
                    //OnIceConnected?.Invoke(iceCandidate, remoteEndPoint);
                    IceConnectionState = IceConnectionStatesEnum.Connected;
                }

                if (_remoteIceCandidates != null && !_remoteIceCandidates.Any(x =>
                                                                              (x.NetworkAddress == remoteEndPoint.Address.ToString() || x.RemoteAddress == remoteEndPoint.Address.ToString()) &&
                                                                              (x.Port == remoteEndPoint.Port || x.RemotePort == remoteEndPoint.Port)))
                {
                    // This STUN request has come from a socket not in the remote ICE candidates list. Add it so we can send our STUN binding request to it.
                    IceCandidate remoteIceCandidate = new IceCandidate("udp", remoteEndPoint.Address, remoteEndPoint.Port, IceCandidateTypesEnum.host);
                    logger.LogDebug("Adding missing remote ICE candidate for " + remoteEndPoint + ".");
                    _remoteIceCandidates.Add(remoteIceCandidate);

                    // Some browsers require a STUN binding request from our end before the DTLS handshake will be initiated.
                    // The STUN connectivity checks are already scheduled but we can speed things up by sending a binding
                    // request immediately.
                    SendStunConnectivityChecks(null);
                }
            }
            else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingSuccessResponse)
            {
                // TODO: What needs to be done here?

                //if (_turnServerEndPoint != null && remoteEndPoint.ToString() == _turnServerEndPoint.ToString())
                //{
                //    if (iceCandidate.IsGatheringComplete == false)
                //    {
                //        var reflexAddressAttribute = stunMessage.Attributes.FirstOrDefault(y => y.AttributeType == STUNv2AttributeTypesEnum.XORMappedAddress) as STUNv2XORAddressAttribute;

                //        if (reflexAddressAttribute != null)
                //        {
                //            iceCandidate.StunRflxIPEndPoint = new IPEndPoint(reflexAddressAttribute.Address, reflexAddressAttribute.Port);
                //            iceCandidate.IsGatheringComplete = true;

                //            logger.LogDebug("ICE gathering complete for local socket " + iceCandidate.RtpChannel.RTPLocalEndPoint + ", rflx address " + iceCandidate.StunRflxIPEndPoint + ".");
                //        }
                //        else
                //        {
                //            iceCandidate.IsGatheringComplete = true;

                //            logger.LogDebug("The STUN binding response received on " + iceCandidate.RtpChannel.RTPLocalEndPoint + " from " + remoteEndPoint + " did not have an XORMappedAddress attribute, rlfx address can not be determined.");
                //        }
                //    }
                //}
                //else
                //{
                //    iceCandidate.LastStunResponseReceivedAt = DateTime.Now;

                //    if (iceCandidate.IsStunLocalExchangeComplete == false)
                //    {
                //        iceCandidate.IsStunLocalExchangeComplete = true;
                //        logger.LogDebug("WebRTC client STUN exchange complete for call " + CallID + ", candidate local socket " + iceCandidate.RtpChannel.RTPLocalEndPoint + ", remote socket " + remoteEndPoint + ".");

                //        SetIceConnectionState(IceConnectionStatesEnum.Connected);
                //    }
                //}
            }
            else if (stunMessage.Header.MessageType == STUNv2MessageTypesEnum.BindingErrorResponse)
            {
                logger.LogWarning($"A STUN binding error response was received from {remoteEndPoint}.");
            }
            else
            {
                logger.LogWarning($"An unrecognised STUN request was received from {remoteEndPoint}.");
            }
        }
Exemple #20
0
        public static SDP ParseSDPDescription(string sdpDescription)
        {
            try
            {
                if (sdpDescription != null && sdpDescription.Trim().Length > 0)
                {
                    SDP sdp = new SDP();
                    sdp.m_rawSdp = sdpDescription;
                    SDPMediaAnnouncement activeAnnouncement = null;

                    string[] sdpLines = Regex.Split(sdpDescription, CRLF);

                    foreach (string sdpLine in sdpLines)
                    {
                        string sdpLineTrimmed = sdpLine.Trim();
                        if (sdpLineTrimmed.StartsWith("v="))
                        {
                            if (!Decimal.TryParse(sdpLineTrimmed.Substring(2), out sdp.Version))
                            {
                                logger.LogWarning("The Version value in an SDP description could not be parsed as a decimal: " + sdpLine + ".");
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("o="))
                        {
                            string[] ownerFields = sdpLineTrimmed.Substring(2).Split(' ');
                            sdp.Username  = ownerFields[0];
                            sdp.SessionId = ownerFields[1];
                            Int32.TryParse(ownerFields[2], out sdp.AnnouncementVersion);
                            sdp.NetworkType   = ownerFields[3];
                            sdp.AddressType   = ownerFields[4];
                            sdp.AddressOrHost = ownerFields[5];
                        }
                        else if (sdpLineTrimmed.StartsWith("s="))
                        {
                            sdp.SessionName = sdpLineTrimmed.Substring(2);
                        }
                        else if (sdpLineTrimmed.StartsWith("c="))
                        {
                            if (sdp.Connection == null)
                            {
                                sdp.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLineTrimmed);
                            }

                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.Connection = SDPConnectionInformation.ParseConnectionInformation(sdpLineTrimmed);
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("b="))
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.BandwidthAttributes.Add(sdpLineTrimmed.Substring(2));
                            }
                            else
                            {
                                sdp.BandwidthAttributes.Add(sdpLineTrimmed.Substring(2));
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("t="))
                        {
                            sdp.Timing = sdpLineTrimmed.Substring(2);
                        }
                        else if (sdpLineTrimmed.StartsWith("m="))
                        {
                            Match mediaMatch = Regex.Match(sdpLineTrimmed.Substring(2), @"(?<type>\w+)\s+(?<port>\d+)\s+(?<transport>\S+)(\s*)(?<formats>.*)$");
                            if (mediaMatch.Success)
                            {
                                SDPMediaAnnouncement announcement = new SDPMediaAnnouncement();
                                announcement.Media = SDPMediaTypes.GetSDPMediaType(mediaMatch.Result("${type}"));
                                Int32.TryParse(mediaMatch.Result("${port}"), out announcement.Port);
                                announcement.Transport = mediaMatch.Result("${transport}");
                                announcement.ParseMediaFormats(mediaMatch.Result("${formats}"));
                                sdp.Media.Add(announcement);

                                activeAnnouncement = announcement;
                            }
                            else
                            {
                                logger.LogWarning("A media line in SDP was invalid: " + sdpLineTrimmed.Substring(2) + ".");
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("a=" + ICE_UFRAG_ATTRIBUTE_PREFIX))
                        {
                            sdp.IceUfrag = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                        }
                        else if (sdpLineTrimmed.StartsWith("a=" + ICE_PWD_ATTRIBUTE_PREFIX))
                        {
                            sdp.IcePwd = sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1);
                        }
                        else if (sdpLineTrimmed.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                Match formatAttributeMatch = Regex.Match(sdpLineTrimmed, SDPMediaAnnouncement.MEDIA_FORMAT_ATTRIBUE_PREFIX + @"(?<id>\d+)\s+(?<attribute>.*)$");
                                if (formatAttributeMatch.Success)
                                {
                                    int formatID;
                                    if (Int32.TryParse(formatAttributeMatch.Result("${id}"), out formatID))
                                    {
                                        activeAnnouncement.AddFormatAttribute(formatID, formatAttributeMatch.Result("${attribute}"));
                                    }
                                    else
                                    {
                                        logger.LogWarning("Invalid media format attribute in SDP: " + sdpLine);
                                    }
                                }
                                else
                                {
                                    activeAnnouncement.AddExtra(sdpLineTrimmed);
                                }
                            }
                            else
                            {
                                logger.LogWarning("There was no active media announcement for a media format attribute, ignoring.");
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith(SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                Match formatAttributeMatch = Regex.Match(sdpLineTrimmed, SDPMediaAnnouncement.MEDIA_FORMAT_PARAMETERS_ATTRIBUE_PREFIX + @"(?<id>\d+)\s+(?<attribute>.*)$");
                                if (formatAttributeMatch.Success)
                                {
                                    int formatID;
                                    if (Int32.TryParse(formatAttributeMatch.Result("${id}"), out formatID))
                                    {
                                        activeAnnouncement.AddFormatParameterAttribute(formatID, formatAttributeMatch.Result("${attribute}"));
                                    }
                                    else
                                    {
                                        logger.LogWarning("Invalid media format parameter attribute in SDP: " + sdpLine);
                                    }
                                }
                                else
                                {
                                    activeAnnouncement.AddExtra(sdpLineTrimmed);
                                }
                            }
                            else
                            {
                                logger.LogWarning("There was no active media announcement for a media format parameter attribute, ignoring.");
                            }
                        }
                        else if (sdpLineTrimmed.StartsWith("a=" + ICE_CANDIDATE_ATTRIBUTE_PREFIX))
                        {
                            if (sdp.IceCandidates == null)
                            {
                                sdp.IceCandidates = new List <IceCandidate>();
                            }

                            sdp.IceCandidates.Add(IceCandidate.Parse(sdpLineTrimmed.Substring(sdpLineTrimmed.IndexOf(':') + 1)));
                        }
                        //2018-12-21 rj2: add a=crypto
                        else if (sdpLineTrimmed.StartsWith(SDPSecurityDescription.CRYPTO_ATTRIBUE_PREFIX))
                        {
                            if (activeAnnouncement != null)
                            {
                                try
                                {
                                    activeAnnouncement.AddCryptoLine(sdpLineTrimmed);
                                }
                                catch (FormatException fex)
                                {
                                    logger.LogWarning("Error Parsing SDP-Line(a=crypto) " + fex);
                                }
                            }
                        }
                        else if (MediaStreamStatusType.IsMediaStreamStatusAttribute(sdpLine.Trim(), out var mediaStreamStatus))
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.MediaStreamStatus = mediaStreamStatus;
                            }
                            else
                            {
                                sdp.SessionMediaStreamStatus = mediaStreamStatus;
                            }
                        }
                        else
                        {
                            if (activeAnnouncement != null)
                            {
                                activeAnnouncement.AddExtra(sdpLineTrimmed);
                            }
                            else
                            {
                                sdp.AddExtra(sdpLineTrimmed);
                            }
                        }
                    }

                    return(sdp);
                }
                else
                {
                    return(null);
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception ParseSDPDescription. " + excp.Message);
                throw excp;
            }
        }
Exemple #21
0
        public static IceCandidate Parse(string candidateLine)
        {
            IceCandidate candidate = new IceCandidate();

            candidate.RawString = candidateLine;

            string[] candidateFields = candidateLine.Trim().Split(' ');
            candidate.Transport = candidateFields[2];
            candidate.NetworkAddress = candidateFields[4];
            candidate.Port = Convert.ToInt32(candidateFields[5]);
            Enum.TryParse<IceCandidateTypesEnum>(candidateFields[7], out candidate.CandidateType);

            if (candidateFields.Length > 8 && candidateFields[8] == REMOTE_ADDRESS_KEY)
            {
                candidate.RemoteAddress = candidateFields[9];
            }

            if (candidateFields.Length > 10 && candidateFields[10] == REMOTE_PORT_KEY)
            {
                candidate.RemotePort = Convert.ToInt32(candidateFields[11]);
            }

            return candidate;
        }
Exemple #22
0
        private void GetIceCandidates(ManualResetEvent iceGatheringCompleteMRE)
        {
            IceNegotiationStartedAt = DateTime.Now;
            LocalIceCandidates      = new List <IceCandidate>();
            List <UnicastIPAddressInformation> addresses = new List <UnicastIPAddressInformation>();

            // CAUTION: GetUnicastAddresses canj take up to 60 seconds to return if the machine has IP addresses in the IpDadStateTentative state,
            // such as DHCP addresses still checking for their lease. More info at: https://msdn.microsoft.com/en-us/library/windows/desktop/aa814507(v=vs.85).aspx
            //var addresses = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().GetUnicastAddresses()
            //    .Where(x =>
            //    x.Address.AddressFamily == AddressFamily.InterNetwork &&    // Exclude IPv6 at this stage.
            //    IPAddress.IsLoopback(x.Address) == false &&
            //    (x.Address != null && x.Address.ToString().StartsWith(AUTOMATIC_PRIVATE_ADRRESS_PREFIX) == false));

            foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
            {
                if (ni.OperationalStatus == OperationalStatus.Up)
                {
                    foreach (UnicastIPAddressInformation ip in ni.GetIPProperties().UnicastAddresses)
                    {
                        if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && IPAddress.IsLoopback(ip.Address) == false && ip.IsTransient == false)
                        {
                            //Console.WriteLine(ip.Address.ToString());
                            addresses.Add(ip);
                        }
                    }
                }
            }

            foreach (var address in addresses)
            {
                logger.Debug("Attempting to create RTP socket with IP address " + address.Address + ".");

                Socket rtpSocket     = null;
                Socket controlSocket = null;

                NetServices.CreateRtpSocket(address.Address, WEBRTC_START_PORT, WEBRTC_END_PORT, false, out rtpSocket, out controlSocket);

                if (rtpSocket != null)
                {
                    logger.Debug("RTP socket successfully created on " + rtpSocket.LocalEndPoint + ".");

                    var iceCandidate = new IceCandidate()
                    {
                        LocalAddress = address.Address, Port = ((IPEndPoint)rtpSocket.LocalEndPoint).Port, LocalRtpSocket = rtpSocket, LocalControlSocket = controlSocket,
                        TurnServer   = (_turnServerEndPoint != null) ? new TurnServer()
                        {
                            ServerEndPoint = _turnServerEndPoint
                        } : null
                    };

                    LocalIceCandidates.Add(iceCandidate);

                    var listenerTask = Task.Run(() => { StartWebRtcRtpListener(iceCandidate); });

                    iceCandidate.RtpListenerTask = listenerTask;

                    if (_turnServerEndPoint != null)
                    {
                        var stunBindingTask = Task.Run(() => { SendInitialStunBindingRequest(iceCandidate, iceGatheringCompleteMRE); });
                    }
                    else
                    {
                        iceCandidate.IsGatheringComplete = true;

                        // Potentially save a few seconds if all the ICE candidates are now ready.
                        if (LocalIceCandidates.All(x => x.IsGatheringComplete))
                        {
                            iceGatheringCompleteMRE.Set();
                        }
                    }
                }
            }
        }