You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:03:44