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 должно быть осторожным, чтобы не передавать объекты STA или прокси-серверы в API Media Foundation. Объекты, которые являются sta-only, не поддерживаются в 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.

 

Все реализации IMFAsyncCallback должны быть совместимыми с MTA. Эти объекты вообще не должны быть COM-объектами. Но если они есть, они не могут работать в STA. Функция IMFAsyncCallback::Invoke будет вызываться в потоке рабочего запроса MTA, а предоставленный объект 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 также должны быть свободными или не зависящими от квартиры. В следующей таблице указаны требования к реализации пользовательского интерфейса:

Интерфейс Категория Требуется квартира
IMFActivate Прокси-сервер между процессами Свободный поток или нейтральный
IMFByteStreamHandler COM-объект MTA
IMFContentProtectionManager Прокси-сервер между процессами Свободный поток или нейтральный
IMFQualityManager COM-объект Свободный поток или нейтральный
IMFMediaSource Прокси-сервер между процессами Свободный поток или нейтральный
IMFSchemeHandler COM-объект MTA
IMFTopoLoader COM-объект Свободный поток или нейтральный
IMFTransform COM-объект MTA

 

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

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

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

Сводка

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

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

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