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


Обучение возникновению события

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

Для обработки событий DirectShow приложению требуется способ узнать, когда события ожидаются в очереди. Диспетчер графов фильтров предоставляет два способа.

  • Уведомление окна: Диспетчер фильтров Graph отправляет определяемое пользователем сообщение Windows в окно приложения всякий раз, когда возникает новое событие.
  • Сигнализация о событиях: Диспетчер фильтров Graph сообщает о событии 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 . Перед вызовом GetEvent в коде обработки событий проверка, является ли указатель IMediaEventEx допустимым. Эти действия предотвращают возможную ошибку, при которой приложение получает уведомление о событии после освобождения указателя IMediaEventEx .

Сигнализация событий

Диспетчер графов фильтров сохраняет событие сброса вручную, отражающее состояние очереди событий. Если очередь содержит уведомления об ожидающих событиях, диспетчер Фильтров Graph сообщает о событии сброса вручную. Если очередь пуста, вызов метода 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, &param1, &param2, 0)) 
        {
            printf("Event code: %#04x\n Params: %d, %d\n", evCode, param1, param2);
            pEvent->FreeEventParams(evCode, param1, param2);
            bDone = (EC_COMPLETE == evCode);
        }
    }
} 

Так как граф фильтра автоматически задает или сбрасывает событие, когда это необходимо, приложение не должно делать этого. Кроме того, при освобождении графа фильтра граф фильтра закрывает дескриптор события, поэтому не используйте дескриптор событий после этой точки.