Esempio n. 1
0
        public async Task BeginScreenCasting(ScreenCastRequest screenCastRequest)
        {
            try
            {
                var    sendFramesLock = new SemaphoreSlim(1, 1);
                Bitmap currentFrame   = null;
                Bitmap previousFrame  = null;
                var    fpsQueue       = new Queue <DateTimeOffset>();

                var viewer = ServiceContainer.Instance.GetRequiredService <Viewer>();
                viewer.Name = screenCastRequest.RequesterName;
                viewer.ViewerConnectionID = screenCastRequest.ViewerID;

                Logger.Write($"Starting screen cast.  Requester: {viewer.Name}. " +
                             $"Viewer ID: {viewer.ViewerConnectionID}.  App Mode: {_conductor.Mode}");

                _conductor.Viewers.AddOrUpdate(viewer.ViewerConnectionID, viewer, (id, v) => viewer);

                if (_conductor.Mode == AppMode.Normal)
                {
                    _conductor.InvokeViewerAdded(viewer);
                }

                if (_conductor.Mode == AppMode.Unattended && screenCastRequest.NotifyUser)
                {
                    _sessionIndicator.Show();
                }

                await viewer.SendViewerConnected();

                await viewer.SendMachineName(Environment.MachineName);

                await viewer.SendScreenData(
                    viewer.Capturer.SelectedScreen,
                    viewer.Capturer.GetDisplayNames().ToArray());

                await viewer.SendScreenSize(viewer.Capturer.CurrentScreenBounds.Width,
                                            viewer.Capturer.CurrentScreenBounds.Height);

                await viewer.SendCursorChange(_cursorIconWatcher.GetCurrentCursor());

                await viewer.SendWindowsSessions();

                viewer.Capturer.ScreenChanged += async(sender, bounds) =>
                {
                    await viewer.SendScreenSize(bounds.Width, bounds.Height);
                };

                using (var initialFrame = viewer.Capturer.GetNextFrame())
                {
                    if (initialFrame != null)
                    {
                        await viewer.SendScreenCapture(new CaptureFrame[]
                        {
                            new CaptureFrame()
                            {
                                EncodedImageBytes = ImageUtils.EncodeBitmap(initialFrame, viewer.EncoderParams),
                                Left   = viewer.Capturer.CurrentScreenBounds.Left,
                                Top    = viewer.Capturer.CurrentScreenBounds.Top,
                                Width  = viewer.Capturer.CurrentScreenBounds.Width,
                                Height = viewer.Capturer.CurrentScreenBounds.Height
                            }
                        });
                    }
                }


                if (EnvironmentHelper.IsWindows)
                {
                    await viewer.InitializeWebRtc();
                }

                // Wait until the first image is received.
                TaskHelper.DelayUntil(() => !viewer.PendingSentFrames.Any(), TimeSpan.MaxValue);

                while (!viewer.DisconnectRequested && viewer.IsConnected)
                {
                    try
                    {
                        if (viewer.IsUsingWebRtcVideo)
                        {
                            Thread.Sleep(100);
                            continue;
                        }
                        if (viewer.IsStalled)
                        {
                            // Viewer isn't responding.  Abort sending.
                            break;
                        }

                        if (EnvironmentHelper.IsDebug)
                        {
                            while (fpsQueue.Any() && DateTimeOffset.Now - fpsQueue.Peek() > TimeSpan.FromSeconds(1))
                            {
                                fpsQueue.Dequeue();
                            }
                            fpsQueue.Enqueue(DateTimeOffset.Now);
                            Debug.WriteLine($"Capture FPS: {fpsQueue.Count}");
                        }

                        viewer.ThrottleIfNeeded();

                        if (currentFrame != null)
                        {
                            previousFrame?.Dispose();
                            previousFrame = (Bitmap)currentFrame.Clone();
                        }

                        currentFrame?.Dispose();
                        currentFrame = viewer.Capturer.GetNextFrame();

                        var diffAreas = ImageUtils.GetDiffAreas(currentFrame, previousFrame, viewer.Capturer.CaptureFullscreen);

                        if (!diffAreas.Any())
                        {
                            continue;
                        }


                        viewer.Capturer.CaptureFullscreen = false;

                        var frameClone = (Bitmap)currentFrame.Clone();
                        Debug.WriteLine($"Sending {diffAreas.Count} frames.");
                        await sendFramesLock.WaitAsync();

                        SendFrames(frameClone, diffAreas, viewer, sendFramesLock);
                    }
                    catch (Exception ex)
                    {
                        Logger.Write(ex);
                    }
                }

                Logger.Write($"Ended screen cast.  Requester: {viewer.Name}. Viewer ID: {viewer.ViewerConnectionID}.");
                _conductor.Viewers.TryRemove(viewer.ViewerConnectionID, out _);
                viewer.Dispose();
            }
            catch (Exception ex)
            {
                Logger.Write(ex);
            }
            finally
            {
                // Close if no one is viewing.
                if (_conductor.Viewers.IsEmpty && _conductor.Mode == AppMode.Unattended)
                {
                    Logger.Write("No more viewers.  Calling shutdown service.");
                    await _shutdownService.Shutdown();
                }
            }
        }
Esempio n. 2
0
        public async Task BeginScreenCasting(ScreenCastRequest screenCastRequest)
        {
            try
            {
                var    sendFramesLock = new SemaphoreSlim(1, 1);
                var    refreshTimer   = Stopwatch.StartNew();
                var    refreshNeeded  = false;
                var    currentQuality = _maxQuality;
                Bitmap currentFrame   = null;
                Bitmap previousFrame  = null;
                var    sw             = Stopwatch.StartNew();

                var viewer = ServiceContainer.Instance.GetRequiredService <Viewer>();
                viewer.Name = screenCastRequest.RequesterName;
                viewer.ViewerConnectionID = screenCastRequest.ViewerID;

                var screenBounds = viewer.Capturer.CurrentScreenBounds;

                Logger.Write($"Starting screen cast.  Requester: {viewer.Name}. " +
                             $"Viewer ID: {viewer.ViewerConnectionID}.  App Mode: {_conductor.Mode}");

                _conductor.Viewers.AddOrUpdate(viewer.ViewerConnectionID, viewer, (id, v) => viewer);

                if (_conductor.Mode == AppMode.Normal)
                {
                    _conductor.InvokeViewerAdded(viewer);
                }

                if (_conductor.Mode == AppMode.Unattended && screenCastRequest.NotifyUser)
                {
                    _sessionIndicator.Show();
                }

                await viewer.SendViewerConnected();

                await viewer.SendMachineName(Environment.MachineName);

                await viewer.SendScreenData(
                    viewer.Capturer.SelectedScreen,
                    viewer.Capturer.GetDisplayNames().ToArray());

                await viewer.SendScreenSize(screenBounds.Width, screenBounds.Height);

                await viewer.SendCursorChange(_cursorIconWatcher.GetCurrentCursor());

                await viewer.SendWindowsSessions();

                viewer.Capturer.ScreenChanged += async(sender, bounds) =>
                {
                    await viewer.SendScreenSize(bounds.Width, bounds.Height);
                };

                using (var initialFrame = viewer.Capturer.GetNextFrame())
                {
                    if (initialFrame != null)
                    {
                        await viewer.SendScreenCapture(new CaptureFrame()
                        {
                            EncodedImageBytes = ImageUtils.EncodeJpeg(initialFrame, _maxQuality),
                            Left   = screenBounds.Left,
                            Top    = screenBounds.Top,
                            Width  = screenBounds.Width,
                            Height = screenBounds.Height
                        });
                    }
                }


                if (EnvironmentHelper.IsWindows && screenCastRequest.UseWebRtc)
                {
                    await viewer.InitializeWebRtc();
                }

                // Wait until the first image is received.
                TaskHelper.DelayUntil(() => !viewer.PendingSentFrames.Any(), TimeSpan.MaxValue);

                while (!viewer.DisconnectRequested && viewer.IsConnected)
                {
                    try
                    {
                        TaskHelper.DelayUntil(() => sw.Elapsed.TotalMilliseconds > 40, TimeSpan.FromSeconds(5));
                        sw.Restart();

                        if (viewer.IsUsingWebRtcVideo)
                        {
                            Thread.Sleep(100);
                            continue;
                        }
                        if (viewer.IsStalled)
                        {
                            // Viewer isn't responding.  Abort sending.
                            Logger.Write("Viewer stalled.  Ending send loop.");
                            break;
                        }

                        viewer.ThrottleIfNeeded();

                        if (currentFrame != null)
                        {
                            previousFrame?.Dispose();
                            previousFrame = (Bitmap)currentFrame.Clone();
                        }

                        currentFrame?.Dispose();
                        currentFrame = viewer.Capturer.GetNextFrame();

                        if (currentFrame is null)
                        {
                            continue;
                        }

                        if (refreshTimer.Elapsed.TotalSeconds > 10 ||
                            refreshNeeded && refreshTimer.Elapsed.TotalSeconds > 5)
                        {
                            viewer.Capturer.CaptureFullscreen = true;
                        }


                        var diffArea = ImageUtils.GetDiffArea(currentFrame, previousFrame, viewer.Capturer.CaptureFullscreen);

                        if (diffArea.IsEmpty)
                        {
                            continue;
                        }


                        if (viewer.Capturer.CaptureFullscreen)
                        {
                            refreshTimer.Restart();
                            refreshNeeded = false;
                        }

                        byte[] encodedImageBytes;
                        if (viewer.Capturer.CaptureFullscreen)
                        {
                            // Recalculate Bps.
                            viewer.AverageBytesPerSecond = 0;
                            encodedImageBytes            = ImageUtils.EncodeJpeg(currentFrame, _maxQuality);
                        }
                        else
                        {
                            if (viewer.AverageBytesPerSecond > 0)
                            {
                                var expectedSize = diffArea.Height * diffArea.Width * 4 * .1;
                                var timeToSend   = expectedSize / viewer.AverageBytesPerSecond;
                                currentQuality = Math.Max(_minQuality, Math.Min(_maxQuality, (int)(.1 / timeToSend * _maxQuality)));
                                if (currentQuality < _maxQuality - 10)
                                {
                                    refreshNeeded = true;
                                    Debug.WriteLine($"Quality Reduced: {currentQuality}");
                                }
                            }

                            using var clone = currentFrame.Clone(diffArea, currentFrame.PixelFormat);
                            //var resizeW = diffArea.Width * currentQuality / _maxQuality;
                            //var resizeH = diffArea.Height * currentQuality / _maxQuality;
                            //using var resized = new Bitmap(clone, new Size(resizeW, resizeH));
                            encodedImageBytes = ImageUtils.EncodeJpeg(clone, currentQuality);
                        }

                        viewer.Capturer.CaptureFullscreen = false;

                        await sendFramesLock.WaitAsync();

                        SendFrame(encodedImageBytes, diffArea, viewer, sendFramesLock);
                    }
                    catch (Exception ex)
                    {
                        Logger.Write(ex);
                    }
                }

                Logger.Write($"Ended screen cast.  " +
                             $"Requester: {viewer.Name}. " +
                             $"Viewer ID: {viewer.ViewerConnectionID}. " +
                             $"Viewer WS Connected: {viewer.IsConnected}.  " +
                             $"Viewer Stalled: {viewer.IsStalled}.  " +
                             $"Viewer Disconnected Requested: {viewer.DisconnectRequested}");

                _conductor.Viewers.TryRemove(viewer.ViewerConnectionID, out _);
                viewer.Dispose();
            }
            catch (Exception ex)
            {
                Logger.Write(ex);
            }
            finally
            {
                // Close if no one is viewing.
                if (_conductor.Viewers.IsEmpty && _conductor.Mode == AppMode.Unattended)
                {
                    Logger.Write("No more viewers.  Calling shutdown service.");
                    await _shutdownService.Shutdown();
                }
            }
        }
Esempio n. 3
0
        private async Task CastScreen(ScreenCastRequest screenCastRequest)
        {
            try
            {
                Bitmap currentFrame  = null;
                Bitmap previousFrame = null;
                long   sequence      = 0;

                var viewer = ServiceContainer.Instance.GetRequiredService <Viewer>();
                viewer.Name = screenCastRequest.RequesterName;
                viewer.ViewerConnectionID = screenCastRequest.ViewerID;

                var screenBounds = viewer.Capturer.CurrentScreenBounds;

                Logger.Write($"Starting screen cast.  Requester: {viewer.Name}. " +
                             $"Viewer ID: {viewer.ViewerConnectionID}.  App Mode: {_conductor.Mode}");

                _conductor.Viewers.AddOrUpdate(viewer.ViewerConnectionID, viewer, (id, v) => viewer);

                if (_conductor.Mode == AppMode.Normal)
                {
                    _conductor.InvokeViewerAdded(viewer);
                }

                if (_conductor.Mode == AppMode.Unattended && screenCastRequest.NotifyUser)
                {
                    _sessionIndicator.Show();
                }

                await viewer.SendViewerConnected();

                await viewer.SendScreenData(
                    viewer.Capturer.SelectedScreen,
                    viewer.Capturer.GetDisplayNames(),
                    screenBounds.Width,
                    screenBounds.Height);

                await viewer.SendScreenSize(screenBounds.Width, screenBounds.Height);

                await viewer.SendCursorChange(_cursorIconWatcher.GetCurrentCursor());

                await viewer.SendWindowsSessions();

                viewer.Capturer.ScreenChanged += async(sender, bounds) =>
                {
                    await viewer.SendScreenSize(bounds.Width, bounds.Height);
                };

                using (var initialFrame = viewer.Capturer.GetNextFrame())
                {
                    if (initialFrame != null)
                    {
                        await viewer.SendScreenCapture(new CaptureFrame()
                        {
                            EncodedImageBytes = ImageUtils.EncodeJpeg(initialFrame),
                            Left     = screenBounds.Left,
                            Top      = screenBounds.Top,
                            Width    = screenBounds.Width,
                            Height   = screenBounds.Height,
                            Sequence = sequence++
                        });
                    }
                }


                if (EnvironmentHelper.IsWindows && screenCastRequest.UseWebRtc)
                {
                    await viewer.InitializeWebRtc();
                }

                // Wait until the first image is received.
                if (!TaskHelper.DelayUntil(() => !viewer.PendingSentFrames.Any(), TimeSpan.FromSeconds(30)))
                {
                    Logger.Write("Timed out while waiting for first frame receipt.");
                    _conductor.Viewers.TryRemove(viewer.ViewerConnectionID, out _);
                    viewer.Dispose();
                    return;
                }

                while (!viewer.DisconnectRequested && viewer.IsConnected)
                {
                    try
                    {
                        if (viewer.IsUsingWebRtcVideo)
                        {
                            Thread.Sleep(100);
                            continue;
                        }

                        if (viewer.IsStalled)
                        {
                            // Viewer isn't responding.  Abort sending.
                            Logger.Write("Viewer stalled.  Ending send loop.");
                            break;
                        }

                        viewer.CalculateFps();

                        viewer.ApplyAutoQuality();

                        if (currentFrame != null)
                        {
                            previousFrame?.Dispose();
                            previousFrame = (Bitmap)currentFrame.Clone();
                        }

                        currentFrame?.Dispose();
                        currentFrame = viewer.Capturer.GetNextFrame();

                        if (currentFrame is null)
                        {
                            continue;
                        }

                        var diffArea = ImageUtils.GetDiffArea(currentFrame, previousFrame, viewer.Capturer.CaptureFullscreen);

                        if (diffArea.IsEmpty)
                        {
                            continue;
                        }

                        viewer.Capturer.CaptureFullscreen = false;

                        using var croppedFrame = currentFrame.Clone(diffArea, currentFrame.PixelFormat);

                        byte[] encodedImageBytes;

                        if (viewer.ImageQuality == Viewer.DefaultQuality)
                        {
                            encodedImageBytes = ImageUtils.EncodeJpeg(croppedFrame);
                        }
                        else
                        {
                            encodedImageBytes = ImageUtils.EncodeJpeg(croppedFrame, viewer.ImageQuality);
                        }

                        await SendFrame(encodedImageBytes, diffArea, sequence ++, viewer);
                    }
                    catch (Exception ex)
                    {
                        Logger.Write(ex);
                    }
                }

                Logger.Write($"Ended screen cast.  " +
                             $"Requester: {viewer.Name}. " +
                             $"Viewer ID: {viewer.ViewerConnectionID}. " +
                             $"Viewer WS Connected: {viewer.IsConnected}.  " +
                             $"Viewer Stalled: {viewer.IsStalled}.  " +
                             $"Viewer Disconnected Requested: {viewer.DisconnectRequested}");

                _conductor.Viewers.TryRemove(viewer.ViewerConnectionID, out _);
                viewer.Dispose();
            }
            catch (Exception ex)
            {
                Logger.Write(ex);
            }
            finally
            {
                // Close if no one is viewing.
                if (_conductor.Viewers.IsEmpty && _conductor.Mode == AppMode.Unattended)
                {
                    Logger.Write("No more viewers.  Calling shutdown service.");
                    await _shutdownService.Shutdown();
                }
            }
        }