private void WorkerThread()
{
int width = region.Width;
int height = region.Height;
int x = region.Location.X;
int y = region.Location.Y;
Size size = region.Size;
// Create 10 frames (which we will keep overwriting and reusing)
Context[] buffer = new Context[10];
for (int i = 0; i < buffer.Length; i++)
{
// Note: It's important to use 32-bpp ARGB to avoid problems with FFmpeg later
//var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
var bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
buffer[i] = new Context
{
original = bmp,
hwndGraphics = Graphics.FromHwnd(windowHandle),
imgGraphics = Graphics.FromImage(bmp),
args = new NewFrameEventArgs(bmp)
};
}
// download start time and duration
DateTime start;
TimeSpan span;
int counter = 0;
int bufferPos = 0;
Context captureContext = buffer[bufferPos];
Context displayContext = null;
while (!stopEvent.WaitOne(0, false))
{
// set download start time
start = DateTime.Now;
try
{
// Start capturing a new frame at the same
// time we send the previous one to listeners
#if !NET35
Task.WaitAll(
#if NET40
Task.Factory.StartNew(() =>
#else
Task.Run(() =>
#endif
{
#endif
// wait for a while ?
if (frameInterval > 0)
{
// get download duration
span = DateTime.Now.Subtract(start);
// miliseconds to sleep
int msec = frameInterval - (int)span.TotalMilliseconds;
// if we should sleep, then sleep as long as needed
if ((msec > 0) && (stopEvent.WaitOne(msec, false)))
return;
}
// capture the screen
var wndHdc = captureContext.hwndGraphics.GetHdc();
var imgHdc = captureContext.imgGraphics.GetHdc();
captureContext.args.CaptureStarted = DateTime.Now;
BitBlt(imgHdc, 0, 0, width, height, wndHdc, x, y, (int)CopyPixelOperation.SourceCopy);
captureContext.args.FrameSize = size;
captureContext.args.CaptureFinished = DateTime.Now;
captureContext.hwndGraphics.ReleaseHdc(wndHdc);
captureContext.imgGraphics.ReleaseHdc(imgHdc);
// increment frames counter
captureContext.args.FrameIndex = counter++;
framesReceived++;
#if !NET35
}
),
#if NET40
Task.Factory.StartNew(() =>
#else
Task.Run(() =>
#endif
{
#endif
// provide new image to clients
if (displayContext != null)
{
// reset whatever listeners had done with the frame
displayContext.args.Frame = displayContext.original;
if (NewFrame != null)
NewFrame(this, displayContext.args);
}
#if !NET35
}));
#endif
// Update buffer position
displayContext = buffer[bufferPos];
bufferPos = (bufferPos + 1) % buffer.Length;
captureContext = buffer[bufferPos];
Debug.Assert(displayContext != captureContext);
}
catch (ThreadAbortException)
{
break;
}
catch (Exception exception)
{
#if !NET35
AggregateException ae = exception as AggregateException;
if (ae != null && ae.InnerExceptions.Count == 1)
exception = ae.InnerExceptions[0];
#endif
// provide information to clients
if (VideoSourceError == null)
throw;
VideoSourceError(this, new VideoSourceErrorEventArgs(exception));
// wait for a while before the next try
Thread.Sleep(250);
}
// need to stop ?
if (stopEvent.WaitOne(0, false))
break;
}
// release resources
foreach (var c in buffer)
{
c.imgGraphics.Dispose();
c.args.Frame.Dispose();
}
if (PlayingFinished != null)
PlayingFinished(this, ReasonToFinishPlaying.StoppedByUser);
}