我正在开发使用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事件。
我在这里分享了我的代码供你参考:
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天里,我一直在努力解决这个问题。
提前谢谢。
发布于 2017-02-26 00:15:23
首先,你不需要调用它:
hr = pAttributes->SetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE,TRUE);Mft对此负责,因为它是异步的,所以您可以假定它是真的。你可以检查一下调用GetUINT32是不是真的。
第二:
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
开始
您永远不会收到outputsample,因为您告诉MFT流已经在第一个inputsample进程之后结束。
请阅读:MFT_MESSAGE_NOTIFY_START_OF_STREAM
通知媒体基础转换(MFT)将要处理第一个样本。
是的,第一个样本,不是所有的样本。
编辑
在CMFSourceReader::OnReadSample中还有另一个问题:
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_
发布于 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实例解决了这个问题。
https://stackoverflow.com/questions/42277350
复制相似问题