I am trying to output whatever is captured from a webcam to an Image
control in a WPF window. I am using the AForge.NET library.
Unfortunately, after a few minutes of successful capturing, I am getting an OutOfMemoryException
. Likewise, as soon as I start capturing, I can see my memory usage rise continuously in the task manager until the moment of the exception (although there have been a few occasions where memory usage kept rising, then steeply dropped back to its original state, and then kept rising again to the point of the exception).
This is my code for the handler of the NewFrame
event of the VideoCaptureDevice
class (whose code for converting the Bitmap
instance to an ImageSource
is largely based on an answer by Sascha Hennig):
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
using (var streamBitmap = (Bitmap)eventArgs.Frame.Clone()) {
BitmapSource bitmapSourceVideo;
var hBitmap = streamBitmap.GetHbitmap();
try
{
bitmapSourceVideo = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap);
}
bitmapSourceVideo.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
videoControl.Source = bitmapSourceVideo;
}));
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
In case you wonder, the call to eventArgs.Frame.Clone()
seems to be required. Explanations can be found here, and possibly here.
While trying to isolate the source of the issue, I have commented out various portions of this code until I arrived at this state:
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
using (var streamBitmap = (Bitmap)eventArgs.Frame.Clone()) {
BitmapSource bitmapSourceVideo;
var hBitmap = streamBitmap.GetHbitmap();
try
{/*
bitmapSourceVideo = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
*/}
finally
{
DeleteObject(hBitmap);
}
/*
bitmapSourceVideo.Freeze();
Dispatcher.BeginInvoke(new ThreadStart(delegate
{
videoControl.Source = bitmapSourceVideo;
}));*/
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
(Obviously, this doesn't draw anything to the window, but that is beside the point now.) This version of the method does not feature the memory leak. Removing the comment signs around the statement with the call to CreateBitmapSourceFromHBitmap
brings the memory leak back. What am I missing here?
There have been various resources about seemingly similar problems, none of which have helped me find a solution:
- This answer assumes I am loading from a URI that I could instead load into a stream.
- This answer seems to assume I am loading the bitmap data directly from a stream that I can access, and likewise, this blogpost suggests to create a stream wrapper.
- The
Freeze
solution from this blogpost should not apply, as the leak does not change based on whether I comment or uncomment my call toFreeze
. This is further corroborated by this answer. - This answer, this answer, this answer, and all answers to this question point out that
DeleteObject
needs to be invoked once the handle obtained fromGetHbitmap
is no longer needed. This is also suggested by this blogpost. I am already doing that in the code. - Information from this question suggests I need to dispose of the
Bitmap
, but I am already disposing of anyBitmap
instance that I create myself, thanks to theusing
block. - This forum thread sounds vaguely similar, but it ends in an inconclusive way.