我使用DirectShow.NET进行网络摄像头控制。我创建了一个自定义控件来显示视频并从网络摄像头捕获图像。我在另一个WPF窗口中使用该自定义控件。我在自定义控件中有一个函数public Bitmap CaptureImage()
来抽象出一点DirectShow编程并简单地返回一个Bitmap
。由于图像相对较大(1920x1080),GetCurrentImage()
的{{1}}函数需要相当长的时间来处理(2-3秒)。我已经完成了我的代码,并且可以确认此调用是唯一一个需要很长时间才能处理的调用。
因此,我的主WPF窗口中的GUI线程挂起,导致它几秒钟没有响应,因此如果我想在捕获图像时显示进度微调器,它将保持冻结状态。
以下是IVMRWindowlessControl9
的代码:
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);
}
}
s,但似乎每次异步进行此调用时,都会产生此错误:
无法转换“DirectShowLib.VideoMixingRenderer9”类型的COM对象 接口类型'DirectShowLib.IVMRWindowlessControl9'。这个 操作失败,因为QueryInterface调用COM组件 用于IID'{8F537D09-F85E-4414-B23B-502E54C79927}的接口 由于以下错误而失败:不支持此类接口 (HRESULT异常:0x80004002(E_NOINTERFACE))。
在这一行上总是会发生错误:
BackgroundWorker
同步调用int hr = this.windowlessControl.GetCurrentImage(out currentImage);
会产生正常结果。捕获图像,一切都按预期工作。但是,切换到使用任何类型的异步功能都会产生错误。
答案 0 :(得分:2)
这里有两个问题。首先,API的原始缓慢是设计行为。 MSDN mentions this为:
但是,频繁调用此方法会降低视频播放性能。
回读的视频内存可能相当慢 - 这是2-3秒处理问题,而不是图像本身的分辨率。坏消息是,即使从后台线程轮询快照,也很可能会影响可视流。
此方法过去是用于拍摄偶发快照,特别是。由用户以交互方式启动的,而非自动化的。需要更加密集和自动化的应用程序以及那些不影响可视进给快照的应用程序应该在将数据发送到视频内存之前拦截它(有选项可以执行此操作,而最流行但笨拙的是使用Sample Grabber)。
其次,您可能会遇到.NET线程问题described in this question,这会触发上述异常。通过偷偷摸摸违反COM线程规则并在公寓之间传递接口指针,很容易在本机代码开发中使用相同的接口指针。由于CLR在您的代码和COM对象之间添加了一个中间层,进行了额外的安全检查,因此您无法再使用后台线程中的COM对象/接口,因为强制执行COM线程规则。
我想你要么必须忍受与直接API调用相关的长时间冻结,要么添加有助于绕过冻结的本机代码开发(特别是,例如,在发送到视频内存之前捕获帧的辅助过滤器)同时为.NET调用程序实现帮助程序功能以支持后台线程调用)。据推测,您也可以在帮助MTA线程池中执行所有与DirectShow相关的东西,这些线程可以解决后台线程调用者问题,但在这种情况下,您很可能需要从您的UI线程(即STA)移动此代码 - 如果有的话,我认为人们经常不会这样做。