媒体事件生成器

媒体基础为对象提供一致的方式来发送事件。 对象可以使用事件来发出异步方法完成的信号,或通知应用程序对象状态的更改。

如果对象发送事件,它将公开 IMFMediaEventGenerator 接口。 每当对象发送新事件时,该事件就会置于队列中。 应用程序可以通过调用以下方法之一从队列请求下一个事件:

GetEvent 方法是同步的。 如果事件已在队列中,该方法将立即返回。 如果队列中没有事件,该方法会立即失败,或阻止下一个事件排队,具体取决于传入 GetEvent 的标志。

BeginGetEvent 方法是异步的,因此它永远不会阻止。 此方法采用指向 IMFAsyncCallback 接口的指针,应用程序必须实现该接口。 调用回调时,应用程序会调用 IMFMediaEventGenerator::EndGetEvent 从队列中获取事件。 有关 IMFAsyncCallback 的详细信息,请参阅 异步回调方法

事件由 IMFMediaEvent 接口表示。 此接口具有以下方法:

  • GetType:检索事件类型。 事件类型指示触发事件时发生的情况。

  • GetStatus:检索 HRESULT 值,指示触发事件的操作是否成功。 如果操作异步失败, GetStatus 将返回失败代码。

  • GetValue:检索包含事件数据的 PROPVARIANT 。 事件数据取决于事件类型。 某些事件没有任何数据。

  • GetExtendedType:检索 GUID。 此方法适用于 MEExtendedType 事件,并提供定义自定义事件的方法。

IMFMediaEvent 接口还继承 IMFAttributes 接口。 某些事件将附加信息作为属性。

有关事件类型及其关联数据和属性的列表,请参阅 媒体基础事件

实现 IMFMediaEventGenerator

如果要为媒体基础编写插件组件(例如自定义媒体源或媒体接收器),则可能需要实现 IMFMediaEventGenerator。 为了简化此操作,Media Foundation 提供了实现事件队列的帮助程序对象。 通过调用 MFCreateEventQueue 函数创建事件队列。 事件队列公开 IMFMediaEventQueue 接口。 在对象的 IMFMediaEventGenerator 方法实现中,对事件队列调用相应的 方法。

事件队列对象是线程安全的。 调用 GetEventQueueEvent 时,永远不要保留相同的关键节对象,因为 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