Générateurs d’événements multimédias

Media Foundation fournit un moyen cohérent pour les objets d’envoyer des événements. Un objet peut utiliser des événements pour signaler l’achèvement d’une méthode asynchrone ou pour informer l’application d’une modification de l’état de l’objet.

Si un objet envoie des événements, il expose l’interface IMFMediaEventGenerator . Chaque fois que l’objet envoie un nouvel événement, l’événement est placé dans une file d’attente. L’application peut demander l’événement suivant à partir de la file d’attente en appelant l’une des méthodes suivantes :

La méthode GetEvent est synchrone. Si un événement se trouve déjà dans la file d’attente, la méthode retourne immédiatement. S’il n’y a aucun événement dans la file d’attente, la méthode échoue immédiatement ou se bloque jusqu’à ce que l’événement suivant soit mis en file d’attente, en fonction de l’indicateur que vous passez dans GetEvent.

La méthode BeginGetEvent étant asynchrone, elle ne se bloque jamais. Cette méthode prend un pointeur vers l’interface IMFAsyncCallback , que l’application doit implémenter. Lorsque le rappel est appelé, l’application appelle IMFMediaEventGenerator::EndGetEvent pour obtenir l’événement à partir de la file d’attente. Pour plus d’informations sur IMFAsyncCallback, consultez Méthodes de rappel asynchrones.

Les événements sont représentés par l’interface IMFMediaEvent . Cette interface utilise les méthodes suivantes :

  • GetType : récupère le type d’événement. Le type d’événement indique ce qui s’est passé pour déclencher l’événement.

  • GetStatus : récupère une valeur HRESULT , indiquant si l’opération qui a déclenché l’événement a réussi. Si une opération échoue de manière asynchrone, GetStatus retourne un code d’échec.

  • GetValue : récupère un PROPVARIANT qui contient des données d’événement. Les données d’événement dépendent du type d’événement. Certains événements n’ont pas de données.

  • GetExtendedType : récupère un GUID. Cette méthode s’applique à l’événement MEExtendedType et permet de définir des événements personnalisés.

L’interface IMFMediaEvent hérite également de l’interface IMFAttributes. Certains événements contiennent des informations supplémentaires en tant qu’attributs.

Pour obtenir la liste des types d’événements, ainsi que les données et attributs associés, consultez Événements Media Foundation.

Implémentation d’IMFMediaEventGenerator

Si vous écrivez un composant de plug-in pour Media Foundation, tel qu’une source multimédia personnalisée ou un récepteur multimédia, vous devrez peut-être implémenter IMFMediaEventGenerator. Pour faciliter cela, Media Foundation fournit un objet d’assistance qui implémente une file d’attente d’événements. Créez la file d’attente d’événements en appelant la fonction MFCreateEventQueue . La file d’attente d’événements expose l’interface IMFMediaEventQueue . Dans l’implémentation par votre objet des méthodes IMFMediaEventGenerator , appelez la méthode correspondante sur la file d’attente d’événements.

L’objet file d’attente d’événements est thread safe. Ne tenez jamais le même objet de section critique lors de l’appel de GetEvent et queueEvent, car GetEvent peut bloquer indéfiniment l’attente de QueueEvent. Si vous conservez la même section critique pour les deux méthodes, cela provoquera un blocage.

Avant de libérer la file d’attente d’événements, appelez IMFMediaEventQueue::Shutdown pour libérer les ressources que l’objet contient.

Lorsque votre objet doit déclencher un événement, appelez l’une des méthodes suivantes sur la file d’attente d’événements :

Ces méthodes placent un nouvel événement dans la file d’attente et signalent tout appelant qui attend l’événement suivant.

Le code suivant montre une classe qui implémente IMFMediaEventGenerator à l’aide de cet objet d’assistance. Cette classe définit une méthode d’arrêt publique qui arrête la file d’attente d’événements et libère le pointeur de file d’attente d’événements. Ce comportement est typique pour les sources multimédias et les récepteurs multimédias, qui ont toujours une méthode Shutdown .

class MyObject : public IMFMediaEventGenerator
{
private:
    long                m_nRefCount;    // Reference count.
    CRITICAL_SECTION    m_critSec;      // Critical section.
    IMFMediaEventQueue *m_pQueue;       // Event queue.
    BOOL                m_bShutdown;    // Is the object shut down?

    // CheckShutdown: Returns MF_E_SHUTDOWN if the object was shut down.
    HRESULT CheckShutdown() const 
    {
        return (m_bShutdown? MF_E_SHUTDOWN : S_OK);
    }

public:
    MyObject(HRESULT &hr) : m_nRefCount(0), m_pQueue(NULL), m_bShutdown(FALSE)
    {
        InitializeCriticalSection(&m_critSec);
        
        // Create the event queue.
        hr = MFCreateEventQueue(&m_pQueue);
    }

    // Shutdown: Shuts down this object.
    HRESULT Shutdown()
    {
        EnterCriticalSection(&m_critSec);

        HRESULT hr = CheckShutdown();
        if (SUCCEEDED(hr))
        {
            // Shut down the event queue.
            if (m_pQueue)
            {
                hr = m_pQueue->Shutdown();
            }

            // Release the event queue.
            SAFE_RELEASE(m_pQueue);
            // Release any other objects owned by the class (not shown).

            // Set the shutdown flag.
            m_bShutdown = TRUE;
        }

        LeaveCriticalSection(&m_critSec);
        return hr;
    }

    // TODO: Implement IUnknown (not shown).
    STDMETHODIMP_(ULONG) AddRef();
    STDMETHODIMP_(ULONG) Release();
    STDMETHODIMP QueryInterface(REFIID iid, void** ppv);

    // IMFMediaEventGenerator::BeginGetEvent
    STDMETHODIMP BeginGetEvent(IMFAsyncCallback* pCallback, IUnknown* pState)
    {
        EnterCriticalSection(&m_critSec);

        HRESULT hr = CheckShutdown();
        if (SUCCEEDED(hr))
        {
            hr = m_pQueue->BeginGetEvent(pCallback, pState);
        }

        LeaveCriticalSection(&m_critSec);
        return hr;    
    }

    // IMFMediaEventGenerator::EndGetEvent
    STDMETHODIMP EndGetEvent(IMFAsyncResult* pResult, IMFMediaEvent** ppEvent)
    {
        EnterCriticalSection(&m_critSec);

        HRESULT hr = CheckShutdown();
        if (SUCCEEDED(hr))
        {
            hr = m_pQueue->EndGetEvent(pResult, ppEvent);
        }

        LeaveCriticalSection(&m_critSec);
        return hr;    
    }

    // IMFMediaEventGenerator::GetEvent
    STDMETHODIMP GetEvent(DWORD dwFlags, IMFMediaEvent** ppEvent)
    {
        // Because GetEvent can block indefinitely, it requires
        // a slightly different locking strategy.
        IMFMediaEventQueue *pQueue = NULL;

        // Hold lock.
        EnterCriticalSection(&m_critSec);

        // Check shutdown
        HRESULT hr = CheckShutdown();

        // Store the pointer in a local variable, so that another thread
        // does not release it after we leave the critical section.
        if (SUCCEEDED(hr))
        {
            pQueue = m_pQueue;
            pQueue->AddRef();
        }

        // Release the lock.
        LeaveCriticalSection(&m_critSec);

        if (SUCCEEDED(hr))
        {
            hr = pQueue->GetEvent(dwFlags, ppEvent);
        }

        SAFE_RELEASE(pQueue);
        return hr;
    }

    // IMFMediaEventGenerator::QueueEvent
    STDMETHODIMP QueueEvent(
        MediaEventType met, REFGUID extendedType, 
        HRESULT hrStatus, const PROPVARIANT* pvValue)
    {
        EnterCriticalSection(&m_critSec);

        HRESULT hr = CheckShutdown();
        if (SUCCEEDED(hr))
        {
            hr = m_pQueue->QueueEventParamVar(
                met, extendedType, hrStatus, pvValue);
        }

        LeaveCriticalSection(&m_critSec);
        return hr;
    }

private:

    // The destructor is private. The caller must call Release.
    virtual ~MyObject()
    {
        assert(m_bShutdown);
        assert(m_nRefCount == 0);
    }
};

API de plateforme Media Foundation