通过 ICodecAPI 为 H.264 IMFSinkWriter 编码器设置属性
Posted
技术标签:
【中文标题】通过 ICodecAPI 为 H.264 IMFSinkWriter 编码器设置属性【英文标题】:Set attributes via ICodecAPI for a H.264 IMFSinkWriter Encoder 【发布时间】:2019-06-03 16:50:49 【问题描述】:我正在尝试通过检索 ICodecAPI 接口来调整通过 ActivateObject() 创建的 H.264 编码器的属性。 虽然我没有收到错误,但我的设置没有被考虑在内。
代码在 Windows 10 下运行。
我复制了用于创建 IMFSinkWriter 的代码并检索下面的 ICodecAPI。未显示错误处理,但不会产生错误。
我已阅读此thread,这意味着调整 IMFSinkWriter 使用的编码器可能是不可能的,但由于 MSDN 文档上没有任何声明,我想知道是否有人设法将 ICodecAPI 与 IMFSinkWritter 一起使用。
如果不可能,应该怎么做?我需要在 H.264 中编码并流式传输到 MP4。我想更改 GOP、Qp、CABAC 等,这似乎无法通过输出媒体类型进行。 我应该能够单独创建编码器并将其连接到 MP4 文件编写器吗?任何关于如何做到这一点的指针表示赞赏......
hr = encoderToOpen.activate->ActivateObject(__uuidof(IMFTransform), (LPVOID *)&encoderTransform);
hr = encoderTransform->GetAttributes(&attributes);
hr = attributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE);
hr = MFCreateSinkWriterFromURL(fileName, NULL, attributes, &encoder);
// Initialise outputMediaType (code not shown)
hr = encoder->AddStream(outputMediaType, &streamIndex);
hr = encoder->SetInputMediaType(streamIndex, sourceMediaType, NULL);
// Retrieve the ICodecAPI
ICodecAPI *codecApi;
streamIndex = 0;
hr = encoder->GetServiceForStream(streamIndex, GUID_NULL, __uuidof(ICodecAPI), (LPVOID*)&codecApi);
VARIANT v;
hr = codecApi->GetValue(&CODECAPI_AVEncCommonQuality, &v);
v.vt = VT_UI4;
v.ulVal = 8;
hr = codecApi->SetValue(&CODECAPI_AVEncMPVGOPSize, &v);
// Start encoding (code not shown)
【问题讨论】:
C++ 显然只是次要相关。我相信代码是用 c++ 而不是 c# 或 Visual Basic 编写的,这只是偶然。您可以考虑通过删除 c++ 标签来向更广泛的受众公开您的问题,并在问题中说明显示的实际代码是使用 c++ 编写的。 另外,您的代码中没有单一的错误检查。您可能想使用hr
结果来测试您的调用是否成功。
类似问题Change h.264 quality when using SinkWriter to encode video ?
Re-C++ - 好的,谢谢。为清楚起见省略了错误检查,但在我的应用程序中没有生成错误。
我也面临同样的问题。你能解决这个问题吗?如果是,请您指导我查看样本或一些文档吗?
【参考方案1】:
Media Foundation 的 Sink Writer 是一个简化的 API,其中省略了编码器配置问题。这里的基本问题是您不拥有编码器 MFT 并且您正在通过编写者的头访问它,然后在设置完所有内容后编码器围绕更改设置的行为取决于实现,在编码器的情况下是供应商特定的实现并且可能因硬件而异。
您更可靠的选择是直接管理编码 MFT 并为 Sink Writer 提供已编码的视频。
让事情变得更轻松的潜在技巧是检索编码器的IMFTransform
并清除,然后在完成ICodecAPI
更新后设置回输入/输出媒体类型。轻推媒体类型,您建议编码器重新配置内部结构,并且已经对您进行了微调。请注意,一般来说,这可能会有一些副作用。
“技巧”似乎适用于某些 ICodecAPI 参数(例如 CODECAPI_AVEncCommonQualityVsSpeed),并且仅适用于 Microsoft 的 h.264 编码器。对 CODECAPI_AVEncH264CABACEnable 没有影响。该文档确实似乎专门针对 Microsoft 的编码器,而不是通用 API。我正在使用 QuickSync 和 NVidia 编解码器,如果我自己创建 MFT,你知道它们是否可以通过 ICodecAPI 进行配置?
供应商提供的编码器符合 Certified Hardware Encoder 要求,因此它们必须支持 MSDN 文章中提到的 ICodecAPI
值。重要的是没有定义配置调用的顺序是什么。如果您自己管理编码器,您将在设置媒体类型之前进行ICodecAPI
设置。在 Sink Writer 场景中,它已经配置了媒体类型,然后您可以进行微调。因此,我的技巧建议包括重置现有媒体类型的部分。因为这个技巧对实现细节很敏感,我建议获取当前的媒体类型,然后在 MFT 上清除它们,做ICodecAPI
的事情并取回类型。我认为这应该适用于更多的场景,而不仅仅是 MS 编码器。然而,它仍然是一个不可靠的 hack。
IMO Nvidia 的编码器实现很糟糕(在供应商中最差),英特尔的更好,但它仍然有自己的问题。同样,IMO 提供 MFT 只是为了满足硬件视频编码的最低认证要求,因此它们的实施并没有很好地对齐。各种软件包更喜欢通过供应商 SDK 而不是 Media Foundation Transform 接口来实现视频编码。在其中一个项目中,我也跳过了利用 MFT 进行编码的想法,并在供应商 SDK 之上实现了我自己的 MFT。
这篇文章中的类工厂方法是否适用于
IMFSinkWriter
?这样可以避免编写太多代码...
我想是的,这应该可以工作,即使我觉得以这种方式修补它不是一件令人愉快的工作。此外,您可能需要考虑对硬件编码器的支持,因为 Sink Writer 在某些情况下也倾向于使用硬件辅助编码,包括给定 DXGI 设备的场景。
另一种类似的 hack 方法是在 Sink Writer 初始化范围内重新定义供应商特定的编码器 CLSID,它与此类似但可能侵入性较小(尽管在其实现中您必须对内部有更好的了解)。只有三个编码器(AMD、Intel、Nvidia;好吧,还有第四个来自上海兆芯半导体,但它并不是很受欢迎),并且它们的 CLSID 是已知的。如果您以聪明的方式CoRegisterClassObject
,您可以挂钩 MFT 实例化,让 Media Foundation 决定选择哪个编码器。不过,这只是另一个想法,所以它可能取决于其他因素的最佳做法。
【讨论】:
“技巧”似乎适用于一些的 ICodecAPI 参数(例如 CODECAPI_AVEncCommonQualityVsSpeed)并且仅适用于 Microsoft 的 h.264 编码器。对 CODECAPI_AVEncH264CABACEnable 没有影响。 The doc 确实似乎是专门针对微软的编码器而不是通用 API。我正在使用 QuickSync 和 NVidia 编解码器,如果我自己创建 MFT,你知道它们是否可以通过 ICodecAPI 进行配置? post 中的类工厂方法是否可以与 IMFSinkWritter 一起使用?这样可以避免编写太多代码...【参考方案2】:它不完全是来自客户端的ICodecAPI
,但CODECAPI_AVEncMPVGOPSize
和CODECAPI_AVEncCommonQuality
可以使用IMFSinkWriter->SetInputMediaType(/* ... */,, IMFAttributes pEncodingParameters)
传递给h.264 编码器。我怀疑其他 CODECAPI_
值也会起作用。
CComPtr<IMFAttributes> pEncAttrs;
ATLENSURE_SUCCEEDED(MFCreateAttributes(&pEncAttrs, 1));
ATLENSURE_SUCCEEDED(pEncAttrs->SetUINT32(CODECAPI_AVEncCommonRateControlMode, eAVEncCommonRateControlMode_Quality));
ATLENSURE_SUCCEEDED(pEncAttrs->SetUINT32(CODECAPI_AVEncCommonQuality, 40));
ATLENSURE_SUCCEEDED(pEncAttrs->SetUINT32(CODECAPI_AVEncMPVGOPSize, 4));
ATLENSURE_SUCCEEDED(writer->SetInputMediaType(sink_stream, mtSource, pEncAttrs));
// ^^^^^^^^^
可以验证 GOP 设置已在MP4Box.js / ISOBMFF Box Structure Viewer 上查看生效。可以观察到质量会影响文件大小。
【讨论】:
以上是关于通过 ICodecAPI 为 H.264 IMFSinkWriter 编码器设置属性的主要内容,如果未能解决你的问题,请参考以下文章
使用 FFmpeg 通过 RTMP 发送 H.264 编码流