Генераторы событий мультимедиа

Media Foundation предоставляет согласованный способ отправки событий объектами. Объект может использовать события, чтобы сообщить о завершении асинхронного метода или уведомить приложение об изменении состояния объекта.

Если объект отправляет события, он предоставляет интерфейс IMFMediaEventGenerator . Всякий раз, когда объект отправляет новое событие, событие помещается в очередь. Приложение может запросить следующее событие из очереди, вызвав один из следующих методов:

Метод GetEvent является синхронным. Если событие уже находится в очереди, метод возвращает немедленно. Если в очереди нет события, метод либо немедленно завершается сбоем, либо блокируется, пока следующее событие не будет помещено в очередь, в зависимости от того, какой флаг передается в GetEvent.

Метод BeginGetEvent является асинхронным, поэтому он никогда не блокируется. Этот метод принимает указатель на интерфейс IMFAsyncCallback , который приложение должно реализовать. При вызове обратного вызова приложение вызывает IMFMediaEventGenerator::EndGetEvent , чтобы получить событие из очереди. Дополнительные сведения о IMFAsyncCallback см. в разделе Асинхронные методы обратного вызова.

События представлены интерфейсом IMFMediaEvent . Этот интерфейс имеет следующие методы:

  • GetType: извлекает тип события. Тип события указывает, что произошло для активации события.

  • GetStatus: извлекает значение HRESULT , указывающее, была ли операция, которая вызвала событие, успешной. Если операция завершается асинхронным сбоем, GetStatus возвращает код сбоя.

  • GetValue: извлекает PROPVARIANT , содержащий данные о событиях. Данные события зависят от типа события. Некоторые события не имеют данных.

  • GetExtendedType: извлекает GUID. Этот метод применяется к событию MEExtendedType и предоставляет способ определения пользовательских событий.

Интерфейс IMFMediaEvent также наследует интерфейс IMFAttributes . Некоторые события содержат дополнительные сведения в качестве атрибутов.

Список типов событий и связанных с ними данных и атрибутов см. в разделе События Media Foundation.

Реализация IMFMediaEventGenerator

При написании подключаемого компонента для Media Foundation, например пользовательского источника мультимедиа или приемника мультимедиа, может потребоваться реализовать IMFMediaEventGenerator. Чтобы упростить эту задачу, Media Foundation предоставляет вспомогательный объект, реализующий очередь событий. Создайте очередь событий, вызвав функцию MFCreateEventQueue . Очередь событий предоставляет интерфейс IMFMediaEventQueue . В реализации объекта методов IMFMediaEventGenerator вызовите соответствующий метод в очереди событий.

Объект очереди событий является потокобезопасном. Никогда не удерживайте один и тот же объект критического раздела при вызове GetEvent и QueueEvent, так как GetEvent может блокировать бесконечное ожидание QueueEvent. Если вы удерживаете один и тот же критический раздел для обоих методов, это приведет к взаимоблокировки.

Перед освобождением очереди событий вызовите IMFMediaEventQueue::Shutdown , чтобы освободить ресурсы, которые содержит объект.

Если объекту необходимо вызвать событие, вызовите один из следующих методов в очереди событий:

Эти методы помещают новое событие в очередь и сигнализируют любому вызывающей стороне, ожидающей следующего события.

В следующем коде показан класс, реализующий IMFMediaEventGenerator с помощью этого вспомогательного объекта. Этот класс определяет общедоступный метод Shutdown , который завершает работу очереди событий и освобождает указатель очереди событий. Такое поведение было бы типичным для источников мультимедиа и приемников мультимедиа, которые всегда имеют метод 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-интерфейсы платформы Media Foundation