如何从SinkWriter获取底层MediaSink?或为任意拓扑创建MP4媒体Sink?
如何为任意拓扑创建MP4媒体Sink,以及SinkWriter无法获取底层MediaSink的原因
首先,你遇到的MF_E_UNSUPPORTED_SERVICE错误是因为SinkWriter的设计就是封装底层MediaSink,不对外暴露这个服务。IMFSinkWriter并没有实现GetServiceForStream来返回内部的MediaSink,这个方法仅适用于某些特定的MediaSink实现,而SinkWriter的内部MediaSink是私有封装的,无法通过这种方式获取。
针对你的需求——为任意拓扑添加MP4 Sink,这里提供两种可行方案:
方案一:统一使用MPEG4 MediaSink处理所有源类型
拓扑是基于MediaSession的Pipeline,核心是使用IMFMediaSink,所以不管源是MP4还是YUV,都可以通过配置MPEG4 MediaSink来实现,不需要切换到SinkWriter。
分情况处理逻辑:
- 当源是MP4(已有H.264编码数据):启用
MF_MPEG4SINK_SPSPPS_PASSTHROUGH,直接传递SPS/PPS和编码帧,避免重复编码。 - 当源是非MP4(比如原始YUV):让Media Foundation自动插入编码器,将输入类型转换为H.264编码类型后写入MP4。
完整代码示例:
HRESULT CreateVideoFileSink( IMFStreamDescriptor* pSourceSD, LPCWSTR pFilename, IMFStreamSink** ppStream) { HRESULT hr = S_OK; CComPtr<IMFMediaTypeHandler> pHandler; CComPtr<IMFMediaType> pInputType; CComPtr<IMFMediaSink> pMediaSink; CComPtr<IMFStreamSink> pStreamSink; CComPtr<IMFByteStream> pByteStream; CComPtr<IMFAttributes> pSinkAttrs; *ppStream = nullptr; // 获取源流的媒体类型处理器 IFR(pSourceSD->GetMediaTypeHandler(&pHandler)); // 获取源的当前媒体类型 IFR(pHandler->GetCurrentMediaType(&pInputType)); // 创建文件字节流 IFR(MFCreateFile( MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, pFilename, &pByteStream)); // 创建MPEG4 MediaSink的属性 IFR(MFCreateAttributes(&pSinkAttrs, 2)); // 启用硬件加速(可选) IFR(pSinkAttrs->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE)); GUID guidMajorType; IFR(pInputType->GetGUID(MF_MT_MAJOR_TYPE, &guidMajorType)); if (guidMajorType == MFMediaType_Video) { GUID guidSubtype; IFR(pInputType->GetGUID(MF_MT_SUBTYPE, &guidSubtype)); // 判断是否是H.264编码的MP4源 if (guidSubtype == MFVideoFormat_H264) { // 启用SPS/PPS透传,避免重新编码 IFR(pSinkAttrs->SetUINT32(MF_MPEG4SINK_SPSPPS_PASSTHROUGH, TRUE)); } // 创建MPEG4 MediaSink IFR(MFCreateMPEG4MediaSink( pByteStream, pInputType, pSinkAttrs, &pMediaSink)); // 获取对应的StreamSink(MP4 Sink默认只有一个视频流) IFR(pMediaSink->GetStreamSinkByIndex(0, &pStreamSink)); // 验证输入类型是否被支持(如果不支持,MF会自动查找合适的编码器转换) IFR(pStreamSink->GetMediaTypeHandler()->IsMediaTypeSupported(pInputType, nullptr)); *ppStream = pStreamSink.Detach(); } return hr; }
关键细节:
- 当源是原始YUV时,
IsMediaTypeSupported会自动检测并匹配系统中可用的H.264编码器,Media Foundation会在拓扑中自动插入编码器转换节点,你不需要手动处理编码逻辑。 - 启用
MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS可以让MF优先使用硬件编码器,提升性能。
方案二:如果必须使用SinkWriter(不依赖拓扑)
如果你不需要基于拓扑的Pipeline,而是想简化编码写入流程,那么可以直接使用SinkWriter,但此时无法将其集成到拓扑中,因为SinkWriter是独立的API,自己管理整个写入流程。
示例代码片段(直接用SinkWriter写入YUV数据):
HRESULT WriteYUVToMP4(LPCWSTR pFilename, IMFMediaType* pYUVType) { HRESULT hr = S_OK; CComPtr<IMFSinkWriter> pSinkWriter; DWORD streamIndex = 0; // 创建SinkWriter IFR(MFCreateSinkWriterFromURL( pFilename, nullptr, nullptr, &pSinkWriter)); // 添加视频流,设置输入类型为YUV,SinkWriter会自动选择编码器 IFR(pSinkWriter->AddStream(pYUVType, &streamIndex)); // 设置输出类型(可以指定H.264,也可以让SinkWriter自动选择) CComPtr<IMFMediaType> pOutputType; IFR(MFCreateMediaType(&pOutputType)); IFR(pOutputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video)); IFR(pOutputType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264)); // 设置分辨率、帧率等(可以从输入类型复制) UINT32 width, height; IFR(pYUVType->GetUINT32(MF_MT_FRAME_SIZE_WIDTH, &width)); IFR(pYUVType->GetUINT32(MF_MT_FRAME_SIZE_HEIGHT, &height)); IFR(pOutputType->SetUINT32(MF_MT_FRAME_SIZE_WIDTH, width)); IFR(pOutputType->SetUINT32(MF_MT_FRAME_SIZE_HEIGHT, height)); IFR(pSinkWriter->SetInputMediaType(streamIndex, pYUVType, nullptr)); IFR(pSinkWriter->BeginWriting()); // 后续调用pSinkWriter->WriteSample()写入YUV样本即可 // ... IFR(pSinkWriter->Finalize()); return hr; }
总结
- 如果你需要集成到拓扑中,方案一是唯一可行的选择,通过配置
MPEG4 MediaSink即可处理所有源类型,MF会自动处理编码转换。 - SinkWriter无法和拓扑结合,它是独立的简化API,适合不需要复杂拓扑的场景。
- 不要尝试从SinkWriter获取底层MediaSink,这不符合它的设计意图,也没有官方支持的方法。
内容的提问来源于stack exchange,提问作者Mastax
相关产品推荐
相关产品推荐

