Beispiel #1
0
        public static async void BeginScreenCasting(string viewerID,
                                                    string requesterName,
                                                    ICapturer capturer,
                                                    Conductor conductor)
        {
            Viewer viewer;

            byte[] encodedImageBytes;


            Logger.Write($"Starting screen cast.  Requester: {requesterName}. Viewer ID: {viewerID}. Capturer: {capturer.GetType().ToString()}.  App Mode: {conductor.Mode}  Desktop: {conductor.CurrentDesktopName}");

            viewer = new Viewer()
            {
                Capturer            = capturer,
                DisconnectRequested = false,
                Name = requesterName,
                ViewerConnectionID = viewerID,
                HasControl         = true
            };

            conductor.Viewers.AddOrUpdate(viewerID, viewer, (id, v) => viewer);

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

            await conductor.CasterSocket.SendMachineName(Environment.MachineName, viewerID);

            await conductor.CasterSocket.SendScreenCount(
                capturer.SelectedScreen,
                capturer.GetScreenCount(),
                viewerID);

            await conductor.CasterSocket.SendScreenSize(capturer.CurrentScreenBounds.Width, capturer.CurrentScreenBounds.Height, viewerID);

            capturer.ScreenChanged += async(sender, bounds) =>
            {
                await conductor.CasterSocket.SendScreenSize(bounds.Width, bounds.Height, viewerID);
            };

            var desktopName = string.Empty;

            if (OSUtils.IsWindows)
            {
                desktopName = Win32Interop.GetCurrentDesktop();
            }

            while (!viewer.DisconnectRequested)
            {
                try
                {
                    var currentDesktopName = Win32Interop.GetCurrentDesktop();
                    if (desktopName.ToLower() != currentDesktopName.ToLower())
                    {
                        desktopName = currentDesktopName;
                        Logger.Write($"Switching to desktop {desktopName} in ScreenCaster.");
                        Win32Interop.SwitchToInputDesktop();
                        continue;
                    }

                    while (viewer.PendingFrames > 10)
                    {
                        await Task.Delay(1);
                    }

                    capturer.Capture();

                    var diffArea = ImageUtils.GetDiffArea(capturer.CurrentFrame, capturer.PreviousFrame, capturer.CaptureFullscreen);

                    if (diffArea.IsEmpty)
                    {
                        continue;
                    }

                    using (var newImage = capturer.CurrentFrame.Clone(diffArea, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
                    {
                        if (capturer.CaptureFullscreen)
                        {
                            capturer.CaptureFullscreen = false;
                        }

                        encodedImageBytes = ImageUtils.EncodeBitmap(newImage, viewer.EncoderParams);

                        if (encodedImageBytes?.Length > 0)
                        {
                            await conductor.CasterSocket.SendScreenCapture(encodedImageBytes, viewerID, diffArea.Left, diffArea.Top, diffArea.Width, diffArea.Height, DateTime.UtcNow);

                            viewer.PendingFrames++;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.Write(ex);
                }
                finally
                {
                    // TODO: Even after disposing of the bitmap, GC doesn't collect in time.  Memory usage soars quickly.
                    // Need to revisit this later.
                    GC.Collect();
                }
            }
            Logger.Write($"Ended screen cast.  Requester: {requesterName}. Viewer ID: {viewerID}.");
            conductor.Viewers.TryRemove(viewerID, out _);

            capturer.Dispose();

            // Close if no one is viewing.
            if (conductor.Viewers.Count == 0 && conductor.Mode == Enums.AppMode.Unattended)
            {
                Environment.Exit(0);
            }
        }
Beispiel #2
0
        public async Task BeginScreenCasting(string viewerID,
                                             string requesterName,
                                             ICapturer capturer)
        {
            var conductor    = ServiceContainer.Instance.GetRequiredService <Conductor>();
            var viewers      = conductor.Viewers;
            var mode         = conductor.Mode;
            var casterSocket = ServiceContainer.Instance.GetRequiredService <CasterSocket>();

            Logger.Write($"Starting screen cast.  Requester: {requesterName}. Viewer ID: {viewerID}. Capturer: {capturer.GetType().ToString()}.  App Mode: {mode}");

            byte[] encodedImageBytes;
            var    fpsQueue = new Queue <DateTime>();

            var viewer = new Viewer()
            {
                Capturer            = capturer,
                DisconnectRequested = false,
                Name = requesterName,
                ViewerConnectionID = viewerID,
                HasControl         = true
            };

            viewers.AddOrUpdate(viewerID, viewer, (id, v) => viewer);

            if (mode == Enums.AppMode.Normal)
            {
                conductor.InvokeViewerAdded(viewer);
            }

            if (OSUtils.IsWindows)
            {
                await InitializeWebRtc(viewer, casterSocket);
            }

            await casterSocket.SendMachineName(Environment.MachineName, viewerID);

            await casterSocket.SendScreenCount(
                capturer.SelectedScreen,
                capturer.GetScreenCount(),
                viewerID);

            await casterSocket.SendScreenSize(capturer.CurrentScreenBounds.Width, capturer.CurrentScreenBounds.Height, viewerID);

            capturer.ScreenChanged += async(sender, bounds) =>
            {
                await casterSocket.SendScreenSize(bounds.Width, bounds.Height, viewerID);
            };

            while (!viewer.DisconnectRequested)
            {
                try
                {
                    if (viewer.IsStalled())
                    {
                        // Viewer isn't responding.  Abort sending.
                        break;
                    }

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

                    await viewer.ThrottleIfNeeded();


                    capturer.GetNextFrame();

                    var diffArea = ImageUtils.GetDiffArea(capturer.CurrentFrame, capturer.PreviousFrame, capturer.CaptureFullscreen);

                    if (diffArea.IsEmpty)
                    {
                        continue;
                    }

                    using (var newImage = capturer.CurrentFrame.Clone(diffArea, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
                    {
                        if (capturer.CaptureFullscreen)
                        {
                            capturer.CaptureFullscreen = false;
                        }

                        if (viewer.ShouldAdjustQuality())
                        {
                            var quality = (int)(viewer.ImageQuality * 1000 / viewer.Latency);
                            Logger.Debug($"Auto-adjusting image quality. Latency: {viewer.Latency}. Quality: {quality}");
                            encodedImageBytes = ImageUtils.EncodeBitmap(newImage, new EncoderParameters()
                            {
                                Param = new[]
                                {
                                    new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality)
                                }
                            });
                        }
                        else
                        {
                            encodedImageBytes = ImageUtils.EncodeBitmap(newImage, viewer.EncoderParams);
                        }

                        if (encodedImageBytes?.Length > 0)
                        {
                            if (viewer.IsUsingWebRtc())
                            {
                                viewer.RtcSession.SendCaptureFrame(diffArea.Left, diffArea.Top, diffArea.Width, diffArea.Height, encodedImageBytes);
                                viewer.WebSocketBuffer = 0;
                                viewer.Latency         = 0;
                            }
                            else
                            {
                                await casterSocket.SendScreenCapture(encodedImageBytes, viewerID, diffArea.Left, diffArea.Top, diffArea.Width, diffArea.Height, DateTime.UtcNow);

                                viewer.Latency += 300;
                                // Shave some off so it doesn't get deadlocked by dropped frames.
                                viewer.WebSocketBuffer += (int)(encodedImageBytes.Length * .9);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.Write(ex);
                }
                finally
                {
                    GC.Collect();
                }
            }

            Logger.Write($"Ended screen cast.  Requester: {requesterName}. Viewer ID: {viewerID}.");
            viewers.TryRemove(viewerID, out _);
            var shouldExit = viewers.Count == 0 && mode == Enums.AppMode.Unattended;

            try
            {
                viewer.Dispose();

                if (shouldExit)
                {
                    capturer.Dispose();

                    await casterSocket.Disconnect();
                }
            }
            catch (Exception ex)
            {
                Logger.Write(ex);
            }
            finally
            {
                // Close if no one is viewing.
                if (shouldExit)
                {
                    Logger.Debug($"Exiting process ID {Process.GetCurrentProcess().Id}.");
                    Environment.Exit(0);
                }
            }
        }