媒體事件產生器

媒體基礎為物件提供一致的方式來傳送事件。 物件可以使用事件來發出非同步方法完成的訊號,或通知應用程式有關物件狀態的變更。

如果物件傳送事件,則會公開 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