Media Foundation e COM

O Microsoft Media Foundation usa uma combinação de construções COM, mas não é uma API totalmente baseada em COM. Este tópico descreve a interação entre o COM e a Media Foundation. Ele também define algumas práticas recomendadas para o desenvolvimento de componentes de plug-in do Media Foundation. Seguir essas práticas pode ajudá-lo a evitar alguns erros de programação comuns, porém sutis.

Práticas recomendadas para aplicativos

No Media Foundation, o processamento assíncrono e os retornos de chamada são tratados por filas de trabalho. As filas de trabalho sempre têm threads MTA ( Multithreaded Apartment), portanto, um aplicativo terá uma implementação mais simples se também for executado em um thread MTA. Portanto, é recomendável chamar o CoInitializeEx com o sinalizador COINIT_MULTITHREADED.

O Media Foundation não empacota objetos STA (single-threaded apartment) para threads de fila de trabalho. Também não garante que os invariantes STA sejam mantidos. Portanto, um aplicativo STA deve ter cuidado para não passar objetos STA ou proxies para APIs do Media Foundation. Não há suporte para objetos somente STA no Media Foundation.

Se você tiver um proxy STA para um MTA ou objeto de thread livre, o objeto poderá ser empacotado para um proxy MTA usando um retorno de chamada de fila de trabalho. A função CoCreateInstance pode retornar a um ponteiro bruto ou a um proxy STA, dependendo do modelo de objeto definido no registro para esse CLSID. Se um proxy STA for retornado, você não deverá passar o ponteiro para uma API do Media Foundation.

Por exemplo, suponha que você queira passar um ponteiro IPropertyStore para um método IMFSourceResolver::BeginCreateObjectFromURL. Você pode chamar PSCreateMemoryPropertyStore para criar o ponteiro IPropertyStore. Se você estiver chamando de um STA, deverá empacotar o ponteiro antes de passá-lo para BeginCreateObjectFromURL.

O código a seguir mostra como empacotar um proxy STA para uma API do 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;
};

Para obter mais informações sobre a tabela de interface global, consulte IGlobalInterfaceTable.

Se você estiver usando o Media Foundation em processo, os objetos retornados dos métodos e funções do Media Foundation serão ponteiros diretos para o objeto. Para o Media Foundation de processo cruzado, esses objetos podem ser proxies MTA e devem ser empacotados em um thread STA, se necessário. Da mesma forma, os objetos obtidos dentro de um retorno de chamada como por exemplo, uma topologia do evento MESessionTopologyStatus são ponteiros diretos quando o Media Foundation é usado no processo, mas são proxies de MTA quando o Media Foundation é usado entre processos.

Observação

O cenário mais comum para usar o processo cruzado do Media Foundation é com o caminho de mídia protegido (PMP). No entanto, essas observações se aplicam a qualquer situação em que as APIs do Media Foundation são usadas por meio do RPC.

 

Todas as implementações de IMFAsyncCallback devem ser compatíveis com MTA. Esses objetos não precisam ser objetos COM. Mas se forem, não podem concorrer no STA. A função IMFAsyncCallback::Invoke será invocado em um thread de fila de trabalho do MTA, e o objeto IMFAsyncResult será um ponteiro de objeto direto ou um proxy MTA.

Práticas recomendadas para componentes do Media Foundation

Há duas categorias de objetos do Media Foundation que precisam se preocupar com COM. Alguns componentes, como transformações ou manipuladores de fluxo de bytes, são objetos COM completos criados pelo CLSID. Esses objetos devem seguir as regras para COM apartments, tanto para o Media Foundation em processo quanto entre processos. Outros componentes do Media Foundation não são objetos COM completos, mas precisam de proxies COM para reprodução entre processos. Os objetos nesta categoria incluem fontes de mídia e objeto de ativação. Esses objetos podem ignorar problemas de apartamento se forem usados apenas para o Media Foundation em processo.

Embora nem todos os objetos do Media Foundation sejam objetos COM, todas as interfaces do Media Foundation derivam de IUnknown. Portanto, todos os objetos do Media Foundation devem implementar IUnknown de acordo com as especificações COM, incluindo as regras para contagem de referência e QueryInterface. Todos os objetos contados de referência também devem garantir que DllCanUnloadNow não permita que o módulo seja descarregado enquanto os objetos ainda persistirem.

Os componentes do Media Foundation não podem ser objetos STA. Muitos objetos do Media Foundation não precisam ser objetos COM. Mas se forem, não podem concorrer no STA. Todos os componentes do Media Foundation devem ser thread-safe. Alguns objetos do Media Foundation também devem ser de thread livre ou neutros. A tabela a seguir especifica os requisitos para implementações de interface personalizada:

Interface Categoria Apartment necessário
IMFActivate Proxy entre processos Threaded livre ou neutra
IMFByteStreamHandler Objeto COM MTA
IMFContentProtectionManager Proxy entre processos Threaded livre ou neutra
IMFQualityManager Objeto COM Threaded livre ou neutra
IMFMediaSource Proxy entre processos Threaded livre ou neutra
IMFSchemeHandler Objeto COM MTA
IMFTopoLoader Objeto COM Threaded livre ou neutra
IMFTransform Objeto COM MTA

 

Pode haver requisitos adicionais dependendo da implementação. Por exemplo, se um coletor de mídia implementar outra interface que habilite que o aplicativo faça chamadas de função diretas para o coletor, o coletor precisará ser de thread livre ou neutro, para que possa lidar com chamadas diretas entre processos. Qualquer objeto pode ser de thread livre. Esta tabela especifica os requisitos mínimos.

A maneira recomendada de implementar objetos de thread livre ou neutros é agregando o marshaler livre de threads Para obter mais detalhes, consulte CoCreateFreeThreadedMarshaler. De acordo com o requisito de não passar objetos STA ou proxies para APIs do Media Foundation, os objetos livre de thread não precisam se preocupar com o marshaling de ponteiros de entrada STA em componentes de livres de thread.

Os componentes que usam a fila de trabalho de função longa (MFASYNC_CALLBACK_QUEUE_LONG_FUNCTION) devem ter mais cuidado. Os threads na fila de trabalho de função longa criam seu próprio STA. Os componentes que usam a fila de trabalho de função longa para retornos de chamada devem evitar a criação de objetos COM nesses threads e precisam ter cuidado para empacotar proxies para o STA conforme necessário.

Resumo

Os aplicativos terão mais facilidade se interagirem com o Media Foundation a partir de um thread MTA, mas é possível, com algum cuidado, usar o Media Foundation a partir de um thread STA. O Media Foundation não lida com componentes STA e os aplicativos devem ter cuidado para não passar objetos STA para APIs do Media Foundation. Alguns objetos têm requisitos adicionais, especialmente objetos que são executados em uma situação de processo cruzado. Seguir essas diretrizes ajudará a evitar erros COM, deadlocks e atrasos inesperados no processamento de mídia.

APIs da plataforma Media Foundation

Arquitetura do Media Foundation