首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >异步MFT未发送MFTransformHaveOutput事件(英特尔硬件MJPEG解码器MFT)

异步MFT未发送MFTransformHaveOutput事件(英特尔硬件MJPEG解码器MFT)
EN

Stack Overflow用户
提问于 2017-02-16 22:53:18
回答 2查看 866关注 0票数 1

我正在开发使用MediaFoundation SourceReader技术的USB摄像头流桌面应用程序。摄像头支持USB3.0,1080p MJPG视频格式分辨率为60fps。

我使用软件MJPEG解码器MFT转换MJPG到YUY2帧,然后转换成RGB32帧在窗口上绘制。当使用这个软件解码器时,我只能在窗口上渲染30fps,而不是60fps。我已经在这个网站上发布了一个问题,并得到了一些使用英特尔硬件MJPEG解码器MFT来解决帧丢失问题的建议。

为了使用这个硬件MJPEG解码器,我提出了异步多傅立叶变换处理模型,并通过IMFTransform接口为IMFMediaEventGenerator配置了一个异步回调。

在使用ProcessMessage方法调用MFT_MESSAGE_NOTIFY_START_OF_STREAM之后,我收到了两次MFTransfromNeedInput事件,但没有收到来自MFT的MFTransformHaveOutput事件。

我在这里分享了我的代码供你参考:

代码语言:javascript
复制
IMFTransform* m_pTransform = NULL;

HRESULT EnumDecoderMFT ()
{
    HRESULT hr;
    IMFActivate** ppActivate;   
    UINT32 numDecodersMJPG = 0;
    LPWSTR lpMFTName = 0;

    MFT_REGISTER_TYPE_INFO inputFilter = {MFMediaType_Video,MFVideoFormat_MJPG};
    MFT_REGISTER_TYPE_INFO outputFilter = {MFMediaType_Video,MFVideoFormat_YUY2};

    UINT32 unFlags = MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_SORTANDFILTER;

    hr = MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER, unFlags, &inputFilter, &outputFilter, &ppActivate, &numDecodersMJPG);
    if (FAILED(hr)) return hr; 

    hr = ppActivate[0]->GetAllocatedString(MFT_FRIENDLY_NAME_Attribute,&lpMFTName,0);
    if (FAILED(hr)) return hr;

    // Activate transform
    hr = ppActivate[0]->ActivateObject(__uuidof(IMFTransform), (void**)&m_pTransform);
    if (FAILED(hr)) return hr;

    hr = hr = m_pTransform->GetAttributes(&pAttributes);
    if (SUCCEEDED(hr))
    {
        hr = pAttributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);           
        if(FAILED(hr)) return hr;

        hr = pAttributes->SetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE,TRUE);        
        if(FAILED(hr)) return hr;

        hr = pAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE);
        if(FAILED(hr)) return hr;

        hr = m_pTransform->QueryInterface(IID_IMFMediaEventGenerator,(void**)&m_pEventGenerator);           
        if(FAILED(hr)) return hr;

        hr = m_pEventGenerator->BeginGetEvent((IMFAsyncCallback*)this,NULL);
        if(FAILED(hr)) return hr;

        pAttributes->Release();
    }

    SafeRelease(&ppActivate[0]);

    CoTaskMemFree(ppActivate);

    return hr;      
}

HRESULT Invoke(IMFAsyncResult *pResult)
{
    HRESULT hr = S_OK,hrStatus;
    MediaEventType meType = MEUnknown;  // Event type
    IMFMediaEvent *pEvent = NULL;

    // Get the event from the event queue.
    hr = m_pEventGenerator->EndGetEvent(pResult, &pEvent);      //Completes an asynchronous request for the next event in the queue.
    if(FAILED(hr)) return hr;

    // Get the event type. 
    hr = pEvent->GetType(&meType);
    if(FAILED(hr)) return hr;

    hr = pEvent->GetStatus(&hrStatus);
    if(FAILED(hr)) return hr;

    if(SUCCEEDED(hrStatus))
    {
        if(meType == METransformNeedInput)
        {       
            SetEvent(m_hNeedInputEvent);
        }
        else if(meType == METransformHaveOutput)
        {           
            SetEvent(m_hHaveOutputEvent);
        }
        else if(meType == METransformDrainComplete)
        {
            hr = m_pTransform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH,0);
            if(FAILED(hr)) return hr;
        }
        else if(meType == MEError)
        {
            PROPVARIANT pValue;
            hr = pEvent->GetValue(&pValue);         
            if(FAILED(hr)) return hr;   
        }

        hr = m_pEventGenerator->BeginGetEvent((IMFAsyncCallback*)this,NULL);    
        if(FAILED(hr)) return hr;
    }

done:
    SafeRelease(&pEvent);
    return S_OK;
}

HRESULT CMFSourceReader::OnReadSample(
    HRESULT hrStatus,
    DWORD  dwStreamIndex ,
    DWORD  dwStreamFlags ,
    LONGLONG  llTimestamp ,
    IMFSample *pSample      // Can be NULL
    )
{
    HRESULT hr = S_OK;
    IMFMediaBuffer *pBuffer = NULL;
    DWORD dwcbTotLen = 0;           
    IMFSample *mftOutSample = NULL;

    EnterCriticalSection(&m_critsec);

    if (FAILED(hrStatus))
    {
        hr = hrStatus;
    }

    if (SUCCEEDED(hr))
    {
        if (pSample != NULL)
        {
            if(dwStreamIndex == 0)      //VideoStream
            {                   
                if(m_pTransform)
                {   
                    hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
                    if(FAILED(hr))  return hr;

                    m_dwWaitObj = WaitForSingleObject(m_hNeedInputEvent,INFINITE);
                    if(m_dwWaitObj == WAIT_OBJECT_0)
                    {                           
                        hr = ProcessInputSample(pSample);
                        if(FAILED(hr))  return hr;
                    }

                    m_dwWaitObj = WaitForSingleObject(m_hHaveOutputEvent,INFINITE);
                    if(m_dwWaitObj == WAIT_OBJECT_0)
                    {
                        hr = ProcessOutputSample(&mftOutSample);
                        if(FAILED(hr))  return hr;
                    }
                }
            }
        }
    }

    if(SUCCEEDED(hr))
    {
        if(m_pReader != NULL)
        {
            hr = m_pReader->ReadSample(
                (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
                0,
                NULL,   // actual
                NULL,   // flags
                NULL,   // timestamp
                NULL    // sample
                );
            if(FAILED(hr)) return hr;
        }
    }

    SafeRelease(&mftOutSample);

    LeaveCriticalSection(&m_critsec);
    return hr; 
}

HRESULT ProcessOutputSample(IMFSample **pOutSample)
{
    HRESULT hr = S_OK;
    MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
    DWORD processOutputStatus = 0,mftOutFlags = 0;
    MFT_OUTPUT_STREAM_INFO StreamInfo;
    IMFSample *mftOutSample = NULL;
    IMFMediaBuffer *pOutBuffer = NULL;

    if(m_pTransform != NULL)
    {   
        hr = m_pTransform->GetOutputStreamInfo(0, &StreamInfo);
        if(FAILED(hr)) return hr;

        DWORD status = 0;
        hr = m_pTransform->GetOutputStatus(&status);
        if (FAILED(hr)) return hr;

        hr = MFCreateSample(&mftOutSample);
        if(FAILED(hr)) return hr;

        hr = MFCreateMemoryBuffer(StreamInfo.cbSize, &pOutBuffer);
        if(FAILED(hr)) return hr;

        hr = mftOutSample->AddBuffer(pOutBuffer);
        if(FAILED(hr)) return hr;

        outputDataBuffer.dwStreamID = 0;
        outputDataBuffer.dwStatus = 0;
        outputDataBuffer.pEvents = NULL;
        outputDataBuffer.pSample = mftOutSample;

        hr = m_pTransform->ProcessOutput(0, 1, &outputDataBuffer, &processOutputStatus);            
        if(FAILED(hr)) return hr;

        hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0);
        if (FAILED(hr))  return hr;

        hr = m_pTransform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
        if (FAILED(hr))  return hr;

        if(mftOutSample)
        {
            *pOutSample = mftOutSample;
            (*pOutSample)->AddRef();
        }

        ResetEvent(m_hHaveOutputEvent);
    }

    SafeRelease(&mftOutSample);
    SafeRelease(&pOutBuffer);

    return hr;
}

HRESULT ProcessInputSample(IMFSample *pInputSample)
{
    HRESULT hr;

    if(m_pTransform != NULL)
    {               
        hr = m_pTransform->ProcessInput(0, pInputSample, 0);
        if(FAILED(hr)) return hr;

        hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM,0);
        if(FAILED(hr)) return hr;

        ResetEvent(m_hNeedInputEvent);
    }

    return hr;
}

我在代码中注释了ProcessOutputSample()方法,并检查了连续MFT发送MFTransformNeedInput事件类型。在ProcessInput示例之后,我有了ProcessOutput方法,但它返回了一个E_UNEXPECTED错误。我在MSDN上读到了这个错误,他们提到我不应该在没有接收MFTransformHaveOutput事件的情况下调用IMFTransform::ProcessOutput方法。

我是否遗漏了什么?我可以在MediaFoundation中使用英特尔硬件MJPEG解码器MFT吗?有人提供了一个使用这个解码器的样本吗?在过去的4天里,我一直在努力解决这个问题。

提前谢谢。

EN

回答 2

Stack Overflow用户

发布于 2017-02-26 00:15:23

首先,你不需要调用它:

代码语言:javascript
复制
hr = pAttributes->SetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE,TRUE);

Mft对此负责,因为它是异步的,所以您可以假定它是真的。你可以检查一下调用GetUINT32是不是真的。

第二:

代码语言:javascript
复制
hr = pAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE);

这不是MFT的意图。此属性用于源读取器或接收器写入器:MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS attribute

从您的代码中,我可以看到的问题是,您总是在OnReadSample中调用hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);,并且应该在一开始调用一次。

同样的道理也适用于ProcessInputSample和ProcessOutputSample,你调用hr = m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM,0);,你告诉MFT流已经结束了……

你的代码应该处理这样的事情:

解码输入作为needed

  • Process输出作为needed

  • ...

  • ...

  • Process输入作为needed

  • Process输出作为needed

  • MFT_MESSAGE_NOTIFY_END_OF_STREAM

  • End

开始

您永远不会收到outputsample,因为您告诉MFT流已经在第一个inputsample进程之后结束。

请阅读:MFT_MESSAGE_NOTIFY_START_OF_STREAM

通知媒体基础转换(MFT)将要处理第一个样本。

是的,第一个样本,不是所有的样本。

编辑

在CMFSourceReader::OnReadSample中还有另一个问题:

代码语言:javascript
复制
m_dwWaitObj = WaitForSingleObject(m_hNeedInputEvent,INFINITE);

if(m_dwWaitObj == WAIT_OBJECT_0)
{                           
   hr = ProcessInputSample(pSample);
   if(FAILED(hr))  return hr;
}

m_dwWaitObj = WaitForSingleObject(m_hHaveOutputEvent,INFINITE);

if(m_dwWaitObj == WAIT_OBJECT_0)
{
   hr = ProcessOutputSample(&mftOutSample);
   if(FAILED(hr))  return hr;
}

您首先等待m_hNeedInputEvent,然后等待m_hHaveOutputEvent。但是如果你在m_hHaveOutputEvent之前收到两次m_hNeedInputEvent,会发生什么呢?此代码不正确。您没有正确处理Invoke。仅当ProcessInput完成时才应调用OnReadSample。总体设计似乎不正确。

更新

当您在CMFSourceReader::OnReadSample中接收到一个示例时,您只需要在一个列表( queue ( sample ))中对该示例进行排队。要管理示例列表,可以使用以下类型的代码:SamplePool/ThreadSafeQueue

在CMFSourceReader::Invoke中,当您收到示例时,只需DeQueue( METransformNeedInput )并调用ProcessInputSample。

在CMFSourceReader::Invoke中,当您收到m_hHaveOutputEvent时,调用ProcessOutputSample。

两件事:

ReadSample您可以在程序开始时调用m_

  • ->ReadSample三次,然后等待三个样本出现在列表中。当你有三个样本时,你就可以开始解码了,就像这样,你将确保当METransformNeedInput发生时,你有一个样本准备好进行处理。你可以在ProcessInputSample之后调用m_ reader >ReadSample来维护列表中的三个样本。

  • 可能解码器的处理速度比源阅读器读取样本的速度快,或者相反。因此,请检查当为METransformNeedInput时,列表中始终有样本。我们的策略是在解码过程中保持合理的样本计数,比如说三个。
票数 1
EN

Stack Overflow用户

发布于 2019-11-22 18:22:06

在英特尔硬件上输入第一个输入样本后,Transform的事件生成器立即返回了相同的E_UNEXPECTED ("Unspecified Error")错误,进一步的调用只是返回了"Transform Need more input",但没有产生输出。不过,同样的代码在Nvidia机器上运行良好。经过大量的实验和研究,我发现我创建了太多的D3d11Device实例,在我的例子中,我创建了2到3个分别用于捕获、颜色转换和硬件编码器的设备。然而,我可以简单地重用单个D3dDevice实例。不过,在高端机器上创建多个D3d11Device实例可能是可行的。这在任何地方都没有文档记录。我甚至找不到任何关于"E_UNEXPECTED“错误原因的线索。没有任何地方提到它。有许多与此类似的StackOverflow线程一直没有回复,即使是微软的人也无法指出问题所在,因为有完整的源代码。

重用D3D11Device实例解决了这个问题。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42277350

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档