11

I'm writing an WPF application where I need to show a Webcam feed. I was able to do this easly with the AForge framework.But when I've changed from a computer to a another computer the same code doesn't work the same way.

In the first one the webcam feed works perfectly, but in the other one this does't occur, the feed has a lot of delay, and the application doesn't work properly.

Here is the code:

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

        this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, (SendOrPostCallback)delegate
            {
                IntPtr hBitmap = img.GetHbitmap();
                System.Windows.Media.Imaging.BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                    hBitmap,
                    IntPtr.Zero,
                    Int32Rect.Empty,
                    System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

                DeleteObject(hBitmap);

                img.Dispose();
                GC.Collect();
                image1.Source = bitmapSource;

            }, null);

    }

What this code is really simple, it gets a new_frame from the webcam in a form of a Bitmap, and what I need to do is to convert it to a BitmapSource, so I can show in the image frame of the WPF. I think this conversion is the responsible of the mess that is happening, but I don't understand why it works in a computer and in the other doesn't.

The computer specs are almost the same, the processor is the same, as well the system memory.

My problem here is about performance, this code in one computer runs smoothly, and the webcam feed is presented as it should, when I port it to another PC this doesn't happen.

NoWar
  • 36,338
  • 80
  • 323
  • 498
João Cardoso
  • 351
  • 3
  • 5
  • 13
  • Dude, without telling us what the error is (including any InnerExceptions and call stacks) there's almost no chance of anybody helping you. –  Jan 05 '10 at 12:50
  • There is no error, my problem here is about performance, this code in one computer runs smoothly, and the webcam feed is presented as it should, when I port it to another PC this doesn't happen, the webcam feed has an awful delay, and for example the close window button doesn't work. – João Cardoso Jan 05 '10 at 14:20
  • You have got a new answer man. Have look please. – NoWar Feb 08 '13 at 18:49

4 Answers4

23

Here is working code based on this article.

(1) Download and install last AForge framework. (I have used version 2.2.4)

(2) Create WPF Application project.

(3) Add references to those AForge DLLs. (You can find them under C:\Program Files (x86)\AForge.NET\Framework\Release folder i.e.)

enter image description here

(4) Build your project. (I have used VS 2012)

(5) Add WPF Image control and name it "frameHolder".

So you have something like

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Image HorizontalAlignment="Stretch" Name="frameHolder"  VerticalAlignment="Stretch"  Stretch="Fill"/>
    </Grid>
</Window>

(6) Add C# code:

using AForge.Video;
    using AForge.Video.DirectShow;
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;

/////

namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
            VideoCaptureDevice LocalWebCam;
            public FilterInfoCollection LoaclWebCamsCollection; 

        void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            try
            {
                System.Drawing.Image img = (Bitmap)eventArgs.Frame.Clone();

                MemoryStream ms = new MemoryStream();
                img.Save(ms, ImageFormat.Bmp);
                ms.Seek(0, SeekOrigin.Begin);
                BitmapImage bi = new BitmapImage();
                bi.BeginInit();
                bi.StreamSource = ms;
                bi.EndInit();

                bi.Freeze();
                Dispatcher.BeginInvoke(new ThreadStart(delegate
                {
                    frameHolder.Source = bi;
                }));
            }
            catch (Exception ex)
            {
            }
        } 

        public MainWindow()
        {
            InitializeComponent();
            Loaded += MainWindow_Loaded;
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            LoaclWebCamsCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice);
            LocalWebCam = new VideoCaptureDevice(LoaclWebCamsCollection[0].MonikerString);
            LocalWebCam.NewFrame += new NewFrameEventHandler(Cam_NewFrame);

            LocalWebCam.Start();
        }
    }
}

(7) Re-Build project and it works!

Note: We use first detected WebCam by default. Make sure you have WebCam driver insalled and WebCam is working in general... :)

NoWar
  • 36,338
  • 80
  • 323
  • 498
  • 5
    Also add `LocalWebCam.Stop();` in Unloaded event if you intend to use this as UserControl multiple times in your WPF app. There may be troubles when creating control again without stopping camera first. Anyway, this solution gave me trouble for 20 minutes until I noticed I forgot to put `LocalWebCam.Start();` at the end of Loaded event :) Yeah, it happens. – Marko Stanojevic Apr 01 '15 at 10:00
6

I know the original post is over 3 years old, but I just have been trying to figure out how to use this code. I found that the answer given by Dimi is nearly a fully functional code. However, I found that I have issues with memory leaking and the frame not being render reliably on some computers. The code worked perfectly on my beefier development computer (i7, 16GB RAM, Quadro Pro Grapthics card), but when I deployed the app on a computer with more limited resources (i5, 4GB RAM, Integrated Intel graphics), the frame disappears once in a while and the program would also crash after the system memory runs out. After searching the internet for a while, I think I finally patched together a working code based on all the feedback people had. I know that the other computer is capable of running frame capture from the webcam because I have a WinForm C# app that I wrote using AForge.NET and it has no issues rendering the frame reliably and with no memory leak. Unfortunately WPF doesn't handle graphics the same way as WinForm and we have to do this hack to get AForge.NET to work with it.

Basically, the code is the same as Dimi's except for the Cam_NewFrame method.

void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        try
        {
            BitmapImage bi;
            using(var bitmap = (Bitmap)eventArgs.Frame.Clone())
            {
                bi = new BitmapImage();
                bi.BeginInit();
                MemoryStream ms = new MemoryStream();
                bitmap.Save(ms, ImageFormat.Bmp);
                bi.StreamSource = ms;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.EndInit();
            }
            bi.Freeze();
            Dispatcher.BeginInvoke(new ThreadStart(delegate { frameHolder.Source = bi; }));


        }
        catch (Exception ex)
        {
            //catch your error here
        }

    } 

The changes that were made are the following:

  1. Enclosing the bitmap handling with Using scope so that any unused memory is cleaned up right away after the end of scope.
  2. Moving the bi.BeginInit() before dealing with the memory stream so that the bitmap is ready for the memomory dump right away.
  3. Changing the CacheOption to OnLoad so that all the image memory gets dump right at the loading. Otherwise, it uses BitmapCacheOption.Default which could allow the image to hold on to the memory even when bi.Freeze() is issued. This caused the frame to not be rendered even with the Dispatcher.BeginInvoke is called to render the image.

So far it's been working well but if anyone else spot other issues please make a comment so we know how to fix it.

Patratacus
  • 1,651
  • 1
  • 16
  • 19
  • 2
    There is one small mistake in the code. If you start your application the image is not displayed. It could be solved by adding `ms.Seek(0, SeekOrigin.Begin);` after `bitmap.Save(ms, ImageFormat.Bmp);` as Dmitry did. – daniel59 May 04 '17 at 11:24
2

In my WPF MediaKit, I have a control called VideoCaptureElement that will render a webcam to WPF. You can also get access to the samples by hooking into the new image event and setting the EnableSampleGrabbing on the element.

Jeremiah Morrill
  • 4,248
  • 2
  • 17
  • 21
-1

Maybe the webcam on the other computer is broken/faulty? Or has one of the webcams that doesnt support the DirectShow api, which i think AForge builds on.

Kurru
  • 14,180
  • 18
  • 64
  • 84
  • I've already written a program using Windows Forms in which I use the Aforge API and it works fine. I think the problem here is in the conversion of Bitmap to BitmapSource that needs to be done, in order to show the image taken by the webcam. – João Cardoso Jan 10 '10 at 13:53