DirectShow视频质量问题

时间:2012-04-14 00:12:55

标签: .net video directshow

使用directshow视频控件显示来自摄像头的实时视频,一切正常,但运动失真非常严重。它会在视频的边缘创建像锯齿一样的锯齿。从usb改为PCI采集卡,没有任何改进。相机供应商的应用程序显示漂亮的全动态视频,所以似乎h / w是好的。有什么想法吗?

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

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

        private static int WM_GRAPHNOTIFY = Convert.ToInt32("0X8000", 16) + 1;

        private PlayState _currentState = PlayState.Stopped;
        private IVideoWindow _videoWindow = null;
        private IMediaControl _mediaControl = null;
        private IMediaEventEx _mediaEventEx = null;
        private IGraphBuilder _graphBuilder = null;
        private ICaptureGraphBuilder2 _captureGraphBuilder = null;
        private float _imageAspectRatio = 0f;
        private System.Windows.Forms.Label labelError;

        public PlayState State
        {
            get { return _currentState; }
        }

        public VideoDisplayControl()
        {
            components = new System.ComponentModel.Container();

            this.labelError = new System.Windows.Forms.Label();
            this.SuspendLayout();

            // Error label
            this.labelError.BorderStyle = System.Windows.Forms.BorderStyle.None;
            this.labelError.ForeColor = Color.DimGray;
            this.labelError.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.labelError.Location = new System.Drawing.Point(0, 0);
            this.labelError.Name = "labelError";
            this.labelError.Size = new System.Drawing.Size(50, 50);
            this.labelError.TabIndex = 1;
            this.labelError.Text = "Video Source Not Available";
            this.labelError.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
            this.labelError.Visible = false;

            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
//            System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(VideoDisplayControl));
            this.Controls.Add(this.labelError);
            this.ResumeLayout(false);
            this.PerformLayout();

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

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

        private void VideoDisplayControl_Resize(object sender, System.EventArgs e)
        {
            if (this._videoWindow == null)
                return;

            // Attempt to keep aspect ratio of source
            if (_imageAspectRatio > 0)
            {
                int width = Math.Min(this.Width, (int)(this.ClientSize.Height * _imageAspectRatio));
                int height = Math.Min(this.ClientSize.Height, (int)(this.Width / _imageAspectRatio));
                int posX = (this.Width - width) / 2;   // Center within control
                int posY = (this.ClientSize.Height - height) / 2;
                this._videoWindow.SetWindowPosition(posX, posY, width, height);
            }
            else // resize to size of control
                this._videoWindow.SetWindowPosition(0, 0, this.Width, this.ClientSize.Height);

            this.labelError.Size = new System.Drawing.Size(this.Width, this.ClientSize.Height);
        }

        private void CaptureVideo()
        {
            int hr = 0;
            IBaseFilter sourceFilter = null;
            try
            {
                GetInterfaces(); // Interface with DirectShow

                hr = _captureGraphBuilder.SetFiltergraph(_graphBuilder);
                DsError.ThrowExceptionForHR(hr);

                sourceFilter = FindCaptureDevice();

                hr = _graphBuilder.AddFilter(sourceFilter, "VideoDisplay Filter");
                DsError.ThrowExceptionForHR(hr);

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

                Marshal.ReleaseComObject(sourceFilter);

                SetupVideoWindow();

                hr = _mediaControl.Run();
                DsError.ThrowExceptionForHR(hr);

                int width, height;
                hr = _videoWindow.GetMaxIdealImageSize(out width, out height);
                if ((hr == 0) && (width > 0) && (height > 0))
                {
                    _imageAspectRatio = (float)width / (float)height;
                    VideoDisplayControl_Resize(this, null);
                }

                _currentState = PlayState.Running;
            }
            catch (Exception)
            {
                _currentState = PlayState.Error;   // Set error state
                this.labelError.Visible = true;    // Display the "video not available" label
                VideoDisplayControl_Resize(this, null); // resize control to center label
            }
        }

        private IBaseFilter FindCaptureDevice()
        {
            IEnumMoniker classEnum = null;
            IMoniker[] moniker = new IMoniker[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.");
            }

            System.IntPtr none = System.IntPtr.Zero;

            if (classEnum.Next(moniker.Length, moniker, 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 GetInterfaces()
        {
            _graphBuilder = (IGraphBuilder)(new FilterGraph());
            _captureGraphBuilder = (ICaptureGraphBuilder2)(new CaptureGraphBuilder2());
            _mediaControl = (IMediaControl)_graphBuilder;
            _videoWindow = (IVideoWindow)_graphBuilder;
            _mediaEventEx = (IMediaEventEx)_graphBuilder;

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

            DsError.ThrowExceptionForHR(hr);
        }

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

            while (this._mediaEventEx != null && _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 = _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 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 = _videoWindow.put_Owner(this.Handle);
            DsError.ThrowExceptionForHR(hr);

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

            //Use helper function to position video window in client rect of main application window
            VideoDisplayControl_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 = _videoWindow.put_Visible(OABool.True);
            DsError.ThrowExceptionForHR(hr);
        }

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

1 个答案:

答案 0 :(得分:1)

这是隔行扫描的问题。相机正在输出隔行扫描视频,但是采集卡并没有对其进行反交错。我在vid cap卡驱动程序上启用了逐行扫描,一切都很顺利。这是由捕获卡Osprey(Viewcast)的供应商提出的。