Media Foundation и COM

Microsoft Media Foundation использует сочетание конструкций COM, но не является полностью COM-API. В этом разделе описывается взаимодействие между COM и Media Foundation. Он также определяет некоторые рекомендации по разработке компонентов подключаемых модулей Media Foundation. Следуя этим рекомендациям, вы можете избежать некоторых распространенных, но тонких ошибок программирования.

Рекомендации по приложениям

В Media Foundation асинхронная обработка и обратные вызовы обрабатываются рабочими очередями. Рабочие очереди всегда имеют многопоточные потоки (MTA), поэтому приложение будет иметь более простую реализацию, если она выполняется в потоке MTA, а также. Поэтому рекомендуется вызвать CoInitializeEx с флагом COINIT_MULTITHREADED.

Media Foundation не маршалирует однопоточные объекты квартиры (STA) для работы потоков очереди. Он не гарантирует, что инварианты STA поддерживаются. Поэтому приложение STA должно быть осторожным, чтобы не передавать объекты ИЛИ прокси-серверы в API Media Foundation. Объекты, которые являются только STA, не поддерживаются в Media Foundation.

Если у вас есть прокси-сервер STA для объекта MTA или свободного потока, объект можно маршалировать в прокси-сервер MTA с помощью обратного вызова рабочей очереди. Функция CoCreateInstance может возвращать необработанный указатель или прокси-сервер STA в зависимости от объектной модели, определенной в реестре для этого CLSID. Если возвращается прокси-сервер STA, не следует передавать указатель на API Media Foundation.

Например, предположим, что необходимо передать указатель IPropertyStore методу IMFSourceResolver::BeginCreateObjectFromURL. Чтобы создать указатель IPropertyStore, можно вызвать PSCreateMemoryPropertyStore. При вызове из STA необходимо маршалировать указатель перед передачей в BeginCreateObjectFromURL.

В следующем коде показано, как маршалировать прокси-сервер STA в API Media Foundation.

class CCreateSourceMarshalCallback
    : public IMFAsyncCallback
{
public:
    CCreateSourceMarshalCallback(
        LPCWSTR szURL, 
        IMFSourceResolver* pResolver, 
        IPropertyStore* pSourceProps, 
        IMFAsyncCallback* pCompletionCallback, 
        HRESULT& hr
        )
        : m_szURL(szURL), 
          m_pResolver(pResolver), 
          m_pCompletionCallback(pCompletionCallback),
          m_pGIT(NULL),
          m_cRef(1)
    {
        hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL, 
            CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pGIT));

        if(SUCCEEDED(hr))
        {
            hr = m_pGIT->RegisterInterfaceInGlobal(
                pSourceProps, IID_IPropertyStore, &m_dwInterfaceCookie);
        }
    }
    ~CCreateSourceMarshalCallback()
    {
        SafeRelease(&m_pResolver);
        SafeRelease(&m_pCompletionCallback);
        SafeRelease(&m_pGIT);
    }


    STDMETHOD_(ULONG, AddRef)()
    {
        return InterlockedIncrement(&m_cRef);
    }

    STDMETHOD_(ULONG, Release)()
    {
        LONG cRef = InterlockedDecrement(&m_cRef);
        if (0 == cRef)
        {
            delete this;
        }
        return cRef;
    }

    STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject)
    {
        static const QITAB qit[] = 
        {
            QITABENT(CCreateSourceMarshalCallback, IMFAsyncCallback),
            { 0 }
        };
        return QISearch(this, qit, riid, ppvObject);

    }

    STDMETHOD(GetParameters)(DWORD* pdwFlags, DWORD* pdwQueue)
    {
        return E_NOTIMPL;
    }

    STDMETHOD(Invoke)(IMFAsyncResult* pResult)
    {
        IPropertyStore *pSourceProps = NULL;

        HRESULT hr = m_pGIT->GetInterfaceFromGlobal(
            m_dwInterfaceCookie, 
            IID_PPV_ARGS(&pSourceProps)
            );

        if(SUCCEEDED(hr))
        {
            hr = m_pResolver->BeginCreateObjectFromURL(
                m_szURL, MF_RESOLUTION_MEDIASOURCE, pSourceProps, NULL, 
                m_pCompletionCallback, NULL);
        }

        SafeRelease(&pSourceProps);
        return hr;
    }

private:
    LPCWSTR m_szURL;
    IMFSourceResolver *m_pResolver;
    IMFAsyncCallback *m_pCompletionCallback;
    IGlobalInterfaceTable *m_pGIT;
    DWORD m_dwInterfaceCookie;
    LONG m_cRef;
};

Дополнительные сведения о глобальной таблице интерфейсов см. в разделе IGlobalInterfaceTable.

Если вы используете Media Foundation в процессе, объекты, возвращаемые методами и функциями Media Foundation, являются прямыми указателями на объект. Для межпроцессного media Foundation эти объекты могут быть прокси-серверами MTA и должны быть маршалированы в поток STA при необходимости. Аналогичным образом объекты, полученные внутри обратного вызова ( например, топология из события MESessionTopologyStatus ), являются прямыми указателями, когда Media Foundation используется в процессе, но являются прокси-серверами MTA, когда Media Foundation используется межпроцессно.

Примечание.

Наиболее распространенным сценарием использования Media Foundation является межпроцессный путь к защищенному носителю (PMP). Однако эти замечания применяются к любой ситуации, когда API Media Foundation используются через RPC.

 

Все реализации МВФAsyncCallback должны быть совместимыми с MTA. Эти объекты не должны быть COM-объектами вообще. Но если они есть, они не могут работать в STA. Функция IMFAsyncCallback::Invoke будет вызываться в потоке MTA workqueue, и предоставленный объект IMFAsyncResult будет либо прямым указателем объекта, либо прокси-сервером MTA.

Рекомендации по компонентам Media Foundation

Существует две категории объектов Media Foundation, которые должны быть обеспокоены COM. Некоторые компоненты, такие как преобразования или обработчики потоков байтов, являются полными COM-объектами, созданными CLSID. Эти объекты должны соответствовать правилам для com-квартир, как для внутрипроцессного, так и межпроцессного фонда Media Foundation. Другие компоненты Media Foundation не являются полными COM-объектами, но требуются прокси-серверы COM для воспроизведения между процессами. Объекты в этой категории включают источники мультимедиа и объект активации. Эти объекты могут игнорировать проблемы с квартирой, если они будут использоваться только для in-process Media Foundation.

Хотя не все объекты Media Foundation являются COM-объектами, все интерфейсы Media Foundation являются производными от IUnknown. Поэтому все объекты Media Foundation должны реализовать IUnknown в соответствии со спецификациями COM, включая правила подсчета ссылок и QueryInterface. Все объекты с подсчетом ссылок также должны гарантировать, что DllCanUnloadNow не позволит выгрузить модуль во время сохранения объектов.

Компоненты Media Foundation не могут быть объектами STA. Многие объекты Media Foundation не должны быть COM-объектами вообще. Но если они есть, они не могут работать в STA. Все компоненты Media Foundation должны быть потокобезопасны. Некоторые объекты Media Foundation также должны быть свободные потоки или нейтрализуются в квартире. В следующей таблице указаны требования к реализации пользовательского интерфейса:

Интерфейс Категория Необходимая квартира
МВФActivate Прокси-сервер между процессами Свободный поток или нейтральный
МВФByteStreamHandler COM-объект MTA
МВФContentProtectionManager Прокси-сервер между процессами Свободный поток или нейтральный
МВФQualityManager COM-объект Свободный поток или нейтральный
IMFMediaSource Прокси-сервер между процессами Свободный поток или нейтральный
МВФSchemeHandler COM-объект MTA
МВФTopoLoader COM-объект Свободный поток или нейтральный
МВФTransform COM-объект MTA

 

В зависимости от реализации могут быть дополнительные требования. Например, если приемник мультимедиа реализует другой интерфейс, позволяющий приложению выполнять прямые вызовы функций к приемнику, приемник должен быть свободным потоком или нейтральным, чтобы он мог обрабатывать прямые межпроцессные вызовы. Любой объект может быть свободным потоком; В этой таблице указаны минимальные требования.

Рекомендуемый способ реализации свободных потоков или нейтральных объектов — агрегирование маршалера с бесплатным потоком. Дополнительные сведения см. в разделе CoCreateFreeThreadedMarshaler. В соответствии с требованием не передавать объекты STA или прокси-серверы в API Media Foundation, объекты с бесплатным потоком не должны беспокоиться о маршалинге указателей ввода STA в бесплатных потоках компонентов.

Компоненты, использующие очередь работы с длинной функцией (MFASYNC_CALLBACK_QUEUE_LONG_FUNCTION), должны выполнять больше заботы. Потоки в долгой функции создают собственные STA. Компоненты, использующие длинную функцию для обратных вызовов, должны избегать создания COM-объектов в этих потоках и должны быть осторожны, чтобы маршалировать прокси-серверы в STA по мере необходимости.

Итоги

Приложения будут иметь более удобное время, если они взаимодействуют с Media Foundation из потока MTA, но это возможно с некоторой осторожностью использовать Media Foundation из потока STA. Media Foundation не обрабатывает компоненты STA, а приложения должны быть осторожны, чтобы не передавать объекты STA в API Media Foundation. Некоторые объекты имеют дополнительные требования, особенно объекты, которые выполняются в межпроцессной ситуации. Следуя этим рекомендациям, вы сможете избежать ошибок COM, взаимоблокировок и непредвиденных задержек в обработке мультимедиа.

API платформы Media Foundation

Архитектура Media Foundation