Обучение при возникновении события
[Функция, связанная с этой страницей, DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngineи видео и аудиозахват в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует использовать новый код MediaPlayer, IMFMediaEngine и аудио-видеозахват в Media Foundation вместо DirectShowпо возможности. Корпорация Майкрософт предлагает, что существующий код, использующий устаревшие API, будет перезаписан для использования новых API, если это возможно.]
Чтобы обработать события DirectShow, приложению требуется способ узнать, когда события ожидают в очереди. Диспетчер графов фильтров предоставляет два способа сделать это.
- уведомление окна : диспетчер фильтрующих графов отправляет пользовательское сообщение Windows в окно приложения всякий раз, когда возникает новое событие.
- Сигнализация событий: Диспетчер графа фильтров сигнализирует о событии Windows, если в очереди есть события DirectShow, и сбрасывает событие, если очередь пуста.
Приложение может использовать любой метод. Оконное уведомление обычно проще.
уведомления о окне
Чтобы настроить уведомление о окне, вызовите метод IMediaEventEx::SetNotifyWindow и укажите частное сообщение. Приложения могут использовать номера сообщений в диапазоне от WM_APP до 0xBFFF в качестве частных сообщений. Всякий раз, когда диспетчер графов фильтров помещает новое уведомление о событии в очередь, оно отправляет это сообщение в указанное окно. Приложение реагирует на сообщение из цикла сообщений окна.
В следующем примере кода показано, как задать окно уведомлений.
#define WM_GRAPHNOTIFY WM_APP + 1 // Private message.
pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);
Сообщение является обычным сообщением Windows и размещается отдельно от очереди уведомлений о событиях DirectShow. Преимущество этого подхода заключается в том, что большинство приложений уже реализуют цикл сообщений. Таким образом, вы можете включить обработку событий DirectShow без дополнительной работы.
В следующем примере кода показано, как реагировать на сообщение уведомления. Полный пример см. в разделе Реагирование на события.
LRESULT CALLBACK WindowProc( HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
switch (msg)
{
case WM_GRAPHNOTIFY:
HandleEvent(); // Application-defined function.
break;
// Handle other Windows messages here too.
}
return (DefWindowProc(hwnd, msg, wParam, lParam));
}
Так как уведомление о событиях и цикл сообщений являются асинхронными, очередь может содержать несколько событий по времени ответа приложения на сообщение. Кроме того, события иногда можно удалить из очереди, если они становятся недействительными. Поэтому в коде обработки событий вызовите IAMMediaEvent::GetEvent, пока не возвращает код сбоя, указывающий, что очередь пуста.
Перед выпуском указателя IMediaEventEx, отмените уведомление о событии, вызвав функцию SetNotifyWindow с указателем NULL. В коде обработки событий проверьте, валидный ли указатель IMediaEventEx перед вызовом GetEvent. Эти действия предотвращают возможную ошибку, из-за которой приложение получает уведомление о событии после того, как оно выпустило указатель IMediaEventEx.
Сообщение о событиях
Диспетчер графов фильтров сохраняет событие ручного сброса, которое отражает состояние очереди событий. Если в очереди есть ожидающие уведомления о событиях, диспетчер графа фильтров сигнализирует событие ручного сброса. Если очередь пуста, вызов метода IMediaEvent::GetEvent сбрасывает событие. Приложение может использовать это событие для определения состояния очереди.
Заметка
Терминология может быть запутана здесь. Событие ручного сброса — это тип события, созданного функцией Windows CreateEvent; Это не имеет ничего общего с событиями, определенными DirectShow.
Вызовите метод IMediaEvent::GetEventHandle, чтобы получить дескриптор события сброса вручную. Подождите, пока событие не будет сигнализировано, вызвав функцию, такую как WaitForMultipleObjects. После сигнала события вызовите IMediaEvent::GetEvent, чтобы получить событие DirectShow.
В следующем примере кода показан этот подход. Он получает дескриптор события, а затем ожидает с интервалом в 100 миллисекунд, пока событие не будет сигнализировано. Если событие сигнализирует, он вызывает GetEvent и выводит код события и параметры события в окно консоли. Цикл завершается, когда происходит событие EC_COMPLETE, указывающее, что воспроизведение завершено.
HANDLE hEvent;
long evCode, param1, param2;
BOOLEAN bDone = FALSE;
HRESULT hr = S_OK;
hr = pEvent->GetEventHandle((OAEVENT*)&hEvent);
if (FAILED(hr))
{
/* Insert failure-handling code here. */
}
while(!bDone)
{
if (WAIT_OBJECT_0 == WaitForSingleObject(hEvent, 100))
{
while (S_OK == pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0))
{
printf("Event code: %#04x\n Params: %d, %d\n", evCode, param1, param2);
pEvent->FreeEventParams(evCode, param1, param2);
bDone = (EC_COMPLETE == evCode);
}
}
}
Поскольку граф фильтров автоматически устанавливает или сбрасывает событие при необходимости, ваше приложение не должно этого делать. Кроме того, при освобождении графа фильтров граф фильтров закрывает дескриптор событий, поэтому не используйте дескриптор событий после этого.