public async Task BeginScreenCasting(string viewerID, string requesterName, ICapturer capturer) { var conductor = Conductor.Current; Logger.Write($"Starting screen cast. Requester: {requesterName}. Viewer ID: {viewerID}. Capturer: {capturer.GetType().ToString()}. App Mode: {conductor.Mode}"); byte[] encodedImageBytes; var fpsQueue = new Queue <DateTime>(); var 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); }; while (!viewer.DisconnectRequested) { try { if (viewer.Latency > 30000) { // Viewer isn't responding. Abort sending. break; } if (Conductor.Current.IsDebug) { while (fpsQueue.Any() && DateTime.Now - fpsQueue.Peek() > TimeSpan.FromSeconds(1)) { fpsQueue.Dequeue(); } fpsQueue.Enqueue(DateTime.Now); Debug.WriteLine($"Capture FPS: {fpsQueue.Count}"); } if (viewer.OutputBuffer > 150_000) { Debug.WriteLine($"Waiting for buffer to clear. Size: {viewer.OutputBuffer}"); await Task.Delay(50); continue; } 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.AutoAdjustQuality && viewer.Latency > 1000) { var quality = (int)(viewer.ImageQuality * 1000 / viewer.Latency); Debug.WriteLine($"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) { await conductor.CasterSocket.SendScreenCapture(encodedImageBytes, viewerID, diffArea.Left, diffArea.Top, diffArea.Width, diffArea.Height, DateTime.UtcNow); viewer.Latency += 300; viewer.OutputBuffer += encodedImageBytes.Length; } } } catch (Exception ex) { Logger.Write(ex); } finally { 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) { await conductor.CasterSocket.Disconnect(); Environment.Exit(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, 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); } } }
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); } }