Media Event Generators

Media Foundation provides a consistent way for objects to send events. An object can use events to signal the completion of an asynchronous method, or to notify the application about a change in the object's state.

If an object sends events, it exposes the IMFMediaEventGenerator interface. Whenever the object sends a new event, the event is placed in a queue. The application can request the next event from the queue by calling one of the following methods:

The GetEvent method is synchronous. If an event is already in the queue, the method returns immediately. If there is no event in the queue, the method either fails immediately, or blocks until the next event is queued, depending on which flag you pass into GetEvent.

The BeginGetEvent method is asynchronous, so it never blocks. This method takes a pointer to the IMFAsyncCallback interface, which the application must implement. When the callback is invoked, the application calls IMFMediaEventGenerator::EndGetEvent to get the event from the queue. For more information about IMFAsyncCallback, see Asynchronous Callback Methods.

Events are represented by the IMFMediaEvent interface. This interface has the following methods:

  • GetType: Retrieves the event type. The event type indicates what happened to trigger the event.

  • GetStatus: Retrieves an HRESULT value, indicating whether the operation that triggered the event was successful. If an operation fails asynchronously, GetStatus returns a failure code.

  • GetValue: Retrieves a PROPVARIANT that contains event data. The event data depends on the event type. Some events do not have any data.

  • GetExtendedType: Retrieves a GUID. This method applies to the MEExtendedType Event, and provides a way to define custom events.

The IMFMediaEvent interface also inherits the IMFAttributes interface. Some events carry additional information as attributes.

For a list of event types and their associated data and attributes, see Media Foundation Events.

Implementing IMFMediaEventGenerator

If you are writing a plug-in component for Media Foundation, such as a custom media source or media sink, you might need to implement IMFMediaEventGenerator. To make this easier, Media Foundation provides a helper object that implements an event queue. Create the event queue by calling the MFCreateEventQueue function. The event queue exposes the IMFMediaEventQueue interface. In your object's implementation of the IMFMediaEventGenerator methods, call the corresponding method on the event queue.

The event queue object is thread safe. Never hold the same critical section object when calling GetEvent and QueueEvent, because GetEvent might block indefinitely waiting for QueueEvent. If you hold the same critical section for both methods, it will cause a deadlock.

Before releasing the event queue, call IMFMediaEventQueue::Shutdown to release the resources that the object holds.

When your object needs to raise an event, call one of the following methods on the event queue:

These methods put a new event on the queue and signal any caller that is waiting for the next event.

The following code shows a class that implements IMFMediaEventGenerator using this helper object. This class defines a public Shutdown method that shuts down the event queue and releases the event queue pointer. This behavior would be typical for media sources and media sinks, which always have a Shutdown method.

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