| ofs | hex dump | ascii |
|---|
| 0000 | 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 c8 00 00 00 60 08 06 00 00 00 58 17 3d | .PNG........IHDR.......`.....X.= |
| 0020 | 1d 00 00 00 01 73 52 47 42 00 ae ce 1c e9 00 00 23 e7 49 44 41 54 78 01 ed 5d 07 60 14 55 fa 7f | .....sRGB.......#.IDATx..].`.U.. |
| 0040 | b3 2d 85 04 08 4d 4a 80 80 80 60 41 3c 51 d1 03 15 2b 72 96 b3 a1 a7 28 16 20 78 9e 48 17 f0 af | .-...MJ...`A<Q...+r....(..x.H... |
| 0060 | 86 93 13 51 69 7a 0a 21 28 1e 2a 48 b0 a0 88 8a 48 47 90 a6 88 34 a5 85 96 50 52 48 48 dd 64 77 | ...Qiz.!(.*H....HG...4...PRHH.dw |
| 0080 | fe bf df ec ce 66 76 77 36 75 53 20 f3 c1 2f ef bd ef f5 ef bd ef b5 79 33 2b 44 2d 25 59 96 db | .....fvw6uS.../........y3+D-%Y.. |
| 00a0 | 00 4b 81 b3 c0 32 20 aa 96 16 d5 28 96 21 81 ea 95 00 94 e1 42 e0 38 a0 a5 79 d5 5b 0a 23 37 43 | .K...2.....(.!......B.8..y.[.#7C |
| 00c0 | 02 b5 50 02 d0 88 18 e0 88 56 33 dc f6 3d b5 b0 b8 46 91 ce 73 09 98 6a 53 fd a0 08 ad 51 9e 55 | ..P......V3..=...F..s..jS....Q.U |
| 00e0 | 00 4d 5f 5a e2 cb 30 dc 86 04 ea 8c 04 a0 1c ad 80 fd ee d9 c2 d7 58 02 86 ad ce 08 c3 a8 a8 21 | .M_Z..0...............X........! |
| 0100 | 01 ad 04 d0 f9 9b 03 7b 7d b5 c2 ed 5e 0c d3 50 0e ad c0 0c 7b dd 91 00 3a 7f 53 60 97 5b 19 7c | .......{}...^..P....{...:.S`.[.| |
| 0120 | 8d cf c1 b0 d6 1d 69 18 35 35 24 a0 91 00 3a 7f 63 60 87 af 56 b8 dd 8b 60 5a 34 c1 0d ab 21 81 | ......i.55$...:.c`..V...`Z4...!. |
| 0140 | ba 23 01 74 fe 28 e0 17 b7 32 f8 1a 9f 82 61 28 47 dd e9 0e 46 4d b5 12 40 e7 6f 00 6c f6 d5 0a | .#.t.(...2....a(G...FM..@.o.l... |
| 0160 | b7 fb 13 98 66 6d 78 c3 6e 48 a0 ce 48 00 9d 3f 12 d8 e0 56 06 5f 63 1e 18 b5 ea e8 b9 ce 34 8c | ....fmx.nH..H..?...V._c.......4. |
| 0180 | 51 d1 9a 97 00 3a 7f 3d 60 ad af 56 b8 dd 73 61 1a ca 51 f3 cd 64 94 a0 26 24 80 ce 1f 06 ac 04 | Q....:.=`..V..sa..Q..d..&$...... |
| 01a0 | f4 68 0e 98 86 72 d4 44 c3 18 79 d6 bc 04 d0 f9 43 81 1f f4 34 03 bc 78 40 aa f9 52 1a 25 30 24 | .h...r.D..y.....C...4..x@..R.%0$ |
| 01c0 | e0 2f 81 2a 3f 29 42 e7 e7 43 be cf 81 5b fd b3 17 33 c1 7b 56 92 24 59 c7 cf 60 05 53 02 71 f1 | ./.*?)B..C...[...3.{V.$Y..`.S.q. |
| 01e0 | e1 22 53 1a 21 24 d1 5d 08 f9 98 08 15 ff 11 af c5 a6 04 33 8b f3 31 2d cf c8 8d 8e cc fb 4f 61 | ."S.!$.]...........3..1-......Oa |
| 0200 | 55 50 c9 37 91 e6 dd 3a e9 2e 05 6f 24 50 55 ca 91 0b c5 3b a6 93 6f dd 63 c5 61 f9 7a 26 fe 90 | UP.7...:...o$PU....;..o.c.a.z&.. |
| 0220 | 30 89 a6 90 b6 da c6 47 85 d9 7c a7 78 6b d0 8e ba 27 90 b2 d7 d8 02 c5 b8 0a c1 bf 00 a2 cb 1e | 0......G..|.xk...'.............. |
| 0240 | 2d 28 21 ff 86 54 88 2a 23 d4 6d 3f 12 ef 0b 45 d9 57 65 99 9c 0b 09 67 ce 7c 4c 48 e6 86 02 fb | -(!..T.*#.m?...E.We....g.|LH.... |
| 0260 | 40 4d 71 5b 0b a7 73 02 dc f7 6a 78 86 d5 47 02 dc 18 bf 03 54 b7 72 f8 14 a3 ca 9c 1d 90 72 5c | @Mq[..s...jx..G.....T.r.......r\ |
| 0280 | 95 a5 7e ae 24 2c 99 3b 62 a2 ae af 53 dc 76 3a 3c 83 a5 91 00 15 24 46 e3 3e 1f ad 2d cf c7 4a | ..~.$,.;b...S.v:<.....$F.>..-..J |
| 02a0 | 95 ab 4e 92 bc 0b 7b 8f 0c ff 38 d2 09 7f 9e c1 d1 4a 80 0a f2 a9 96 71 1e da b9 d7 a9 db 74 4d | ..N...{...8......J.....q......tM |
| 02c0 | a3 44 21 a4 e3 50 92 3c 45 10 92 70 c2 3c 28 ac f2 e0 ba 2d 98 d2 6b 2f 61 9d 4e 25 79 0a e8 05 | .D!..P.<E..p.<(....-..k/a.N%y... |
| 02e0 | 84 96 1e a5 c4 10 cd e0 7b a3 4e 08 6e c4 17 03 85 3a 7e 55 c5 62 67 58 0d fc cf 38 25 83 14 b8 | ........{.N.n....:~U.bgX...8%... |
| 0300 | 51 cf 4c 78 4e 48 4e ee 39 8f 08 c9 12 2f a6 0c 3c 0c bb 41 d5 21 01 28 5a 43 60 3b a0 47 c3 aa | Q.LxNHN.9..../..<..A.!.(ZC`;.G.. |
| 0320 | a3 0c 46 1e 86 04 6a a5 04 a0 11 e1 c0 7a 3d cd 00 8f c7 bc 06 19 12 38 27 25 20 55 b6 d4 50 00 | ..F...j......z=........8'%.U..P. |
| 0340 | be d0 f4 35 d0 47 27 ad f9 e0 f5 37 96 38 3a 92 a9 20 2b 0e 4b e2 46 df ed b7 0e ed db b1 a0 5c | ...5.G'....7.8:...+.K.F........\ |
| 0360 | 49 bc fd 6d 88 38 7c 64 88 70 4a 78 60 8b e3 de 06 11 f7 8a b8 fe 59 e5 4a 43 1b 38 2e d1 26 32 | I..m.8|d.pJx`.........Y.JC.8..&2 |
| 0380 | cf 5c 2b cc 52 07 1c 17 77 43 9a 4d e1 1d 29 84 a9 08 c7 c9 99 58 ca 25 09 49 da 2e 4c 21 bf 8b | .\+.R...wC.M..)......X.%.I..L!.. |
| 03a0 | 37 9f ac fa 63 f6 b8 55 16 91 fb 47 37 e1 30 f1 41 68 6b 94 21 5a c8 52 73 94 e9 2c f6 5f c7 84 | 7...c..U...G7.0.Ahk.!Z.Rs..,._.. |
| 03c0 | 49 c6 72 52 de 26 22 43 36 8b b8 27 f3 b5 55 29 c9 5e 29 05 81 72 70 ff 42 25 78 48 27 93 15 e0 | I.rR.&"C6..'..U).^)..rp.B%xH'... |
| 03e0 | f1 19 84 5d c7 cf 60 55 40 02 8f 2e 5c 33 60 d1 8e c3 73 65 a7 43 fa 4b ab c6 5f 8d 7c f6 ae fb | pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */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.Core;
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> : ExtendedObject, 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.GdiScreenCapture();
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;
}
}
/// <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();
LogManager.Log($"Screen capture engine started with the following definitions:\nCapture Region: {CaptureRegion}\nCapture Method: {CaptureMethod.GetType().Name}\nFrame Rate: {FrameRate}\nComparer Enabled: {EnableComparer}");
while (IsStarted)
{
watch.Restart();
try
{
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, 0);
}
else
{
try
{
BitmapComparerResult<TFrame> result = Comparer.CreateDifference(_previousBitmap, bitmap);
_previousBitmap.Dispose();
_previousBitmap = bitmap.Clone() as Bitmap;
OnFrameReceived(bitmap, result.Frame, result.DifferenceCount);
}
catch (MaxDifferencesReachedException)
{
_previousBitmap.Dispose();
_previousBitmap = bitmap.Clone() as Bitmap;
OnFrameReceived(bitmap, null, (uint)(bitmap.Width * bitmap.Height));
}
}
}
else
{
OnFrameReceived(bitmap, null, 0);
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error in screen capture engine: {ex.Message}");
_previousBitmap = null;
}
int delay = Math.Max(5, (1000 / FrameRate) - (int)watch.ElapsedMilliseconds);
Thread.Sleep(delay);
}
_previousBitmap = null;
LogManager.Log("Screen capture engine stopped.");
}
private void OnFrameReceived(Bitmap currentBitmap, TFrame diffFrame, uint differenceCount)
{
if (!IsStarted) return;
FrameReceived?.Invoke(this, new ScreenCaptureFrameReceivedEventArgs<TFrame>()
{
Frame = new ScreenCaptureFrame<TFrame>(currentBitmap, diffFrame)
{
DifferenceCount = differenceCount
}
});
}
}
}
|