Partager via


Media Foundation et COM

Microsoft Media Foundation utilise un mélange de constructions COM, mais n’est pas une API entièrement basée sur COM. Cette rubrique décrit l’interaction entre COM et Media Foundation. Elle définit également quelques bonnes pratiques pour développer des composants plug-in de Media Foundation. Suivre ces pratiques peut vous aider à éviter certaines erreurs de programmation courantes mais subtiles.

Bonnes pratiques pour les applications

Dans Media Foundation, le traitement asynchrone et les rappels sont gérés par des files de travail. Les files de travail ont toujours des threads d’appartement multithread (MTA), donc une application aura une implémentation plus simple si elle fonctionne également sur un thread MTA. Il est donc recommandé d’appeler CoInitializeEx avec l’indicateur COINIT_MULTITHREADED.

Media Foundation ne transfère pas les objets d’appartement monothread (STA) vers les threads de file d’attente de travail. Elle ne garantit pas non plus que les invariants STA soient maintenus. Par conséquent, une application STA doit veiller à ne pas passer d’objets ou de proxies STA aux APIs de Media Foundation. Les objets qui sont uniquement STA ne sont pas pris en charge dans Media Foundation.

Si vous avez un proxy STA vers un objet MTA ou à thread libre, l’objet peut être transféré vers un proxy MTA en utilisant un rappel de file d’attente de travail. La fonction CoCreateInstance peut renvoyer soit un pointeur brut, soit un proxy STA, selon le modèle d’objet défini dans le registre pour ce CLSID. Si un proxy STA est renvoyé, vous ne devez pas passer le pointeur à une API de Media Foundation.

Par exemple, supposons que vous souhaitiez passer un pointeur IPropertyStore à la méthode IMFSourceResolver::BeginCreateObjectFromURL. Vous pourriez appeler PSCreateMemoryPropertyStore pour créer le pointeur IPropertyStore. Si vous appelez depuis un STA, vous devez transférer le pointeur avant de le passer à BeginCreateObjectFromURL.

Le code suivant montre comment transférer un proxy STA vers une 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;
};

Pour plus d’informations sur la table des interfaces globales, veuillez consulter la section IGlobalInterfaceTable.

Si vous utilisez Media Foundation en mode processus, les objets renvoyés par les méthodes et fonctions de Media Foundation sont des pointeurs directs vers l’objet. Pour Media Foundation en mode inter-processus, ces objets peuvent être des proxies MTA, et doivent être transférés dans un thread STA si nécessaire. De même, les objets obtenus à l’intérieur d’un rappel, par exemple une topologie de l’événement MESessionTopologyStatus, sont des pointeurs directs lorsque Media Foundation est utilisée en mode processus, mais sont des proxies MTA lorsque Media Foundation est utilisée en mode inter-processus.

Remarque

Le scénario le plus courant pour utiliser Media Foundation en mode inter-processus est avec le chemin d’accès de média protégé (PMP). Cependant, ces remarques s’appliquent à toute situation où les APIs de Media Foundation sont utilisées via RPC.

 

Toutes les implémentations de IMFAsyncCallback doivent être compatibles MTA. Ces objets n’ont pas besoin d’être des objets COM du tout. Mais s’ils le sont, ils ne peuvent pas fonctionner dans le STA. La fonction IMFAsyncCallback::Invoke sera invoquée sur un thread de file d’attente de travail MTA, et l’objet IMFAsyncResult fourni sera soit un pointeur d’objet direct, soit un proxy MTA.

Bonnes pratiques pour les composants de Media Foundation

Il existe deux catégories d’objets Media Foundation qui doivent se soucier de COM. Certains composants, tels que les transformateurs ou les gestionnaires de flux de bytes, sont des objets COM complets créés par CLSID. Ces objets doivent suivre les règles des appartements COM, pour Media Foundation en mode processus et en mode inter-processus. D’autres composants de Media Foundation ne sont pas des objets COM complets, mais ont besoin de proxies COM pour la lecture en mode inter-processus. Les objets de cette catégorie incluent les sources de médias et les objets d’activation. Ces objets peuvent ignorer les problèmes d’appartement s’ils ne seront utilisés que pour Media Foundation en mode processus.

Bien que tous les objets Media Foundation ne soient pas des objets COM, toutes les interfaces Media Foundation dérivent de IUnknown. Par conséquent, tous les objets Media Foundation doivent implémenter IUnknown selon les spécifications COM, y compris les règles pour le comptage de références et QueryInterface. Tous les objets comptés en référence doivent également s’assurer que DllCanUnloadNow ne permettra pas au module d’être déchargé tant que les objets persistent.

Les composants de Media Foundation ne peuvent pas être des objets STA. De nombreux objets Media Foundation n’ont pas besoin d’être des objets COM du tout. Mais s’ils le sont, ils ne peuvent pas fonctionner dans le STA. Tous les composants de Media Foundation doivent être thread-safe. Certains objets Media Foundation doivent également être à thread libre ou neutre en termes d’appartement. Le tableau suivant spécifie les exigences pour les implémentations d’interfaces personnalisées :

Interface Catégorie Appartement requis
IMFActivate Proxy inter-processus À thread libre ou neutre
IMFByteStreamHandler Objet COM MTA
IMFContentProtectionManager Proxy inter-processus À thread libre ou neutre
IMFQualityManager Objet COM À thread libre ou neutre
IMFMediaSource Proxy inter-processus À thread libre ou neutre
IMFSchemeHandler Objet COM MTA
IMFTopoLoader Objet COM À thread libre ou neutre
IMFTransform Objet COM MTA

 

Il peut y avoir des exigences supplémentaires en fonction de l’implémentation. Par exemple, si un récepteur de média implémente une autre interface qui permet à l’application de faire des appels de fonction directs au récepteur, le récepteur devrait être à thread libre ou neutre, afin de pouvoir gérer les appels directs inter-processus. Tout objet peut être à thread libre ; ce tableau spécifie les exigences minimales.

La manière recommandée d’implémenter des objets à thread libre ou neutre est d’agréger le marshaler à thread libre. Pour plus de détails, veuillez consulter la section CoCreateFreeThreadedMarshaler. Conformément à l’exigence de ne pas passer d’objets ou de proxies STA aux APIs de Media Foundation, les objets à thread libre n’ont pas besoin de se soucier du transfert des pointeurs d’entrée STA dans les composants à thread libre.

Les composants qui utilisent la file d’attente de travail de longue fonction (MFASYNC_CALLBACK_QUEUE_LONG_FUNCTION) doivent faire preuve de plus de prudence. Les threads dans la file de longue fonction créent leur propre STA. Les composants qui utilisent la file de longue fonction pour les rappels devraient éviter de créer des objets COM sur ces threads, et doivent être attentifs à transférer les proxies vers le STA si nécessaire.

Résumé

Les applications auront plus de facilité à interagir avec Media Foundation depuis un thread MTA, mais il est possible avec quelques précautions d’utiliser Media Foundation depuis un thread STA. Media Foundation ne gère pas les composants STA, et les applications doivent veiller à ne pas passer d’objets STA aux APIs de Media Foundation. Certains objets ont des exigences supplémentaires, en particulier les objets qui fonctionnent en situation inter-processus. Suivre ces lignes directrices vous aidera à éviter les erreurs COM, les blocages et les retards inattendus dans le traitement des médias.

API de plateforme Media Foundation

Architecture Media Foundation