媒体事件生成器
媒体基础为对象提供一致的方式来发送事件。 对象可以使用事件来发出异步方法完成的信号,或通知应用程序对象状态的更改。
如果对象发送事件,它将公开 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 方法实现中,对事件队列调用相应的 方法。
事件队列对象是线程安全的。 调用 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);
}
};
相关主题