이벤트에 응답

[이 페이지와 연결된 기능인 DirectShow는 레거시 기능입니다. MediaPlayer, IMFMediaEngineMedia Foundation의 오디오/비디오 캡처로 대체되었습니다. 이러한 기능은 Windows 10 및 Windows 11 최적화되었습니다. 가능한 경우 새 코드에서 DirectShow 대신 MediaPlayer, IMFMediaEngine오디오/비디오 캡처를 사용하는 것이 좋습니다. 가능한 경우 레거시 API를 사용하는 기존 코드를 다시 작성하여 새 API를 사용하도록 제안합니다.]

이 문서에서는 필터 그래프에서 발생하는 이벤트에 응답하는 방법을 설명합니다.

이벤트 알림 작동 방식

DirectShow 애플리케이션이 실행되는 동안 필터 그래프 내에서 이벤트가 발생할 수 있습니다. 예를 들어 필터에 스트리밍 오류가 발생할 수 있습니다. 필터는 이벤트 코드와 두 개의 이벤트 매개 변수로 구성된 이벤트를 전송하여 Filter Graph Manager에 경고합니다. 이벤트 코드는 이벤트 유형을 나타내며 이벤트 매개 변수는 추가 정보를 제공합니다. 매개 변수의 의미는 이벤트 코드에 따라 달라집니다. 이벤트 코드의 전체 목록은 이벤트 알림 코드를 참조하세요.

일부 이벤트는 애플리케이션에 알리지 않고 Filter Graph Manager에 의해 자동으로 처리됩니다. 다른 이벤트는 애플리케이션의 큐에 배치됩니다. 애플리케이션에 따라 처리해야 할 다양한 이벤트가 있습니다. 이 문서에서는 매우 일반적인 세 가지 이벤트에 중점을 둡니다.

  • EC_COMPLETE 이벤트는 재생이 정상적으로 완료되었음을 나타냅니다.
  • EC_USERABORT 이벤트는 사용자가 재생을 중단했음을 나타냅니다. 사용자가 비디오 창을 닫으면 비디오 렌더러가 이 이벤트를 보냅니다.
  • EC_ERRORABORT 이벤트는 오류로 인해 재생이 중단되었음을 나타냅니다.

이벤트 알림 사용

애플리케이션은 새 이벤트가 발생할 때마다 지정된 창에 Windows 메시지를 보내도록 Filter Graph Manager에 지시할 수 있습니다. 이렇게 하면 애플리케이션이 창의 메시지 루프 내에서 응답할 수 있습니다. 먼저 애플리케이션 창으로 보낼 메시지를 정의합니다. 애플리케이션은 WM_APP 0xBFFF 범위의 메시지 번호를 개인 메시지로 사용할 수 있습니다.

#define WM_GRAPHNOTIFY  WM_APP + 1

다음으로 IMediaEventEx 인터페이스에 대한 Filter Graph Manager를 쿼리하고 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 메시지입니다. Filter Graph Manager는 이벤트 큐에 새 이벤트를 배치할 때마다 지정된 애플리케이션 창에 WM_GRAPHNOTIFY 메시지를 게시합니다. 메시지의 lParam 매개 변수는 SetNotifyWindow의 세 번째 매개 변수와 같습니다. 이 매개 변수를 사용하면 메시지와 함께 instance 데이터를 보낼 수 있습니다. 창 메시지의 wParam 매개 변수는 항상 0입니다.

애플리케이션의 WindowProc 함수에서 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 메시지에 대한 응답으로 이 메서드를 호출하기 때문에 이벤트가 이미 큐에 대기 중입니다. 따라서 시간 제한 값을 0으로 설정합니다.

이벤트 알림과 메시지 루프는 모두 비동기이므로 애플리케이션이 메시지에 응답할 때까지 큐에 두 개 이상의 이벤트가 있을 수 있습니다. 또한 필터 그래프 관리자는 유효하지 않은 경우 큐에서 특정 이벤트를 제거할 수 있습니다. 따라서 큐가 비어 있음을 나타내는 오류 코드를 반환할 때까지 GetEvent 를 호출해야 합니다.

이 예제에서 애플리케이션은 애플리케이션에서 정의한 CleanUp 함수를 호출하여 EC_COMPLETE, EC_USERABORTEC_ERRORABORT 응답합니다. 그러면 애플리케이션이 정상적으로 종료됩니다. 이 예제에서는 두 이벤트 매개 변수를 무시합니다. 이벤트를 검색한 후 IMediaEvent::FreeEventParams 를 이벤트 매개 변수와 연결된 무료 리소스로 호출합니다.

EC_COMPLETE 이벤트로 인해 필터 그래프가 중지되지 않습니다. 애플리케이션은 그래프를 중지하거나 일시 중지할 수 있습니다. 그래프를 중지하면 필터가 보유하고 있는 모든 리소스를 해제합니다. 그래프를 일시 중지하면 필터에 리소스가 계속 보관됩니다. 또한 비디오 렌더러가 일시 중지되면 최신 프레임의 정적 이미지가 표시됩니다.

IMediaEventEx 포인터를 해제하기 전에 NULL 창 핸들을 사용하여 SetNotifyWindow를 호출하여 이벤트 알림을 취소합니다.

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

WM_GRAPHNOTIFY 메시지 처리기에서 GetEvent를 호출하기 전에 IMediaEventEx 포인터를 검사.

if (g_pEvent == NULL) return;

이렇게 하면 애플리케이션이 포인터를 해제한 후 이벤트 알림을 받을 때 발생할 수 있는 오류가 발생할 수 있습니다.

기본 DirectShow 작업

DirectShow의 이벤트 알림