Generatori di eventi multimediali

Media Foundation consente agli oggetti di inviare eventi in modo coerente. Un oggetto può usare eventi per segnalare il completamento di un metodo asincrono o per notificare all'applicazione una modifica dello stato dell'oggetto.

Se un oggetto invia eventi, espone l'interfaccia IMFMediaEventGenerator . Ogni volta che l'oggetto invia un nuovo evento, l'evento viene inserito in una coda. L'applicazione può richiedere l'evento successivo dalla coda chiamando uno dei metodi seguenti:

Il metodo GetEvent è sincrono. Se un evento è già presente nella coda, il metodo restituisce immediatamente. Se non è presente alcun evento nella coda, il metodo ha esito negativo immediatamente o si blocca finché l'evento successivo non viene accodato, a seconda del flag passato in GetEvent.

Il metodo BeginGetEvent è asincrono, quindi non si blocca mai. Questo metodo accetta un puntatore all'interfaccia IMFAsyncCallback , che l'applicazione deve implementare. Quando viene richiamato il callback, l'applicazione chiama IMFMediaEventGenerator::EndGetEvent per ottenere l'evento dalla coda. Per altre informazioni su IMFAsyncCallback, vedere Metodi di callback asincroni.

Gli eventi sono rappresentati dall'interfaccia IMFMediaEvent . Questa interfaccia include i metodi seguenti:

  • GetType: recupera il tipo di evento. Il tipo di evento indica cosa è successo per attivare l'evento.

  • GetStatus: recupera un valore HRESULT , che indica se l'operazione che ha attivato l'evento è riuscita. Se un'operazione ha esito negativo in modo asincrono, GetStatus restituisce un codice di errore.

  • GetValue: recupera un PROPVARIANT che contiene i dati dell'evento. I dati dell'evento dipendono dal tipo di evento. Alcuni eventi non contengono dati.

  • GetExtendedType: recupera un GUID. Questo metodo si applica all'evento MEExtendedType e consente di definire eventi personalizzati.

L'interfaccia IMFMediaEvent eredita anche l'interfaccia IMFAttributes . Alcuni eventi contengono informazioni aggiuntive come attributi.

Per un elenco dei tipi di evento e dei relativi attributi e dati associati, vedere Eventi di Media Foundation.

Implementazione di IMFMediaEventGenerator

Se si scrive un componente plug-in per Media Foundation, ad esempio un'origine multimediale personalizzata o un sink multimediale, potrebbe essere necessario implementare IMFMediaEventGenerator. Per semplificare questa operazione, Media Foundation fornisce un oggetto helper che implementa una coda di eventi. Creare la coda di eventi chiamando la funzione MFCreateEventQueue . La coda di eventi espone l'interfaccia IMFMediaEventQueue . Nell'implementazione dell'oggetto dei metodi IMFMediaEventGenerator chiamare il metodo corrispondente nella coda di eventi.

L'oggetto coda eventi è thread-safe. Non contenere mai lo stesso oggetto sezione critica quando si chiama GetEvent e QueueEvent, perché GetEvent potrebbe bloccare l'attesa illimitata di QueueEvent. Se si mantiene la stessa sezione critica per entrambi i metodi, verrà generato un deadlock.

Prima di rilasciare la coda di eventi, chiamare IMFMediaEventQueue::Shutdown per rilasciare le risorse contenute nell'oggetto.

Quando l'oggetto deve generare un evento, chiamare uno dei metodi seguenti nella coda di eventi:

Questi metodi inseriscono un nuovo evento nella coda e segnalano qualsiasi chiamante in attesa dell'evento successivo.

Il codice seguente mostra una classe che implementa IMFMediaEventGenerator usando questo oggetto helper. Questa classe definisce un metodo Shutdown pubblico che arresta la coda di eventi e rilascia il puntatore alla coda di eventi. Questo comportamento è tipico per le origini multimediali e i sink multimediali, che hanno sempre un metodo 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 della piattaforma Media Foundation