private async Task InitializeORTC()
        {
            var gatherOptions = new RTCIceGatherOptions()
            {
                IceServers = new List <RTCIceServer>()
                {
                    new RTCIceServer {
                        Urls = new string[] { "stun.l.google.com:19302" }
                    },
                    new RTCIceServer {
                        Username = "******", Credential = "redmond123", CredentialType = RTCIceGathererCredentialType.Password, Urls = new string[] { "turn:turn-testdrive.cloudapp.net:3478?transport=udp" }
                    }
                }
            };

            _gatherer = new RTCIceGatherer(gatherOptions);
            _gatherer.OnStateChange += IceGatherer_OnStateChange;

            _gatherer.OnLocalCandidate += async(candidate) =>
            {
                await _signaler.SendToPeer(RemotePeer.Id, candidate.Candidate.ToJsonString());
            };

            var cert = await RTCCertificate.GenerateCertificate();

            _ice = new RTCIceTransport(_gatherer);
            _ice.OnStateChange += IceTransport_OnStateChange;

            _dtls = new RTCDtlsTransport(_ice, new RTCCertificate[] { cert });
            _dtls.OnStateChange += Dtls_OnStateChange;

            _sctp = new RTCSctpTransport(_dtls);
        }
        protected virtual void Dispose(bool disposing)
        {
            // clean all unmanaged resources (if any)

            if (disposing)
            {
                // clean all managed resources
                if (_dataChannel != null)
                {
                    _dataChannel.Close();
                    _dataChannel = null;
                }
                if (_sctp != null)
                {
                    _sctp.Stop();
                    _sctp = null;
                }
                if (_dtls != null)
                {
                    _dtls.Stop();
                    _dtls = null;
                }
                if (_ice != null)
                {
                    _ice.Stop();
                    _ice = null;
                }
                if (_gatherer != null)
                {
                    _gatherer.Close();
                    _gatherer = null;
                }
            }
        }
        private async Task InitializeORTC()
        {
            var gatherOptions = new RTCIceGatherOptions()
            {
                IceServers = new List <RTCIceServer>()
                {
                    new RTCIceServer
                    {
                        Urls = new string[] { _stunUrl }
                    },
                    new RTCIceServer
                    {
                        Username       = _turnUsername,
                        Credential     = _turnCredential,
                        CredentialType = RTCIceCredentialType.Password,
                        Urls           = new string[] { _turnUrl }
                    }
                }
            };

            _gatherer = new RTCIceGatherer(gatherOptions);

            _gatherer.OnStateChange += IceGatherer_OnStateChange;

            _gatherer.OnLocalCandidate += (@event) =>
            {
                OnSignalMessageToPeer(@event.Candidate.ToJson().ToString());
            };
            _gatherer.OnLocalCandidateComplete += (@event) =>
            {
                OnSignalMessageToPeer(@event.Candidate.ToJson().ToString());
            };

            _ice = new RTCIceTransport(_gatherer);
            _ice.OnStateChange += IceTransport_OnStateChange;

            _dtls = new RTCDtlsTransport(_ice, new RTCCertificate[]
            {
                await RTCCertificate.GenerateCertificate()
            });
            _dtls.OnStateChange += Dtls_OnStateChange;

            _sctp = new RTCSctpTransport(_dtls);

            _gatherer.Gather(null);
        }
        /// <summary>
        /// Establishes a DataChannel with the parameter peer.
        /// </summary>
        private void OpenDataChannel()
        {
            Debug.WriteLine($"Opening data channel to peer id: {RemotePeer.Id}");

            var iceParams = _gatherer.LocalParameters;

            OnSignalMessageToPeer(iceParams.ToJson().ToString());

            // this order guarantees: alice -> bob; bob.start(); bob -> alice; alice.start(); alice -> datachannel -> bob
            if (_isInitiator)
            {
                var sctpCaps = RTCSctpTransport.GetCapabilities();
                OnSignalMessageToPeer(sctpCaps.ToJson().ToString());
            }
            var dtlsParams = _dtls.LocalParameters;

            OnSignalMessageToPeer(dtlsParams.ToJson().ToString());
        }
        /// <summary>
        /// Estabilishes a DataChannel with the parameter peer.
        /// </summary>
        private async Task OpenDataChannel(Peer peer)
        {
            Debug.WriteLine($"Opening data channel to peer: {peer.Name}");

            Conversation = string.Empty;

            Debug.WriteLine("Ice: Gathering candidates.");
            _gatherer.Gather(null);

            var iceParams = _gatherer.GetLocalParameters();
            await _signaler.SendToPeer(peer.Id, iceParams.ToJsonString());

            // this order guarantees: alice -> bob; bob.start(); bob -> alice; alice.start(); alice -> datachannel -> bob
            if (_isInitiator)
            {
                var sctpCaps = RTCSctpTransport.GetCapabilities();
                await _signaler.SendToPeer(peer.Id, sctpCaps.ToJsonString());
            }

            var dtlsParams = _dtls.GetLocalParameters();
            await _signaler.SendToPeer(peer.Id, dtlsParams.ToJsonString());
        }
        private void Signaler_MessageFromPeer(object sender, Peer peer)
        {
            var message = peer.Message;

            if (message.Contains("OpenDataChannel"))
            {
                // A peer has let us know that they have begun initiating a data channel.  In this scenario,
                // we are the "remote" peer so make sure _isInitiator is false.  Begin gathering ice candidates.
                _isInitiator = false;
                RemotePeer   = peer;
                OpenDataChannel(peer);    // TODO: This is incorrect - message is definitely not the peer's name.
                return;
            }

            if (message.Contains("IceCandidate"))
            {
                var remoteCandidate = RTCIceCandidate.FromJsonString(message);
                _ice.AddRemoteCandidate(remoteCandidate);

                // TODO: Notify the ice transport when gathering is complete via the line below so it can change state.
                //_ice.AddRemoteCandidate(new RTCIceCandidateComplete());
                return;
            }

            if (message.Contains("IceParameters"))
            {
                var iceParameters = RTCIceParameters.FromJsonString(message);
                // Start the ice transport with the appropriate role based on whether this is the initiator of the call.
                var role = _isInitiator ? RTCIceRole.Controlling : RTCIceRole.Controlled;
                _ice.Start(_gatherer, iceParameters, role);
                return;
            }

            if (message.Contains("DtlsParameters"))
            {
                var dtlsParameters = RTCDtlsParameters.FromJsonString(message);
                _dtls.Start(dtlsParameters);
                Debug.WriteLine("Dtls start called.");
                return;
            }

            // this order guarantees:
            if (message.Contains("SctpCapabilities"))
            {
                // Message ordering: alice -> bob; bob.start(); bob -> alice; alice.start(); alice -> datachannel -> bob
                var sctpCaps = RTCSctpCapabilities.FromJsonString(message);

                if (!_isInitiator)
                {
                    Debug.WriteLine("Receiver: Waiting for OnDataChannel event and starting sctp.");

                    // The remote side will receive notification when the data channel is opened.
                    _sctp.OnDataChannel += Sctp_OnDataChannel;
                    _sctp.OnStateChange += Sctp_OnStateChange;

                    _sctp.Start(sctpCaps);

                    var caps = RTCSctpTransport.GetCapabilities();
                    _signaler.SendToPeer(RemotePeer.Id, caps.ToJsonString());
                }
                else
                {
                    // The initiator has received sctp caps back from the remote peer, which means the remote peer
                    // has already called sctp.start().  It's now safe to open a data channel, which will fire the
                    // Sctp_OnDataChannel event on the remote side.
                    Debug.WriteLine("Initiator: Creating the data channel and starting sctp.");

                    _sctp.OnStateChange += Sctp_OnStateChange;
                    _sctp.Start(sctpCaps);
                    _dataChannel                = new RTCDataChannel(_sctp, _dataChannelParams);
                    _dataChannel.OnMessage     += DataChannel_OnMessage;
                    _dataChannel.OnError       += DataChannel_OnError;
                    _dataChannel.OnStateChange += DataChannel_OnStateChange;

                    Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        IsSendEnabled = true;
                    });
                }
            }
        }
        public void HandleMessageFromPeer(string message)
        {
            if (message.StartsWith("{\"candidate"))
            {
                Debug.WriteLine("contains IceCandidate (or IceCandidateComplete)");

                Json jsonMessage = new Json(message);
                RTCIceGathererCandidate remoteCandidate = RTCIceGathererCandidate.Create(jsonMessage);
                _ice.AddRemoteCandidate(remoteCandidate);
                return;
            }
            if (message.StartsWith("{\"RTCIceParameters\":"))
            {
                Debug.WriteLine("contains IceParameters");

                Json             jsonMessage   = new Json(message);
                RTCIceParameters iceParameters = new RTCIceParameters(jsonMessage);

                // Start the ice transport with the appropriate role based on whether this is the initiator of the call.
                var role = _isInitiator ? RTCIceRole.Controlling : RTCIceRole.Controlled;
                _ice.Start(_gatherer, iceParameters, role);
                return;
            }
            if (message.StartsWith("{\"RTCDtlsParameters\":"))
            {
                Debug.WriteLine("contains DtlsParameters");

                Json jsonMessage = new Json(message);
                RTCDtlsParameters dtlsParameters = new RTCDtlsParameters(jsonMessage);

                _dtls.Start(dtlsParameters);
                Debug.WriteLine("Dtls start called.");
                return;
            }
            // this order guarantees:
            if (message.StartsWith("{\"RTCSctpCapabilities\":"))
            {
                Debug.WriteLine("contains SctpCapabilities");
                Json jsonMessage = new Json(message);
                // Message ordering: alice -> bob; bob.start(); bob -> alice; alice.start(); alice -> datachannel -> bob
                RTCSctpCapabilities sctpCaps = new RTCSctpCapabilities(jsonMessage);

                if (!_isInitiator)
                {
                    Debug.WriteLine("Receiver: Waiting for OnDataChannel event and starting sctp.");

                    // The remote side will receive notification when the data channel is opened.
                    _sctp.OnDataChannel += Sctp_OnDataChannel;
                    _sctp.OnStateChange += Sctp_OnStateChange;

                    _sctp.Start(sctpCaps);

                    RTCSctpCapabilities caps = RTCSctpTransport.GetCapabilities();

                    OnSignalMessageToPeer(caps.ToJson().ToString());
                }
                else
                {
                    // The initiator has received sctp caps back from the remote peer,
                    // which means the remote peer has already called sctp.start().
                    // It's now safe to open a data channel, which will fire the
                    // Sctp_OnDataChannel event on the remote side.
                    Debug.WriteLine("Initiator: Creating the data channel and starting sctp.");

                    _sctp.OnStateChange += Sctp_OnStateChange;
                    _sctp.Start(sctpCaps);

                    RTCDataTransport data = RTCDataTransport.Cast(_sctp);

                    _dataChannel            = new RTCDataChannel(data, _dataChannelParams);
                    _dataChannel.OnMessage += DataChannel_OnMessage;
                    _dataChannel.OnError   += DataChannel_OnError;
                    _dataChannel.OnOpen    += DataChannel_OnOpen;
                    _dataChannel.OnClose   += DataChannel_OnClose;
                }
            }
        }