Memory Leak from ImageSource/CreateBitmapSourceFro

2019-09-05 23:45发布

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:

1条回答
太酷不给撩
2楼-- · 2019-09-06 00:10

My instinct tells me that it is something to do with the bitmapSourceVideo being inside the delegate, and therefore creating a closure and not being cleaned up.

EDIT

Try

        Dispatcher.BeginInvoke(new ThreadStart(delegate
        {
            videoControl.Source = bitmapSourceVideo;
            bitmapSourceVideo = null;
        }));
查看更多
登录 后发表回答