/// <summary>
 /// Constructor called internally by <see cref="PeerConnection.AddMediaLine(MediaKind)"/>.
 /// </summary>
 /// <param name="kind">Immutable value assigned to the <see cref="MediaKind"/> property on construction.</param>
 internal MediaLine(PeerConnection peer, MediaKind kind)
 {
     _peer      = peer;
     _mediaKind = kind;
 }
 /// <summary>
 /// Callback fired from the <see cref="PeerConnection"/> before it starts
 /// uninitializing itself and disposing of the underlying implementation object.
 /// </summary>
 /// <param name="peer">The peer connection about to be deinitialized</param>
 public void OnPeerUninitializing(PeerConnection peer)
 {
     // Unregister handlers for the SDP events
     //_nativePeer.IceCandidateReadytoSend -= OnIceCandiateReadyToSend_Listener;
     //_nativePeer.LocalSdpReadytoSend -= OnLocalSdpReadyToSend_Listener;
 }
Example #3
0
        public override void OnPeerInitialized(PeerConnection peer)
        {
            base.OnPeerInitialized(peer);

            _nativePeer?.CreateOffer();
        }
Example #4
0
        protected async void OnEnable()
        {
            if (Source != null)
            {
                return;
            }

#if PLATFORM_ANDROID
            // Ensure Android binding is initialized before accessing the native implementation
            Android.Initialize();

            // Check for permission to access the camera
            if (!Permission.HasUserAuthorizedPermission(Permission.Camera))
            {
                if (!_androidCameraRequestPending)
                {
                    // Monitor the OnApplicationFocus(true) event during the next 5 minutes,
                    // and check for permission again each time (see below why).
                    _androidCameraRequestPending        = true;
                    _androidCameraRequestRetryUntilTime = Time.time + 300;

                    // Display dialog requesting user permission. This will return immediately,
                    // and unfortunately there's no good way to tell when this completes. As a rule
                    // of thumb, application should lose focus, so check when focus resumes should
                    // be sufficient without having to poll every frame.
                    Permission.RequestUserPermission(Permission.Camera);
                }
                return;
            }
#elif UNITY_WSA && !UNITY_EDITOR
            // Request UWP access to video capture. The OS may show some popup dialog to the
            // user to request permission. This will succeed only if the user grants permission.
            try
            {
                // Note that the UWP UI thread and the main Unity app thread are always different.
                // https://docs.unity3d.com/Manual/windowsstore-appcallbacks.html
                // We leave the code below as an example of generic handling in case this would be used in
                // some other place, and in case a future version of Unity decided to change that assumption,
                // but currently OnEnable() is always invoked from the main Unity app thread so here the first
                // branch is never taken.
                if (UnityEngine.WSA.Application.RunningOnUIThread())
                {
                    await RequestAccessAsync();
                }
                else
                {
                    UnityEngine.WSA.Application.InvokeOnUIThread(() => RequestAccessAsync(), waitUntilDone: true);
                }
            }
            catch (Exception ex)
            {
                // Log an error and prevent activation
                Debug.LogError($"Video access failure: {ex.Message}.");
                this.enabled = false;
                return;
            }
#endif

            // Handle automatic capture format constraints
            string videoProfileId   = VideoProfileId;
            var    videoProfileKind = VideoProfileKind;
            int    width            = Constraints.width;
            int    height           = Constraints.height;
            double framerate        = Constraints.framerate;
#if ENABLE_WINMD_SUPPORT
            if (FormatMode == LocalVideoSourceFormatMode.Automatic)
            {
                // Do not constrain resolution by default, unless the device calls for it (see below).
                width  = 0; // auto
                height = 0; // auto

                // Avoid constraining the framerate; this is generally not necessary (formats are listed
                // with higher framerates first) and is error-prone as some formats report 30.0 FPS while
                // others report 29.97 FPS.
                framerate = 0; // auto

                // For HoloLens, use video profile to reduce resolution and save power/CPU/bandwidth
                if (global::Windows.Graphics.Holographic.HolographicSpace.IsAvailable)
                {
                    if (!global::Windows.Graphics.Holographic.HolographicDisplay.GetDefault().IsOpaque)
                    {
                        if (global::Windows.ApplicationModel.Package.Current.Id.Architecture == global::Windows.System.ProcessorArchitecture.X86)
                        {
                            // Holographic AR (transparent) x86 platform - Assume HoloLens 1
                            videoProfileKind = WebRTC.VideoProfileKind.VideoRecording; // No profile in VideoConferencing
                            width            = 896;                                    // Target 896 x 504
                        }
                        else
                        {
                            // Holographic AR (transparent) non-x86 platform - Assume HoloLens 2
                            videoProfileKind = WebRTC.VideoProfileKind.VideoConferencing;
                            width            = 960; // Target 960 x 540
                        }
                    }
                }
            }
#elif PLATFORM_ANDROID
            if (FormatMode == LocalVideoSourceFormatMode.Automatic)
            {
                // Avoid constraining the framerate; this is generally not necessary (formats are listed
                // with higher framerates first) and is error-prone as some formats report 30.0 FPS while
                // others report 29.97 FPS.
                framerate = 0; // auto

                string deviceId = WebcamDevice.id;
                if (string.IsNullOrEmpty(deviceId))
                {
                    List <VideoCaptureDevice> listedDevices = await PeerConnection.GetVideoCaptureDevicesAsync();

                    if (listedDevices.Count > 0)
                    {
                        deviceId = listedDevices[0].id;
                    }
                }
                if (!string.IsNullOrEmpty(deviceId))
                {
                    // Find the closest format to 720x480, independent of framerate
                    List <VideoCaptureFormat> formats = await DeviceVideoTrackSource.GetCaptureFormatsAsync(deviceId);

                    double smallestDiff = double.MaxValue;
                    bool   hasFormat    = false;
                    foreach (var fmt in formats)
                    {
                        double diff = Math.Abs(fmt.width - 720) + Math.Abs(fmt.height - 480);
                        if ((diff < smallestDiff) || !hasFormat)
                        {
                            hasFormat    = true;
                            smallestDiff = diff;
                            width        = (int)fmt.width;
                            height       = (int)fmt.height;
                        }
                    }
                    if (hasFormat)
                    {
                        Debug.Log($"WebcamSource automated mode selected resolution {width}x{height} for Android video capture device #{deviceId}.");
                    }
                }
            }
#endif

            // TODO - Fix codec selection (was as below before change)

            // Force again PreferredVideoCodec right before starting the local capture,
            // so that modifications to the property done after OnPeerInitialized() are
            // accounted for.
            //< FIXME
            //PeerConnection.Peer.PreferredVideoCodec = PreferredVideoCodec;

            // Check H.264 requests on Desktop (not supported)
            //#if !ENABLE_WINMD_SUPPORT
            //            if (PreferredVideoCodec == "H264")
            //            {
            //                Debug.LogError("H.264 encoding is not supported on Desktop platforms. Using VP8 instead.");
            //                PreferredVideoCodec = "VP8";
            //            }
            //#endif

            // Create the track
            var deviceConfig = new LocalVideoDeviceInitConfig
            {
                videoDevice                 = WebcamDevice,
                videoProfileId              = videoProfileId,
                videoProfileKind            = videoProfileKind,
                width                       = (width > 0 ? (uint?)width : null),
                height                      = (height > 0 ? (uint?)height : null),
                framerate                   = (framerate > 0 ? (double?)framerate : null),
                enableMrc                   = EnableMixedRealityCapture,
                enableMrcRecordingIndicator = EnableMRCRecordingIndicator
            };
            try
            {
                var source = await DeviceVideoTrackSource.CreateAsync(deviceConfig);

                AttachSource(source);
            }
            catch (Exception ex)
            {
                Debug.LogError($"Failed to create device track source for {nameof(WebcamSource)} component '{name}'.");
                Debug.LogException(ex, this);
                return;
            }
        }
Example #5
0
        /// <summary>
        /// Internal coroutine helper for receiving HTTP data from the DSS server using GET
        /// and processing it as needed
        /// </summary>
        /// <returns>the message</returns>
        private IEnumerator CO_GetAndProcessFromServer()
        {
            if (HttpServerAddress.Length == 0)
            {
                throw new InvalidOperationException("Cannot receive SDP messages from remote peer; invalid empty HTTP server address.");
            }
            if (LocalPeerId.Length == 0)
            {
                throw new InvalidOperationException("Cannot receive SDP messages from remote peer; invalid empty local peer ID.");
            }

            var www = UnityWebRequest.Get($"{HttpServerAddress}data/{LocalPeerId}");

            yield return(www.SendWebRequest());

            if (!www.isNetworkError && !www.isHttpError)
            {
                var json = www.downloadHandler.text;

                var msg = JsonUtility.FromJson <NodeDssMessage>(json);

                // if the message is good
                if (msg != null)
                {
                    // depending on what type of message we get, we'll handle it differently
                    // this is the "glue" that allows two peers to establish a connection.
                    DebugLogLong($"Received SDP message: type={msg.MessageType} data={msg.Data}");
                    switch (msg.MessageType)
                    {
                    case NodeDssMessage.Type.Offer:
                        // Apply the offer coming from the remote peer to the local peer
                        var sdpOffer = new WebRTC.SdpMessage {
                            Type = SdpMessageType.Offer, Content = msg.Data
                        };
                        PeerConnection.HandleConnectionMessageAsync(sdpOffer).ContinueWith(_ =>
                        {
                            // If the remote description was successfully applied then immediately send
                            // back an answer to the remote peer to acccept the offer.
                            _nativePeer.CreateAnswer();
                        }, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.RunContinuationsAsynchronously);
                        break;

                    case NodeDssMessage.Type.Answer:
                        // No need to wait for completion; there is nothing interesting to do after it.
                        var sdpAnswer = new WebRTC.SdpMessage {
                            Type = SdpMessageType.Answer, Content = msg.Data
                        };
                        _ = PeerConnection.HandleConnectionMessageAsync(sdpAnswer);
                        break;

                    case NodeDssMessage.Type.Ice:
                        // this "parts" protocol is defined above, in OnIceCandidateReadyToSend listener
                        _nativePeer.AddIceCandidate(msg.ToIceCandidate());
                        break;

                    default:
                        Debug.Log("Unknown message: " + msg.MessageType + ": " + msg.Data);
                        break;
                    }

                    timeSincePollMs = PollTimeMs + 1f; //fast forward next request
                }
                else if (AutoLogErrors)
                {
                    Debug.LogError($"Failed to deserialize JSON message : {json}");
                }
            }
            else if (AutoLogErrors && www.isNetworkError)
            {
                Debug.LogError($"Network error trying to send data to {HttpServerAddress}: {www.error}");
            }
            else
            {
                // This is very spammy because the node-dss protocol uses 404 as regular "no data yet" message, which is an HTTP error
                //Debug.LogError($"HTTP error: {www.error}");
            }

            lastGetComplete = true;
        }
        /// <summary>
        /// Internal coroutine helper for receiving HTTP data from the DSS server using GET
        /// and processing it as needed
        /// </summary>
        /// <returns>the message</returns>
        private IEnumerator CO_GetAndProcessFromServer()
        {
            if (HttpServerAddress.Length == 0)
            {
                throw new InvalidOperationException("Cannot receive SDP messages from remote peer; invalid empty HTTP server address.");
            }
            if (LocalPeerId.Length == 0)
            {
                throw new InvalidOperationException("Cannot receive SDP messages from remote peer; invalid empty local peer ID.");
            }

            var www = UnityWebRequest.Get($"{HttpServerAddress}data/{LocalPeerId}");

            yield return(www.SendWebRequest());

            if (!www.isNetworkError && !www.isHttpError)
            {
                var json = www.downloadHandler.text;

                NodeDssMessage msg;

                if (Encrypt)
                {
                    EncryptedMessage encryptedMessage = JsonUtility.FromJson <EncryptedMessage>(json);
                    msg = EncryptedMessage.DecryptMessage(encryptedMessage.Cipher, encryptedMessage.Hmac, this.Key, this.IV);
                }
                else
                {
                    msg = JsonUtility.FromJson <NodeDssMessage>(json);
                }

                // {"Cipher":"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","Hmac":"b4302c1cc75d5916f009768e9396194d6bb7ff4fead75c62534779a778bb7f94"}



                // if the message is good
                if (msg != null)
                {
                    // depending on what type of message we get, we'll handle it differently
                    // this is the "glue" that allows two peers to establish a connection.
                    DebugLogLong($"Received SDP message: type={msg.MessageType} data={msg.Data}");
                    switch (msg.MessageType)
                    {
                    case NodeDssMessage.Type.Offer:
                        // Apply the offer coming from the remote peer to the local peer
                        var sdpOffer = new WebRTC.SdpMessage {
                            Type = SdpMessageType.Offer, Content = msg.Data
                        };
                        PeerConnection.HandleConnectionMessageAsync(sdpOffer).ContinueWith(_ =>
                        {
                            // If the remote description was successfully applied then immediately send
                            // back an answer to the remote peer to acccept the offer.
                            _nativePeer.CreateAnswer();
                            _nativePeer.DataChannelAdded += OnDataChannelAdded;
                        }, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.RunContinuationsAsynchronously);
                        break;

                    case NodeDssMessage.Type.Answer:
                        // No need to wait for completion; there is nothing interesting to do after it.
                        var sdpAnswer = new WebRTC.SdpMessage {
                            Type = SdpMessageType.Answer, Content = msg.Data
                        };
                        _ = PeerConnection.HandleConnectionMessageAsync(sdpAnswer);
                        break;

                    case NodeDssMessage.Type.Ice:
                        // this "parts" protocol is defined above, in OnIceCandidateReadyToSend listener
                        var iceCandidate = msg.ToIceCandidate();
                        break;

                    default:
                        Debug.Log("Unknown message: " + msg.MessageType + ": " + msg.Data);
                        break;
                    }

                    timeSincePollMs = PollTimeMs + 1f; //fast forward next request
                }
                else if (AutoLogErrors)
                {
                    Debug.LogError($"Failed to deserialize JSON message : {json}");
                }
            }
            else if (AutoLogErrors && www.isNetworkError)
            {
                Debug.LogError($"Network error trying to send data to {HttpServerAddress}: {www.error}");
            }
            else
            {
                // This is very spammy because the node-dss protocol uses 404 as regular "no data yet" message, which is an HTTP error
                //Debug.LogError($"HTTP error: {www.error}");
            }

            lastGetComplete = true;
        }
Example #7
0
        /// <inheritdoc/>
        protected override void Update()
        {
            // Do not forget to call the base class Update(), which processes events from background
            // threads to fire the callbacks implemented in this class.
            base.Update();

            if (startConnection)
            {
                PeerConnection.StartConnection();
                _nativePeer.RenegotiationNeeded      += Connection_RenegotiationNeeded;
                _nativePeer.IceGatheringStateChanged += Connection_IceGatheringStateChanged;
                _nativePeer.IceStateChanged          += Connection_IceStateChanged;

                startConnection = false;
            }


            //if there's a pending sdpanswer, then connect and consume it
            if (sdpAnswer != null)
            {
                //PeerConnection.HandleConnectionMessageAsync(sdpAnswer); // If i call this I publish my video but I'm not able to subscribe

                long i = idMessage++;

                // follow with a ReceiveVideoFrom on RPC
                RemotePeerId = joinRoomAnswer.result.value[0].id;
                string message = "{\"jsonrpc\": \"2.0\"," +
                                 "\"method\": \"receiveVideoFrom\"," +
                                 "\"params\": { \"sender\": \"" + joinRoomAnswer.result.value[0].streams[0].Id + "\"" +
                                 ",\"sdpOffer\": \"" + lastOffer.Content + "\"" +
                                 "},\"id\": " + i + " }";

                Debug.Log("ReceiveVideoFrom : " + message);

                webSocket.SendText(message);
                messages.Add(i, OpenViduType.ReceiveVideoFrom);

                sdpAnswer = null;
            }



            // If we have not reached our PollTimeMs value...
            if (timeSincePollMs <= PollTimeMs)
            {
                // ...then we keep incrementing our local counter until we do.
                timeSincePollMs += Time.deltaTime * 1000.0f;
                return;
            }

            // If we have a pending request still going, don't queue another yet.
            if (!lastGetComplete)
            {
                return;
            }

            // When we have reached our PollTimeMs value...
            timeSincePollMs = 0f;

            // ...begin the poll and process.
            lastGetComplete = false;
            Ping();
        }
Example #8
0
        public void OnReceivedData(object sender, EventArgs args)
        {
            if (args == null)
            {
                return;
            }

            // return early if wrong type of EventArgs
            var myArgs = args as TextEventArgs;

            if (myArgs == null)
            {
                Debug.Log("Got somethin elseg from ws:" + args.ToString());
                return;
            }

            var json = myArgs.Text;

            var msg = JsonConvert.DeserializeObject <OpenViduMessageJson>(json);

            // if the message is good
            if (msg != null)
            {
                if (!String.IsNullOrEmpty(msg.Method))
                {
                    if (msg.Method.Equals("iceCandidate"))
                    {
                        OpenViduIceCandidateEvent msg2 = JsonConvert.DeserializeObject <OpenViduIceCandidateEvent>(json);
                        var ic = new IceCandidate
                        {
                            SdpMid        = msg2.Params.SdpMid,
                            SdpMlineIndex = msg2.Params.SdpMLineIndex,
                            Content       = msg2.Params.Candidate,
                        };
                        Debug.Log("<color=white>IceCandidate</color>(SdpMid=" + ic.SdpMid +
                                  ", SdpMlineIndex=" + ic.SdpMlineIndex +
                                  ", Content=" + ic.Content +
                                  ")");
                        _nativePeer.AddIceCandidate(ic);
                    }
                    else
                    {
                        Debug.Log("<color=red>" + json + "</color>");
                    }
                }
                else if (messages.Contains(msg.id))
                {
                    //var id = Int32.Parse(msg.Id);
                    long         id          = msg.id;
                    OpenViduType messageType = (OpenViduType)messages[id];

                    switch (messageType)
                    {
                    case OpenViduType.Ping:
                        break;

                    case OpenViduType.JoinRoom:
                        joinRoomAnswer = JsonConvert.DeserializeObject <OpenViduJoinRoomAnswer>(json);



                        LocalPeerId = joinRoomAnswer.result.id;

                        startConnection = true;

                        break;

                    case OpenViduType.PublishVideo:
                        Debug.Log("<color=yellow>" + json + "</color>");
                        var msg2 = JsonConvert.DeserializeObject <OpenViduPublishVideoAnswer>(json);

                        sdpAnswer = new WebRTC.SdpMessage {
                            Type = SdpMessageType.Answer, Content = msg2.Result.SdpAnswer
                        };

                        break;

                    case OpenViduType.ReceiveVideoFrom:
                        Debug.Log("<color=yellow>" + json + "</color>");
                        var msg3 = JsonConvert.DeserializeObject <OpenViduReceiveVideoAnswer>(json);
                        sdpAnswerReceiveVideo = new WebRTC.SdpMessage {
                            Type = SdpMessageType.Answer, Content = msg3.Result.SdpAnswer
                        };

                        _mainThreadWorkQueue.Enqueue(() =>
                        {
                            PeerConnection.HandleConnectionMessageAsync(sdpAnswerReceiveVideo);

                            /*PeerConnection.HandleConnectionMessageAsync(sdpAnswerReceiveVideo).ContinueWith(_ =>
                             * {
                             *  _nativePeer.CreateAnswer(); //this only works if local video is not published
                             * }, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.RunContinuationsAsynchronously);*/
                        });

                        break;

                    case OpenViduType.OnIceCandidate:
                        msg = JsonConvert.DeserializeObject <OpenViduOnIceCandidateAnswer>(json);
                        break;

                    default:
                        break;
                    }

                    timeSincePollMs = PollTimeMs + 1f; //fast forward next request
                }
            }
            else if (AutoLogErrors)
            {
                Debug.LogError($"Failed to deserialize JSON message : {json}");
            }
        }
        // This method might be run outside the app thread and should not access the Unity API.
        private static async Task <WebRTC.VideoTrackSource> CreateSourceAsync(
            LocalVideoSourceFormatMode formatMode, LocalVideoDeviceInitConfig deviceConfig)
        {
            // Handle automatic capture format constraints
#if ENABLE_WINMD_SUPPORT
            if (formatMode == LocalVideoSourceFormatMode.Automatic)
            {
                // Do not constrain resolution by default, unless the device calls for it (see below).
                deviceConfig.width  = 0; // auto
                deviceConfig.height = 0; // auto

                // Avoid constraining the framerate; this is generally not necessary (formats are listed
                // with higher framerates first) and is error-prone as some formats report 30.0 FPS while
                // others report 29.97 FPS.
                deviceConfig.framerate = 0; // auto

                // For HoloLens, use video profile to reduce resolution and save power/CPU/bandwidth
                if (global::Windows.Graphics.Holographic.HolographicSpace.IsAvailable)
                {
                    if (!global::Windows.Graphics.Holographic.HolographicDisplay.GetDefault().IsOpaque)
                    {
                        if (global::Windows.ApplicationModel.Package.Current.Id.Architecture == global::Windows.System.ProcessorArchitecture.X86)
                        {
                            // Holographic AR (transparent) x86 platform - Assume HoloLens 1
                            deviceConfig.videoProfileKind = WebRTC.VideoProfileKind.VideoRecording; // No profile in VideoConferencing
                            deviceConfig.width            = 896;                                    // Target 896 x 504
                        }
                        else
                        {
                            // Holographic AR (transparent) non-x86 platform - Assume HoloLens 2
                            deviceConfig.videoProfileKind = WebRTC.VideoProfileKind.VideoConferencing;
                            deviceConfig.width            = 960; // Target 960 x 540
                        }
                    }
                }
            }
#elif !UNITY_EDITOR && UNITY_ANDROID
            if (formatMode == LocalVideoSourceFormatMode.Automatic)
            {
                // Avoid constraining the framerate; this is generally not necessary (formats are listed
                // with higher framerates first) and is error-prone as some formats report 30.0 FPS while
                // others report 29.97 FPS.
                deviceConfig.framerate = 0; // auto

                string deviceId = deviceConfig.videoDevice.id;
                if (string.IsNullOrEmpty(deviceId))
                {
                    // Continue the task outside the Unity app context, in order to avoid deadlock
                    // if OnDisable waits on this task.
                    IReadOnlyList <VideoCaptureDevice> listedDevices =
                        await PeerConnection.GetVideoCaptureDevicesAsync()
                        .ConfigureAwait(continueOnCapturedContext: false);

                    if (listedDevices.Count > 0)
                    {
                        deviceId = listedDevices[0].id;
                    }
                }
                if (!string.IsNullOrEmpty(deviceId))
                {
                    // Find the closest format to 720x480, independent of framerate
                    // Continue the task outside the Unity app context, in order to avoid deadlock
                    // if OnDisable waits on this task.
                    IReadOnlyList <VideoCaptureFormat> formats =
                        await DeviceVideoTrackSource.GetCaptureFormatsAsync(deviceId)
                        .ConfigureAwait(continueOnCapturedContext: false);

                    double smallestDiff = double.MaxValue;
                    bool   hasFormat    = false;
                    foreach (var fmt in formats)
                    {
                        double diff = Math.Abs(fmt.width - 720) + Math.Abs(fmt.height - 480);
                        if ((diff < smallestDiff) || !hasFormat)
                        {
                            hasFormat           = true;
                            smallestDiff        = diff;
                            deviceConfig.width  = fmt.width;
                            deviceConfig.height = fmt.height;
                        }
                    }
                    if (hasFormat)
                    {
                        Debug.Log($"WebcamSource automated mode selected resolution {deviceConfig.width}x{deviceConfig.height} for Android video capture device #{deviceId}.");
                    }
                }
            }
#endif

            // TODO - Fix codec selection (was as below before change)

            // Force again PreferredVideoCodec right before starting the local capture,
            // so that modifications to the property done after OnPeerInitialized() are
            // accounted for.
            //< FIXME
            //PeerConnection.Peer.PreferredVideoCodec = PreferredVideoCodec;

            // Check H.264 requests on Desktop (not supported)
            //#if !ENABLE_WINMD_SUPPORT
            //            if (PreferredVideoCodec == "H264")
            //            {
            //                Debug.LogError("H.264 encoding is not supported on Desktop platforms. Using VP8 instead.");
            //                PreferredVideoCodec = "VP8";
            //            }
            //#endif

            // Create the track
            var createTask = DeviceVideoTrackSource.CreateAsync(deviceConfig);

            // Continue the task outside the Unity app context, in order to avoid deadlock
            // if OnDisable waits on this task.
            return(await createTask.ConfigureAwait(continueOnCapturedContext : false));
        }