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

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

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

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

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

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

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

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

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

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

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

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

Реализация 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