미디어 이벤트 생성기

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 메서드 구현에서 이벤트 큐에서 해당 메서드를 호출합니다.

이벤트 큐 개체는 스레드로부터 안전합니다. 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);
    }
};

Media Foundation Platform API