Media Foundation e COM

O Microsoft Media Foundation usa uma combinação de constructos COM, mas não é uma API totalmente baseada em COM. Este tópico descreve a interação entre COM e Media Foundation. Ele também define algumas práticas recomendadas para desenvolver componentes de plug-in do Media Foundation. Seguir essas práticas pode ajudá-lo a evitar alguns erros de programação comuns, mas 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 ele também for executado em um thread MTA. Portanto, é recomendável chamar CoInitializeEx com o sinalizador COINIT_MULTITHREADED .

O Media Foundation não realiza marshaling de objetos STA (apartment de thread único) para threads de fila de trabalho. Também não garante que as invariáveis sta sejam mantidas. 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 que são somente STA no Media Foundation.

Se você tiver um proxy STA para um MTA ou um objeto de thread livre, o objeto poderá ser empacotado em um proxy MTA usando um retorno de chamada de fila de trabalho. A função CoCreateInstance pode retornar um ponteiro bruto ou 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 o método IMFSourceResolver::BeginCreateObjectFromURL . Você pode chamar PSCreateMemoryPropertyStore para criar o ponteiro IPropertyStore . Se você estiver chamando de um STA, deverá realizar marshaling do ponteiro antes de passá-lo para BeginCreateObjectFromURL.

O código a seguir mostra como realizar marshaling de 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 de métodos e funções do Media Foundation serão ponteiros diretos para o objeto . Para o Media Foundation entre processos, 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 — por exemplo, uma topologia do evento MESessionTopologyStatus — são ponteiros diretos quando o Media Foundation é usado em processo, mas são proxies 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 PMP ( Caminho de Mídia Protegida ). No entanto, essas observações se aplicam a qualquer situação em que as APIs do Media Foundation são usadas por meio de RPC.

 

Todas as implementações de IMFAsyncCallback devem ser compatíveis com MTA. Esses objetos não precisam ser objetos COM. Mas, se estiverem, não poderão ser executados no STA. A função IMFAsyncCallback::Invoke será invocada em um thread de workqueue do MTA e o objeto IMFAsyncResult fornecido 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 o 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 apartamentos COM, tanto para o Media Foundation em processo quanto para o cross-process. Outros componentes do Media Foundation não são objetos COM completos, mas precisam de proxies COM para reprodução entre processos. Os objetos nessa categoria incluem fontes de mídia e objeto de ativação. Esses objetos poderão 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 o 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 permitirá 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 estiverem, não poderão ser executados no STA. Todos os componentes do Media Foundation devem ser thread-safe. Alguns objetos do Media Foundation também devem ser livres ou neutros em apartamentos. A tabela a seguir especifica os requisitos para implementações de interface personalizada:

Interface Categoria Apartamento necessário
IMFActivate Proxy entre processos Thread livre ou neutro
IMFByteStreamHandler Objeto COM MTA
IMFContentProtectionManager Proxy entre processos Thread livre ou neutro
IMFQualityManager Objeto COM Thread livre ou neutro
IMFMediaSource Proxy entre processos Thread livre ou neutro
IMFSchemeHandler Objeto COM MTA
IMFTopoLoader Objeto COM Thread livre ou neutro
IMFTransform Objeto COM MTA

 

Pode haver requisitos adicionais dependendo da implementação. Por exemplo, se um coletor de mídia implementar outra interface que permita que o aplicativo faça chamadas de função diretas para o coletor, o coletor precisará ser livre ou neutro, para que ele 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 neutro é agregando o marshaler de thread livre. Para obter mais detalhes, consulte a documentação do MSDN em CoCreateFreeThreadedMarshaler. De acordo com o requisito de não passar objetos STA ou proxies para APIs do Media Foundation, os objetos de thread livre não precisam se preocupar com o marshaling de ponteiros de entrada STA em componentes com thread livre.

Os componentes que usam a fila de trabalho de funções longas (MFASYNC_CALLBACK_QUEUE_LONG_FUNCTION) devem ter mais cuidado. Os threads na pasta de trabalho de função longa criam seu próprio STA. Os componentes que usam a pasta 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 realizar marshaling de proxies para o STA conforme necessário.

Resumo

Os aplicativos terão um tempo mais fácil se interagirem com o Media Foundation a partir de um thread MTA, mas é possível com algum cuidado usar o Media Foundation 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 executados em uma situação entre processos. Seguir essas diretrizes ajudará a evitar erros de COM, deadlocks e atrasos inesperados no processamento de mídia.

Media Foundation Platform APIs

Arquitetura Media Foundation