I have a DLL that was originally written for Windows Forms. It creates 2 window forms one for Viewing a Live Video Feed from a BlackMagics Card, and one window for Static Images captured from Video.
The DLL is all Static Classes, called by including the Reference to the dll (VideoCapture.dll) and in the program I Start the Capture Screen by calling
VideoCapture.RSVideoControler.Start();
In a Windows Form Test Program I've got a Button that calls this command only. the video window opens , and leaves the Main Window free to function normally. CPU Usage is about (Task Manager 50) and displays the Video feed as expected. Closing the Main Program shuts everything down smoothly.
Now the Same DLL & Code to start, but put in a WPF Application. Before Pressing the Button the app is running at 0 CPU (Task manager) and you can move the window normally. But after running the Start Command the app runs at 99 CPU (Task Manager) and becomes unresponsive.
What is WPF Doing in starting this thread differently than Windows Forms ?
ETA: RSVidoControler
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using VideoCapture.Classes;
namespace VideoCapture
{
public class RSVideoControler
{
public static Bitmap LastImage = new Bitmap(1920, 1080, PixelFormat.Format16bppRgb565);
private static VidCardGlobal VideoInterface;
private static IntPtr m_ip = IntPtr.Zero;
//[STAThread]
public static void Start() {VideoInterface = new VidCardGlobal();}
public static Bitmap GrabFrame()
{
if (m_ip != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(m_ip);
m_ip = IntPtr.Zero;
}
m_ip = VideoInterface.Click();
using (Bitmap b = new Bitmap(VideoInterface.Width, VideoInterface.Height, VideoInterface.Stride, PixelFormat.Format16bppRgb565, m_ip))
{
if (b != null)
{
b.RotateFlip(RotateFlipType.RotateNoneFlipY);
LastImage.Dispose();
LastImage = new Bitmap(b);
b.Dispose();
}
}
return LastImage;
}
public static void VideoVisable(bool Visibility)
{
VidCardGlobal.VideoViewer.Visible = Visibility;
}
public static void ShutDownVideo()
{
VideoInterface.Dispose(); // Shuts down Image Capturing interface
VidCardGlobal.MediaControl.Stop(); // Shuts down Video Viewing Screen
}
}
}
VidCardGlobal
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Threading;
using System.Windows.Forms;
using DirectShowLib;
using VideoCapture.Forms;
namespace VideoCapture.Classes
{
internal class VidCardGlobal : ISampleGrabberCB, IDisposable
{
//private object syncLock = new object();
#region Varables for SingleFrameCapture
private ManualResetEvent m_PictureReady = null;
private bool m_WantOne = false;
private int m_videoWidth;
private int m_videoHeight;
private int m_stride;
private IntPtr m_ipBuffer = IntPtr.Zero;
private IAMVideoControl m_VidControl = null;
private IPin m_pinStill = null;
#endregion
public int Width { get { return m_videoWidth; } }
public int Height { get { return m_videoHeight; } }
public int Stride { get { return m_stride; } }
public static VideoFeed VideoViewer = new VideoFeed();
#region DirectShow Stuff
public MemoryStream stream { get; private set; }
public static IGraphBuilder graph;
public static IVMRWindowlessControl9 WindowlessControl = null;
public static IBaseFilter pinfilter;
public static bool VideoHandlersAdded = false;
public static IMediaControl MediaControl;
public static ISampleGrabber SampleGrabberFilter;
//public static AMMediaType DefaultMediaType = new AMMediaType();
//private static IMediaControl mediaControl = null;
public int SampleCB(double Unknown1, IMediaSample MySample) { return 0; }
public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
{
Debug.Assert(BufferLen == Math.Abs(m_stride) * m_videoHeight, "Incorrect buffer length");
if (m_WantOne)
{
m_WantOne = false;
Debug.Assert(m_ipBuffer != IntPtr.Zero, "Unitialized buffer");
CopyMemory(m_ipBuffer, pBuffer, BufferLen);
m_PictureReady.Set();
}
return 0;
}
#region IDisposable
public void Dispose(bool disposing)
{
if (disposing)
{
//if (stream != null)
{
//stream.Dispose();
//stream = null;
}
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
public static IBaseFilter CreateFilterbyName(string filterName, Guid category)
{
int hr = 0;
DsDevice[] Devices = DsDevice.GetDevicesOfCat(category);
foreach (DsDevice dev in Devices)
if (dev.Name == filterName)
{
IBaseFilter filter = null;
IBindCtx bindCtx = null;
try
{
hr = CreateBindCtx(0, out bindCtx);
DsError.ThrowExceptionForHR(hr);
Guid guid = typeof(IBaseFilter).GUID;
object obj;
dev.Mon.BindToObject(bindCtx, null, ref guid, out obj);
filter = (IBaseFilter)obj;
}
finally
{
if (bindCtx != null) Marshal.ReleaseComObject(bindCtx);
}
return filter;
}
return null;
}
[DllImport("ole32.dll")]
public static extern int CreateBindCtx(int reserved, out IBindCtx ppcb);
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory")]
private static extern void CopyMemory(IntPtr Destination, IntPtr Source, [MarshalAs(UnmanagedType.U4)] int Length);
private static IPin GetPin(IBaseFilter filter, string pinname)
{
IEnumPins epins;
bool FoundPin = false;
IPin ValidPin = null;
int hr = filter.EnumPins(out epins);
checkHR(hr, "Can't enumerate pins");
IntPtr Fetched = Marshal.AllocCoTaskMem(4);
IPin[] Pins = new IPin[1];
while (epins.Next(1, Pins, Fetched) == 0)
{
PinInfo pinfo;
Pins[0].QueryPinInfo(out pinfo);
pinfilter = pinfo.filter;
bool found = (pinfo.name == pinname);
DsUtils.FreePinInfo(pinfo);
if (found)
{
ValidPin = Pins[0];
FoundPin = true;
}
//return Pins[0];
}
if (!FoundPin) checkHR(-1, "Pin not found");
return ValidPin;
}
private static IPin GetVPin(IBaseFilter filter, string pinname)
{
IEnumPins epins;
bool FoundPin = false;
IPin ValidPin = null;
int hr = filter.EnumPins(out epins);
checkHR(hr, "Can't enumerate pins");
IntPtr Fetched = Marshal.AllocCoTaskMem(4);
IPin[] Pins = new IPin[1];
while (epins.Next(1, Pins, Fetched) == 0)
{
PinInfo pinfo;
Pins[0].QueryPinInfo(out pinfo);
pinfilter = pinfo.filter;
bool found = (pinfo.name == pinname);
DsUtils.FreePinInfo(pinfo);
if (found)
{
ValidPin = Pins[0];
FoundPin = true;
}
//return Pins[0];
}
if (!FoundPin) checkHR(-1, "Pin not found");
return ValidPin;
}
private static IPin FindPinIterface(IBaseFilter filter, Guid pFormat, PinDirection PinDir, IIPDVDec riid)
{
IPin FoundPin = null;
//hr = GetPin(pFilter, pFormat, PinDir, &FoundPin);
return FoundPin;
}
#endregion
public VidCardGlobal()
{
graph = (IGraphBuilder)new FilterGraph();
SampleGrabberFilter = (ISampleGrabber)new SampleGrabber();
MediaControl = (IMediaControl)graph;
BuildGraph(graph); //Builds Graph And Connects camera, ect to graph
m_PictureReady = new ManualResetEvent(false);
MediaControl.Run();
VideoViewer.VideoFeed_ResizeMove(null, null);
}
~VidCardGlobal()
{
Dispose();
}
private static void checkHR(int HR, string ErrorMessage)
{
if (HR < 0)
{
MessageBox.Show(ErrorMessage, "CheckER Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
try
{
DsError.ThrowExceptionForHR(HR);
}
catch (Exception e)
{
MessageBox.Show(e.Message);
//DsError.ThrowExceptionForHR(HR);
}
}
}
public void BuildGraph(IGraphBuilder pGraph)
{
int hr = 0;
int DVC = 0;
int DVCi = 0;
#region Find Video Capture Card
DsDevice[] CardFinder = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);
foreach (DsDevice Check in CardFinder)
{
if (Check.Name == "Decklink Video Capture") DVC = DVCi;
DVCi++;
}
// Magics Card should be CardFinder[0]
// graph builder
ICaptureGraphBuilder2 pBuilder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
hr = pBuilder.SetFiltergraph(pGraph);
checkHR(hr, "Can't SetFiltergraph");
#endregion
#region Create Connection to Capture Card
//Guid HDYC = new Guid(0x43594448, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
IBaseFilter pDecklinkVideoCature;// = CreateFilterbyName(@"Decklink Video Capture", CLSID_VideoCaptureSource);
IFilterGraph2 TempFilter = (IFilterGraph2)new FilterGraph();
hr = TempFilter.AddSourceFilterForMoniker(CardFinder[DVC].Mon, null, CardFinder[DVC].Name, out pDecklinkVideoCature);
checkHR(hr, "Can't Find Camera");
hr = pGraph.AddFilter(pDecklinkVideoCature, "Decklink Video Capture");
checkHR(hr, "Can't add Decklink video Capture to graph");
// Configure pDecklinkVideoCature for 1080i @ 59.94fps somehow
IPin VideoOutPin = GetVPin(pDecklinkVideoCature, "Capture");
//AMMediaType VidMed = new AMMediaType();
//VidMed.majorType = MediaType.Video;
//VidMed.formatType = FormatType.VideoInfo;
//VidMed.subType = MediaSubType.RGB24;
//hr = VideoOutPin.ConnectionMediaType(VidMed);
IAMStreamConfig streamConfig = (IAMStreamConfig)VideoOutPin;
AMMediaType searchmedia;
AMMediaType CorectvidFormat = new AMMediaType();
IntPtr ptr;
int piCount, piSize;
hr = streamConfig.GetNumberOfCapabilities(out piCount, out piSize);
ptr = Marshal.AllocCoTaskMem(piSize);
for (int i = 0; i < piCount; i++)
{
hr = streamConfig.GetStreamCaps(i, out searchmedia, ptr);
VideoInfoHeader v = new VideoInfoHeader();
Marshal.PtrToStructure(searchmedia.formatPtr, v);
VideoInfoHeader TestInfoHeader = (VideoInfoHeader)Marshal.PtrToStructure(searchmedia.formatPtr, typeof(VideoInfoHeader));
int TestWidth = TestInfoHeader.BmiHeader.Width;
int TestHeight = TestInfoHeader.BmiHeader.Height;
int TestCompression = TestInfoHeader.BmiHeader.Compression;
long TestTime = TestInfoHeader.AvgTimePerFrame;
int TestError = TestInfoHeader.BitErrorRate;
if ((TestWidth == 1920) & (TestHeight == 1080) & (TestTime == 333667))
{
CorectvidFormat = searchmedia;
}
}
VideoInfoHeader videoInfoHeader = (VideoInfoHeader)Marshal.PtrToStructure(CorectvidFormat.formatPtr, typeof(VideoInfoHeader));
m_videoWidth = videoInfoHeader.BmiHeader.Width;
m_videoHeight = videoInfoHeader.BmiHeader.Height;
m_stride = m_videoWidth * (videoInfoHeader.BmiHeader.BitCount / 8);
hr = streamConfig.SetFormat(CorectvidFormat);
checkHR(hr, "Can't Get Pin Query");
#endregion
#region Create Color Space Converter filter for Sample Grabber (I Hope)
IBaseFilter pAVIDecompressor2 = (IBaseFilter)new AVIDec();
hr = pGraph.AddFilter(pAVIDecompressor2, "AVI Decompressor");
checkHR(hr, "Can't add AVI Decompressor to graph");
// No Configuration needed for the AVI Decompresser that I know of
IPin AVIDecompressor2OutPin = GetPin(pAVIDecompressor2, "XForm Out");
IPin AVIDecompressor2InPin = GetPin(pAVIDecompressor2, "XForm In");
//IBaseFilter ColorSpaceConverterFilter = new IBaseFilter;
//hr = pGraph.AddFilter(ColorSpaceConverterFilter, "Color Space Converter");
//checkHR(hr, "Can't Create Color Space Converter");
//IPin CSCFIn = GetPin(ColorSpaceConverterFilter, "Input0");
#endregion
#region Create Sample Grabber
AMMediaType SampleMediaType = new AMMediaType();
SampleMediaType.majorType = MediaType.Video;
SampleMediaType.subType = MediaSubType.RGB565;
//SampleMediaType.subType = MediaSubType.UYVY;
SampleMediaType.formatType = FormatType.VideoInfo;
//hr = SampleGrabberFilter.SetMediaType(CorectvidFormat);
hr = SampleGrabberFilter.SetMediaType(SampleMediaType);
checkHR(hr, "Can't Set Sample Grabber Media Type");
//ISampleGrabberCB pcallback;
SampleGrabberFilter.SetCallback(this, 1);
hr = SampleGrabberFilter.SetBufferSamples(false);
hr = SampleGrabberFilter.SetOneShot(false);
hr = pGraph.AddFilter((IBaseFilter)SampleGrabberFilter, "Sample Grabber");
checkHR(hr, "Can't Add Sample Grabber");
IPin SGFIn = GetPin((IBaseFilter)SampleGrabberFilter, "Input");
IPin SGFOut = GetPin((IBaseFilter)SampleGrabberFilter, "Output");
#endregion
#region Create Null Renderer
IBaseFilter VideoNullRenderer = (IBaseFilter)new NullRenderer();
hr = pGraph.AddFilter(VideoNullRenderer, "Null Renderer");
checkHR(hr, "Can't Create Null Renderer");
IPin NullIn = GetPin(VideoNullRenderer, "In");
#endregion
#region Create Infinite Pin Tree Filter
IBaseFilter InfinitePinTreeFilter = (IBaseFilter)new InfTee();
hr = pGraph.AddFilter(InfinitePinTreeFilter, "Infinite Pin Tee");
checkHR(hr, "Can't Add Pin Tee");
IPin IPTIn = GetPin(InfinitePinTreeFilter, "Input");
IPin IPTOut1 = GetPin(InfinitePinTreeFilter, "Output1");
#region Connect IPTOut -> Sample Grabber -> Null renderer
//hr = pGraph.ConnectDirect(VideoOutPin, IPTIn, CorectvidFormat);
hr = pGraph.Connect(VideoOutPin, IPTIn);
checkHR(hr, "Can't Connect Camera to Infinite Pin Tee");
//hr = pGraph.ConnectDirect(IPTOut1, AVIDecompressor2InPin, CorectvidFormat);
//checkHR(hr, "Can't IPT1 to AVIDecompressor 2");
//hr = pGraph.ConnectDirect(AVIDecompressor2OutPin, SGFIn, SampleMediaType);
//checkHR(hr, "Can't AVIDecompressor 2 to Sample In");
hr = pGraph.Connect(IPTOut1, SGFIn);
checkHR(hr, "Can't IPTOut1 to Sample In");
hr = pGraph.Connect(SGFOut, NullIn);
checkHR(hr, "Can't Sample Out To Null In");
#endregion
IPin IPTOut2 = GetPin(InfinitePinTreeFilter, "Output2");
#endregion
#region Add Video Mixing Renderer9
//IBaseFilter pVideoMixingRenderer9 = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_VideoMixingRenderer9));
IBaseFilter pVideoMixingRenderer9 = (IBaseFilter)new VideoMixingRenderer9();
// Setup Video Renderer to go to Windows Form at this Point??
#region Setup WindowlessMode
IVMRFilterConfig9 MixingRendererFilterConfig = (IVMRFilterConfig9)pVideoMixingRenderer9;
hr = MixingRendererFilterConfig.SetNumberOfStreams(1);
checkHR(hr, "Problem Setting Streams to 1");
hr = MixingRendererFilterConfig.SetRenderingMode(VMR9Mode.Windowless);
checkHR(hr, "Can't Set Mode to Windowed"); // Doesn't work Windowless?
WindowlessControl = (IVMRWindowlessControl9)pVideoMixingRenderer9;
hr = WindowlessControl.SetVideoClippingWindow(VideoViewer.VideoPanel.Handle);
checkHR(hr, "Problem Setting Clipping Window to VideoPanel");
hr = WindowlessControl.SetAspectRatioMode(VMR9AspectRatioMode.LetterBox);
checkHR(hr, "Problem Setting Aspect Ration");
VideoViewer.AddHandlers();
VideoViewer.Visible = true;
#endregion
hr = pGraph.AddFilter(pVideoMixingRenderer9, "Video Mixing Renderer 9");
checkHR(hr, "Can't Create Video Mixing Renderer 9");
IPin VideoFormPin = GetPin(pVideoMixingRenderer9, "VMR Input0");
#endregion
#region Create AVI Decompressor
IBaseFilter pAVIDecompressor = (IBaseFilter)new AVIDec();
AMMediaType AVIInMedia = new AMMediaType();
AMMediaType AVIOutMedia = new AMMediaType();
AVIOutMedia.majorType = MediaType.Video;
AVIOutMedia.subType = MediaSubType.UYVY;
hr = pGraph.AddFilter(pAVIDecompressor, "AVI Decompressor");
checkHR(hr, "Can't add AVI Decompressor to graph");
// No Configuration needed for the AVI Decompresser that I know of
IPin AVIDecompressorOutPin = GetPin(pAVIDecompressor, "XForm Out");
IPin AVIDecompressorInPin = GetPin(pAVIDecompressor, "XForm In");
#endregion
#region Connect Infinite Pin Out2 to Input of AVI Decompressor
//hr = pGraph.ConnectDirect(IPTOut2, AVIDecompressorInPin, null);
hr = pGraph.Connect(IPTOut2, AVIDecompressorInPin);
checkHR(hr, "Can't Connect Infinite Pin 2 to AVI Decompressor");
#endregion
#region Connect Output of AVI Decompressor to Video Mixing Renderer9
//hr = pGraph.ConnectDirect(AVIDecompressorOutPin, VideoFormPin, null);
hr = pGraph.Connect(AVIDecompressorOutPin, VideoFormPin);
checkHR(hr, "Can't Connect AVI Out to Renderer");
#endregion
DsUtils.FreeAMMediaType(SampleMediaType);
DsUtils.FreeAMMediaType(CorectvidFormat);
SampleMediaType = null;
CorectvidFormat = null;
VideoViewer.Activate();
}
public IntPtr Click()
{
int hr;
// get ready to wait for new image
m_PictureReady.Reset();
m_ipBuffer = Marshal.AllocCoTaskMem(Math.Abs(m_stride) * m_videoHeight);
try
{
m_WantOne = true;
// If we are using a still pin, ask for a picture
if (m_VidControl != null)
{
// Tell the camera to send an image
hr = m_VidControl.SetMode(m_pinStill, VideoControlFlags.Trigger);
checkHR(hr, "Can't SetMode");
DsError.ThrowExceptionForHR(hr);
}
// Start waiting
if (!m_PictureReady.WaitOne(9000, false))
{
throw new Exception("Timeout waiting to get picture");
}
}
catch
{
Marshal.FreeCoTaskMem(m_ipBuffer);
m_ipBuffer = IntPtr.Zero;
throw;
}
// Got one
return m_ipBuffer;
}
}
}
VideoFeed (Windows Form)
using System;
using System.Windows.Forms;
using DirectShowLib;
using Microsoft.Win32;
using VideoCapture.Classes;
namespace VideoCapture.Forms
{
public partial class VideoFeed : Form
{
public VideoFeed()
{
InitializeComponent();
this.Visible = true;
}
public void AddHandlers()
{
this.Paint += new PaintEventHandler(VideoFeed_Paint);
this.Resize += new EventHandler(VideoFeed_ResizeMove);
this.Move += new EventHandler(VideoFeed_ResizeMove);
SystemEvents.DisplaySettingsChanged += new EventHandler(SystemEvents_DisplaySettingsChanged);
VidCardGlobal.VideoHandlersAdded = true;
}
public void RemoveHandlers()
{
this.Paint -= new PaintEventHandler(VideoFeed_Paint);
this.Resize -= new EventHandler(VideoFeed_ResizeMove);
this.Move -= new EventHandler(VideoFeed_ResizeMove);
SystemEvents.DisplaySettingsChanged -= new EventHandler(SystemEvents_DisplaySettingsChanged);
VidCardGlobal.VideoHandlersAdded = false;
}
public void VideoFeed_ResizeMove(object sender, EventArgs e)
{
if (VidCardGlobal.WindowlessControl != null)
{
int hr = VidCardGlobal.WindowlessControl.SetVideoPosition(null, DsRect.FromRectangle(this.VideoPanel.ClientRectangle));
}
}
private void VideoFeed_Paint(object sender, PaintEventArgs e)
{
if (VidCardGlobal.WindowlessControl != null)
{
IntPtr hdc = e.Graphics.GetHdc();
int hr = VidCardGlobal.WindowlessControl.RepaintVideo(this.VideoPanel.Handle, hdc);
e.Graphics.ReleaseHdc(hdc);
}
}
private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
{
if (VidCardGlobal.WindowlessControl != null)
{
int hr = VidCardGlobal.WindowlessControl.DisplayModeChanged();
}
}
}
}
ETA: FilterGraph Definition
using System;
using System.Runtime.InteropServices;
namespace DirectShowLib
{
// CODE SNIPPED
#region COM Class Objects
/// <summary>
/// CLSID_FilterGraph
/// </summary>
[ComImport, Guid("e436ebb3-524f-11ce-9f53-0020af0ba770")]
public class FilterGraph
{
}
}