MediaFoundation: регистрация пользовательских ClassFactory не работает - PullRequest
5 голосов
/ 06 января 2020

Справочная информация: Я кодирую буферы NV12 в видеопоток h264, обернутый в контейнер MPEG4, используя SinkWriter. Все работает нормально, но есть одна проблема, так как SinkWriter абстрагирует конфигурации низкоуровневого кодера, я не могу управлять такими свойствами, как размер GOP, количество B-изображений, CODECAPI_AVEncCommonRateControlMode и т. Д. c.

Проблема связано с тем, что SinkWriter создает преобразование кодера только после вызова SetInputMediaType , и мы сможем получить экземпляр CodecAPI только после этой точки , Таким образом, у нас нет возможности управлять кодировщиком и настраивать необходимые реквизиты до того, как все это произойдет, он также никогда не учитывает дальнейшие изменения в кодировщике через экземпляр CodecAPI.

Эксперименты: Я попробовал PropertyStore ( MF_SINK_WRITER_ENCODER_CONFIG ), но, похоже, ничего не меняется (это может быть поведение платформы / кодировщика, определяющее c поведение), я также мог видеть, что многие люди жалуются на непредсказуемое поведение этих API. Затем я наткнулся на эту ветку MSDN (почти 7-летний пост), в которой пользователь описал, как он справился с этой проблемой, локально зарегистрировав фабрику пользовательских классов на машине Windows7.

Проблема: Имея поток MSDN в качестве ссылки, я попытался реализовать IClassFactory и зарегистрировать его через MFTRegisterLocal , но функция CreateInstance никогда не получает позвал меня (Windows 10 автомат). Я получаю только метод QueryInterface, вызываемый для интерфейсов IID_IClassFactory и IID_IMFAttributes. И, похоже, 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;
};

Регистрация фабрики пользовательских классов:

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);

Буду признателен за любую помощь.

1 Ответ

2 голосов
/ 06 января 2020

Вы идете по тонкому льду здесь, потому что то, что вы пытаетесь сделать, не должно работать. Вы действительно (или, по крайней мере, можете) зарегистрировать локальное преобразование, но API обычно предпочитает другие существующие MFT вашим, потому что они имеют более высокую внутреннюю ценность (и с поддержкой аппаратного обеспечения), поэтому от вас не ожидается переопределение существующего поведения.

Ваши реальные варианты:

  1. для использования MF_SINK_WRITER_ENCODER_CONFIG атрибута для передачи спецификатора кодировщика c конфигурация
  2. зарегистрировать фабрику классов с COM, а чем с MF для существующего CLSID кодера, чтобы COM-реализация MFT пошла своим путем; вам нужно было бы выяснить подробности о COM, чтобы реализовать это, и я, вообще, не рекомендовал бы вмешиваться в стандартное поведение регистрации / создания COM без веской причины
  3. запустить кодировку MFT (или ее эквивалент - вы не не нужно использовать MFT именно в этом случае) отдельно, вне Sink Writer API, и подавать в Sink Writer уже сжатые данные
...