MediaFoundation:注册自定义 ClassFactory 不起作用
MediaFoundation: Registering custom ClassFactory doesn't work
背景:
我正在使用 SinkWriter 将 NV12 缓冲区编码为包装在 MPEG4 容器中的 h264 视频流。一切正常,但有一个问题,因为 SinkWriter 抽象了低级编码器配置,我无法控制 GOP 大小、B 图片计数、CODECAPI_AVEncCommonRateControlMode 等属性
问题是由于 SinkWriter 仅在 SetInputMediaType 调用后实例化编码器转换,我们将能够仅在该点之后获取 CodecAPI 实例。因此,我们无法在这一切发生之前控制编码器并配置必要的道具,它也永远不会通过 CodecAPI 实例对编码器进行进一步更改。
实验:
我尝试了 PropertyStore(MF_SINK_WRITER_ENCODER_CONFIG) 方法,但似乎没有任何变化(这可能是 platform/encoder 特定行为),我也可以看到很多人抱怨这些 API 的不可预测的行为。然后,我遇到了 this MSDN 线程(将近 7 年 post),其中用户描述了他如何通过在 [=53= 上本地注册自定义 class 工厂来处理这个问题] 机器。
问题:
有 the MSDN thread as a reference, I tried implementing IClassFactory and registering it through MFTRegisterLocal 但 CreateInstance 函数从来没有为我调用过(Windows 10 台机器)。我只得到为 IID_IClassFactory 和 IID_IMFAttributes 接口调用的 QueryInterface 方法。而且,SinkWriter 似乎正在继续自行获取 MFT。
我知道我可能做错了什么,而且我不是 COM 方面的专家。还有其他方法可以实现吗?
自定义class工厂实现:
class MyClassFactory : public IClassFactory {
public:
MyClassFactory () : _cRef(1) {}
~MyClassFactory() {}
// Only this method is getting called
STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
HRESULT hr = E_NOTIMPL;
// Only the below 2 cases (IID_IClassFactory and IID_IMFAttributes) are getting hit
if (IID_IClassFactory == riid)
{
*ppv = static_cast<IClassFactory*>(this);
if (*ppv) {
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
}
hr = S_OK;
}
else if (IID_IMFAttributes == riid)
{
if (!pEncoder) {
hr = FindEncoderEx(&pEncoder);
}
IMFAttributes *attributes;
hr = pEncoder->GetAttributes(&attributes);
*ppv = attributes;
}
else
{
//This case has never been reached
}
return hr;
}
//This is never called
STDMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv)
{
HRESULT hr = S_OK;
if (pUnkOuter != NULL)
{
if (riid != __uuidof(IUnknown))
{
return E_NOINTERFACE;
}
}
if (!pEncoder) {
hr = FindEncoderEx(&pEncoder);
}
hr = pEncoder->QueryInterface(riid, ppv);
return hr;
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
IFACEMETHODIMP_(ULONG) Release()
{
assert(_cRef > 0);
LONG cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
STDMETHODIMP LockServer(BOOL fLock)
{
if (fLock)
{
AddRef();
}
else {
Release();
}
return S_OK;
}
HRESULT FindEncoderEx(IMFTransform** ppEncoder)
{
...
}
protected:
LONG _cRef;
CComPtr<IMFTransform> pEncoder = NULL;
};
注册定制class工厂:
MyClassFactory* cf = new MyClassFactory();
MFT_REGISTER_TYPE_INFO infoInput = { MFMediaType_Video, MFVideoFormat_NV12 };
MFT_REGISTER_TYPE_INFO infoOutput = { MFMediaType_Video, MFVideoFormat_H264 };
MFTRegisterLocal(cf, MFT_CATEGORY_VIDEO_ENCODER, L"MyClassFactory", 0, 1, &infoInput, 1, &infoOutput);
如有任何帮助,我们将不胜感激。
您在这里如履薄冰,因为您尝试做的事情不应该起作用。您确实(或至少可以)注册一个本地转换,但 API 通常会更喜欢其他现有的 MFT 而不是您的 MFT,因为它们具有更高的内部价值(并且有硬件辅助支持),因此您不需要覆盖现有的行为。
您的实际选择是:
- 使用
MF_SINK_WRITER_ENCODER_CONFIG
attribute传递编码器特定配置
- 为编码器的现有 CLSID 使用 COM 而不是 MF 注册 class 工厂,以便 MFT 的 COM 实例化按照您的方式进行;您必须弄清楚有关 COM 的详细信息才能实现这一点,一般来说,我不建议在没有充分理由的情况下干扰 COM registration/instantiation 的标准行为
- 运行 在 Sink Writer API 之外单独编码 MFT(或其等价物 - 在这种情况下你不必完全使用 MFT),并为 Sink Writer 提供已经压缩的数据
背景: 我正在使用 SinkWriter 将 NV12 缓冲区编码为包装在 MPEG4 容器中的 h264 视频流。一切正常,但有一个问题,因为 SinkWriter 抽象了低级编码器配置,我无法控制 GOP 大小、B 图片计数、CODECAPI_AVEncCommonRateControlMode 等属性
问题是由于 SinkWriter 仅在 SetInputMediaType 调用后实例化编码器转换,我们将能够仅在该点之后获取 CodecAPI 实例。因此,我们无法在这一切发生之前控制编码器并配置必要的道具,它也永远不会通过 CodecAPI 实例对编码器进行进一步更改。
实验: 我尝试了 PropertyStore(MF_SINK_WRITER_ENCODER_CONFIG) 方法,但似乎没有任何变化(这可能是 platform/encoder 特定行为),我也可以看到很多人抱怨这些 API 的不可预测的行为。然后,我遇到了 this MSDN 线程(将近 7 年 post),其中用户描述了他如何通过在 [=53= 上本地注册自定义 class 工厂来处理这个问题] 机器。
问题: 有 the MSDN thread as a reference, I tried implementing IClassFactory and registering it through MFTRegisterLocal 但 CreateInstance 函数从来没有为我调用过(Windows 10 台机器)。我只得到为 IID_IClassFactory 和 IID_IMFAttributes 接口调用的 QueryInterface 方法。而且,SinkWriter 似乎正在继续自行获取 MFT。
我知道我可能做错了什么,而且我不是 COM 方面的专家。还有其他方法可以实现吗?
自定义class工厂实现:
class MyClassFactory : public IClassFactory {
public:
MyClassFactory () : _cRef(1) {}
~MyClassFactory() {}
// Only this method is getting called
STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
HRESULT hr = E_NOTIMPL;
// Only the below 2 cases (IID_IClassFactory and IID_IMFAttributes) are getting hit
if (IID_IClassFactory == riid)
{
*ppv = static_cast<IClassFactory*>(this);
if (*ppv) {
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
}
hr = S_OK;
}
else if (IID_IMFAttributes == riid)
{
if (!pEncoder) {
hr = FindEncoderEx(&pEncoder);
}
IMFAttributes *attributes;
hr = pEncoder->GetAttributes(&attributes);
*ppv = attributes;
}
else
{
//This case has never been reached
}
return hr;
}
//This is never called
STDMETHODIMP CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv)
{
HRESULT hr = S_OK;
if (pUnkOuter != NULL)
{
if (riid != __uuidof(IUnknown))
{
return E_NOINTERFACE;
}
}
if (!pEncoder) {
hr = FindEncoderEx(&pEncoder);
}
hr = pEncoder->QueryInterface(riid, ppv);
return hr;
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
IFACEMETHODIMP_(ULONG) Release()
{
assert(_cRef > 0);
LONG cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
STDMETHODIMP LockServer(BOOL fLock)
{
if (fLock)
{
AddRef();
}
else {
Release();
}
return S_OK;
}
HRESULT FindEncoderEx(IMFTransform** ppEncoder)
{
...
}
protected:
LONG _cRef;
CComPtr<IMFTransform> pEncoder = NULL;
};
注册定制class工厂:
MyClassFactory* cf = new MyClassFactory();
MFT_REGISTER_TYPE_INFO infoInput = { MFMediaType_Video, MFVideoFormat_NV12 };
MFT_REGISTER_TYPE_INFO infoOutput = { MFMediaType_Video, MFVideoFormat_H264 };
MFTRegisterLocal(cf, MFT_CATEGORY_VIDEO_ENCODER, L"MyClassFactory", 0, 1, &infoInput, 1, &infoOutput);
如有任何帮助,我们将不胜感激。
您在这里如履薄冰,因为您尝试做的事情不应该起作用。您确实(或至少可以)注册一个本地转换,但 API 通常会更喜欢其他现有的 MFT 而不是您的 MFT,因为它们具有更高的内部价值(并且有硬件辅助支持),因此您不需要覆盖现有的行为。
您的实际选择是:
- 使用
MF_SINK_WRITER_ENCODER_CONFIG
attribute传递编码器特定配置 - 为编码器的现有 CLSID 使用 COM 而不是 MF 注册 class 工厂,以便 MFT 的 COM 实例化按照您的方式进行;您必须弄清楚有关 COM 的详细信息才能实现这一点,一般来说,我不建议在没有充分理由的情况下干扰 COM registration/instantiation 的标准行为
- 运行 在 Sink Writer API 之外单独编码 MFT(或其等价物 - 在这种情况下你不必完全使用 MFT),并为 Sink Writer 提供已经压缩的数据