我有一个使用DirectShow.NET的摄像头控件,我创建了一个自定义控件来显示视频并从摄像头中获取图像。我在另一个WPF窗口中使用这个自定义控件。我在自定义控件中有一个函数public Bitmap CaptureImage(),可以抽象出一点DirectShow编程,只返回一个Bitmap。由于图像相对较大(1920x1080),IVMRWindowlessControl9的IVMRWindowlessControl9函数需要很长时间才能处理(2-3秒)。我已经完成了我的代码,并且可以确认这个调用是唯一需要很长时间处理的调用。
正因为如此,我的主WPF窗口中的GUI线程挂起,导致它在几秒钟内没有响应,所以如果我想在捕获图像时显示一个进度旋转器,它将保持冻结状态。
以下是CaptureImage()的代码
public Bitmap CaptureImage()
{
if (!IsCapturing)
return null;
this.mediaControl.Stop();
IntPtr currentImage = IntPtr.Zero;
Bitmap bmp = null;
try
{
int hr = this.windowlessControl.GetCurrentImage(out currentImage);
DsError.ThrowExceptionForHR(hr);
if (currentImage != IntPtr.Zero)
{
BitmapInfoHeader bih = new BitmapInfoHeader();
Marshal.PtrToStructure(currentImage, bih);
...
// Irrelevant code removed
...
bmp = new Bitmap(bih.Width, bih.Height, stride, pixelFormat, new IntPtr(currentImage.ToInt64() + Marshal.SizeOf(bih)));
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
}
}
catch (Exception ex)
{
MessageBox.Show("Failed to capture image:" + ex.Message);
}
finally
{
Marshal.FreeCoTaskMem(currentImage);
}
return bmp;
}为了解决这个问题,我尝试将其作为后台任务运行,如下所示:
public async void CaptureImageAsync()
{
try
{
await Task.Run(() =>
{
CaptureImage();
});
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}我尝试过多种方法来做到这一点,包括使用BackgroundWorker,但似乎每当我异步调用该调用时,它都会创建此错误:
无法将类型为“DirectShowLib.VideoMixingRenderer9”的COM对象转换为接口类型“DirectShowLib.IVMRWindowless cast 9”。此操作失败,因为对IID '{8F537D09-F85E-4414-B23B-502E54C79927}‘接口的COM组件的QueryInterface调用失败:不支持此类接口( HRESULT: 0x80004002 (E_NOINTERFACE)例外)。
此错误总是发生在以下情况:
int hr = this.windowlessControl.GetCurrentImage(out currentImage);同步调用CaptureImage()将产生正常结果。图像被捕获,一切都按预期工作。但是,切换到使用任何类型的异步功能都会导致错误。
发布于 2016-10-05 20:39:55
你在这里有两个问题。首先,API最初的缓慢性是通过设计的行为。MSDN 提到这个为:
然而,频繁调用此方法会降低视频播放性能。
视频内存的读取速度可能很慢--这是2-3秒的处理问题,而不是图像本身的分辨率问题。坏消息是,即使是来自后台线程的轮询快照也很可能会影响可视流。
这个方法过去和现在都是用来拍摄零星的快照的,尤指。由用户交互发起的,而不是自动化的。需要更密集和自动化的应用程序,以及那些不影响可视化提要快照的应用程序,应该在将提要发送到视频内存之前拦截它(有选项可供选择,最流行但笨拙的是使用示例Grabber)。
其次,您可能会碰到.NET线程问题在这个问题中描述,这会触发上述异常。在本机代码开发中很容易使用相同的接口指针,方法是偷偷摸摸地违反COM线程规则,在公寓间传递接口指针。由于CLR在代码和COM对象之间添加了一个中间层以进行额外的安全检查,因此您不能再使用后台线程中的COM对象/接口操作,因为COM线程规则是强制执行的。
我认为您必须继续忍受与直接API调用相关的长期冻结,或者添加有助于绕过冻结的本机代码开发(例如,帮助过滤器在发送到视频内存之前捕获帧,同时为.NET调用方实现助手功能以支持后台线程调用)。想必您也可以在帮助器MTA线程池中完成所有与MTA相关的工作,这些线程解决了后台线程调用者的问题,但在这种情况下,您很可能需要将这段代码从UI线程(也就是STA )中移出--我不认为这是人们经常做的事情。
https://stackoverflow.com/questions/39882689
复制相似问题