将视觉C 媒体基础捕获应用程序转换为C 构建器

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

本文关键字:程序转换 应用 构建 视觉 媒体      更新时间:2023-10-16

我正在尝试将Microsoft" CaptureEngine视频捕获示例"从Visual C 转换为Embarcadero C 构建器。

https://code.msdn.microsoft.com/windowsdesktop/media-foundation-capture-78504c83

该代码可以通过Visual C 运行良好,但是我需要在C 构建器应用程序中包含。我基本上有代码工作,但是有几个问题我需要帮助。

我可以选择视频源,预览视频源,甚至开始捕获要文件。但是,视频捕获文件仅包含视频长度重复的一个帧,即使音频已正确记录。

我想知道这是否是由于事件无法正确处理。媒体基础捕获引擎的事件使用Windows消息传递给主线程,然后将Windows Mess传递调用媒体引擎事件处理程序。但是,我注意到事件处理程序可以停止录制并停止预览,以便等待结果

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 构建器事件处理程序触发的,该事件处理程序是同一主线程的一部分,该主题正在等待该事件的处理,因此在试图停止视频录制时,我会得到线程锁定。如果我发表评论,我不会锁定,但也没有有效的录制视频文件。

我不确定视觉C 如何将事件与捕获引擎代码区分开,关于我如何为C 构建器做到这一点的建议?

捕获引擎事件回调在工作线程上调用,它不是"同一主线程的一部分"。

// 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);

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

如果这不是您在应用程序中看到的内容,我建议您在第一行回调功能上设置一个断点,以确保您收到通知。如果您收到,则可以制定代码并确保到达事件设置行。如果您没有收到,其他事情正在阻止,您必须弄清楚,例如,通过闯入并检查应用程序的线程状态。

我找到了问题的原因。捕获引擎示例中的单个例程绝对是在其自己的线程中。问题在于,它然后将消息发布到主应用程序线程,而不是将其本身处理。这意味着主线在静音上等待时被冷冻。

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