Reputation: 1931
Using a directshow video control to display live video from a camera, it all works but has really bad distortion with motion. It creates sawtooth like jags at edges in the video. Changed from usb to PCI capture card, no improvement. The camera vendor's app shows beautiful full-motion video, so it seems the h/w is ok. Any ideas?
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);
}
}
}
Upvotes: 1
Views: 1150
Reputation: 1931
This turned out to be an issue with interlacing. The camera was outputting interlaced video, but the capture card was not de-interlacing it. I enabled de-interlacing on the vid cap card driver, and all is well. This was suggested by the vendor of the capture card, Osprey (Viewcast).
Upvotes: 1