2

对不起我的英语不好。我对在 Windows 10 中使用 MF 进行硬件编码有疑问。我有 Nvidia gtx 650(带 nvenc)。我在 c# 中开发了将实时帧流编码为 h264 文件的应用程序。

我使用此代码(https://codereview.stackexchange.com/questions/136144/h-264-image-encoding-using-media-foundation-net)作为示例。我创建了 IMFSinkWriter 对象(sinkWriter),例如,使用 MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS=true

private int InitializeSinkWriter(String outputFile, int videoWidth, int videoHeight)
{            
    IMFMediaType mediaTypeIn = null;
    IMFMediaType mediaTypeOut = null;
    IMFAttributes attributes = null;

    int hr = 0;

    if (Succeeded(hr)) hr = MFExtern.MFCreateAttributes(out attributes, 1);
    if (Succeeded(hr)) hr = attributes.SetUINT32(MFAttributesClsid.MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, 1);
    if (Succeeded(hr)) hr = attributes.SetUINT32(MFAttributesClsid.MF_LOW_LATENCY, 1);

    // Create the sink writer 
    if (Succeeded(hr)) hr = MFExtern.MFCreateSinkWriterFromURL(outputFile, null, attributes, out sinkWriter);

    // Create the output type
    if (Succeeded(hr)) hr = MFExtern.MFCreateMediaType(out mediaTypeOut);
    if (Succeeded(hr)) hr = mediaTypeOut.SetGUID(MFAttributesClsid.MF_MT_MAJOR_TYPE, MFMediaType.Video);
    if (Succeeded(hr)) hr = mediaTypeOut.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, MFMediaType.H264);
    if (Succeeded(hr)) hr = mediaTypeOut.SetUINT32(MFAttributesClsid.MF_MT_AVG_BITRATE, videoBitRate);
    if (Succeeded(hr)) hr = mediaTypeOut.SetUINT32(MFAttributesClsid.MF_MT_INTERLACE_MODE, (int) MFVideoInterlaceMode.Progressive);
    if (Succeeded(hr)) hr = MFExtern.MFSetAttributeSize(mediaTypeOut, MFAttributesClsid.MF_MT_FRAME_SIZE, videoWidth, videoHeight);
    if (Succeeded(hr)) hr = MFExtern.MFSetAttributeRatio(mediaTypeOut, MFAttributesClsid.MF_MT_FRAME_RATE, VIDEO_FPS, 1);
    if (Succeeded(hr)) hr = MFExtern.MFSetAttributeRatio(mediaTypeOut, MFAttributesClsid.MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
    if (Succeeded(hr)) hr = sinkWriter.AddStream(mediaTypeOut, out streamIndex);

    // Create the input type 
    if (Succeeded(hr))  hr = MFExtern.MFCreateMediaType(out mediaTypeIn);
    if (Succeeded(hr)) hr = mediaTypeIn.SetGUID(MFAttributesClsid.MF_MT_MAJOR_TYPE, MFMediaType.Video);
    if (Succeeded(hr)) hr = mediaTypeIn.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, MFMediaType.RGB24);
    if (Succeeded(hr)) hr = mediaTypeIn.SetUINT32(MFAttributesClsid.MF_MT_INTERLACE_MODE, (int)MFVideoInterlaceMode.Progressive);
    if (Succeeded(hr)) hr = MFExtern.MFSetAttributeSize(mediaTypeIn, MFAttributesClsid.MF_MT_FRAME_SIZE, videoWidth, videoHeight);
    if (Succeeded(hr)) hr = MFExtern.MFSetAttributeRatio(mediaTypeIn, MFAttributesClsid.MF_MT_FRAME_RATE, VIDEO_FPS, 1);
    if (Succeeded(hr)) hr = MFExtern.MFSetAttributeRatio(mediaTypeIn, MFAttributesClsid.MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
    if (Succeeded(hr)) hr = sinkWriter.SetInputMediaType(streamIndex, mediaTypeIn, null);

    // Start accepting data
    if (Succeeded(hr))  hr = sinkWriter.BeginWriting();

    COMBase.SafeRelease(mediaTypeIn);
    COMBase.SafeRelease(mediaTypeOut);

    return hr;            
}

如示例所示,我为每个帧编码执行了以下步骤: 1. 创建 IMFMediaBuffer 对象(缓冲区)并将帧复制到那里 2. 创建 IMFSample 对象(样本)和 sample.AddBuffer(缓冲区) 3. 将样本写入 IMFSinkWriter 对象(sinkWriter)

结果是我 100% 的 CPU 负载(这不是硬件编码!!!)。如何使用相同的代码进行硬件编码?

4

1 回答 1

3

Sink Writer API 为您提供数据的自动转换(尤其是编码),其中包括在您请求时依次进行 H.264 编码。您确实通过 do AddStreamwithMFMediaType.H264后跟SetInputMediaTypewith来请求它MFMediaType.RGB24

但是,您没有编码器选择的细粒度灵活性。API 为您选择“最佳”编码器是设计行为。您的申请MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS意味着您可以使用硬件 MFT,但这并不意味着您强制使用该选项。您甚至不知道硬件 MFT 是否首先可用,是吗?同样,API 通过保留选择最佳适用编码器本身的权利来避免被称为“编解码器地狱”的歧义,这是设计行为。文档是这样解释的:如果存在经过认证的硬件编码器,通常会在 Media Foundation 相关场景中使用它而不是收件箱系统编码器。

最大化 CPU 负载暗示压缩可能是软件,但也可能是由其他原因引起的。在您的情况下,您需要检查可用的 H.264 编码器、调试和跟踪您的应用程序。要在 API 未自动选择编码器时强制使用 Media Foundation 进行硬件 H.264 编码,您别无选择,只能自己将视频压缩为 H.264(使用 MFT,这对应于编码器作为 MFT 但 API 存在的不太可能的情况不拾取它,或以其他方式编码,尤其是使用供应商特定的 SDK),然后将压缩数据提供给 Sink Writer API 以生成格式良好的 MP4 文件,其中可能混入音频。

也可以看看:

您可以使用MediaFoundationVideoEncoderTransforms 工具检查硬件编码器的可用性。

于 2016-11-29T12:07:36.897 回答