5

我正在编写一个媒体基础应用程序,以从 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 应用程序,如果只是为了仔细检查它是否正常工作。

4

0 回答 0