我正在编写一个媒体基础应用程序,以从 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线程模型有关;我不明白页面底部的注释。
多线程单元旨在供非 GUI 线程使用。多线程单元中的线程不应执行 UI 操作。这是因为 UI 线程需要消息泵,而 COM 不会为多线程单元中的线程泵送消息。
这是否意味着我不应该在我的 UI 线程中创建 COM 对象?
我尝试在另一个线程中创建它们,并使用它自己的 msg 循环,但得到的结果完全相同。
编辑 已经使用基于 DirectShow 的 VLC 运行测试并且没有看到这些问题。它在 Win7 下运行良好,FPS 达到预期的 60Hz。注意到 VLC 将定时器中断降至 1ms(timeBeginPeriod ())。试过在我身上做同样的事情,但无济于事。
这里的想法用完了......看起来我可能不得不放弃 MF 并编写一个 DirectShow 应用程序,如果只是为了仔细检查它是否正常工作。