diff --git a/Remotely_ScreenCast/Capture/BitBltCapture.cs b/Remotely_ScreenCast/Capture/BitBltCapture.cs index 2963a29b..5880232e 100644 --- a/Remotely_ScreenCast/Capture/BitBltCapture.cs +++ b/Remotely_ScreenCast/Capture/BitBltCapture.cs @@ -62,9 +62,6 @@ namespace Remotely_ScreenCast.Capture PreviousFrame = new Bitmap(CurrentScreenBounds.Width, CurrentScreenBounds.Height, PixelFormat.Format32bppArgb); graphic = Graphics.FromImage(CurrentFrame); desktopName = Win32Interop.GetCurrentDesktop(); - - Debug.WriteLine($"Starting BitBltCapture."); - Debug.WriteLine($"Current Desktop: {desktopName}"); } public void Capture() @@ -97,12 +94,5 @@ namespace Remotely_ScreenCast.Capture Logger.Write(ex); } } - - public Point GetAbsoluteScreenCoordinatesFromPercentages(decimal percentX, decimal percentY) - { - var absoluteX = (CurrentScreenBounds.Width * percentX) + CurrentScreenBounds.Left; - var absoluteY = (CurrentScreenBounds.Height * percentY) + CurrentScreenBounds.Top; - return new Point((int)absoluteX, (int)absoluteY); - } } } diff --git a/Remotely_ScreenCast/Capture/DXCapture.cs b/Remotely_ScreenCast/Capture/DXCapture.cs index 7c6228c3..cb09f60f 100644 --- a/Remotely_ScreenCast/Capture/DXCapture.cs +++ b/Remotely_ScreenCast/Capture/DXCapture.cs @@ -119,10 +119,6 @@ namespace Remotely_ScreenCast.Capture }; screenTexture = new Texture2D(device, textureDesc); duplicatedOutput = output1.DuplicateOutput(device); - - Debug.WriteLine($"Starting DXCapture."); - Debug.WriteLine($"Current Desktop: {desktopName}"); - } public void Capture() { @@ -216,12 +212,5 @@ namespace Remotely_ScreenCast.Capture Capture(); } } - - public Point GetAbsoluteScreenCoordinatesFromPercentages(decimal percentX, decimal percentY) - { - var absoluteX = (CurrentScreenBounds.Width * percentX) + CurrentScreenBounds.Left; - var absoluteY = (CurrentScreenBounds.Height * percentY) + CurrentScreenBounds.Top; - return new Point((int)absoluteX, (int)absoluteY); - } } } diff --git a/Remotely_ScreenCast/Capture/ICapturer.cs b/Remotely_ScreenCast/Capture/ICapturer.cs index c01968d1..44200cef 100644 --- a/Remotely_ScreenCast/Capture/ICapturer.cs +++ b/Remotely_ScreenCast/Capture/ICapturer.cs @@ -16,7 +16,5 @@ namespace Remotely_ScreenCast.Capture void Capture(); EventHandler ScreenChanged { get; set; } int SelectedScreen { get; set; } - - Point GetAbsoluteScreenCoordinatesFromPercentages(decimal percentX, decimal percentY); } } diff --git a/Remotely_ScreenCast/Capture/ImageDiff.cs b/Remotely_ScreenCast/Capture/ImageDiff.cs index 7ce2985a..f2dfea0f 100644 --- a/Remotely_ScreenCast/Capture/ImageDiff.cs +++ b/Remotely_ScreenCast/Capture/ImageDiff.cs @@ -14,16 +14,9 @@ namespace Remotely_ScreenCast.Capture public class ImageDiff { - private static BitmapData bd1; - private static BitmapData bd2; - private static BitmapData bd3; - private static Bitmap mergedFrame; - private static byte[] rgbValues1; - private static byte[] rgbValues2; - private static byte[] rgbValues3; - public static Bitmap GetImageDiff(Bitmap currentFrame, Bitmap previousFrame, bool captureFullscreen) { + if (currentFrame.Height != previousFrame.Height || currentFrame.Width != previousFrame.Width) { throw new Exception("Bitmaps are not of equal dimensions."); @@ -36,11 +29,11 @@ namespace Remotely_ScreenCast.Capture var width = currentFrame.Width; var height = currentFrame.Height; - mergedFrame = new Bitmap(width, height); + var mergedFrame = new Bitmap(width, height); - bd1 = previousFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, currentFrame.PixelFormat); - bd2 = currentFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, previousFrame.PixelFormat); - bd3 = mergedFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, currentFrame.PixelFormat); + var bd1 = previousFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, currentFrame.PixelFormat); + var bd2 = currentFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, previousFrame.PixelFormat); + var bd3 = mergedFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, currentFrame.PixelFormat); // Get the address of the first line. @@ -50,9 +43,9 @@ namespace Remotely_ScreenCast.Capture // Declare an array to hold the bytes of the bitmap. int arraySize = Math.Abs(bd1.Stride) * currentFrame.Height; - rgbValues1 = new byte[arraySize]; - rgbValues2 = new byte[arraySize]; - rgbValues3 = new byte[arraySize]; + var rgbValues1 = new byte[arraySize]; + var rgbValues2 = new byte[arraySize]; + var rgbValues3 = new byte[arraySize]; // Copy the RGBA values into the array. Marshal.Copy(ptr1, rgbValues1, 0, arraySize); @@ -89,7 +82,7 @@ namespace Remotely_ScreenCast.Capture currentFrame.UnlockBits(bd2); mergedFrame.UnlockBits(bd3); - return mergedFrame; + return mergedFrame; } public static byte[] EncodeBitmap(Bitmap bitmap) @@ -101,152 +94,5 @@ namespace Remotely_ScreenCast.Capture return ms.ToArray(); } } - - - public byte[] GetDiffImage(Rectangle imageArea, Bitmap CurrentFrame) - { - using (var ms = new MemoryStream()) - { - using (var croppedFrame = CurrentFrame.Clone(imageArea, PixelFormat.Format24bppRgb)) - { - var encoderParams = new EncoderParameters(1); - encoderParams.Param = new EncoderParameter[] { new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 1) }; - croppedFrame.Save(ms, ImageCodecInfo.GetImageEncoders().FirstOrDefault(x => x.FormatID == ImageFormat.Png.Guid), encoderParams); - // Byte array that indicates top left coordinates of the image. - byte[] header; - using (var headerStream = new MemoryStream()) - { - var formatter = new BinaryFormatter(); - formatter.Serialize(headerStream, new Point(imageArea.X, imageArea.Y)); - header = headerStream.ToArray(); - } - - return header.Concat(ms.ToArray()).ToArray(); - } - } - - } - - public Rectangle GetDiffArea(Bitmap previousFrame, Bitmap currentFrame, bool captureFullscreen) - { - if (captureFullscreen) - { - captureFullscreen = false; - return new Rectangle(0, 0, currentFrame.Width, currentFrame.Height); - } - if (currentFrame.Height != previousFrame.Height || currentFrame.Width != previousFrame.Width) - { - throw new Exception("Bitmaps are not of equal dimensions."); - } - if (!Bitmap.IsAlphaPixelFormat(currentFrame.PixelFormat) || !Bitmap.IsAlphaPixelFormat(previousFrame.PixelFormat) || - !Bitmap.IsCanonicalPixelFormat(currentFrame.PixelFormat) || !Bitmap.IsCanonicalPixelFormat(previousFrame.PixelFormat)) - { - throw new Exception("Bitmaps must be 32 bits per pixel and contain alpha channel."); - } - var width = currentFrame.Width; - var height = currentFrame.Height; - var left = int.MaxValue; - var top = int.MaxValue; - var right = int.MinValue; - var bottom = int.MinValue; - - try - { - bd1 = currentFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, currentFrame.PixelFormat); - } - catch - { - try - { - currentFrame.UnlockBits(bd1); - bd1 = currentFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, currentFrame.PixelFormat); - } - catch - { - return Rectangle.Empty; - } - } - try - { - bd2 = previousFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, previousFrame.PixelFormat); - } - catch - { - try - { - previousFrame.UnlockBits(bd2); - bd2 = previousFrame.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, previousFrame.PixelFormat); - } - catch - { - return Rectangle.Empty; - } - } - // Get the address of the first line. - IntPtr ptr1 = bd1.Scan0; - IntPtr ptr2 = bd2.Scan0; - - // Declare an array to hold the bytes of the bitmap. - int arraySize = Math.Abs(bd1.Stride) * currentFrame.Height; - rgbValues1 = new byte[arraySize]; - rgbValues2 = new byte[arraySize]; - - // Copy the RGBA values into the array. - Marshal.Copy(ptr1, rgbValues1, 0, arraySize); - Marshal.Copy(ptr2, rgbValues2, 0, arraySize); - - // Check RGBA value for each pixel. - for (int counter = 0; counter < rgbValues1.Length - 4; counter += 4) - { - if (rgbValues1[counter] != rgbValues2[counter] || - rgbValues1[counter + 1] != rgbValues2[counter + 1] || - rgbValues1[counter + 2] != rgbValues2[counter + 2] || - rgbValues1[counter + 3] != rgbValues2[counter + 3]) - { - // Change was found. - var pixel = counter / 4; - var row = (int)Math.Floor((double)pixel / bd1.Width); - var column = pixel % bd1.Width; - if (row < top) - { - top = row; - } - if (row > bottom) - { - bottom = row; - } - if (column < left) - { - left = column; - } - if (column > right) - { - right = column; - } - } - } - - if (left < right && top < bottom) - { - // Bounding box is valid. - - left = Math.Max(left - 20, 0); - top = Math.Max(top - 20, 0); - right = Math.Min(right + 20, currentFrame.Width); - bottom = Math.Min(bottom + 20, currentFrame.Height); - - currentFrame.UnlockBits(bd1); - previousFrame.UnlockBits(bd2); - - return new Rectangle(left, top, right - left, bottom - top); - } - else - { - currentFrame.UnlockBits(bd1); - previousFrame.UnlockBits(bd2); - return Rectangle.Empty; - } - } - } } diff --git a/Remotely_ScreenCast/Capture/ScreenCaster.cs b/Remotely_ScreenCast/Capture/ScreenCaster.cs new file mode 100644 index 00000000..98be82c8 --- /dev/null +++ b/Remotely_ScreenCast/Capture/ScreenCaster.cs @@ -0,0 +1,119 @@ +using Microsoft.AspNetCore.SignalR.Client; +using Remotely_ScreenCast.Sockets; +using Remotely_ScreenCast.Utilities; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Remotely_ScreenCast.Capture +{ + public class ScreenCaster + { + private static object CaptureLock { get; } = new object(); + public static async void BeginScreenCasting(HubConnection hubConnection, + string viewerID, + string requesterName, + OutgoingMessages outgoingMessages) + { + ICapturer capturer; + CaptureMode captureMode; + + try + { + if (Program.Viewers.Count == 0) + { + capturer = new DXCapture(); + captureMode = CaptureMode.DirectX; + } + else + { + capturer = new BitBltCapture(); + captureMode = CaptureMode.BitBtl; + } + } + catch (Exception ex) + { + Logger.Write(ex); + capturer = new BitBltCapture(); + captureMode = CaptureMode.BitBtl; + } + + Logger.Write($"Starting screen cast. Requester: {requesterName}. Viewer ID: {viewerID}. Capture Mode: {captureMode.ToString()}."); + + var viewer = new Models.Viewer() + { + Capturer = capturer, + CurrentScreenIndex = 0, + DisconnectRequested = false, + Name = requesterName, + ViewerConnectionID = viewerID, + HasControl = Program.Mode == "unattended" + }; + + + var success = false; + while (!success) + { + success = Program.Viewers.TryAdd(viewerID, viewer); + } + + await outgoingMessages.SendScreenCount( + Screen.AllScreens.ToList().IndexOf(Screen.PrimaryScreen), + Screen.AllScreens.Length, + viewerID); + + await outgoingMessages.SendScreenSize(capturer.CurrentScreenBounds.Width, capturer.CurrentScreenBounds.Height, viewerID); + + capturer.ScreenChanged += async (sender, size) => + { + await outgoingMessages.SendScreenSize(size.Width, size.Height, viewerID); + }; + + while (!viewer.DisconnectRequested) + { + try + { + if (viewer.NextCaptureDelay > 0) + { + await Task.Delay(viewer.NextCaptureDelay); + viewer.NextCaptureDelay = 0; + } + + capturer.Capture(); + + var newImage = ImageDiff.GetImageDiff(capturer.CurrentFrame, capturer.PreviousFrame, capturer.CaptureFullscreen); + var img = ImageDiff.EncodeBitmap(newImage); + if (capturer.CaptureFullscreen) + { + capturer.CaptureFullscreen = false; + } + if (img?.Length > 0) + { + await outgoingMessages.SendScreenCapture(img, viewerID, DateTime.Now); + } + } + catch (Exception ex) + { + Logger.Write($"Outer Error: {ex.Message}{Environment.NewLine}{ex.StackTrace}"); + } + } + + success = false; + while (!success) + { + Program.Viewers.TryRemove(viewerID, out _); + } + Logger.Write($"Ended screen cast. Requester: {requesterName}. Viewer ID: {viewerID}."); + } + public static Tuple GetAbsoluteScreenCoordinatesFromPercentages(double percentX, double percentY, ICapturer capturer) + { + var absoluteX = (capturer.CurrentScreenBounds.Width * percentX) + capturer.CurrentScreenBounds.Left; + var absoluteY = (capturer.CurrentScreenBounds.Height * percentY) + capturer.CurrentScreenBounds.Top; + return new Tuple(absoluteX / SystemInformation.VirtualScreen.Width, absoluteY / SystemInformation.VirtualScreen.Height); + } + } +} diff --git a/Remotely_ScreenCast/Program.cs b/Remotely_ScreenCast/Program.cs index 39b92441..c7fc4907 100644 --- a/Remotely_ScreenCast/Program.cs +++ b/Remotely_ScreenCast/Program.cs @@ -4,7 +4,6 @@ using Remotely_ScreenCast.Capture; using Remotely_ScreenCast.Models; using Remotely_ScreenCast.Sockets; using Remotely_ScreenCast.Utilities; -using Remotely_ScreenCast.Capture; using System; using System.Collections.Concurrent; using System.Collections.Generic; diff --git a/Remotely_ScreenCast/Remotely_ScreenCast.csproj b/Remotely_ScreenCast/Remotely_ScreenCast.csproj index ca55024a..b62c4782 100644 --- a/Remotely_ScreenCast/Remotely_ScreenCast.csproj +++ b/Remotely_ScreenCast/Remotely_ScreenCast.csproj @@ -146,6 +146,7 @@ + diff --git a/Remotely_ScreenCast/Sockets/MessageHandlers.cs b/Remotely_ScreenCast/Sockets/MessageHandlers.cs index 5fe8d762..75fe0c0c 100644 --- a/Remotely_ScreenCast/Sockets/MessageHandlers.cs +++ b/Remotely_ScreenCast/Sockets/MessageHandlers.cs @@ -2,7 +2,6 @@ using Remotely_ScreenCast.Capture; using Remotely_ScreenCast.Utilities; using Remotely_ScreenCast; -using Remotely_ScreenCast.Capture; using System; using System.Collections.Generic; using System.Linq; @@ -26,7 +25,7 @@ namespace Remotely_ScreenCast.Sockets hubConnection.On("GetScreenCast", (string viewerID, string requesterName) => { - BeginScreenCasting(hubConnection, viewerID, requesterName, outgoingMessages); + ScreenCaster.BeginScreenCasting(hubConnection, viewerID, requesterName, outgoingMessages); }); hubConnection.On("KeyDown", (int keyCode, string viewerID) => @@ -57,13 +56,13 @@ namespace Remotely_ScreenCast.Sockets } }); - hubConnection.On("MouseMove", (decimal percentX, decimal percentY, string viewerID) => + hubConnection.On("MouseMove", (double percentX, double percentY, string viewerID) => { var viewer = Program.Viewers[viewerID]; if (viewer.HasControl) { - var mousePoint = viewer.Capturer.GetAbsoluteScreenCoordinatesFromPercentages(percentX, percentY); - Win32Interop.SendMouseMove(mousePoint.X, mousePoint.Y); + var mousePoint = ScreenCaster.GetAbsoluteScreenCoordinatesFromPercentages(percentX, percentY, viewer.Capturer); + Win32Interop.SendMouseMove(mousePoint.Item1, mousePoint.Item2); } }); @@ -93,91 +92,5 @@ namespace Remotely_ScreenCast.Sockets } }); } - - private static async void BeginScreenCasting(HubConnection hubConnection, - string viewerID, - string requesterName, - OutgoingMessages outgoingMessages) - { - ICapturer capturer; - CaptureMode captureMode; - - try - { - capturer = new DXCapture(); - captureMode = CaptureMode.DirectX; - } - catch (Exception ex) - { - Logger.Write(ex); - capturer = new BitBltCapture(); - captureMode = CaptureMode.BitBtl; - } - - Logger.Write($"Starting screen cast. Requester: {requesterName}. Viewer ID: {viewerID}. Capture Mode: {captureMode.ToString()}."); - - var viewer = new Models.Viewer() - { - Capturer = capturer, - CurrentScreenIndex = 0, - DisconnectRequested = false, - Name = requesterName, - ViewerConnectionID = viewerID, - HasControl = Program.Mode == "unattended" - }; - - - var success = false; - while (!success) - { - success = Program.Viewers.TryAdd(viewerID, viewer); - } - - await outgoingMessages.SendScreenCount( - Screen.AllScreens.ToList().IndexOf(Screen.PrimaryScreen), - Screen.AllScreens.Length, - viewerID); - - await outgoingMessages.SendScreenSize(capturer.CurrentScreenBounds.Width, capturer.CurrentScreenBounds.Height, viewerID); - - capturer.ScreenChanged += async (sender, size) => - { - await outgoingMessages.SendScreenSize(size.Width, size.Height, viewerID); - }; - - while (!viewer.DisconnectRequested) - { - try - { - if (viewer.NextCaptureDelay > 0) - { - await Task.Delay(viewer.NextCaptureDelay); - viewer.NextCaptureDelay = 0; - } - capturer.Capture(); - var newImage = ImageDiff.GetImageDiff(capturer.CurrentFrame, capturer.PreviousFrame, capturer.CaptureFullscreen); - var img = ImageDiff.EncodeBitmap(newImage); - if (capturer.CaptureFullscreen) - { - capturer.CaptureFullscreen = false; - } - if (img?.Length > 0) - { - await outgoingMessages.SendScreenCapture(img, viewerID, DateTime.Now); - } - } - catch (Exception ex) - { - Logger.Write($"Outer Error: {ex.Message}{Environment.NewLine}{ex.StackTrace}"); - } - } - - success = false; - while (!success) - { - Program.Viewers.TryRemove(viewerID, out _); - } - Logger.Write($"Ended screen cast. Requester: {requesterName}. Viewer ID: {viewerID}."); - } } } diff --git a/Remotely_ScreenCast/Win32/Win32Interop.cs b/Remotely_ScreenCast/Win32/Win32Interop.cs index 9508aab3..d2988a40 100644 --- a/Remotely_ScreenCast/Win32/Win32Interop.cs +++ b/Remotely_ScreenCast/Win32/Win32Interop.cs @@ -138,7 +138,7 @@ namespace Win32 } // Offsets are used in case there's a multi-monitor setup where the left-most or top-most edge of the virtual screen - // is not 0. The coordinates sent from the web viewer are always zero-based, so the offset must be applied. + // is not 0. public static uint SendMouseMove(double x, double y) { // Coordinates must be normalized. The bottom-right coordinate is mapped to 65535.