使用 EVR 直接通过媒体基础显示相机设备捕获的帧

Use EVR to display frames captured by camera device directly using Media Foundation

本文关键字:相机 显示 EVR 媒体 使用      更新时间:2023-10-16

我正在尝试编写一个小应用程序,将相机设备捕获的视频帧直接显示在窗口上。我使用的是"源读取器"+"接收器写入器"体系结构而不是"媒体会话",因为我必须直接处理那些捕获的样本。我已经成功创建了源读取器,但是当我尝试创建EVR来显示帧时,遇到了一些问题...以下是我的代码:

HRESULT CCapture::CreatePreviewEVR(HWND hWindow)
{
    // m_pPreviewSink, m_pPreviewStream, m_pPresentationClock, and m_pPresentationTimeSource
    // are all defined as Class members
    HRESULT hr = S_OK;
    DWORD sinkCharacteristics = NULL;
    IMFActivate *pPreviewSinkActive = NULL;
    IMFClockStateSink *pClockStateSink = NULL;
    hr = MFCreateVideoRendererActivate(hWindow, &pPreviewSinkActive);
    if(SUCCEEDED(hr))
    {
        hr = pPreviewSinkActive->ActivateObject(IID_PPV_ARGS(&m_pPreviewSink));
    }
    if(SUCCEEDED(hr))
    {
        hr = m_pPreviewSink->GetCharacteristics(&sinkCharacteristics); // sinkCharacteristics is 0x18
    }
    if(SUCCEEDED(hr))
    {
        hr = m_pPreviewSink->GetStreamSinkByIndex(0, &m_pPreviewStream);
    }
    if(SUCCEEDED(hr))
    {
        hr = MFCreatePresentationClock(&m_pPresentationClock);
    }
    if(SUCCEEDED(hr))
    {
        hr = MFCreateSystemTimeSource(&m_pPresentationTimeSource);
    }
    if(SUCCEEDED(hr))
    {
        hr = m_pPresentationTimeSource->QueryInterface(__uuidof(IMFClockStateSink),(void**)&pClockStateSink);
    }
    if(SUCCEEDED(hr))
    {
        hr = m_pPresentationClock->SetTimeSource(m_pPresentationTimeSource);
    }
    if(SUCCEEDED(hr))
    {
        hr = m_pPresentationClock->AddClockStateSink(pClockStateSink);
    }
    if(SUCCEEDED(hr))
    {
        hr = m_pPreviewSink->SetPresentationClock(m_pPresentationClock);
    }
    return hr;
}
HRESULT CCapture::ConfigurePreviewEVR()
{
    // This function is implemented trying to do EVR Media Type Negotiation
    DWORD mediaTypeCount = 0;
    HRESULT hr =S_OK;
    IMFMediaType *pSourceReaderType = NULL;
    IMFMediaType *pSourceReaderTypeValid = NULL;
    IMFMediaType *pPreviewSinkMediaType = NULL;
    IMFMediaTypeHandler  *pPreviewSinkMediaTypeHandler = NULL;
    hr = m_pReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, &pSourceReaderType);
    if(SUCCEEDED(hr))
    {
        hr = MFCreateMediaType(&pPreviewSinkMediaType);
    }
    if(SUCCEEDED(hr))
    {
        hr = pPreviewSinkMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    }
    if(SUCCEEDED(hr))
    {
        hr = pPreviewSinkMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
    }
    if(SUCCEEDED(hr))
    {
        hr = pPreviewSinkMediaType->SetUINT32(MF_MT_AVG_BITRATE, 14000000);
    }
    if(SUCCEEDED(hr))
    {
        hr = CopyAttribute(pSourceReaderType, pPreviewSinkMediaType, MF_MT_FRAME_SIZE);
    }
    if(SUCCEEDED(hr))
    {
        hr = CopyAttribute(pSourceReaderType, pPreviewSinkMediaType, MF_MT_FRAME_RATE);
    }
    if(SUCCEEDED(hr))
    {
        hr = CopyAttribute(pSourceReaderType, pPreviewSinkMediaType, MF_MT_PIXEL_ASPECT_RATIO);
    }
    if(SUCCEEDED(hr))
    {
        hr = CopyAttribute(pSourceReaderType, pPreviewSinkMediaType, MF_MT_INTERLACE_MODE);
    }
    if(SUCCEEDED(hr))
    {
        hr = m_pPreviewStream->GetMediaTypeHandler(&pPreviewSinkMediaTypeHandler);
    }
    if(SUCCEEDED(hr))
    {
        hr = pPreviewSinkMediaTypeHandler->GetMediaTypeCount(&mediaTypeCount); // derived mediaTypeCount is 0
    }
    if(SUCCEEDED(hr))
    {
        hr = pPreviewSinkMediaTypeHandler->IsMediaTypeSupported(pSourceReaderType, &pSourceReaderTypeValid); // Failed to get supported Media Type
    }
    if(SUCCEEDED(hr))
    {
        hr = pPreviewSinkMediaTypeHandler->SetCurrentMediaType(pSourceReaderTypeValid); // This function still fails if I use pSourceReaderType directly 
    }
    if(SUCCEEDED(hr))
    {
        SafeRelease(&pSourceReaderType);
        SafeRelease(&pPreviewSinkMediaType);
        SafeRelease(&pPreviewSinkMediaTypeHandler);
    }
    return hr;
}

函数"CopyAttribute"来自MFCaptureToFile示例代码,它可以正常工作。由于变量"sinkFeatures"给出了0x18,似乎我应该能够将我希望使用的媒体类型的新流添加到媒体接收器;但是我尝试了AddStreamSink函数,但它直接返回了错误。"CCapture"类继承自IMFSourceReaderCallBack,如果我的理解是正确的,我应该在OnReadSample回调函数中使用m_pPreviewStream->ProcessSample(pSample)。如果有人能在这方面提供帮助,非常感谢!此致敬意

似乎很混乱。

当您希望 EVR 显示来自多个源的帧时,您必须使用 AddStreamSink。

阅读您的帖子时,您希望仅显示来自相机的帧(一个来源)。

因此,您不需要使用 AddStreamSink。

而且,您会收到来自 AddStreamSink 的错误,因为在将流添加到 EVR 时要使用的媒体类型存在一些限制(这取决于您的 GPU 特征)。