从用于动态 PictureBox 的位图中释放资源时出现异常

Exception when release resources from bitmap used for dynamic PictureBox

我想使用尽可能少的 RAM - 使用 Windows 表单和 Aforge.Net 来捕获视频。 问题是,当我尝试例如“.Dispose()”时,一些元素出现异常:

    static void Main()
    {
            Console.WriteLine("Main");
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());  <----- here VS showing me an exception (An unhandled exception of type 'System.InvalidOperationException' occurred in System.Drawing.dll)
    }

执行此操作的代码:

    public void setLocalCamera(string cameraName)
    {
        videoSource = new VideoCaptureDevice(cameraName);
        videoSource.NewFrame += new NewFrameEventHandler(video_NewFrame);
        videoSource.Start();
    }


    Bitmap bitmap;
    private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        if (bitmap != null) 
            bitmap.Dispose(); <--- here is the problematic code

        bitmap = (Bitmap)eventArgs.Frame.Clone();
        pB_Camera.Image = bitmap;

    }

这段代码产生了随机时间异常(我不知道究竟是什么触发了它)。我还尝试了一些我发现的其他解决方案,但什么都没有 work/help(比如在 pB_Camera.Image = 位图之后制作 bitmap.Dispose();但这也是一个例外)

如何解决这个问题并释放尽可能多的 RAM?

同时避免异常和闪烁的正确顺序是:

Bitmap bitmap;
void SetBitmap(Bitmap value)
{
    pB_Camera.Image = value;
    if (bitmap != null) bitmap.Dispose();
    bitmap = value;
}

而且用法很简单:

private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
    SetBitmap((Bitmap)eventArgs.Frame.Clone());
}

编辑:但是,您的具体情况略有不同,因为事件是在非 UI 线程上引发的。虽然一个简单的 Control.Invoke 就可以工作,但如果框架仅用于显示,我认为最好实现一点 "single item producer/consumer" 非阻塞模式,以便让工作线程在您继续处理时继续处理正在使用图像,如下所示:

private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
    PushNextFrame((Bitmap)eventArgs.Frame.Clone());
}

Bitmap nextFrame;

void PushNextFrame(Bitmap value)
{
    var prevFrame = Interlocked.Exchange(ref nextFrame, value);
    if (prevFrame != null)
    {
        // previous frame has not been consumed, so just discard it.
        // no need to notify the consumer, because it should have been done already.
        prevFrame.Dispose();
    }
    else
    {
        // previos frame has been consumed and we just set a new one, so we need to notity the consumer.
        BeginInvoke((Action)OnNextFrameAvailable);
    }
}

Bitmap PullNextFrame() { return Interlocked.Exchange(ref nextFrame, null); }

void OnNextFrameAvailable()
{
    DisplayFrame(PullNextFrame());
}

Bitmap currentFrame;

void DisplayFrame(Bitmap value)
{
    pb_Camera.Image = value;
    if (currentFrame != null) currentFrame.Dispose();
    currentFrame = value;
}