9

Ok, I've been trying to do something specific with video feed from a webcam. I have a Lumenera Infinity 2 microscope that I am trying to pull feed from, and want to be able to modify the feed as it comes in. Since I couldn't find a way to do that using Video Source Player, I decided to instead pull each frame (max of 15fps for the camera) as a bitmap so I can do my modifications there.

The problem is: I have a HUGE memory leak. When I run the video just using the videoSourcePlayer, it hovers at using around 30 megs. When I run pulling the frames as bitmaps, it breaks 1 gig of memory in a matter of seconds.

What am I missing, here? I figured auto-garbage collection would scoop up the old frames as they became inaccessible. Should I try to force garbage collection on bitmap? Or is it something else entirely and I am noobishly missing it.

FilterInfoCollection captureDevices;
VideoCaptureDevice cam;
Bitmap bitmap;

public Form1()
{
  InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
  try
  {
    captureDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);

    if (captureDevices.Count == 0)
      throw new ApplicationException();

    CameraSelectComboBox.Items.Clear();

    foreach (FilterInfo device in captureDevices)
    {
      CameraSelectComboBox.Items.Add(device.Name);
    }

    CameraSelectComboBox.SelectedIndex = 0;
    CameraSelectComboBox.Enabled = true;
  }
  catch (ApplicationException)
  {
    CameraSelectComboBox.Enabled = false;
  }
}

private void connectButton_Click(object sender, EventArgs e)
{
  cam = new VideoCaptureDevice(captureDevices[CameraSelectComboBox.SelectedIndex].MonikerString);
  cam.NewFrame -= Handle_New_Frame; //Just to avoid the possibility of a second event handler being put on
  cam.NewFrame += new AForge.Video.NewFrameEventHandler(Handle_New_Frame);
  videoSourcePlayer1.Visible = false;
  cam.Start();

  //videoPictureBox1.Visible = false;
  //videoSourcePlayer1.VideoSource = new VideoCaptureDevice(captureDevices[CameraSelectComboBox.SelectedIndex].MonikerString);
  //videoSourcePlayer1.Start();
}

private void Handle_New_Frame(object sender, NewFrameEventArgs eventArgs)
{
  bitmap = (Bitmap)eventArgs.Frame.Clone();

  videoPictureBox1.Image = bitmap;
}
John Willemse
  • 6,608
  • 7
  • 31
  • 45
C Smith
  • 778
  • 2
  • 14
  • 31

2 Answers2

2

I think this is one area that can use improvement:

cam = new 
  VideoCaptureDevice(captureDevices[CameraSelectComboBox.SelectedIndex].MonikerString);

cam.NewFrame -= Handle_New_Frame; // you're pointing to the new instance of VCD, so this will have no effect.

cam.NewFrame += new AForge.Video.NewFrameEventHandler(Handle_New_Frame);
videoSourcePlayer1.Visible = false;
cam.Start();

That code block is bleeding memory every time you press the connect button.

You pretty much need to have a reference to the VCD at the main level. So define a member variable at Form1 class level:

private VideoCaptureDevice _cameraContext;

And in the connect event handler, do this:

if (_camerContext != null)
{
  _cameraContext.NewFrame -= Handle_New_Frame;
}
_cameraContext = new VideoCaptureDevice(blah blah blah);
_cameraContext.NewFrame += Handle_New_Frame;
videoSourcePlayer1.Visible = false;
_cameraContext.Start();

BTW, I'm assuming you're .NET 3.5 or later, hence the new delegate assignment syntax.

code4life
  • 15,655
  • 7
  • 50
  • 82
  • Note: this is only _one_ area of code that I see an obvious leak. Considering that the memory leak is significant, you should also look at how the bitmap is being disposed. – code4life Apr 15 '13 at 14:32
  • Awesome! Thanks a bunch for that information. And yes, your assumption is correct- I am using 4.5 – C Smith Apr 15 '13 at 14:37
2

Try this:

private void Handle_New_Frame(object sender, NewFrameEventArgs eventArgs)
{
    if(bitmap != null)
        bitmap.Dispose();
    bitmap = new Bitmap(eventArgs.Frame);

    if(videoPictureBox1.Image != null)
        this.Invoke(new MethodInvoker(delegate() {videoPictureBox1.Image.Dispose();}));
    videoPictureBox1.Image = bitmap;
}

It solved some of the memory leaks I experienced with Aforge and PictureBoxes, but the VideoSourcePlayer is much better where memory consumption is concerned.

Steven Mills
  • 2,363
  • 26
  • 36
  • Awesome! That should help a bunch! I am running into one issue with it, though- getting a cross thread error on the videoPictureBox1.Image.Dispose(). "Cross-thread operation not valid: Control 'videoPictureBox1' accessed from a thread other than the thread it was created on." – C Smith Apr 15 '13 at 14:37
  • Did you run into that problem when you were working with this, by chance? – C Smith Apr 15 '13 at 15:45
  • ah right yeah, you'd need to use Invoke. I've edited my answer, so it should fix the cross thread error. – Steven Mills Apr 16 '13 at 06:24