Compartir a través de


Respuesta a eventos

[La característica asociada a esta página, DirectShow, es una característica heredada. Se ha reemplazado por MediaPlayer, IMFMediaEngine y Captura de audio/vídeo en Media Foundation. Esas características se han optimizado para Windows 10 y Windows 11. Microsoft recomienda encarecidamente que el nuevo código use MediaPlayer, IMFMediaEngine y Audio/Video Capture en Media Foundation en lugar de DirectShow, siempre que sea posible. Microsoft sugiere que el código existente que usa las API heredadas se reescriba para usar las nuevas API si es posible.

En este artículo se describe cómo responder a eventos que se producen en un gráfico de filtros.

Cómo funciona la notificación de eventos

Mientras se ejecuta una aplicación DirectShow, los eventos pueden producirse dentro del gráfico de filtros. Por ejemplo, un filtro podría encontrar un error de streaming. Los filtros alertan al Administrador de gráficos de filtros mediante el envío de eventos, que constan de un código de evento y dos parámetros de evento. El código de evento indica el tipo de evento y los parámetros del evento proporcionan información adicional. El significado de los parámetros depende del código de evento. Para obtener una lista completa de los códigos de evento, consulte Códigos de notificación de eventos.

El Administrador de gráficos de filtros controla silenciosamente algunos eventos, sin que se notifique a la aplicación. Otros eventos se colocan en una cola para la aplicación. En función de la aplicación, hay varios eventos que es posible que deba controlar. Este artículo se centra en tres eventos que son muy comunes:

  • El evento EC_COMPLETE indica que la reproducción se ha completado normalmente.
  • El evento EC_USERABORT indica que el usuario ha interrumpido la reproducción. Los representadores de vídeo envían este evento si el usuario cierra la ventana de vídeo.
  • El evento EC_ERRORABORT indica que un error ha provocado que la reproducción se detenga.

Usar notificación de eventos

Una aplicación puede indicar al Administrador de gráficos de filtros que envíe un mensaje de Windows a una ventana designada cada vez que se produzca un nuevo evento. Esto permite a la aplicación responder dentro del bucle de mensajes de la ventana. En primer lugar, defina el mensaje que se enviará a la ventana de la aplicación. Las aplicaciones pueden usar números de mensaje en el intervalo desde WM_APP hasta 0xBFFF como mensajes privados:

#define WM_GRAPHNOTIFY  WM_APP + 1

A continuación, consulte el Administrador de gráficos de filtros para la interfaz IMediaEventEx y llame al método IMediaEventEx::SetNotifyWindow :

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

Este método designa la ventana especificada (g_hwnd) como destinatario del mensaje. Llame al método después de crear el gráfico de filtro, pero antes de ejecutar el gráfico.

WM_GRAPHNOTIFY es un mensaje normal de Windows. Cada vez que el Administrador de gráficos de filtros coloca un nuevo evento en la cola de eventos, publica un mensaje WM_GRAPHNOTIFY en la ventana de aplicación designada. El parámetro lParam del mensaje es igual al tercer parámetro de SetNotifyWindow. Este parámetro permite enviar datos de instancia con el mensaje. El parámetro wParam del mensaje de ventana siempre es cero.

En la función WindowProc de la aplicación, agregue una instrucción case para el mensaje WM_GRAPHNOTIFY:

case WM_GRAPHNOTIFY:
    HandleGraphEvent();
    break;

En la función del controlador de eventos, llame al método IMediaEvent::GetEvent para recuperar eventos de la cola:

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;
        }
    } 
}

El método GetEvent recupera el código de evento y los dos parámetros de evento. El cuarto parámetro GetEvent especifica el período de tiempo para esperar un evento, en milisegundos. Dado que la aplicación llama a este método en respuesta a un mensaje de WM_GRAPHNOTIFY, el evento ya está en cola. Por lo tanto, establecemos el valor de tiempo de espera en cero.

La notificación de eventos y el bucle de mensajes son asincrónicos, por lo que la cola puede contener más de un evento en el momento en que la aplicación responde al mensaje. Además, el Administrador de gráficos de filtros puede quitar determinados eventos de la cola, si no son válidos. Por lo tanto, debe llamar a GetEvent hasta que devuelva un código de error, lo que indica que la cola está vacía.

En este ejemplo, la aplicación responde a EC_COMPLETE, EC_USERABORT y EC_ERRORABORT invocando la función CleanUp definida por la aplicación, lo que hace que la aplicación salga correctamente. En el ejemplo se omiten los dos parámetros de evento. Después de recuperar un evento, llame a IMediaEvent::FreeEventParams a cualquier recurso gratuito asociado a los parámetros del evento.

Tenga en cuenta que un evento de EC_COMPLETE no hace que el gráfico de filtro se detenga. La aplicación puede detener o pausar el gráfico. Si detiene el gráfico, los filtros liberan los recursos que contienen. Si pausa el gráfico, los filtros continúan manteniendo los recursos. Además, cuando un representador de vídeo se detiene, muestra una imagen estática del fotograma más reciente.

Antes de liberar el puntero IMediaEventEx , cancele la notificación de eventos llamando a SetNotifyWindow con un identificador de ventana NULL :

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

En el controlador de mensajes WM_GRAPHNOTIFY, compruebe el puntero IMediaEventEx antes de llamar a GetEvent:

if (g_pEvent == NULL) return;

Esto evita un posible error que puede producirse si la aplicación recibe la notificación de eventos después de liberar el puntero.

Tareas básicas de DirectShow

Notificación de eventos en DirectShow