IMFStreamSink::ProcessSample返回E_NOINTERFACE错误求助
Media Foundation中调用IMFStreamSink::ProcessSample返回E_NOINTERFACE错误
问题描述
我正在做一个快速POC验证,使用无容器的原始H264文件(仅NALU封装的裸H264数据)模拟设备数据源。通过标准fstream以1024字节块读取H264文件模拟流数据,手动创建IMFSample发送给解码器,积累足够数据后解码器能成功解码,但将解码后的样本传入从EVR获取的IMFStreamSink时,调用ProcessSample返回HRESULT错误E_NOINTERFACE No such interface supported。已确认解码后的样本包含有效数据,困惑于无额外信息的情况下出现该错误。
核心循环代码
while (true) { // My Loop Stuff IMFMediaBuffer* inputBuffer = CreateMediaBuffer(1024); IMFSample* compressedSample = CreateMediaSample(inputBuffer); if (compressedSample == NULL) { return 1; } BYTE* tempBuff; DWORD tempMaxLength = 0; DWORD tempCurrentLength = 0; hr = inputBuffer->Lock( &tempBuff, &tempMaxLength, &tempCurrentLength ); if (FAILED(hr)) { std::cout << "Failed to lock buffer" << std::endl; return 1; } inputTestFile.read((char*)tempBuff, tempMaxLength - tempCurrentLength); hr = inputBuffer->SetCurrentLength(tempMaxLength - tempCurrentLength); if (FAILED(hr)) { std::cout << "Failed to set the lenght of the buffer" << std::endl; return 1; } hr = inputBuffer->Unlock(); if (FAILED(hr)) { std::cout << "FAILED to unlock buffer!" << std::endl; return 1; } hr = pDecoder->ProcessInput(0, compressedSample, 0); if (FAILED(hr)) { std::cout << "Failed sending compressedSample to decoder" << std::endl; IMFMediaBuffer* outputBuffer = CreateMediaBuffer(streamInfo.cbSize); IMFSample* decompressedSample = CreateMediaSample(outputBuffer); _MFT_OUTPUT_DATA_BUFFER outBuff = {}; outBuff.dwStreamID = 0; outBuff.pSample = decompressedSample; _MFT_OUTPUT_DATA_BUFFER outBuffs[] = { outBuff }; DWORD outStatus = 0; hr = pDecoder->ProcessOutput(0, 1, outBuffs, &outStatus); if (FAILED(hr)) { std::cout << "FAILED TO PROCESS OUTPUT" << std::endl; continue; //return 1; } else { std::cout << "Successfully decoded data!!!" << std::endl; IMFMediaEvent* mediaEvent = NULL; ssink->GetEvent(0, &mediaEvent); if (mediaEvent == NULL) { // Nothing to do.. Wait for a request for more continue; } hr = ssink->ProcessSample(decompressedSample); if (FAILED(hr)) { std::cout << "Failed to process sample to sink" << std::endl; continue; } } } // END My Loop Stuff }
完整核心逻辑代码
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // Initialize global strings LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadStringW(hInstance, IDC_H264RENDERINGWINDOW, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_H264RENDERINGWINDOW)); MSG msg; // My Stuff HRESULT hr = S_OK; UINT32 count = 0; hr = MFStartup(MF_VERSION); if (FAILED(hr)) { return 1; } IMFActivate** ppActivate = NULL; IMFTransform* pDecoder = NULL; MFT_REGISTER_TYPE_INFO info = { MFMediaType_Video, MFVideoFormat_H264 }; UINT32 unFlags = MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT | MFT_ENUM_FLAG_SORTANDFILTER; hr = MFTEnumEx( MFT_CATEGORY_VIDEO_DECODER, unFlags, &info, NULL, &ppActivate, &count ); if (SUCCEEDED(hr) && count == 0) { std::cout << "NOT SUCCESS!!!!" << std::endl; return 1; } if (SUCCEEDED(hr)) { std::cout << "SUCCESS AGAIN!!!" << std::endl; hr = ppActivate[0]->ActivateObject(IID_PPV_ARGS(&pDecoder)); } DWORD pcInputStreamsNum = 0; DWORD pcOutputStreamsNum = 0; hr = pDecoder->GetStreamCount( &pcInputStreamsNum, &pcOutputStreamsNum ); if (FAILED(hr)) { std::cout << "Unable to get stream count" << std::endl; return 1; } std::cout << "Available Input Streams: " << pcInputStreamsNum << " Available Output Streams: " << pcOutputStreamsNum << std::endl; IMFMediaType* pInMediaType = NULL; hr = MFCreateMediaType(&pInMediaType); if (FAILED(hr)) { std::cout << "Unable to create Input Media Type" << std::endl; return 1; } hr = pDecoder->GetInputAvailableType(0, 0, &pInMediaType); if (FAILED(hr)) { std::cout << "Unable to get Input Media Type for Decoder" << std::endl; return 1; } hr = pDecoder->SetInputType(0, pInMediaType, 0); if (FAILED(hr)) { std::cout << "Unable to set Media Type for Decoder" << std::endl; return 1; } IMFMediaType* pOutMediaType = NULL; hr = MFCreateMediaType(&pOutMediaType); hr = pDecoder->GetOutputAvailableType(0, 0, &pOutMediaType); if (FAILED(hr)) { std::cout << "Unable to get output type" << std::endl; return 1; } GUID v; hr = pOutMediaType->GetGUID(MF_MT_SUBTYPE, &v); if (FAILED(hr)) { std::cout << "Unable to get output media sub-type" << std::endl; return 1; } hr = pDecoder->SetOutputType(0, pOutMediaType, 0); if (FAILED(hr)) { std::cout << "Failed setting output type" << std::endl; return 1; } hr = pDecoder->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL); if (FAILED(hr)) { std::cout << "Failed to send begin streaming message" << std::endl; return 1; } std::cout << "Successfully Initialized the Decoder..." << std::endl; std::ifstream inputTestFile = std::ifstream("c:\\open\\fbsource\\arvr\\apps\\MagicIsle\\magic-capture.h264", std::ios::binary); if (!inputTestFile.is_open()) { std::cout << "unable to open test input file, did run with --capture previously?" << std::endl; return 1; } MFT_INPUT_STREAM_INFO inStreamInfo{}; hr = pDecoder->GetInputStreamInfo(0, &inStreamInfo); if (FAILED(hr)) { std::cout << "Failed to get the stream info" << std::endl; return 1; } std::cout << "Stream Info: "; if (inStreamInfo.dwFlags == MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES) { std::cout << "MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES" << std::endl; } else if (inStreamInfo.dwFlags == MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES) { std::cout << "MFT_OUTPUT_STREAM_PROVIDES_SAMPLES" << std::endl; } MFT_OUTPUT_STREAM_INFO streamInfo{}; hr = pDecoder->GetOutputStreamInfo(0, &streamInfo); if (FAILED(hr)) { std::cout << "Failed to get the stream info" << std::endl; return 1; } std::cout << "Stream Info: "; if (streamInfo.dwFlags == MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES) { std::cout << "MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES" << std::endl; } else if (streamInfo.dwFlags == MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES) { std::cout << "MFT_OUTPUT_STREAM_PROVIDES_SAMPLES" << std::endl; } else { std::cout << "NEITHER" << std::endl; } IMFPresentationClock* clock = NULL; hr = MFCreatePresentationClock(&clock); if (FAILED(hr)) { return 1; } IMFPresentationTimeSource* timeSource = NULL; hr = MFCreateSystemTimeSource(&timeSource); if (FAILED(hr)) { return 1; } hr = clock->SetTimeSource(timeSource); if (FAILED(hr)) { return 1; } hr = clock->Start(0); if (FAILED(hr)) { return 1; } hr = pRenderer->SetPresentationClock(clock); if (FAILED(hr)) { return 1; } DWORD rendererCharacteristics = 0; hr = pRenderer->GetCharacteristics(&rendererCharacteristics); if ((rendererCharacteristics & MEDIASINK_CANNOT_MATCH_CLOCK) == MEDIASINK_CANNOT_MATCH_CLOCK) { std::cout << "Cannot Match Clock" << std::endl; } if ((rendererCharacteristics & MEDIASINK_RATELESS) == MEDIASINK_RATELESS) { std::cout << "Rateless" << std::endl; } if (FAILED(hr)) { return 1; } IMFStreamSink* ssink = NULL; hr = pRenderer->GetStreamSinkById(0, &ssink); if (FAILED(hr)) { return 1; } IMFMediaTypeHandler* ssinkMediaTypeHandler = NULL; hr = ssink->GetMediaTypeHandler(&ssinkMediaTypeHandler); if (FAILED(hr)) { return 1; } hr = ssinkMediaTypeHandler->SetCurrentMediaType(pOutMediaType); // End My Stuff auto f = [&] { while (true) { // My Loop Stuff IMFMediaBuffer* inputBuffer = CreateMediaBuffer(1024); IMFSample* compressedSample = CreateMediaSample(inputBuffer); if (compressedSample == NULL) { return 1; } BYTE* tempBuff; DWORD tempMaxLength = 0; DWORD tempCurrentLength = 0; hr = inputBuffer->Lock( &tempBuff, &tempMaxLength, &tempCurrentLength ); if (FAILED(hr)) { std::cout << "Failed to lock buffer" << std::endl; return 1; } inputTestFile.read((char*)tempBuff, tempMaxLength - tempCurrentLength); hr = inputBuffer->SetCurrentLength(tempMaxLength - tempCurrentLength); if (FAILED(hr)) { std::cout << "Failed to set the lenght of the buffer" << std::endl; return 1; } hr = inputBuffer->Unlock(); if (FAILED(hr)) { std::cout << "FAILED to unlock buffer!" << std::endl; return 1; } hr = pDecoder->ProcessInput(0, compressedSample, 0); if (FAILED(hr)) { std::cout << "Failed sending compressedSample to decoder" << std::endl; IMFMediaBuffer* outputBuffer = CreateMediaBuffer(streamInfo.cbSize); IMFSample* decompressedSample = CreateMediaSample(outputBuffer); _MFT_OUTPUT_DATA_BUFFER outBuff = {}; outBuff.dwStreamID = 0; outBuff.pSample = decompressedSample; _MFT_OUTPUT_DATA_BUFFER outBuffs[] = { outBuff }; DWORD outStatus = 0; hr = pDecoder->ProcessOutput(0, 1, outBuffs, &outStatus); if (FAILED(hr)) { std::cout << "FAILED TO PROCESS OUTPUT" << std::endl; continue; } else { std::cout << "Successfully decoded data!!!" << std::endl; IMFMediaEvent* mediaEvent = NULL; ssink->GetEvent(0, &mediaEvent); if (mediaEvent == NULL) { continue; } hr = ssink->ProcessSample(decompressedSample); if (FAILED(hr)) { std::cout << "Failed to process sample to sink" << std::endl; continue; } } } // END My Loop Stuff } }; std::thread t(f); while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } t.join(); return (int) msg.wParam; }
解决方案
1. 错误核心原因
EVR的IMFStreamSink要求传入的IMFSample中的媒体缓冲区必须实现**IMF2DBuffer接口**,你手动创建的decompressedSample使用的是普通IMFMediaBuffer,未实现该接口,导致ProcessSample调用时找不到所需接口,返回E_NOINTERFACE。
2. 关键修复步骤
(1)让解码器自动分配输出样本
不要手动创建ProcessOutput的样本,解码器输出的样本默认会实现IMF2DBuffer接口,完全适配EVR的要求:
// 移除手动创建outputBuffer和decompressedSample的代码 _MFT_OUTPUT_DATA_BUFFER outBuff = {}; outBuff.dwStreamID = 0; outBuff.pSample = NULL; // 让解码器自动分配样本 DWORD outStatus = 0; hr = pDecoder->ProcessOutput(0, 1, &outBuff, &outStatus); if (SUCCEEDED(hr)) { IMFSample* decompressedSample = outBuff.pSample; // 后续操作使用解码器提供的sample }
(2)正确处理EVR的流事件
当前直接调用ssink->GetEvent(0, &mediaEvent)的方式错误,需要等待MESinkRequestSample事件(EVR主动请求新样本的信号):
IMFMediaEvent* mediaEvent = NULL; // 使用MF_EVENT_FLAG_WAIT等待事件,而不是立即返回 hr = ssink->GetEvent(MF_EVENT_FLAG_WAIT, &mediaEvent); if (SUCCEEDED(hr)) { MediaEventType eventType = MEUnknown; mediaEvent->GetType(&eventType); // 仅在EVR请求样本时发送 if (eventType == MESinkRequestSample) { hr = ssink->ProcessSample(decompressedSample); } mediaEvent->Release(); }
(3)为解码样本设置正确时间戳
EVR依赖时间戳进行渲染同步,必须为样本设置有效时间戳(单位:100纳秒):
// 示例:假设帧率为30fps,逐帧累加时间戳 static LONGLONG currentTimestamp = 0; const LONGLONG frameDuration = 10000000 / 30; // 30fps单帧时长 decompressedSample->SetSampleTime(currentTimestamp); decompressedSample->SetSampleDuration(frameDuration); currentTimestamp += frameDuration;
3. 其他注意事项
- 确保
相关产品推荐
相关产品推荐

