我有一个最初为 Windows 窗体编写的 DLL。它创建了 2 个窗口形式,一个用于查看来自 BlackMagics 卡的实时视频源,一个用于从视频捕获的静态图像。
DLL 都是静态类,通过包含对 dll 的引用(VideoCapture.dll)来调用,并且在程序中我通过调用来启动捕获屏幕
VideoCapture.RSVideoControler.Start();
在 Windows 窗体测试程序中,我有一个仅调用此命令的按钮。视频窗口打开,并让主窗口正常运行。CPU 使用率约为(任务管理器 50)并按预期显示视频源。关闭主程序可以顺利关闭所有内容。
现在启动相同的 DLL 和代码,但放入 WPF 应用程序。在按下按钮之前,应用程序以 0 CPU(任务管理器)运行,您可以正常移动窗口。但在运行启动命令后,应用程序以 99 CPU(任务管理器)运行并且变得无响应。
WPF 在启动此线程时与 Windows 窗体有何不同?
预计到达时间: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
}
}
}
VidCard全球
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 窗体)
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 定义
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
{
}
}