1

I am working in VS 2012, my project is a C# WPF application with .NetFramework 4.0

My Objective - Need to show the live webcam in user control and process the current frame(as bitmap) for every 2 second.

What I Achieved - I am able to initialize the webcam and able to stream the live video with DirectShowLib. I used this example.

What I Need - I am not able to get the frame(as bitmap). Is there any default methods to get the Current frame in DirectShow ? Will it need any implementation in my User Control(WebCamControl2 as mentioned in example). Is there any example for my need?

Thanks.

Update:

Here is the code I am using:

using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using DirectShowLib;
using System.Runtime.InteropServices.ComTypes;

namespace WebCamControl2
{
    [Guid("43878F19-1E0E-42d2-B72B-88A94418A302"),
    ComVisible(true)]
    public partial class WebCamControl2 : UserControl
    {
        public enum PlayState : int
        {
            Stopped,
            Paused,
            Running,
            Init
        }       

        private PlayState CurrentState = PlayState.Stopped;
        private int WM_GRAPHNOTIFY = Convert.ToInt32("0X8000", 16) + 1;
        private IVideoWindow videoWindow = null;
        private IMediaControl mediaControl = null;
        private IMediaEventEx mediaEventEx = null;
        private IGraphBuilder graphBuilder = null;
        private ICaptureGraphBuilder2 captureGraphBuilder = null;

        public WebCamControl2()
        {
            InitializeComponent();           
        }

        private void WebCamControl_Load(object sender, System.EventArgs e)
        {
            this.Resize += new System.EventHandler(WebCamControl_Resize);            
            CaptureVideo();
        }

        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(WebCamControl2));

            this.Load += new System.EventHandler(WebCamControl_Load);
        }

        private void CaptureVideo()
        {
            int hr = 0;
            IBaseFilter sourceFilter = null;
            try
            {
                // create the necessary DirectShow interfaces
                GetInterfaces();

                hr = this.captureGraphBuilder.SetFiltergraph(this.graphBuilder);
                DsError.ThrowExceptionForHR(hr);

                sourceFilter = FindCaptureDevice();

                hr = this.graphBuilder.AddFilter(sourceFilter, "WebCamControl Video");
                DsError.ThrowExceptionForHR(hr);

                hr = this.captureGraphBuilder.RenderStream(PinCategory.Preview, MediaType.Video, sourceFilter, null, null);
                Debug.WriteLine(DsError.GetErrorText(hr));
                DsError.ThrowExceptionForHR(hr);

                Marshal.ReleaseComObject(sourceFilter);

                SetupVideoWindow();

                hr = this.mediaControl.Run();
                DsError.ThrowExceptionForHR(hr);

                this.CurrentState = PlayState.Running;
            }
            catch (Exception ex)
            {
                MessageBox.Show("An unrecoverable error has occurred.\r\n" + ex.ToString());
            }
        }

        private void GetInterfaces()
        {
            this.graphBuilder = (IGraphBuilder)(new FilterGraph());
            this.captureGraphBuilder = (ICaptureGraphBuilder2)(new CaptureGraphBuilder2());
            this.mediaControl = (IMediaControl)this.graphBuilder;
            this.videoWindow = (IVideoWindow)this.graphBuilder;
            this.mediaEventEx = (IMediaEventEx)this.graphBuilder;

            // send notification messages to the control window
            int hr = this.mediaEventEx.SetNotifyWindow(this.Handle, WM_GRAPHNOTIFY, IntPtr.Zero);

            DsError.ThrowExceptionForHR(hr);
        }

        private IBaseFilter FindCaptureDevice()
        {
            UCOMIEnumMoniker classEnum = null;
            UCOMIMoniker[] moniker = new UCOMIMoniker[1];
            object source = null;

            ICreateDevEnum devEnum = (ICreateDevEnum)(new CreateDevEnum());
            int hr = devEnum.CreateClassEnumerator(FilterCategory.VideoInputDevice, out classEnum, CDef.None);
            DsError.ThrowExceptionForHR(hr);
            Marshal.ReleaseComObject(devEnum);

            if (classEnum == null)
            {
                throw new ApplicationException("No video capture device was detected.\\r\\n\\r\\n" + "This sample requires a video capture device, such as a USB WebCam,\\r\\nto be installed and working properly.  The sample will now close.");
            }

            int none = 0;

            if (classEnum.Next(moniker.Length, moniker, out none) == 0)
            {
                Guid iid = typeof(IBaseFilter).GUID;
                moniker[0].BindToObject(null, null, ref iid, out source);
            }
            else
            {
                throw new ApplicationException("Unable to access video capture device!");
            }

            Marshal.ReleaseComObject(moniker[0]);
            Marshal.ReleaseComObject(classEnum);

            return (IBaseFilter)source;
        }

        private void SetupVideoWindow()
        {
            int hr = 0;

            //set the video window to be a child of the main window
            //putowner : Sets the owning parent window for the video playback window. 
            hr = this.videoWindow.put_Owner(this.Handle);
            DsError.ThrowExceptionForHR(hr);


            hr = this.videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipChildren);
            DsError.ThrowExceptionForHR(hr);

            //Use helper function to position video window in client rect of main application window
            WebCamControl_Resize(this, null);



            //Make the video window visible, now that it is properly positioned
            //put_visible : This method changes the visibility of the video window. 
            hr = this.videoWindow.put_Visible(OABool.True);
            DsError.ThrowExceptionForHR(hr);
        }

        //protected override void WndProc(ref Message m)
        //{
        //    if (m.Msg == WM_GRAPHNOTIFY)
        //    {
        //        HandleGraphEvent();
        //    }
        //    if (this.videoWindow != null)
        //    {
        //        this.videoWindow.NotifyOwnerMessage(m.HWnd, m.Msg, m.WParam.ToInt32(), m.LParam.ToInt32());
        //    }
        //    base.WndProc(ref m);
        //}         

        private void HandleGraphEvent()
        {
            int hr = 0;
            EventCode evCode = 0;
            int evParam1 = 0;
            int evParam2 = 0;

            while (this.mediaEventEx != null && this.mediaEventEx.GetEvent(out evCode, out evParam1, out evParam2, 0) == 0)
            {
                // Free event parameters to prevent memory leaks associated with
                // event parameter data.  While this application is not interested
                // in the received events, applications should always process them.
                hr = this.mediaEventEx.FreeEventParams(evCode, evParam1, evParam2);
                DsError.ThrowExceptionForHR(hr);

                // Insert event processing code here, if desired (see http://msdn2.microsoft.com/en-us/library/ms783649.aspx)
            }
        }       

        private void ReleaseInterfaces()
        {
            if (this.mediaControl != null)
                this.mediaControl.StopWhenReady();

            this.CurrentState = PlayState.Stopped;

            // stop notifications of events
            if (this.mediaEventEx != null)
                this.mediaEventEx.SetNotifyWindow(IntPtr.Zero, WM_GRAPHNOTIFY, IntPtr.Zero);

            //// Relinquish ownership (IMPORTANT!) of the video window.
            //// Failing to call put_Owner can lead to assert failures within
            //// the video renderer, as it still assumes that it has a valid
            //// parent window.
            if (this.videoWindow != null)
            {
                this.videoWindow.put_Visible(OABool.False);
                this.videoWindow.put_Owner(IntPtr.Zero);
            }

            // Release DirectShow interfaces
            Marshal.ReleaseComObject(this.mediaControl);
            this.mediaControl = null;

            Marshal.ReleaseComObject(this.mediaEventEx);
            this.mediaEventEx = null;

            Marshal.ReleaseComObject(this.videoWindow);
            this.videoWindow = null;

            Marshal.ReleaseComObject(this.graphBuilder);
            this.graphBuilder = null;

            Marshal.ReleaseComObject(this.captureGraphBuilder);
            this.captureGraphBuilder = null;
        }

        private void WebCamControl_Resize(object sender, System.EventArgs e)
        {
            //Resize the video preview window to match owner window size
            if (this.videoWindow != null)
                this.videoWindow.SetWindowPosition(0, 0, this.Width, this.ClientSize.Height);
        }
    }
}
Gopichandar
  • 2,742
  • 2
  • 24
  • 54

3 Answers3

1

Try to use EMGU or Opencv

http://www.emgu.com/wiki/index.php/Main_Page

And this is a sample how to capture frames from you video (cam) http://www.emgu.com/wiki/index.php?title=Camera_Capture

1-Add Emgu dlls to your application 2- this is an example

 private Capture _capture;
 Image<Gray, Byte> frame;

//0 is the default camera
 _capture = new Capture(0);

//here how you can get the frames from your video
 Image<Bgr, Byte> frame = _capture.QueryFrame();
Hazem Abdullah
  • 1,837
  • 4
  • 23
  • 41
  • I tried to run the demo application in the link provided by you. It throwing an error. `An unhandled exception of type 'System.TypeInitializationException' occurred in Emgu.CV.dll Additional information: The type initializer for 'Emgu.CV.CvInvoke' threw an exception.` after clicking 'Start capture'. Am I missing anything? – Gopichandar Mar 05 '15 at 12:26
  • Copy the opencvcore.dll near to the exe – Hazem Abdullah Mar 05 '15 at 13:10
  • Sorry, I could not find the opencvcore.dll in the sample project. – Gopichandar Mar 05 '15 at 14:33
  • Download the emgu and you will see them in the emgu folder http://stackoverflow.com/questions/17274372/the-type-initializer-for-emgu-cv-cvinvoke-threw-an-exception – Hazem Abdullah Mar 05 '15 at 15:02
0

You will find a number of questions addressing the mentioned problem, e.g.

There are two solutions. You either grab back from component that visualizes video (video renderer), or you add Sample Grabber or similar filter into the pipeline and grab from its callback. Both ways you get data you can use to initialize your .NET bitmap from, but not exactly bitmap. That is, you are to do the conversion step (inspiring Q on this).

You can certainly use CV libraries, which are however are tremendous overkill for such simple task, and their video capture might appear to be inflexible if you need to add something else.

Community
  • 1
  • 1
Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • Thanks @Roman . . I have updated with my code sample. Can you guide me on how can I add sample grabber or filter to get the bitmap of current frame. – Gopichandar Mar 06 '15 at 06:19
  • Where you do RenderStream, create a SampleGrabber, AddFilter it, initialize to only work as 24 or 32 bit RGB. Connect its input to your source, and then RenderStream its output. Then you can provide a callback to receive frames through SampleCB call. Search on mentioned keywords and you will have lots of code snippets to use. – Roman R. Mar 06 '15 at 06:34
  • Let me try. Thanks for the info. – Gopichandar Mar 06 '15 at 08:45
-1

I have overcome this issue by taking screenshot of the screen instead of taking frame from rendering video. Here is the code that helped me

//Create a new bitmap.
var bmpScreenshot = new System.Drawing.Bitmap(Screen.PrimaryScreen.Bounds.Width, 
                                              Screen.PrimaryScreen.Bounds.Height,
                                              System.Drawing.Imaging.PixelFormat.Format32bppArgb);

// Create a graphics object from the bitmap.
var gfxScreenshot = System.Drawing.Graphics.FromImage(bmpScreenshot);

// Take the screenshot from the upper left corner to the right bottom corner.
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X,
                                                Screen.PrimaryScreen.Bounds.Y,
                                                0,
                                                0,
                                                Screen.PrimaryScreen.Bounds.Size,
                                                System.Drawing.CopyPixelOperation.SourceCopy);

I know this is not the right way. However, it gives me a solution for my need. I tried with SampleGrabber and EMGU but I am not able to get the solution as expected. I feel my solution is simple enough for my need and may help someone.

Thanks.

Gopichandar
  • 2,742
  • 2
  • 24
  • 54