MediaFoundation:注册自定义 ClassFactory 不起作用

Ram*_*san 5 mpeg video-encoding h.264 ms-media-foundation

背景: 我正在使用 SinkWriter 将 NV12 缓冲区编码为包装在 MPEG4 容器中的 h264 视频流。一切正常,但有一个问题,因为 SinkWriter 抽象了低级编码器配置,我无法控制 GOP 大小、B 画面计数、CODECAPI_AVEncCommonRateControlMode 等属性。

问题是因为SinkWriter仅在SetInputMediaType调用之后实例化编码器转换,并且只有在该点之后我们才能获得CodecAPI实例。因此,在这一切发生之前,我们无法控制编码器并配置必要的道具,它也永远不会通过 CodecAPI 实例对编码器进行进一步更改。

实验: 我尝试了 PropertyStore( MF_SINK_WRITER_ENCODER_CONFIG ) 方法,但似乎没有任何变化(这可能是平台/编码器特定的行为),我还看到很多人抱怨这些 API 的不可预测行为。然后,我遇到了这个MSDN 线程(将近 7 年前的帖子),其中用户描述了他如何通过在 Windows7 机器上本地注册自定义类工厂来处理这个问题。

问题:在MSDN线程作为参考,我想实现的IClassFactory并通过注册它MFTRegisterLocal但功能的CreateInstance永远不会得到所谓的对我(的Windows 10的机器)。我只得到为 IID_IClassFactory 和 IID_IMFAttributes 接口调用的 QueryInterface 方法。而且,SinkWriter 似乎正在自行获取 MFT。

我知道我可能做错了什么,而且我不是 COM 专家。有没有其他方法可以实现这一目标?

自定义类工厂实现:

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;
};
Run Code Online (Sandbox Code Playgroud)

注册自定义类工厂:

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);
Run Code Online (Sandbox Code Playgroud)

任何帮助,将不胜感激。

Rom*_* R. 2

你在这里如履薄冰,因为你想做的事情不应该起作用。您确实(或至少可以)注册本地转换,但 API 通常会更喜欢其他现有的 MFT,因为它们具有更高的内部优点(并且具有硬件辅助支持),因此您不需要覆盖现有行为。

您真正的选择是:

  1. 使用MF_SINK_WRITER_ENCODER_CONFIG属性传递编码器特定配置
  2. 对于编码器现有的 CLSID,使用 COM 而不是 MF 注册类工厂,以便 MFT 的 COM 实例化按照您的方式进行;您必须弄清楚有关 COM 的详细信息才能实现此目的,并且一般来说,我不建议在没有充分理由的情况下干扰 COM 注册/实例化的标准行为
  3. 在 Sink Writer API 之外单独运行编码 MFT(或其等效项 - 在这种情况下您不必完全使用 MFT),并向 Sink Writer 提供已压缩的数据