Поделиться через


Реагирование на события

[Функция, связанная с этой страницей DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngine, и аудио/ видео захвата в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует, чтобы новый код использовал MediaPlayer, IMFMediaEngine и аудио- и видеозахват в Media Foundation вместо DirectShow, когда это возможно. Корпорация Майкрософт предлагает переписать существующий код, использующий устаревшие API, чтобы по возможности использовать новые API.]

В этой статье описывается, как реагировать на события, возникающие в графе фильтров.

Как работает уведомление о событиях

Во время работы приложения DirectShow в графе фильтра могут возникать события. Например, фильтр может столкнуться с ошибкой потоковой передачи. Фильтры оповещают диспетчер графов фильтров, отправляя события, состоящие из кода события и двух параметров события. Код события указывает тип события, а параметры события предоставляют дополнительные сведения. Значение параметров зависит от кода события. Полный список кодов событий см. в разделе Коды уведомлений о событиях.

Некоторые события обрабатываются диспетчером фильтров Graph без уведомления приложения. Другие события помещаются в очередь для приложения. В зависимости от приложения могут потребоваться различные события. В этой статье рассматриваются три очень распространенные события:

  • Событие EC_COMPLETE указывает, что воспроизведение завершилось нормально.
  • Событие EC_USERABORT указывает, что пользователь прервал воспроизведение. Отрисовщики видео отправляют это событие, если пользователь закрывает окно видео.
  • Событие EC_ERRORABORT указывает на то, что воспроизведение было остановлено из-за ошибки.

Использование уведомления о событиях

Приложение может указать диспетчеру фильтров Graph отправлять сообщение Windows в указанное окно при каждом возникновении нового события. Это позволяет приложению отвечать внутри цикла сообщений окна. Сначала определите сообщение, которое будет отправлено в окно приложения. Приложения могут использовать номера сообщений в диапазоне от WM_APP до 0xBFFF в качестве личных сообщений:

#define WM_GRAPHNOTIFY  WM_APP + 1

Затем запросите к диспетчеру графов фильтров интерфейс IMediaEventEx и вызовите метод IMediaEventEx::SetNotifyWindow :

IMediaEventEx *g_pEvent = NULL;
g_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&g_pEvent);
g_pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);

Этот метод назначает указанное окно (g_hwnd) получателем сообщения. Вызовите метод после создания графа фильтра, но перед запуском графа.

WM_GRAPHNOTIFY — это обычное сообщение Windows. Всякий раз, когда диспетчер фильтров графов помещает новое событие в очередь событий, он отправляет сообщение WM_GRAPHNOTIFY в назначенное окно приложения. Параметр lParam сообщения равен третьему параметру в SetNotifyWindow. Этот параметр позволяет отправлять данные экземпляра с сообщением. Параметр wParam сообщения окна всегда равен нулю.

В функции WindowProc приложения добавьте оператор case для сообщения WM_GRAPHNOTIFY:

case WM_GRAPHNOTIFY:
    HandleGraphEvent();
    break;

В функции обработчика событий вызовите метод IMediaEvent::GetEvent , чтобы получить события из очереди:

void HandleGraphEvent()
{
    // Disregard if we don't have an IMediaEventEx pointer.
    if (g_pEvent == NULL)
    {
        return;
    }
    // Get all the events
    long evCode;
    LONG_PTR param1, param2;
    HRESULT hr;
    while (SUCCEEDED(g_pEvent->GetEvent(&evCode, &param1, &param2, 0)))
    {
        g_pEvent->FreeEventParams(evCode, param1, param2);
        switch (evCode)
        {
        case EC_COMPLETE:  // Fall through.
        case EC_USERABORT: // Fall through.
        case EC_ERRORABORT:
            CleanUp();
            PostQuitMessage(0);
            return;
        }
    } 
}

Метод GetEvent извлекает код события и два параметра события. Четвертый параметр GetEvent указывает продолжительность ожидания события в миллисекундах. Так как приложение вызывает этот метод в ответ на сообщение WM_GRAPHNOTIFY, событие уже поставлено в очередь. Поэтому мы задаем значение времени ожидания равным нулю.

Уведомление о событиях и цикл сообщений являются асинхронными, поэтому очередь может содержать несколько событий к тому времени, когда приложение ответит на сообщение. Кроме того, диспетчер графов фильтров может удалять определенные события из очереди, если они становятся недействительными. Поэтому следует вызывать Метод GetEvent , пока он не вернет код сбоя, указывающий, что очередь пуста.

В этом примере приложение реагирует на EC_COMPLETE, EC_USERABORT и EC_ERRORABORT , вызывая определяемую приложением функцию CleanUp, которая приводит к корректному выходу приложения. В примере игнорируется два параметра события. После получения события вызовите метод IMediaEvent::FreeEventParams для всех свободных ресурсов, связанных с параметрами события.

Обратите внимание, что событие EC_COMPLETE не приводит к остановке графа фильтров. Приложение может остановить или приостановить граф. При остановке графа фильтры освобождают все ресурсы, которые они удерживают. При приостановке графа фильтры по-прежнему будут содержать ресурсы. Кроме того, при приостановке отрисовщика видео отображается статическое изображение последнего кадра.

Перед освобождением указателя IMediaEventEx отмените уведомление о событии, вызвав SetNotifyWindow с маркером окна NULL :

// Disable event notification before releasing the graph.
g_pEvent->SetNotifyWindow(NULL, 0, 0);
g_pEvent->Release();
g_pEvent = NULL;

В обработчике сообщений WM_GRAPHNOTIFY проверка указатель IMediaEventEx перед вызовом GetEvent:

if (g_pEvent == NULL) return;

Это предотвращает возможную ошибку, которая может возникнуть, если приложение получает уведомление о событии после освобождения указателя.

Основные задачи DirectShow

Уведомление о событиях в DirectShow