5

I'm writing a Media Foundation app to acquire and display 1920x1080 YUV2 images from a UVC camera at 60Hz.

My problem is that the ReadSample() callback only gets called erratically at a very very low rate (1 FPS or so), in burst of a few frames.

This happens on two laptops, but no desktop I have tried so far. I'm running Windows 10 and all machines I ran tests on are up to date.

But, I have noticed that if I keep the CPU busy with my app, then the callback gets called at 60Hz, as expected.

EDIT

Note: The callback rate also increases when the CPU is busy due to the antivirus kicking-in. Although not to the full 60Hz.

So, if I change my message loop from:

while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

to:

   while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
      TranslateMessage(&Msg);
      DispatchMessage(&Msg);
   }

then the FPS goes back to 60Hz; but of course the CPU usage is nearly 100%...

Ditto, moving the mouse over the window with the 1st message loop causes the FPS to increase a bit (~10FPS).

Dropping the frame rate of the camera to 30Hz causes the ReadSample() callbacks to occur at 30Hz at it should.

I've reproduced the same problem with the MFCaptureD3D example from the examples provided by Microsoft ('Windows-classic-samples').

Note, I slightly modified the example to measure the frame rate in the ReadSample() callback.

On my laptop, the example measures about 25FPS (so a lot of frames dropped). This is because the color space conversion is CPU-based and very inefficient (75% of one core). But, it still manages 25PFS!

Commenting out the conversion (no other code change), causes the frame rate to drop to nearly zero!.. with 0% CPU usage. So the callbacks don't occur.

In both app, the COM library is initialized in the main thread of the app (running the message loop of the window) as follows:

hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

I copy the ReadSample() callback from Microsoft's example for convenience:

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;
}

EDIT

Window procedure shown below (simplified it as much as possible); the MF objects are created in 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);
}

Wondering if it's related to the COM threading model; I don't understand the note at the bottom of the page.

The multi-threaded apartment is intended for use by non-GUI threads. Threads in multi-threaded apartments should not perform UI actions. This is because UI threads require a message pump, and COM does not pump messages for threads in a multi-threaded apartment.

Does this mean I shouldn't create the COM objects in my UI thread?

I have tried creating them in another thread, with its own msg loop, but got exactly the same result.

EDIT Have run tests with VLC, which is based on DirectShow and seen none of those problems. It works fine under Win7 and FPS is 60Hz as expected. Have noticed that VLC drops the timer interrupt to 1ms (timeBeginPeriod()). Have tried doing the same in my, to no avail.

Running out of ideas here... looks like I may have to drop MF and write a DirectShow app if only to double check that it does work properly.

JPh
  • 536
  • 3
  • 20
  • Sounds like a problem with your WindowProc – mofo77 Mar 18 '19 at 18:06
  • WindowProc is as simple as it gets. I added the code for ref. – JPh Mar 19 '19 at 09:58
  • If you create COM object or call CoInitializeEx in the message pump -> HANDLE_MSG(hwnd, WM_CREATE, OnCreate), yes that's a bad idea. Outside is OK, like the MFCaptureD3D sample. – mofo77 Mar 19 '19 at 14:07
  • My mistake, I don't call CoInitializeEx () from the message loop. I do it at the being of WinMain() like the MFCaptureD3D example. But this is the main thread of the app and the thread that subsequently runs the message loop. – JPh Mar 19 '19 at 15:51
  • Both my app and MFCaptureD3D do not work with that particular camera under Win7; but they do work with other cameras (although running at lower res and 30Hz, not 60Hz). With this camera the first OnReadSample() callback has the HRESULT parameter set to 0x80070491. From what I have seen online, others have reported this error under Win7 and blamed the camera (although without explaining what is the problem exactly). However, on the Win10 machine where the app works the 2nd message I get is 0xc0ed, which I don't get on the laptop where it does not work. Can't find anywhere what this message is. – JPh Mar 19 '19 at 16:29
  • @JPh Have you checked dwStreamFlags value when OnReadSample fails (does it actually fail when HRESULT is 0xc0ed)? – VuVirt Mar 19 '19 at 17:01
  • dwStreamFlags == 1 (MF_SOURCE_READERF_ENDOFSTREAM) when hrStatus == 0x80070491. This only happens on Win7. – JPh Mar 20 '19 at 12:27
  • I read this question a few times, now and earlier, and I am afraid it does not have a good valid question in it. The apparently good and relevant suggestion coming from the described is given in the first comment. Instead of adding new statements which make it even more confusing, I would suggest that you focus on this: Win 7 + your camera + MFCaptureD3D (as opposed to your app). If this combination does not work well, which I believe comes from your comment above, then you should investigate what exactly breaks it (something is likely to be wrong in the camera's feed). – Roman R. Apr 01 '19 at 12:02
  • Try this : hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); Perhaps the source needs it with those two laptops. Not sure, off course, but easy to quickly test with MFCaptureD3D. – mofo77 Apr 09 '19 at 19:11
  • Thanks for the suggestion, but that's already the way I initialize the COM lib... :¬( – JPh Apr 11 '19 at 08:09

0 Answers0