Video 异步MFT未发送MFT输出事件(英特尔硬件MJPEG解码器MFT)
我正在使用MediaFoundation SourceReader技术开发USB摄像头流式桌面应用程序。该相机支持USB3.0,1080p MJPG视频格式分辨率为60fps 我使用软件MJPEG解码器MFT将MJPG转换为YUY2帧,然后转换为RGB32帧在窗口上绘制。当使用这个软件解码器时,我能够在窗口上只渲染30fps,而不是60fps。我在这个网站上发布了一个问题,并得到了一些使用Intel硬件MJPEG解码器MFT解决帧丢失问题的建议 为了使用这个硬件MJPEG解码器,我处理了异步MFT处理模型,并通过IMFTTransform接口为IMFMediaEventGenerator配置了一个异步回调 在使用ProcessMessage方法调用MFT_MESSAGE_NOTIFY_START_OF_STREAM之后,我收到了两次MFTransformNeedInput事件,但没有收到MFT的MFTransformHaveOutput事件 我在这里分享了我的代码供您参考:Video 异步MFT未发送MFT输出事件(英特尔硬件MJPEG解码器MFT),video,visual-c++,media,ms-media-foundation,Video,Visual C++,Media,Ms Media Foundation,我正在使用MediaFoundation SourceReader技术开发USB摄像头流式桌面应用程序。该相机支持USB3.0,1080p MJPG视频格式分辨率为60fps 我使用软件MJPEG解码器MFT将MJPG转换为YUY2帧,然后转换为RGB32帧在窗口上绘制。当使用这个软件解码器时,我能够在窗口上只渲染30fps,而不是60fps。我在这个网站上发布了一个问题,并得到了一些使用Intel硬件MJPEG解码器MFT解决帧丢失问题的建议 为了使用这个硬件MJPEG解码器,我处理了异步MF
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 send MFTransformNeedInput事件类型。在ProcessInput示例之后,我得到了ProcessOutput方法,但它返回了一个E_意外错误。我在MSDN中读到这个错误,他们提到我不应该在没有接收MFTransformHaveOutput事件的情况下调用IMFTTransform::ProcessOutput方法
我遗漏了什么吗?我可以在MediaFoundation内部使用Intel硬件MJPEG解码器MFT吗?是否有人提供了使用此解码器的示例?在过去的4天里,我一直在努力解决这个问题
提前感谢。首先,您不必称此为:
hr = pAttributes->SetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE,TRUE);
Mft对此负责,因为它是异步的,所以可以假定它是真的。您只需检查调用GetUINT32是否真实
第二:
hr = pAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE);
这并不意味着MFT。此属性用于源读取器或接收器写入器:
从您的代码中,我看到的问题是,您总是调用hr=m\u pTransform->ProcessMessage(MFT\u MESSAGE\u NOTIFY\u START\u OF_STREAM,0)代码>在OnReadSample中,您应该在开始时调用一次
这同样适用于ProcessInputSample和ProcessOutputSample,您可以调用hr=m\u ptTransform->ProcessMessage(MFT\u MESSAGE\u NOTIFY\u END\u OF_STREAM,0)代码>,您告诉MFT流已结束
您的代码应该处理如下事项:
- 开始解码
- MFT\u消息\u通知\u启动\u流
- 根据需要处理输入
- 根据需要处理输出
- 根据需要处理输入
- 根据需要处理输出
- MFT\u消息\u通知\u结束\u流
- 结束解码
您从未收到outputsample,因为您告诉MFT流在第一个inputsample进程之后就结束了
请阅读以下内容:
通知第一个样本即将被处理的媒体基础转换(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_HnedInputEvent,然后等待m_hHaveOutputEvent。但如果在m_hHaveOutputEvent之前两次收到m_hNedInputEvent,会发生什么。此代码不正确。您没有正确处理调用。仅当ProcessInput完成时才应调用OnReadSample。总体设计似乎不正确
更新
当您在CMFSourceReader::OnReadSample中收到一个样本时,您只需要将样本排入一个列表(队列(样本))。
要管理示例列表,可以使用以下类型的代码:
在CMFSourceReader::Invoke中,当您收到METransformNeedInput时,只需将(Sample)出列并调用ProcessInputSample
在CMFSourceReader::Invoke中,当您收到MhHaveOutputEvent时,调用ProcessOutputSample
两件事:
- 您可以在程序开始时调用m_pReader->ReadSample三次,然后等待列表中有三个样本。当您有三个样本时,您可以开始解码,就像这样,您将确保当METransformNeedInput发生时,您有一个样本可以处理。在ProcessInputSample维护列表中的三个样本后,此时可以调用m_pReader->ReadSample
- 解码器的处理速度可能比源阅读器读取样本的速度快,或者相反。因此,请检查METransformNeedInput时,列表中始终存在样本。策略是在解码过程中保持一个合理的样本数,比如说三个
在英特尔硬件上输入第一个输入样本后,Transform的事件生成器立即返回了相同的E_意外(“未指定错误”)错误,并且,进一步的调用刚刚返回“Transform需要更多输入”,但没有生成输出。同样的代码在Nvidia机器上运行良好。经过大量的实验和研究,我发现我创建了太多D3D11设备的实例,在我的例子中,我分别创建了2到3个用于捕获、颜色转换和硬件编码器的设备。然而,我可以简单地重用一个D3dDevice实例。不过,创建多个D3D11设备实例可能在高端机器上工作。这在任何地方都没有记录。我甚至找不到“E_意外”错误原因的线索。没有提到过。有许多与此类似的StackOverflow线程没有得到回答,即使是微软的人也无法指出问题所在,因为有完整的源代码
重用D3D11设备实例解决了问题。那是什么网络摄像头?(只是好奇)我正在使用链接中提到的相机:@Abi,我也面临着同样的问题。你能解决这个问题吗?如果是的话,你能告诉我什么样的样品吗?谢谢你对我的问题的详细回答,Mofo。投寄前