Geradores de Eventos de Mídia

O Media Foundation fornece uma maneira consistente de os objetos enviarem eventos. Um objeto pode usar eventos para sinalizar a conclusão de um método assíncrono ou para notificar o aplicativo sobre uma alteração no estado do objeto.

Se um objeto enviar eventos, ele exporá a interface IMFMediaEventGenerator . Sempre que o objeto envia um novo evento, o evento é colocado em uma fila. O aplicativo pode solicitar o próximo evento da fila chamando um dos seguintes métodos:

O método GetEvent é síncrono. Se um evento já estiver na fila, o método retornará imediatamente. Se não houver nenhum evento na fila, o método falhará imediatamente ou bloqueará até que o próximo evento seja enfileirado, dependendo de qual sinalizador você passar para GetEvent.

O método BeginGetEvent é assíncrono, portanto, ele nunca bloqueia. Esse método usa um ponteiro para a interface IMFAsyncCallback , que o aplicativo deve implementar. Quando o retorno de chamada é invocado, o aplicativo chama IMFMediaEventGenerator::EndGetEvent para obter o evento da fila. Para obter mais informações sobre IMFAsyncCallback, consulte Métodos de retorno de chamada assíncronos.

Os eventos são representados pela interface IMFMediaEvent . Essa interface tem os seguintes métodos:

  • GetType: recupera o tipo de evento. O tipo de evento indica o que aconteceu para disparar o evento.

  • GetStatus: recupera um valor HRESULT , indicando se a operação que disparou o evento foi bem-sucedida. Se uma operação falhar de forma assíncrona, GetStatus retornará um código de falha.

  • GetValue: recupera um PROPVARIANT que contém dados de evento. Os dados do evento dependem do tipo de evento. Alguns eventos não têm dados.

  • GetExtendedType: recupera um GUID. Esse método se aplica ao evento MEExtendedType e fornece uma maneira de definir eventos personalizados.

A interface IMFMediaEvent também herda a interface IMFAttributes . Alguns eventos têm informações adicionais como atributos.

Para obter uma lista de tipos de eventos e seus dados e atributos associados, consulte Media Foundation Events.

Implementando IMFMediaEventGenerator

Se você estiver escrevendo um componente de plug-in para o Media Foundation, como uma fonte de mídia personalizada ou um coletor de mídia, talvez seja necessário implementar IMFMediaEventGenerator. Para facilitar isso, o Media Foundation fornece um objeto auxiliar que implementa uma fila de eventos. Crie a fila de eventos chamando a função MFCreateEventQueue . A fila de eventos expõe a interface IMFMediaEventQueue . Na implementação do objeto dos métodos IMFMediaEventGenerator , chame o método correspondente na fila de eventos.

O objeto da fila de eventos é thread-safe. Nunca mantenha o mesmo objeto de seção crítico ao chamar GetEvent e QueueEvent, pois GetEvent pode bloquear a espera indefinida por QueueEvent. Se você mantiver a mesma seção crítica para ambos os métodos, isso causará um deadlock.

Antes de liberar a fila de eventos, chame IMFMediaEventQueue::Shutdown para liberar os recursos que o objeto contém.

Quando o objeto precisar gerar um evento, chame um dos seguintes métodos na fila de eventos:

Esses métodos colocam um novo evento na fila e sinalizam qualquer chamador que esteja aguardando o próximo evento.

O código a seguir mostra uma classe que implementa IMFMediaEventGenerator usando esse objeto auxiliar. Essa classe define um método de Desligamento público que desliga a fila de eventos e libera o ponteiro da fila de eventos. Esse comportamento seria típico para fontes de mídia e coletores de mídia, que sempre têm um método 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);
    }
};

Media Foundation Platform APIs