Reputation: 43
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.
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
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()
:
After adding GC.Collect()
:
Upvotes: 1