将 Visual C++ Media Foundation Capture 应用程序转换为 C++ Builder

Converting Visual C++ Media Foundation Capture application to C++ Builder

我正在尝试将 Microsoft "CaptureEngine video capture sample" 代码从 Visual C++ 转换为 Embarcadero C++ Builder。

https://code.msdn.microsoft.com/windowsdesktop/Media-Foundation-Capture-78504c83

代码在 Visual C++ 中运行良好,但我需要包含在 C++ Builder 应用程序中。我的代码基本上可以正常工作,但有几个问题我需要帮助。

我可以 select 视频源、预览视频源甚至开始捕捉到文件。然而,视频捕获文件仅包含视频长度重复的一帧,即使音频已正确录制。

我想知道这是否是由于事件处理不当造成的。 来自媒体基础捕获引擎的事件使用 windows 消息传递到主线程,然后调用媒体引擎事件处理程序。但是我注意到事件处理程序停止录制并停止预览使用 wait for result

void WaitForResult()
{
    WaitForSingleObject(m_hEvent, INFINITE);
}

HRESULT CaptureManager::StopPreview()
{
  HRESULT hr = S_OK;

  if (m_pEngine == NULL)
  {
    return MF_E_NOT_INITIALIZED;
  }

  if (!m_bPreviewing)
  {
    return S_OK;
  }
  hr = m_pEngine->StopPreview();
  if (FAILED(hr))
  {
    goto done;
  }
  WaitForResult();

  if (m_fPowerRequestSet && m_hpwrRequest != INVALID_HANDLE_VALUE)
  {
    PowerClearRequest(m_hpwrRequest, PowerRequestExecutionRequired);
    m_fPowerRequestSet = false;
  }
  done:
    return hr;
}

问题是,这个 m_hEvent 是从 C++ Builder 事件处理程序触发的,它是等待事件处理的同一主线程的一部分,所以我在尝试时获得了线程锁停止视频录制。如果我注释掉该行,我不会锁定,但我也不会获得有效的录制视频文件。

我不确定 Visual C++ 如何将事件与捕获引擎代码分开,关于我如何为 C++ Builder 执行此操作有什么建议吗?

捕获引擎事件回调是在工作线程上调用的,而不是 "a part of same main thread"。

// Callback method to receive events from the capture engine.
STDMETHODIMP CaptureManager::CaptureEngineCB::OnEvent( _In_ IMFMediaEvent* pEvent)
{
...
            if (guidType == MF_CAPTURE_ENGINE_PREVIEW_STOPPED)
            {
                m_pManager->OnPreviewStopped(hrStatus);
                SetEvent(m_pManager->m_hEvent);

这实质上改变了应用程序的行为。控制线程停止预览并阻塞,直到工作线程发送一个通知来设置我上面引用的事件。从那里控制线程从等待操作中唤醒并继续预览停止。

如果这不是您在应用程序中看到的,我建议您在回调函数的第一行设置一个断点,以确保您收到通知。如果收到,您可以单步执行代码并确保到达事件设置行。如果您没有收到,则说明有其他事情正在阻塞,您将不得不解决这个问题,例如,通过中断和检查应用程序的线程状态。

我找到问题的原因了。 Capture 引擎示例中的 OnEvent 例程肯定在其自己的线程中。问题是它随后将消息发布到主应用程序线程,而不是自己处理它。这意味着主线程在等待互斥锁时被冻结。

// Callback method to receive events from the capture engine.
STDMETHODIMP CaptureManager::CaptureEngineCB::OnEvent( _In_ IMFMediaEvent* pEvent)
{
    // Post a message to the application window, so the event is handled
    // on the application's main thread.

    if (m_fSleeping && m_pManager != NULL)
    {
        // We're about to fall asleep, that means we've just asked the CE to stop the preview
        // and record.  We need to handle it here since our message pump may be gone.
        GUID    guidType;
        HRESULT hrStatus;
        HRESULT hr = pEvent->GetStatus(&hrStatus);
        if (FAILED(hr))
        {
            hrStatus = hr;
        }

        hr = pEvent->GetExtendedType(&guidType);
        if (SUCCEEDED(hr))
        {
            if (guidType == MF_CAPTURE_ENGINE_PREVIEW_STOPPED)
            {
                m_pManager->OnPreviewStopped(hrStatus);
                SetEvent(m_pManager->m_hEvent);
            }
            else if (guidType == MF_CAPTURE_ENGINE_RECORD_STOPPED)
            {
                m_pManager->OnRecordStopped(hrStatus);
                SetEvent(m_pManager->m_hEvent);
            }
            else
            {
                // This is an event we don't know about, we don't really care and there's
                // no clean way to report the error so just set the event and fall through.
                SetEvent(m_pManager->m_hEvent);
            }
        }

        return S_OK;
    }
    else
    {
        pEvent->AddRef();  // The application will release the pointer when it handles the message.
        PostMessage(m_hwnd, WM_APP_CAPTURE_EVENT, (WPARAM)pEvent, 0L);
    }

    return S_OK;
}