aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.RemoteDesktop/ScreenCaptureEngine.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio/Tango.RemoteDesktop/ScreenCaptureEngine.cs')
-rw-r--r--Software/Visual_Studio/Tango.RemoteDesktop/ScreenCaptureEngine.cs185
1 files changed, 185 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Tango.RemoteDesktop/ScreenCaptureEngine.cs b/Software/Visual_Studio/Tango.RemoteDesktop/ScreenCaptureEngine.cs
new file mode 100644
index 000000000..915147d2a
--- /dev/null
+++ b/Software/Visual_Studio/Tango.RemoteDesktop/ScreenCaptureEngine.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Tango.RemoteDesktop.Utils;
+
+namespace Tango.RemoteDesktop
+{
+ /// <summary>
+ /// Represents a generic screen capture engine.
+ /// The <see cref="TFrame"/> type will be the type of difference object delivered by the engine.
+ /// </summary>
+ /// <typeparam name="TFrame">The type of the frame.</typeparam>
+ /// <seealso cref="Tango.RemoteDesktop.IScreenCaptureEngine{TFrame}" />
+ public class ScreenCaptureEngine<TFrame> : IScreenCaptureEngine<TFrame> where TFrame : class, IFrame
+ {
+ private Thread _captureThread;
+ private Bitmap _previousBitmap;
+ private bool _isDisposed;
+
+ /// <summary>
+ /// Occurs when a new screen frame is available.
+ /// </summary>
+ public event EventHandler<ScreenCaptureFrameReceivedEventArgs<TFrame>> FrameReceived;
+
+ /// <summary>
+ /// Gets or sets the screen capture method.
+ /// </summary>
+ public ICaptureMethod CaptureMethod { get; set; }
+
+ /// <summary>
+ /// Gets or sets the screen capture region.
+ /// </summary>
+ public CaptureRegion CaptureRegion { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is currently capturing.
+ /// </summary>
+ public bool IsStarted { get; private set; }
+
+ /// <summary>
+ /// Gets or sets the frame rate per second.
+ /// Default is 10.
+ /// </summary>
+ public int FrameRate { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to include the cursor when capturing.
+ /// </summary>
+ public bool CaptureCursor { get; set; }
+
+ /// <summary>
+ /// Gets or sets the bitmap comparer.
+ /// </summary>
+ public IBitmapComparer<TFrame> Comparer { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to enable image comparison.
+ /// </summary>
+ public bool EnableComparer { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ScreenCaptureEngine{TFrame}"/> class using the specified <see cref="IBitmapComparer{TFrame}"/>.
+ /// </summary>
+ /// <param name="comparer">The comparer.</param>
+ public ScreenCaptureEngine(IBitmapComparer<TFrame> comparer)
+ {
+ FrameRate = 10;
+ CaptureMethod = new CaptureMethods.DirectXScreenCapture();
+ CaptureRegion = new CaptureRegion(System.Windows.Forms.Screen.PrimaryScreen.Bounds);
+ Comparer = comparer;
+ CaptureCursor = true;
+
+ if (Comparer != null)
+ {
+ EnableComparer = true;
+ }
+ }
+
+ /// <summary>
+ /// Start capturing.
+ /// </summary>
+ /// <exception cref="ObjectDisposedException">Screen capture engine cannot be started after disposed.</exception>
+ public void Start()
+ {
+ if (_isDisposed)
+ {
+ throw new ObjectDisposedException("Screen capture engine cannot be started after disposed.");
+ }
+
+ if (!IsStarted)
+ {
+ IsStarted = true;
+
+ _captureThread = new Thread(CaptureThreadMethod);
+ _captureThread.IsBackground = true;
+ _captureThread.Name = "Screen Capture Thread";
+ _captureThread.Start();
+ }
+ }
+
+ /// <summary>
+ /// Stop capturing.
+ /// </summary>
+ public void Stop()
+ {
+ if (IsStarted)
+ {
+ IsStarted = false;
+ _previousBitmap?.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ public void Dispose()
+ {
+ if (!_isDisposed)
+ {
+ _isDisposed = true;
+ Stop();
+ CaptureMethod?.Dispose();
+ }
+ }
+
+ private void CaptureThreadMethod()
+ {
+ Stopwatch watch = new Stopwatch();
+
+ while (IsStarted)
+ {
+ watch.Restart();
+
+ var bitmap = CaptureMethod.GetDesktopBitmap(CaptureRegion);
+
+ if (CaptureCursor)
+ {
+ using (Graphics g = Graphics.FromImage(bitmap))
+ {
+ CursorUtils.ApplyCursor(g, bitmap, CaptureRegion.Left, CaptureRegion.Top);
+ }
+ }
+
+ if (EnableComparer && Comparer != null)
+ {
+ if (_previousBitmap == null)
+ {
+ _previousBitmap = bitmap.Clone() as Bitmap;
+ OnFrameReceived(bitmap, null, false);
+ }
+ else
+ {
+ var result = Comparer.CreateDifference(_previousBitmap, bitmap);
+ _previousBitmap.Dispose();
+ _previousBitmap = bitmap.Clone() as Bitmap;
+ OnFrameReceived(bitmap, result.Frame, result.ContainsDifference);
+ }
+ }
+ else
+ {
+ OnFrameReceived(bitmap, null, false);
+ }
+
+ int delay = Math.Max(5, (FrameRate * 10) - (int)watch.ElapsedMilliseconds);
+ Thread.Sleep(delay);
+ }
+ }
+
+ private void OnFrameReceived(Bitmap currentBitmap, TFrame diffFrame, bool hasDifference)
+ {
+ FrameReceived?.Invoke(this, new ScreenCaptureFrameReceivedEventArgs<TFrame>()
+ {
+ Frame = new ScreenCaptureFrame<TFrame>(currentBitmap, diffFrame)
+ {
+ HasDifference = hasDifference
+ }
+ });
+ }
+ }
+}