我正在编写Media Foundation应用程序,以从60Hz的UVC摄像机获取并显示1920x1080 YUV2图像。
我的问题是ReadSample()回调仅以非常低的速率(大约1 FPS)被不稳定地调用,只有几帧。
这在两台笔记本电脑上发生,但到目前为止我还没有尝试过使用台式机。 我正在运行Windows 10,并且在其上运行测试的所有计算机都是最新的。
但是,我注意到,如果我让CPU忙于我的应用程序,则回调将以预期的60Hz频率被调用。
编辑
注:由于防病毒启动,当CPU繁忙时,回调率也会提高。虽然不能达到60Hz的全频率。
因此,如果我将消息循环从以下位置更改:
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
收件人:
while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
然后FPS回到60Hz;但是当然CPU使用率接近100%...
同上,在第一个消息循环中将鼠标移到窗口上方会使FPS增加一点(〜10FPS)。
将摄像头的帧速率降低到30Hz会导致ReadSample()回调以应有的30Hz发生。
我已经从Microsoft提供的示例(“ Windows-classic-samples”)再现了MFCaptureD3D示例的相同问题。
注意,我对示例进行了一些修改,以测量ReadSample()回调中的帧速率。
在我的笔记本电脑上,该示例的速度约为25FPS(因此丢掉了很多帧)。这是因为色彩空间转换是基于CPU的并且效率很低(一个内核的75%)。但是,它仍然管理25PFS!
注释掉转换(没有其他代码更改),导致帧速率下降到几乎为零!.. CPU使用率为0%。因此不会发生回调。
在两个应用程序中,COM库都在应用程序的主线程中初始化(运行窗口的消息循环),如下所示:
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
为方便起见,我从Microsoft的示例中复制了ReadSample()回调:
HRESULT CPreview::OnReadSample(HRESULT hrStatus, DWORD /* dwStreamIndex */, DWORD /* dwStreamFlags */, LONGLONG /* llTimestamp */, IMFSample *pSample)
{
HRESULT hr = S_OK;
IMFMediaBuffer *pBuffer = NULL;
EnterCriticalSection(&m_critsec);
if (FAILED(hrStatus))
hr = hrStatus;
if (SUCCEEDED(hr)) {
if (pSample) {
// Get the video frame buffer from the sample.
hr = pSample->GetBufferByIndex(0, &pBuffer);
// Draw the frame.
if (SUCCEEDED(hr))
hr = m_draw.DrawFrame(pBuffer);
}
rate.Inc();
}
// Request the next frame.
if (SUCCEEDED(hr))
hr = m_pReader->ReadSample(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
NULL, // actual
NULL, // flags
NULL, // timestamp
NULL // sample
);
if (FAILED(hr))
NotifyError(hr);
SafeRelease(&pBuffer);
LeaveCriticalSection(&m_critsec);
return hr;
}
编辑
如下所示的窗口过程(尽可能简化); MF对象是在OnCreate()中创建的:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
想知道它是否与COM threading model有关;我不明白页面底部的注释。
多线程单元旨在供非GUI线程使用。 多线程单元中的线程不应执行UI操作。 这是因为UI线程需要消息泵,而COM则不需要 抽出多线程单元中线程的消息。
这是否意味着我不应该在UI线程中创建COM对象?
我尝试在具有自己的msg循环的另一个线程中创建它们,但得到的结果完全相同。
编辑 使用基于DirectShow的VLC进行了测试,没有发现任何问题。在Win7下工作正常,FPS预期为60Hz。 注意,VLC将计时器中断降至1ms(timeBeginPeriod())。尝试对我做同样的事情,无济于事。
在这里用尽所有想法……如果只是仔细检查它是否可以正常工作,我可能不得不放弃MF并编写DirectShow应用。