MediaFoundation实现UVC预览时GetStreamSinkCount异常及拓扑加载问题
MediaFoundation UVC预览拓扑构建问题分析与解决
问题1:GetStreamSinkCount返回S_OK但抛出bad_hresult异常
原因
MFCreateVideoRenderer创建的EVR媒体接收器,内部实现会在调用GetStreamSinkCount时触发未被用户代码捕获的内部异常,但最终处理后返回S_OK。Visual Studio调试器会捕获这类内部异常并在输出窗口提示,但不会影响代码的正常执行逻辑。
解决办法
- 直接忽略该异常,因为返回值已确认是S_OK,功能不受影响。
- 若要消除调试器的异常提示,可在Visual Studio的调试->窗口->异常设置中,取消勾选
C++ Exceptions下的Microsoft C++ exception: bad_hresult,或者仅针对该异常禁用捕获。
问题2:IMFTopoLoader::Load返回MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED错误
原因
IMFTopoLoader::Load要求输出节点使用激活对象(IMFActivate),而非直接传入IMFStreamSink或IMFMediaSink。当前代码直接将IMFStreamSink绑定到输出节点,不符合拓扑加载器的延迟初始化要求,导致加载失败。
解决办法
修改输出节点的创建逻辑,使用MFCreateVideoRendererActivate创建EVR的激活对象,而非直接实例化媒体接收器:
- 替换媒体接收器创建代码,改为生成激活对象:
// 注释原有IMFMediaSink创建代码 // IMFMediaSink *m_pMediaSink = nullptr; // hr = MFCreateVideoRenderer(IID_PPV_ARGS(&m_pMediaSink)); // if (hr != S_OK) // return -1; // 创建EVR激活对象(传入窗口句柄m_hWnd) IMFActivate* pEVRActivate = nullptr; hr = MFCreateVideoRendererActivate(m_hWnd, &pEVRActivate); if (hr != S_OK) return -1;
- 将激活对象绑定到输出节点,而非
IMFStreamSink:
// 注释原有SetObject代码 // hr = m_pOutputTopologyNode->SetObject(m_pStreamSink); // if (hr != S_OK) // return -1; // 绑定激活对象到输出节点 hr = m_pOutputTopologyNode->SetObject(pEVRActivate); if (hr != S_OK) { pEVRActivate->Release(); return -1; } pEVRActivate->Release(); // 绑定后可释放,拓扑会管理激活对象生命周期
- 调整
IMFVideoDisplayControl的获取时机:由于媒体接收器由拓扑加载后创建,需通过媒体会话获取服务,而非直接从IMFStreamSink获取:
// 在m_pMediaSession->SetTopology成功后添加 IMFGetService* pSessionService = nullptr; hr = m_pMediaSession->QueryInterface(IID_PPV_ARGS(&pSessionService)); if (SUCCEEDED(hr)) { hr = pSessionService->GetService(MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_pVideoDisplayControl)); pSessionService->Release(); } if (hr != S_OK) return -1; // 后续窗口、位置设置逻辑不变 hr = m_pVideoDisplayControl->SetVideoWindow(m_hWnd); // ... 其他设置代码
- 确保资源正确释放,避免内存泄漏。
修正后的关键代码片段
// Output Topology Node - Create hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &m_pOutputTopologyNode); if (hr != S_OK) return -1; // 创建EVR激活对象 IMFActivate* pEVRActivate = nullptr; hr = MFCreateVideoRendererActivate(m_hWnd, &pEVRActivate); if (hr != S_OK) return -1; // 绑定激活对象到输出节点 hr = m_pOutputTopologyNode->SetObject(pEVRActivate); if (hr != S_OK) { pEVRActivate->Release(); return -1; } pEVRActivate->Release(); hr = m_pOutputTopologyNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, TRUE); if (hr != S_OK) return -1; // ... 节点添加、连接逻辑不变 // 加载完整拓扑 IMFTopoLoader *pTopoLoader = nullptr; hr = MFCreateTopoLoader(&pTopoLoader); if (hr != S_OK) return -1; IMFTopology *pFullTopology = nullptr; hr = pTopoLoader->Load(m_pTopology, &pFullTopology, nullptr); if (hr != S_OK) { pTopoLoader->Release(); return -1; } pTopoLoader->Release(); // 会话设置完整拓扑 hr = m_pMediaSession->SetTopology(0, pFullTopology); if (hr != S_OK) { pFullTopology->Release(); return -1; } pFullTopology->Release(); // 获取视频显示控件 IMFGetService* pSessionService = nullptr; hr = m_pMediaSession->QueryInterface(IID_PPV_ARGS(&pSessionService)); if (SUCCEEDED(hr)) { hr = pSessionService->GetService(MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_pVideoDisplayControl)); pSessionService->Release(); } if (hr != S_OK) return -1; // 后续窗口、位置设置 hr = m_pVideoDisplayControl->SetVideoWindow(m_hWnd); // ...
内容的提问来源于stack exchange,提问作者World
相关产品推荐
相关产品推荐

