Exemplo n.º 1
0
        public async void ChecklistProcessingUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            var rtpIceChannel = new RtpIceChannel(null, RTCIceComponent.rtp, null);

            rtpIceChannel.StartGathering();

            Assert.NotNull(rtpIceChannel);
            Assert.NotEmpty(rtpIceChannel.Candidates);

            foreach (var hostCandidate in rtpIceChannel.Candidates)
            {
                logger.LogDebug($"host candidate: {hostCandidate}");
            }

            var remoteCandidate = RTCIceCandidate.Parse("candidate:408132416 1 udp 2113937151 192.168.11.50 51268 typ host generation 0 ufrag CI7o network-cost 999");

            rtpIceChannel.AddRemoteCandidate(remoteCandidate);

            rtpIceChannel.SetRemoteCredentials("CI7o", "xxxxxxxxxxxx");
            rtpIceChannel.StartGathering();

            await Task.Delay(2000);

            var checklistEntry = rtpIceChannel._checklist.Single();

            logger.LogDebug($"Checklist entry state {checklistEntry.State}, last check sent at {checklistEntry.LastCheckSentAt}.");

            Assert.Equal(ChecklistEntryState.InProgress, checklistEntry.State);
        }
Exemplo n.º 2
0
        public void AddMulitpleIceServersTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            var iceServers = new List <RTCIceServer>
            {
                new RTCIceServer {
                    urls = $"stun:127.0.0.1:3478,stun:[::1]:3478"
                },
                new RTCIceServer {
                    urls = $"turn:turn:127.0.0.1:3478,stun:[::1]:3479"
                },
            };

            var rtpIceChannel = new RtpIceChannel(null, RTCIceComponent.rtp, iceServers);

            logger.LogDebug($"RTP ICE channel RTP socket local end point {rtpIceChannel.RTPLocalEndPoint}.");

            foreach (var pair in rtpIceChannel._iceServerConnections)
            {
                logger.LogDebug($"ICE server {pair.Key}, tx ID {pair.Value._id}");

                Assert.Equal(1, rtpIceChannel._iceServerConnections.Values.Count(x => x._id == pair.Value._id));
            }
        }
Exemplo n.º 3
0
        public async void ChecklistProcessingToFailStateUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            var rtpIceChannel = new RtpIceChannel(null, RTCIceComponent.rtp, null);

            rtpIceChannel.StartGathering();

            Assert.NotNull(rtpIceChannel);
            Assert.NotEmpty(rtpIceChannel.Candidates);

            foreach (var hostCandidate in rtpIceChannel.Candidates)
            {
                logger.LogDebug($"host candidate: {hostCandidate}");
            }

            var remoteCandidate = RTCIceCandidate.Parse("candidate:408132416 1 udp 2113937151 192.168.11.50 51268 typ host generation 0 ufrag CI7o network-cost 999");

            rtpIceChannel.AddRemoteCandidate(remoteCandidate);

            rtpIceChannel.SetRemoteCredentials("CI7o", "xxxxxxxxxxxx");

            logger.LogDebug($"ICE session retry interval {rtpIceChannel.RTO}ms.");

            await Task.Delay(1000);

            rtpIceChannel._checklist.Single().FirstCheckSentAt = DateTime.Now.Subtract(TimeSpan.FromSeconds(RtpIceChannel.FAILED_TIMEOUT_PERIOD));

            await Task.Delay(1000);

            Assert.Equal(ChecklistEntryState.Failed, rtpIceChannel._checklist.Single().State);
            Assert.Equal(ChecklistState.Failed, rtpIceChannel._checklistState);
            Assert.Equal(RTCIceConnectionState.failed, rtpIceChannel.IceConnectionState);
        }
Exemplo n.º 4
0
        public void SortChecklistUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            var rtpIceChannel = new RtpIceChannel(null, RTCIceComponent.rtp, null);

            rtpIceChannel.StartGathering();

            Assert.NotNull(rtpIceChannel);
            Assert.NotEmpty(rtpIceChannel.Candidates);

            foreach (var hostCandidate in rtpIceChannel.Candidates)
            {
                logger.LogDebug(hostCandidate.ToString());
            }

            var remoteCandidate = RTCIceCandidate.Parse("candidate:408132416 1 udp 2113937151 192.168.11.50 51268 typ host generation 0 ufrag CI7o network-cost 999");

            rtpIceChannel.AddRemoteCandidate(remoteCandidate);

            var remoteCandidate2 = RTCIceCandidate.Parse("candidate:408132417 1 udp 2113937150 192.168.11.51 51268 typ host generation 0 ufrag CI7o network-cost 999");

            rtpIceChannel.AddRemoteCandidate(remoteCandidate2);

            foreach (var entry in rtpIceChannel._checklist)
            {
                logger.LogDebug($"checklist entry priority {entry.Priority}.");
            }
        }
Exemplo n.º 5
0
        public async void ChecklistConstructionUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            var rtpIceChannel = new RtpIceChannel(null, RTCIceComponent.rtp, null);

            rtpIceChannel.StartGathering();

            Assert.NotNull(rtpIceChannel);
            Assert.NotEmpty(rtpIceChannel.Candidates);

            foreach (var hostCandidate in rtpIceChannel.Candidates)
            {
                logger.LogDebug($"host candidate: {hostCandidate}");
            }

            var remoteCandidate = RTCIceCandidate.Parse("candidate:408132416 1 udp 2113937151 192.168.11.50 51268 typ host generation 0 ufrag CI7o network-cost 999");

            rtpIceChannel.AddRemoteCandidate(remoteCandidate);

            var remoteCandidate2 = RTCIceCandidate.Parse("candidate:408132417 1 udp 2113937150 192.168.11.50 51268 typ host generation 0 ufrag CI7o network-cost 999");

            rtpIceChannel.AddRemoteCandidate(remoteCandidate2);

            await Task.Delay(500);

            foreach (var entry in rtpIceChannel._checklist)
            {
                logger.LogDebug($"checklist entry: {entry.LocalCandidate.ToShortString()}->{entry.RemoteCandidate.ToShortString()}");
            }

            Assert.Single(rtpIceChannel._checklist);
        }
Exemplo n.º 6
0
        public async void ChecklistProcessingToFailStateUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            var rtpIceChannel = new RtpIceChannel(null, RTCIceComponent.rtp, null);

            rtpIceChannel.StartGathering();

            Assert.NotNull(rtpIceChannel);
            Assert.NotEmpty(rtpIceChannel.Candidates);

            foreach (var hostCandidate in rtpIceChannel.Candidates)
            {
                logger.LogDebug($"host candidate: {hostCandidate}");
            }

            var remoteCandidate = RTCIceCandidate.Parse("candidate:408132416 1 udp 2113937151 192.168.11.50 51268 typ host generation 0 ufrag CI7o network-cost 999");

            rtpIceChannel.AddRemoteCandidate(remoteCandidate);

            rtpIceChannel.SetRemoteCredentials("CI7o", "xxxxxxxxxxxx");

            logger.LogDebug($"ICE session retry interval {rtpIceChannel.RTO}ms.");

            // The defaults are 5 STUN requests and for a checklist with one entry they will be 500ms apart.
            await Task.Delay(4000);

            Assert.Equal(ChecklistEntryState.Failed, rtpIceChannel._checklist.Single().State);
            Assert.Equal(ChecklistState.Failed, rtpIceChannel._checklistState);
            Assert.Equal(RTCIceConnectionState.failed, rtpIceChannel.IceConnectionState);
        }
Exemplo n.º 7
0
        public async void CheckSuccessfulConnectionForRelayCandidatesUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            using (MockTurnServer mockTurnServer = new MockTurnServer())
            {
                var iceServers = new List <RTCIceServer> {
                    new RTCIceServer
                    {
                        urls = $"turn:{mockTurnServer.ListeningEndPoint}",
                    }
                };
                var rtpIceChannelRelay = new RtpIceChannel(null, RTCIceComponent.rtp, iceServers, RTCIceTransportPolicy.relay);
                rtpIceChannelRelay.IsController = true;
                logger.LogDebug($"RTP ICE channel RTP socket local end point {rtpIceChannelRelay.RTPLocalEndPoint}.");

                var rtpIceChannelHost = new RtpIceChannel();
                logger.LogDebug($"RTP ICE channel RTP socket local end point {rtpIceChannelHost.RTPLocalEndPoint}.");

                rtpIceChannelRelay.StartGathering();
                rtpIceChannelHost.StartGathering();

                // Need to give some time for the relay channel to connect to the mock TURN server.
                await Task.Delay(200);

                Assert.Single(rtpIceChannelRelay.Candidates);   // Should only have the single local relay candidate.
                Assert.NotEmpty(rtpIceChannelHost.Candidates);
                Assert.Equal(RTCIceGatheringState.complete, rtpIceChannelRelay.IceGatheringState);
                Assert.Equal(RTCIceConnectionState.@new, rtpIceChannelRelay.IceConnectionState);
                Assert.Equal(RTCIceGatheringState.complete, rtpIceChannelHost.IceGatheringState);
                Assert.Equal(RTCIceConnectionState.@new, rtpIceChannelHost.IceConnectionState);

                // Exchange ICE user and passwords.
                rtpIceChannelRelay.SetRemoteCredentials(rtpIceChannelHost.LocalIceUser, rtpIceChannelHost.LocalIcePassword);
                rtpIceChannelHost.SetRemoteCredentials(rtpIceChannelRelay.LocalIceUser, rtpIceChannelRelay.LocalIcePassword);

                Assert.Equal(RTCIceConnectionState.checking, rtpIceChannelRelay.IceConnectionState);
                Assert.Equal(RTCIceConnectionState.checking, rtpIceChannelHost.IceConnectionState);

                // Exchange ICE candidates.
                rtpIceChannelRelay.Candidates.ForEach(x => rtpIceChannelHost.AddRemoteCandidate(x));
                rtpIceChannelHost.Candidates.ForEach(x => rtpIceChannelRelay.AddRemoteCandidate(x));

                await Task.Delay(1000);

                Assert.Equal(RTCIceConnectionState.connected, rtpIceChannelRelay.IceConnectionState);
                Assert.Equal(RTCIceConnectionState.connected, rtpIceChannelHost.IceConnectionState);
                Assert.NotNull(rtpIceChannelRelay.NominatedEntry);
                Assert.NotNull(rtpIceChannelHost.NominatedEntry);
            }
        }
Exemplo n.º 8
0
        public async void CheckSuccessfulConnectionForHostCandidatesUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            var rtpIceChannelA = new RtpIceChannel();

            rtpIceChannelA.IsController = true;
            logger.LogDebug($"RTP ICE channel RTP socket local end point {rtpIceChannelA.RTPLocalEndPoint}.");

            var rtpIceChannelB = new RtpIceChannel();

            logger.LogDebug($"RTP ICE channel RTP socket local end point {rtpIceChannelB.RTPLocalEndPoint}.");

            rtpIceChannelA.StartGathering();
            rtpIceChannelB.StartGathering();

            Assert.NotEmpty(rtpIceChannelA.Candidates);
            Assert.NotEmpty(rtpIceChannelB.Candidates);

            // Because there are no ICE servers gathering completes after the host candidates are gathered.
            Assert.Equal(RTCIceGatheringState.complete, rtpIceChannelA.IceGatheringState);
            Assert.Equal(RTCIceGatheringState.complete, rtpIceChannelB.IceGatheringState);
            Assert.Equal(RTCIceConnectionState.@new, rtpIceChannelA.IceConnectionState);
            Assert.Equal(RTCIceConnectionState.@new, rtpIceChannelB.IceConnectionState);

            // Exchange ICE user and passwords.
            rtpIceChannelA.SetRemoteCredentials(rtpIceChannelB.LocalIceUser, rtpIceChannelB.LocalIcePassword);
            rtpIceChannelB.SetRemoteCredentials(rtpIceChannelA.LocalIceUser, rtpIceChannelA.LocalIcePassword);

            Assert.Equal(RTCIceConnectionState.checking, rtpIceChannelA.IceConnectionState);
            Assert.Equal(RTCIceConnectionState.checking, rtpIceChannelB.IceConnectionState);

            // Give the RTP channel listeners time to start.
            await Task.Delay(500);

            // Exchange ICE candidates.
            rtpIceChannelA.Candidates.ForEach(x => rtpIceChannelB.AddRemoteCandidate(x));
            rtpIceChannelB.Candidates.ForEach(x => rtpIceChannelA.AddRemoteCandidate(x));

            // Give the RTP ICE channel checklists time to send the first few checks.
            await Task.Delay(4000);

            Assert.Equal(RTCIceConnectionState.connected, rtpIceChannelA.IceConnectionState);
            Assert.Equal(RTCIceConnectionState.connected, rtpIceChannelB.IceConnectionState);
            Assert.NotNull(rtpIceChannelA.NominatedEntry);
            Assert.NotNull(rtpIceChannelB.NominatedEntry);
        }
Exemplo n.º 9
0
        /// <summary>
        /// Creates a new RTP ICE channel (which manages the UDP socket sending and receiving RTP
        /// packets) for use with this session.
        /// </summary>
        /// <param name="mediaType">The type of media the RTP channel is for. Must be audio or video.</param>
        /// <returns>A new RTPChannel instance.</returns>
        protected override RTPChannel CreateRtpChannel(SDPMediaTypesEnum mediaType)
        {
            var rtpIceChannel = new RtpIceChannel(
                _configuration?.X_BindAddress,
                RTCIceComponent.rtp,
                _configuration?.iceServers,
                _configuration != null ? _configuration.iceTransportPolicy : RTCIceTransportPolicy.all);

            m_rtpChannels.Add(mediaType, rtpIceChannel);

            rtpIceChannel.OnRTPDataReceived += OnRTPDataReceived;

            // Start the RTP, and if required the Control, socket receivers and the RTCP session.
            rtpIceChannel.Start();

            return(rtpIceChannel);
        }
Exemplo n.º 10
0
        public async void CheckIPAddressOnlyStunServerUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            using (MockTurnServer mockStunServer = new MockTurnServer())
            {
                // Give the TURN server socket receive tasks time to fire up.
                await Task.Delay(1000);

                var iceServers = new List <RTCIceServer> {
                    new RTCIceServer
                    {
                        urls = $"stun:{mockStunServer.ListeningEndPoint}",
                    }
                };
                var rtpIceChannel = new RtpIceChannel(null, RTCIceComponent.rtp, iceServers);
                logger.LogDebug($"RTP ICE channel RTP socket local end point {rtpIceChannel.RTPLocalEndPoint}.");

                ManualResetEventSlim gatheringCompleted = new ManualResetEventSlim();
                rtpIceChannel.OnIceGatheringStateChange += (state) => { if (state == RTCIceGatheringState.complete)
                                                                        {
                                                                            gatheringCompleted.Set();
                                                                        }
                };

                rtpIceChannel.StartGathering();

                Assert.NotEmpty(rtpIceChannel.Candidates);

                // Because there is an ICE server gathering should still be in progress.
                Assert.Equal(RTCIceGatheringState.gathering, rtpIceChannel.IceGatheringState);
                Assert.Equal(RTCIceConnectionState.@new, rtpIceChannel.IceConnectionState);

                Assert.True(gatheringCompleted.Wait(3000));

                // The STUN server check should now have completed and a server reflexive candidate
                // been acquired

                Assert.Equal(RTCIceGatheringState.complete, rtpIceChannel.IceGatheringState);
                // The connection state stays in "new" because no remote ICE user and password has been set.
                Assert.Equal(RTCIceConnectionState.@new, rtpIceChannel.IceConnectionState);
                Assert.Contains(rtpIceChannel.Candidates, x => x.type == RTCIceCandidateType.srflx);
            }
        }
Exemplo n.º 11
0
        public void CreateInstanceUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            RTPSession rtpSession = new RTPSession(true, true, true);

            // Add a track to the session in order to initialise the RTPChannel.
            MediaStreamTrack dummyTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> {
                new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU)
            });

            rtpSession.addTrack(dummyTrack);

            var rtpIceChannel = new RtpIceChannel(null, RTCIceComponent.rtp, null);

            Assert.NotNull(rtpIceChannel);
        }
Exemplo n.º 12
0
        public void GetHostCandidatesUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            var rtpIceChannel = new RtpIceChannel(null, RTCIceComponent.rtp, null);

            logger.LogDebug($"RTP ICE channel RTP socket local end point {rtpIceChannel.RTPLocalEndPoint}.");

            rtpIceChannel.StartGathering();

            Assert.NotNull(rtpIceChannel);
            Assert.NotEmpty(rtpIceChannel.Candidates);

            foreach (var hostCandidate in rtpIceChannel.Candidates)
            {
                logger.LogDebug(hostCandidate.ToString());
            }
        }
Exemplo n.º 13
0
        public void GetHostCandidatesForRTPBindUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            var localAddress  = NetServices.InternetDefaultAddress;
            var rtpIceChannel = new RtpIceChannel(localAddress, RTCIceComponent.rtp, null);

            logger.LogDebug($"RTP ICE channel RTP socket local end point {rtpIceChannel.RTPLocalEndPoint}.");

            rtpIceChannel.StartGathering();

            Assert.NotNull(rtpIceChannel);
            Assert.NotEmpty(rtpIceChannel.Candidates);
            Assert.True(localAddress.Equals(IPAddress.Parse(rtpIceChannel.Candidates.Single().address)));

            foreach (var hostCandidate in rtpIceChannel.Candidates)
            {
                logger.LogDebug(hostCandidate.ToString());
            }
        }
Exemplo n.º 14
0
        public async void CheckPeerReflexiveReplacedByHostCandidatesUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            var rtpIceChannelA = new RtpIceChannel();

            rtpIceChannelA.IsController = true;
            logger.LogDebug($"RTP ICE channel RTP socket local end point {rtpIceChannelA.RTPLocalEndPoint}.");

            var rtpIceChannelB = new RtpIceChannel();

            logger.LogDebug($"RTP ICE channel RTP socket local end point {rtpIceChannelB.RTPLocalEndPoint}.");

            // Set up the triggers so the test can proceed at the right pace.
            ManualResetEventSlim connected = new ManualResetEventSlim();

            rtpIceChannelA.OnIceConnectionStateChange += (state) => { if (state == RTCIceConnectionState.connected)
                                                                      {
                                                                          connected.Set();
                                                                      }
            };

            rtpIceChannelA.StartGathering();
            rtpIceChannelB.StartGathering();

            Assert.NotEmpty(rtpIceChannelA.Candidates);
            Assert.NotEmpty(rtpIceChannelB.Candidates);

            // Because there are no ICE servers gathering completes after the host candidates are gathered.
            Assert.Equal(RTCIceGatheringState.complete, rtpIceChannelA.IceGatheringState);
            Assert.Equal(RTCIceGatheringState.complete, rtpIceChannelB.IceGatheringState);
            Assert.Equal(RTCIceConnectionState.@new, rtpIceChannelA.IceConnectionState);
            Assert.Equal(RTCIceConnectionState.@new, rtpIceChannelB.IceConnectionState);

            // Exchange ICE user and passwords.
            //rtpIceChannelA.SetRemoteCredentials(rtpIceChannelB.LocalIceUser, rtpIceChannelB.LocalIcePassword);
            rtpIceChannelB.SetRemoteCredentials(rtpIceChannelA.LocalIceUser, rtpIceChannelA.LocalIcePassword);

            Assert.Equal(RTCIceConnectionState.@new, rtpIceChannelA.IceConnectionState);
            Assert.Equal(RTCIceConnectionState.checking, rtpIceChannelB.IceConnectionState);

            // Only give the non-controlling peer the remote candidates.
            rtpIceChannelA.Candidates.ForEach(x => rtpIceChannelB.AddRemoteCandidate(x));
            //rtpIceChannelB.Candidates.ForEach(x => rtpIceChannelA.AddRemoteCandidate(x));

            // Want channel B to send checks to A so that it create peer reflexive candidates.
            int retries = 0;

            while (rtpIceChannelA._remoteCandidates.Count == 0 && retries < 5)
            {
                logger.LogDebug("Waiting for channel A to acquire peer reflexive candidates.");
                retries++;
                await Task.Delay(500);
            }

            Assert.True(rtpIceChannelA._remoteCandidates.Count > 0);

            logger.LogDebug("Adding remote candidates from B to A.");

            rtpIceChannelB.Candidates.ForEach(x => rtpIceChannelA.AddRemoteCandidate(x));

            // This pause is so that channel A can process the new remote candidates supplied by B.
            // These candidates are host candidates and should replace the peer reflexive candidates
            // that were automatically created previously.
            await Task.Delay(1000);

            logger.LogDebug("Setting remote credentials for channel A.");

            rtpIceChannelA.SetRemoteCredentials(rtpIceChannelB.LocalIceUser, rtpIceChannelB.LocalIcePassword);

            Assert.True(connected.Wait(3000));

            Assert.Equal(RTCIceConnectionState.connected, rtpIceChannelA.IceConnectionState);
            Assert.Equal(RTCIceConnectionState.connected, rtpIceChannelB.IceConnectionState);
            Assert.NotNull(rtpIceChannelA.NominatedEntry);
            Assert.NotNull(rtpIceChannelB.NominatedEntry);
            Assert.Equal(RTCIceCandidateType.host, rtpIceChannelA.NominatedEntry.LocalCandidate.type);
            Assert.Equal(RTCIceCandidateType.host, rtpIceChannelB.NominatedEntry.LocalCandidate.type);
        }
Exemplo n.º 15
0
        /// <summary>
        /// Constructor to create a new RTC peer connection instance.
        /// </summary>
        /// <param name="configuration">Optional.</param>
        public RTCPeerConnection(RTCConfiguration configuration) :
            base(true, true, true, configuration?.X_BindAddress)
        {
            if (_configuration != null &&
                _configuration.iceTransportPolicy == RTCIceTransportPolicy.relay &&
                _configuration.iceServers?.Count == 0)
            {
                throw new ApplicationException("RTCPeerConnection must have at least one ICE server specified for a relay only transport policy.");
            }

            _configuration = configuration;
            if (_configuration != null)
            {
                if (_configuration.certificates?.Count > 0)
                {
                    _currentCertificate = _configuration.certificates.First();
                }

                if (_configuration.X_UseRtpFeedbackProfile)
                {
                    RTP_MEDIA_PROFILE = RTP_MEDIA_FEEDBACK_PROFILE;
                }
            }

            SessionID         = Guid.NewGuid().ToString();
            LocalSdpSessionID = Crypto.GetRandomInt(5).ToString();

            // Request the underlying RTP session to create a single RTP channel that will
            // be used to multiplex all required media streams.
            addSingleTrack();

            _rtpIceChannel = GetRtpChannel(SDPMediaTypesEnum.audio) as RtpIceChannel;

            _rtpIceChannel.OnIceCandidate             += (candidate) => onicecandidate?.Invoke(candidate);
            _rtpIceChannel.OnIceConnectionStateChange += (state) =>
            {
                if (state == RTCIceConnectionState.connected && _rtpIceChannel.NominatedEntry != null)
                {
                    var connectedEP = _rtpIceChannel.NominatedEntry.RemoteCandidate.DestinationEndPoint;
                    base.SetDestination(SDPMediaTypesEnum.audio, connectedEP, connectedEP);
                }

                iceConnectionState = state;
                oniceconnectionstatechange?.Invoke(iceConnectionState);

                if (base.IsSecureContextReady &&
                    iceConnectionState == RTCIceConnectionState.connected &&
                    connectionState != RTCPeerConnectionState.connected)
                {
                    // This is the case where the ICE connection checks completed after the DTLS handshake.
                    connectionState = RTCPeerConnectionState.connected;
                    onconnectionstatechange?.Invoke(RTCPeerConnectionState.connected);
                }
            };
            _rtpIceChannel.OnIceGatheringStateChange += (state) => onicegatheringstatechange?.Invoke(state);
            _rtpIceChannel.OnIceCandidateError       += (candidate, error) => onicecandidateerror?.Invoke(candidate, error);

            OnRtpClosed += Close;
            OnRtcpBye   += Close;

            onnegotiationneeded?.Invoke();

            _rtpIceChannel.StartGathering();
        }
Exemplo n.º 16
0
        /// <summary>
        /// Constructor to create a new RTC peer connection instance.
        /// </summary>
        /// <param name="configuration">Optional.</param>
        public RTCPeerConnection(RTCConfiguration configuration) :
            base(true, true, true, configuration?.X_BindAddress)
        {
            if (_configuration != null &&
                _configuration.iceTransportPolicy == RTCIceTransportPolicy.relay &&
                _configuration.iceServers?.Count == 0)
            {
                throw new ApplicationException("RTCPeerConnection must have at least one ICE server specified for a relay only transport policy.");
            }

            if (configuration != null)
            {
                _configuration = configuration;
                if (_configuration.certificates?.Count > 0)
                {
                    // Find the first certificate that has a usable private key.
                    RTCCertificate usableCert = null;
                    foreach (var cert in _configuration.certificates)
                    {
                        // Attempting to check that a certificate has an exportable private key.
                        // TODO: Does not seem to be a particularly reliable way of checking private key exportability.
                        if (cert.Certificate.HasPrivateKey)
                        {
                            //if (cert.Certificate.PrivateKey is RSACryptoServiceProvider)
                            //{
                            //    var rsa = cert.Certificate.PrivateKey as RSACryptoServiceProvider;
                            //    if (!rsa.CspKeyContainerInfo.Exportable)
                            //    {
                            //        logger.LogWarning($"RTCPeerConnection was passed a certificate for {cert.Certificate.FriendlyName} with a non-exportable RSA private key.");
                            //    }
                            //    else
                            //    {
                            //        usableCert = cert;
                            //        break;
                            //    }
                            //}
                            //else
                            //{
                            usableCert = cert;
                            break;
                            //}
                        }
                    }

                    if (usableCert == null)
                    {
                        throw new ApplicationException("RTCPeerConnection was not able to find a certificate from the input configuration list with a usable private key.");
                    }
                    else
                    {
                        _currentCertificate = usableCert;
                    }
                }

                if (_configuration.X_UseRtpFeedbackProfile)
                {
                    RTP_MEDIA_PROFILE = RTP_MEDIA_FEEDBACK_PROFILE;
                }
            }
            else
            {
                _configuration = new RTCConfiguration();
            }

            // No certificate was provided so create a new self signed one.
            if (_configuration.certificates == null || _configuration.certificates.Count == 0)
            {
                _currentCertificate = new RTCCertificate {
                    Certificate = DtlsUtils.CreateSelfSignedCert()
                };
                _configuration.certificates = new List <RTCCertificate> {
                    _currentCertificate
                };
            }

            SessionID         = Guid.NewGuid().ToString();
            LocalSdpSessionID = Crypto.GetRandomInt(5).ToString();

            // Request the underlying RTP session to create a single RTP channel that will
            // be used to multiplex all required media streams.
            addSingleTrack();

            _rtpIceChannel = GetRtpChannel();

            _rtpIceChannel.OnIceCandidate             += (candidate) => onicecandidate?.Invoke(candidate);
            _rtpIceChannel.OnIceConnectionStateChange += (state) =>
            {
                if (state == RTCIceConnectionState.connected && _rtpIceChannel.NominatedEntry != null)
                {
                    var connectedEP = _rtpIceChannel.NominatedEntry.RemoteCandidate.DestinationEndPoint;
                    base.SetDestination(SDPMediaTypesEnum.audio, connectedEP, connectedEP);

                    logger.LogInformation($"ICE connected to remote end point {AudioDestinationEndPoint}.");

                    DtlsSrtpTransport dtlsHandle = new DtlsSrtpTransport(
                        IceRole == IceRolesEnum.active ?
                        (IDtlsSrtpPeer) new DtlsSrtpClient(_currentCertificate.Certificate) :
                        (IDtlsSrtpPeer) new DtlsSrtpServer(_currentCertificate.Certificate));

                    OnDtlsPacket += (buf) =>
                    {
                        logger.LogDebug($"DTLS transport received {buf.Length} bytes from {AudioDestinationEndPoint}.");
                        dtlsHandle.WriteToRecvStream(buf);
                    };

                    logger.LogDebug($"Starting DLS handshake with role {IceRole}.");
                    Task.Run <bool>(() => DoDtlsHandshake(dtlsHandle))
                    .ContinueWith(t =>
                    {
                        if (t.IsFaulted)
                        {
                            logger.LogWarning($"RTCPeerConnection DTLS handshake task completed in a faulted state. {t.Exception?.Flatten().Message}");

                            connectionState = RTCPeerConnectionState.failed;
                            onconnectionstatechange?.Invoke(connectionState);
                        }
                        else
                        {
                            connectionState = (t.Result) ? RTCPeerConnectionState.connected : connectionState = RTCPeerConnectionState.failed;
                            onconnectionstatechange?.Invoke(connectionState);
                        }
                    });
                }

                iceConnectionState = state;
                oniceconnectionstatechange?.Invoke(iceConnectionState);
            };
            _rtpIceChannel.OnIceGatheringStateChange += (state) => onicegatheringstatechange?.Invoke(state);
            _rtpIceChannel.OnIceCandidateError       += (candidate, error) => onicecandidateerror?.Invoke(candidate, error);

            OnRtpClosed += Close;
            OnRtcpBye   += Close;

            onnegotiationneeded?.Invoke();

            _rtpIceChannel.StartGathering();
        }