从网络摄像头获取当前帧 - DirectShowLib

时间:2015-03-05 12:03:11

标签: c# directshow directshow.net

我在VS 2012工作,我的项目是一个带有.NetFramework 4.0的C#WPF应用程序

我的目标 - 需要在用户控制中显示实时网络摄像头,并且每2秒处理一次当前帧(作为位图)。

我取得的成就 - 我可以初始化网络摄像头,并能够使用DirectShowLib流式传输实时视频。我使用了this示例。

我需要的东西 - 我无法获取框架(作为位图)。 是否有任何默认方法在DirectShow中获取当前帧?是否需要在我的用户控件中实现任何实现(如示例中所述的WebCamControl2)。我的需要有什么例子吗?

感谢。

更新

以下是我正在使用的代码:

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);
        }
    }
}

3 个答案:

答案 0 :(得分:1)

尝试使用EMGU或Opencv

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

这是一个如何从你的视频中捕获帧的示例(cam) http://www.emgu.com/wiki/index.php?title=Camera_Capture

1 - 将Emgu dll添加到您的应用程序中 2-这是一个例子

 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();

答案 1 :(得分:0)

你会发现一些解决上述问题的问题,例如

有两种解决方案。您可以从可视化视频的组件(视频渲染器)中取回,或者将Sample Grabber或类似的过滤器添加到管道中并从其回调中获取。您可以使用两种方式获取数据,从而初始化.NET位图,但不完全是位图。也就是说,您要执行转换步骤(inspiring Q on this)。

你当然可以使用CV库,但这对于这样简单的任务来说是极大的过度杀伤,如果你需要添加别的东西,他们的视频捕获可能看起来不灵活。

答案 2 :(得分:0)

我通过截取屏幕截图而不是从渲染视频中取帧来克服这个问题。这是帮助我的代码

//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);

我知道这不是正确的方法。但是,它为我提供了解决方案。我尝试使用SampleGrabberEMGU,但我无法按预期获得解决方案。我觉得我的解决方案足够简单,可以满足我的需求并可以帮助别人。

感谢。