Media Foundation y COM

Microsoft Media Foundation usa una combinación de construcciones COM, pero no es una API totalmente basada en COM. En este tema se describe la interacción entre COM y Media Foundation. También define algunos procedimientos recomendados para desarrollar componentes de complementos de Media Foundation. Si sigue estos procedimientos, puede evitar algunos errores de programación comunes pero sutiles.

Procedimientos recomendados para aplicaciones

En Media Foundation, las colas de trabajo controlan el procesamiento asíncrono y las devoluciones de llamada. Las colas de trabajo siempre tienen subprocesos de apartamento multiproceso (MTA), por lo que una aplicación tendrá una implementación más sencilla si también se ejecuta en un subproceso de MTA. Por tanto, se recomienda llamar a CoInitializeEx con la flag COINIT_MULTITHREADED.

Media Foundation no serializa objetos de un solo subproceso controlado (STA) para trabajar con subprocesos de cola. Ni tampoco garantiza que se mantengan las invariables de STA. Por ello, una aplicación de STA debe tener cuidado de no pasar objetos de STA ni servidores proxy a las API de Media Foundation. Los objetos que son solo de STA no se admiten en Media Foundation.

Si tiene un proxy de STA en un objeto de MTA o sin subprocesos, el objeto se puede serializar en un proxy de MTA mediante una devolución de llamada de cola de trabajo. La función CoCreateInstance puede devolver un puntero sin procesar o un proxy de STA, en función del modelo de objetos definido en el registro para ese CLSID. Si se devuelve un proxy de STA, no debe pasar el puntero a una API de Media Foundation.

Por ejemplo, supongamos que desea pasar un puntero IPropertyStore al método IMFSourceResolver::BeginCreateObjectFromURL. Puede llamar al PSCreateMemoryPropertyStore para crear el puntero IPropertyStore. Si llama desde un STA, debe serializar el puntero antes de pasarlo a BeginCreateObjectFromURL.

En el código siguiente se ve cómo serializar un proxy de STA en una API de 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 obtener más información acerca de la tabla de interfaz global, consulte IGlobalInterfaceTable.

Si usa Media Foundation integrado en un proceso, los objetos devueltos de los métodos y funciones de Media Foundation serán punteros directos al objeto. Si se usa Media Foundation en varios procesos, estos objetos pueden ser servidores proxy de MTA y deben serializarse en un subproceso de STA si es necesario. Del mismo modo, los objetos obtenidos dentro de una devolución de llamada (por ejemplo, una topología del evento MESessionTopologyStatus) serán punteros directos cuando Media Foundation se use integrado en un proceso, pero serán servidores proxy de MTA cuando se use Media Foundation en varios procesos.

Nota:

El escenario más común para usar Media Foundation en varios procesos es con la ruta de acceso multimedia protegida (PMP). Sin embargo, estas cuestiones se aplican a cualquier situación cuando las API de Media Foundation se usan a través de RPC.

 

Todas las implementaciones de IMFAsyncCallback deben ser compatibles con MTA. Estos objetos no necesitan ser objetos COM en absoluto. Pero si lo son, no pueden ejecutarse en el STA. La función IMFAsyncCallback::Invoke se invocará en un subproceso de cola de trabajo de MTA y el objeto generado IMFAsyncResult será un puntero de objeto directo o un proxy de MTA.

Procedimientos recomendados para componentes de Media Foundation

Hay dos categorías de objetos de Media Foundation que tienen que ver con COM. Algunos componentes, como transformaciones o controladores de secuencias de bytes, son objetos COM completos creados por CLSID. Estos objetos deben seguir las reglas de los apartamentos COM, tanto cuando se usa Media Foundation integrado en un proceso y en varios procesos. Otros componentes de Media Foundation no son objetos COM completos, pero necesitan servidores proxy COM para la reproducción de varios procesos. Los objetos de esta categoría incluyen orígenes multimedia y objeto de activación. Estos objetos pueden saltarse los problemas de los apartamentos si solo se van a usar con Media Foundation integrado en un proceso.

Aunque no todos los objetos de Media Foundation son objetos COM, todas las interfaces de Media Foundation derivan de IUnknown. Por tanto, todos los objetos de Media Foundation deben implementar IUnknown según las especificaciones COM, incluidas las reglas para la contabilización de referencias y QueryInterface. Todos los objetos con contabilización de referencias también deben asegurarse de que DllCanUnloadNow no permitirá que el módulo se descargue mientras los objetos se conservan.

Los componentes de Media Foundation no pueden ser objetos de STA. Muchos objetos de Media Foundation no necesitan ser objetos COM en absoluto. Pero si lo son, no pueden ejecutarse en el STA. Todos los componentes de Media Foundation deben ser seguros para los subprocesos. Algunos objetos de Media Foundation también tener subprocesos libres o neutrales en cuanto a apartamentos. En la tabla siguiente se indican los requisitos para las implementaciones de interfaz personalizadas:

Interfaz Category Apartamento necesario
IMFActivate Proxy de varios procesos Subproceso libre o neutral
IMFByteStreamHandler Objeto COM MTA
IMFContentProtectionManager Proxy de varios procesos Subproceso libre o neutral
IMFQualityManager Objeto COM Subproceso libre o neutral
IMFMediaSource Proxy de varios procesos Subproceso libre o neutral
IMFSchemeHandler Objeto COM MTA
IMFTopoLoader Objeto COM Subproceso libre o neutral
IMFTransform Objeto COM MTA

 

Pueden existir más requisitos en función de la implementación. Por ejemplo, si un receptor de multimedia implementa otra interfaz que permite a la aplicación realizar llamadas de función directas al receptor, el receptor tendría que ser libre o neutral, de modo que pueda controlar las llamadas directas de varios procesos. Cualquier objeto puede ser un subproceso libre; en esta tabla figuran los requisitos mínimos.

La forma recomendada de implementar objetos con subprocesos libres o neutros es agregando el serializador de subprocesos libres. Para obtener más información, consulte CoCreateFreeThreadedMarshaler. En virtud del requisito de no pasar objetos de STA ni servidores proxy a las API de Media Foundation, los objetos con subprocesos libres no necesitan encargarse de serializar punteros de entrada de STA en componentes con subprocesos libres.

Los componentes que usan la cola de trabajo de función larga (MFASYNC_CALLBACK_QUEUE_LONG_FUNCTION) deben tener más cuidado. Los subprocesos de la función larga crean su propio STA. Los componentes que usan la función de cola de trabajo larga en devoluciones de llamada deben evitar la creación de objetos COM en estos subprocesos y deben tener cuidado a la hora de serializar los servidores proxy en el STA, según sea necesario.

Resumen

Las aplicaciones lo tendrán más sencillo si interactúan con Media Foundation a través de un subproceso MTA, pero es posible usar Media Foundation a través de un subproceso STA si se hace con cuidado. Media Foundation no controla los componentes de STA y las aplicaciones deben tener cuidado de no pasar objetos de STA a las API de Media Foundation. Algunos objetos tienen requisitos adicionales, especialmente los objetos que se ejecutan en un contexto de varios procesos. Si se siguen estas instrucciones, podrá evitar errores COM, interbloqueos y retrasos inesperados en el procesamiento multimedia.

API de plataforma de Media Foundation

Arquitectura de Media Foundation