gpph
gpph

Reputation: 43

C# PictureBox: Memory leak when updating multiple times per second

I am using AForge to capture video from a camera and display it in a WinForms PictureBox. AForge supplies an event that gets triggered on every new frame--so, for my camera, 30 times per second.

I'm finding that even when I am careful to dispose of the PictureBox's image, memory usage climbs rapidly (and in a weird pattern... see image below). My guess is that the event is being called more often than it can dispose of the bitmap, but I'm not sure.

Here is what I have inside the newframe event. I'm using Monitor.TryEnter per this answer, to avoid having events pile up waiting for the PictureBox to unlock. I had hoped that setting the timeout to 1 millisecond would effectively prevent any events from piling up.

private void Camera_NewFrame(object sender, NewFrameEventArgs e)
    {
        if (Monitor.TryEnter(_pictureBox, millisecondsTimeout: 1))
        {
            try
            {
                _pictureBox.Image?.Dispose();
                _pictureBox.Image = AForge.Imaging.Image.Clone(newImage);
            }
            finally
            {
                Monitor.Exit(_pictureBox);
            }
        }
    }

This is the memory usage pattern that results when I run the code.

enter image description here

  1. Video capture started
  2. Unknown event causes memory usage to increase
  3. Unknown event causes memory usage to stabilize
  4. Unknown event causes memory usage to increase

And that repeats. Note that if I change the TryEnter timeout to a higher value, the stable memory usage periods get shorter.

If anyone can offer a solution for this memory leak, I would be very grateful. I did see a similar question here, but it wasn't really answered: C# Picturebox memory leak

Upvotes: 2

Views: 863

Answers (1)

gpph
gpph

Reputation: 43

This is not the perfect way to do this, but I found a workaround that solves the problem of skyrocketing memory usage. I basically just limited the frequency with which the PictureBox image can update.

    private const int MAX_UPDATE_TIME_MS = 70; // Effectively limits displayed video frame rate. Without this, events pile up faster than we can dispose of the PictureBox's current image, leading to a HUGE memory leak
    private DateTime _lastUpdateTimeStamp = DateTime.Now;
    private void Camera_NewFrame(object sender, NewFrameEventArgs e)
    {
        if (DateTime.Now < _lastUpdateTimeStamp.AddMilliseconds(MAX_UPDATE_TIME_MS))
        {
            return;
        }
        else
        {
            _lastUpdateTimeStamp = DateTime.Now;

            try
            {
                UpdatePictureBoxImage(_pictureBox, e.Frame);
            }
            catch
            {
                // Do nothing, else PictureBox could freeze
            }
        }
    }

    private void UpdatePictureBoxImage(
        PictureBox pictureBox,
        Bitmap newImage)
    {
        if (Monitor.TryEnter(pictureBox, millisecondsTimeout: 1))
        {
            try
            {
                pictureBox.Image?.Dispose();
                pictureBox.Image = AForge.Imaging.Image.Clone(newImage);
            }
            finally
            {
                Monitor.Exit(pictureBox);
            }
        }
    }

Additional note specific to AForge

When stopping the video stream, AForge says you should do something like the StopVideoCapture() method I wrote below. I found that the memory AForge was using to control the camera, which was not insignificant, did not get deallocated in a timely way. So I added GC.Collect() to a new method called DisposeVideoSource() that I run when I'm done with the camera.

    public void StopVideoCapture()
    {
        while (_videoCaptureDevice.IsRunning)
        {
            _videoCaptureDevice.SignalToStop();
            _videoCaptureDevice.WaitForStop();
        }
    }

    public void DisposeVideoSource()
    {
        StopVideoCapture();
        GC.Collect();
    }

Before adding GC.Collect():

enter image description here

After adding GC.Collect():

enter image description here

Upvotes: 1

Related Questions