Compartir a través de


Representadores de vídeo alternativos

[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 tema se describe cómo escribir un representador de vídeo personalizado para DirectShow.

Nota:

En lugar de escribir un representador de vídeo personalizado, se recomienda escribir un asignador de complementos para el representador de mezcla de vídeo (VMR) o el representador de vídeo mejorado (EVR). Este enfoque le proporcionará todas las ventajas de VMR/EVR, incluida la compatibilidad con la aceleración de vídeo DirectX (DXVA), la desinterlacación de hardware y la ejecución paso a paso de fotogramas, y es probable que sea más sólida que un representador de vídeo personalizado. Para obtener más información, vea los temas siguientes:

 

Escritura de un representador alternativo

Microsoft DirectShow proporciona un representador de vídeo basado en ventanas; también proporciona un representador de pantalla completa en la instalación en tiempo de ejecución. Puede usar las clases base directShow para escribir representadores de vídeo alternativos. Para que los representadores alternativos interactúen correctamente con las aplicaciones basadas en DirectShow, los representadores deben cumplir las directrices descritas en este artículo. Puede usar las clases CBaseRenderer y CBaseVideoRenderer para ayudar a seguir estas instrucciones al implementar una representación de vídeo alternativa. Debido al desarrollo continuo de DirectShow, revise la implementación periódicamente para asegurarse de que los representadores son compatibles con la versión más reciente de DirectShow.

En este tema se describen muchas notificaciones que un representador es responsable de controlar. Una breve revisión de las notificaciones de DirectShow puede ayudar a establecer la fase. Básicamente hay tres tipos de notificaciones que se producen en DirectShow:

  • Notificaciones de secuencia, que son eventos que se producen en la secuencia multimedia y que se pasan de un filtro a otro. Pueden ser notificaciones de inicio y vaciado, vaciado final o fin de secuencia y se envían llamando al método adecuado en el pin de entrada del filtro de bajada (por ejemplo , IPin::BeginFlush).
  • Filtre las notificaciones del grafo, que son eventos enviados desde un filtro al Administrador de gráficos de filtros, como EC_COMPLETE. Esto se logra mediante una llamada al método IMediaEventSink::Notify en el Administrador de gráficos de filtros.
  • Notificaciones de aplicación, que se recuperan del Administrador de gráficos de filtros mediante la aplicación de control. Una aplicación llama al método IMediaEvent::GetEvent del Administrador de gráficos de filtros para recuperar estos eventos. A menudo, el Administrador de gráficos de filtros pasa por los eventos que recibe a la aplicación.

En este tema se describe la responsabilidad del filtro del representador en el control de las notificaciones de flujo que recibe y en el envío de notificaciones de grafos de filtro adecuadas.

Controlar las notificaciones de fin de secuencia y vaciado

Una notificación de fin de secuencia comienza en un filtro ascendente (como el filtro de origen) cuando ese filtro detecta que no puede enviar más datos. Se pasa a través de cada filtro del gráfico y, finalmente, termina en el representador, que es responsable de enviar posteriormente una notificación de EC_COMPLETE al Administrador de gráficos de filtros. Los representadores tienen responsabilidades especiales cuando se trata de controlar estas notificaciones.

Un representador recibe una notificación de fin de secuencia cuando el filtro ascendente llama al método IPin::EndOfStream de su pin de entrada. Un representador debe anotar esta notificación y seguir representando los datos que ya ha recibido. Una vez recibidos todos los datos restantes, el representador debe enviar una notificación EC_COMPLETE al Administrador de gráficos de filtros. La notificación de EC_COMPLETE solo debe enviarse una vez por un representador cada vez que llega al final de una secuencia. Además, nunca se deben enviar EC_COMPLETE notificaciones, excepto cuando se ejecuta el gráfico de filtros. Por lo tanto, si el gráfico de filtro está en pausa cuando un filtro de origen envía una notificación de fin de secuencia, EC_COMPLETE no se debe enviar hasta que se ejecute finalmente el gráfico de filtro.

Se deben rechazar todas las llamadas a los métodos IMemInputPin::Receive o IMemInputPin::ReceiveMultiple después de que se indique una notificación de fin de secuencia. E_UNEXPECTED es el mensaje de error más adecuado para devolver en este caso.

Cuando se detiene un gráfico de filtro, se debe borrar cualquier notificación de fin de secuencia almacenada en caché y no volver a enviar cuando se inicie a continuación. Esto se debe a que el Administrador de gráficos de filtros siempre pausa todos los filtros justo antes de ejecutarlos para que se produzca el vaciado adecuado. Por lo tanto, por ejemplo, si el gráfico de filtro está en pausa y se recibe una notificación de fin de secuencia y, a continuación, se detiene el gráfico de filtro, el representador no debe enviar una notificación de EC_COMPLETE cuando se ejecute posteriormente. Si no se han producido búsquedas, el filtro de origen enviará automáticamente otra notificación de fin de secuencia durante el estado de pausa que precede a un estado de ejecución. Si, por otro lado, se ha producido una búsqueda mientras se detiene el gráfico de filtros, el filtro de origen podría tener datos para enviar, por lo que no enviará una notificación de fin de secuencia.

Los representadores de vídeo a menudo dependen de las notificaciones de fin de secuencia para más que el envío de notificaciones de EC_COMPLETE . Por ejemplo, si una secuencia ha terminado de reproducirse (es decir, se envía una notificación de fin de secuencia) y se arrastra otra ventana a través de una ventana del representador de vídeo, se generará un número de mensajes de ventana de WM_PAINT . La práctica habitual para ejecutar representadores de vídeo es evitar volver a pintar el fotograma actual al recibir WM_PAINT mensajes (según la suposición de que se recibirá otro fotograma que se va a dibujar). Sin embargo, cuando se ha enviado la notificación de fin de secuencia, el representador está en estado de espera; todavía se está ejecutando, pero es consciente de que no recibirá ningún dato adicional. En estas circunstancias, el representador dibuja habitualmente el área de reproducción negra.

El control del vaciado es una complicación adicional para los representadores. El vaciado se lleva a cabo a través de un par de métodos IPindenominados BeginFlush y EndFlush. El vaciado es básicamente un estado adicional que el representador debe controlar. Es ilegal que un filtro de origen llame a BeginFlush sin llamar a EndFlush, así que esperamos que el estado sea corto y discreto; sin embargo, el representador debe controlar correctamente los datos o las notificaciones que recibe durante la transición de vaciado.

Los datos recibidos después de llamar a BeginFlush deben rechazarse inmediatamente devolviendo S_FALSE. Además, las notificaciones de fin de secuencia almacenadas en caché también deben borrarse cuando se vacía un representador. Normalmente, un representador se vaciará en respuesta a una búsqueda. El vaciado garantiza que los datos antiguos se borren del gráfico de filtros antes de enviar muestras nuevas. (Normalmente, la reproducción de dos secciones de una secuencia, una después de otra, se controla mejor mediante comandos diferidos en lugar de esperar a que una sección finalice y, a continuación, emita un comando seek).

Control de cambios de estado y finalización de pausa

Un filtro de representador se comporta igual que cualquier otro filtro del gráfico de filtros cuando se cambia su estado, con la siguiente excepción. Después de pausarse, el representador tendrá algunos datos en cola, listos para representarse cuando se ejecute posteriormente. Cuando se detiene el representador de vídeo, se mantiene en estos datos en cola. Se trata de una excepción a la regla DirectShow de que no se debe mantener ningún recurso por filtros mientras se detiene el gráfico de filtros.

El motivo de esta excepción es que, al contener recursos, el representador siempre tendrá una imagen con la que volver a pintar la ventana si recibe un mensaje de WM_PAINT . También tiene una imagen para satisfacer métodos, como CBaseControlVideo::GetStaticImage, que solicitan una copia de la imagen actual. Otro efecto de mantener los recursos es que mantener en la imagen impide que el asignador se descommita, lo que a su vez hace que el siguiente cambio de estado se produzca mucho más rápido porque los búferes de imagen ya están asignados.

Un representador de vídeo solo debe representar y liberar ejemplos mientras se ejecuta. Mientras está en pausa, el filtro puede representarlos (por ejemplo, al dibujar una imagen de póster estática en una ventana), pero no debe liberarlos. Los representadores de audio no realizarán ninguna representación mientras están en pausa (aunque pueden realizar otras actividades, como preparar el dispositivo de onda, por ejemplo). La hora en la que se deben representar las muestras se obtiene combinando el tiempo de la secuencia en el ejemplo con el tiempo de referencia pasado como parámetro al método IMediaControl::Run . Los representadores deben rechazar muestras con horas de inicio menores o iguales que las horas de finalización.

Cuando una aplicación pausa un gráfico de filtro, el gráfico de filtros no devuelve su método IMediaControl::P ause hasta que haya datos en cola en los representadores. Para garantizar esto, cuando un representador está en pausa, debe devolver S_FALSE si no hay datos a la espera de representarse. Si tiene datos en cola, puede devolver S_OK.

El Administrador de gráficos de filtros comprueba todos los valores devueltos al pausar un gráfico de filtro para asegurarse de que los representadores tienen datos en cola. Si uno o varios filtros no están listos, el Administrador de gráficos de filtros sondea los filtros del grafo mediante una llamada a IMediaFilter::GetState. El método GetState tarda un parámetro de tiempo de espera. Un filtro (normalmente un representador) que sigue esperando a que lleguen los datos antes de completar el cambio de estado devuelve VFW_S_STATE_INTERMEDIATE si expira el método GetState . Una vez que llegan los datos al representador, GetState debe devolverse inmediatamente con S_OK.

En el estado intermedio y completado, el estado del filtro notificado se State_Paused. Solo el valor devuelto indica si el filtro está realmente listo o no. Si, mientras un representador espera a que lleguen los datos, su filtro de origen envía una notificación de fin de secuencia, esto también debe completar el cambio de estado.

Una vez que todos los filtros tienen datos a la espera de representarse, el gráfico de filtros completará su cambio de estado de pausa.

Control de la terminación

Los representadores de vídeo deben controlar correctamente los eventos de finalización del usuario. Esto implica ocultar correctamente la ventana y saber qué hacer si posteriormente se fuerza a mostrar una ventana. Además, los representadores de vídeo deben notificar al Administrador de gráficos de filtros cuando se destruye su ventana (o más precisamente, cuando el representador se quita del gráfico de filtros) para liberar recursos.

Si el usuario cierra la ventana de vídeo (por ejemplo, presionando ALT+F4), la convención consiste en ocultar la ventana inmediatamente y enviar una notificación de EC_USERABORT al Administrador de gráficos de filtros. Esta notificación se pasa a la aplicación, lo que detendrá la reproducción del gráfico. Después de enviar EC_USERABORT, un representador de vídeo debe rechazar cualquier muestra adicional que se le entregue.

El representador debe dejar la marca de detención del grafo hasta que se detenga posteriormente, momento en el que se debe restablecer para que una aplicación pueda invalidar la acción del usuario y seguir reproduciendo el gráfico si lo desea. Si se presiona ALT+F4 mientras se ejecuta el vídeo, la ventana se ocultará y se rechazarán todas las muestras que se entreguen. Si la ventana se muestra posteriormente (quizás a través de IVideoWindow::p ut_Visible), no se debe generar ninguna EC_REPAINT notificaciones.

El representador de vídeo también debe enviar la notificación EC_WINDOW_DESTROYED al gráfico de filtros cuando el representador de vídeo finaliza. De hecho, es mejor controlar esto cuando se llama al método IBaseFilter::JoinFilterGraph del representador con un parámetro NULL (lo que indica que el representador está a punto de quitarse del gráfico de filtros), en lugar de esperar hasta que se destruye la ventana de vídeo real. El envío de esta notificación permite al distribuidor del complemento en filter Graph Manager pasar recursos que dependen del foco de la ventana a otros filtros, como los dispositivos de audio.

Control de cambios de formato dinámico

En algunos casos, el filtro ascendente del representador podría intentar cambiar el formato de vídeo mientras se reproduce el vídeo. Suele ser el descomprimor de vídeo que inicia un cambio de formato dinámico.

Un filtro ascendente que intente cambiar los formatos de forma dinámica siempre debe llamar al método IPin::QueryAccept en el pin de entrada del representador. Un representador de vídeo tiene algún margen de margen para los tipos de cambios de formato dinámico que debe admitir. Como mínimo, debe permitir que el filtro ascendente cambie las paletas. Cuando un filtro ascendente cambia los tipos de medios, adjunta el tipo de medio al primer ejemplo entregado en el nuevo formato. Si el representador contiene muestras en una cola para la representación, no debe cambiar el formato hasta que represente el ejemplo con el cambio de tipo.

Un representador de vídeo también puede solicitar un cambio de formato del descodificador. Por ejemplo, podría pedir al descodificador que proporcione un formato compatible con DirectDraw con una biHeight negativa. Cuando el representador está en pausa, debe llamar a QueryAccept en la patilla ascendente para ver qué formatos puede proporcionar el descodificador. Es posible que el descodificador no enumere todos los tipos que puede aceptar, por lo que el representador debe ofrecer algunos tipos incluso si el descodificador no los anuncia.

Si el descodificador puede cambiar al formato solicitado, devuelve S_OK de QueryAccept. A continuación, el representador adjunta el nuevo tipo de medio al siguiente ejemplo multimedia en el asignador ascendente. Para que esto funcione, el representador debe proporcionar un asignador personalizado que implemente un método privado para asociar el tipo de medio al ejemplo siguiente. (Dentro de este método privado, llame a IMediaSample::SetMediaType para establecer el tipo).

El pin de entrada del representador debe devolver el asignador personalizado del representador en el método IMemInputPin::GetAllocator . Invalide IMemInputPin::NotifyAllocator para que se produzca un error si el filtro ascendente no usa el asignador del representador.

Con algunos descodificadores, establecer biHeight en un número positivo en los tipos YUV hace que el descodificador dibuje la imagen al revés. (Esto es incorrecto y se debe considerar un error en el descodificador).

Cada vez que el representador de vídeo detecta un cambio de formato, debe enviar una notificación EC_DISPLAY_CHANGED . La mayoría de los representadores de vídeo seleccionan un formato durante la conexión para que el formato se pueda dibujar de forma eficaz a través de GDI. Si el usuario cambia el modo de presentación actual sin reiniciar el equipo, es posible que un representador se encuentre con una conexión de formato de imagen incorrecta y debe enviar esta notificación. El primer parámetro debe ser el pin que necesita volver a conectarse. El Administrador de gráficos de filtros organizará que el gráfico de filtro se detenga y se vuelva a conectar el anclaje. Durante la reconexión posterior, el representador puede aceptar un formato más adecuado.

Cada vez que un representador de vídeo detecta un cambio de paleta en la secuencia, debe enviar la notificación EC_PALETTE_CHANGED al Administrador de gráficos de filtros. Los representadores de vídeo directShow detectan si una paleta ha cambiado realmente en formato dinámico o no. Los representadores de vídeo no solo lo hacen para filtrar el número de notificaciones de EC_PALETTE_CHANGED enviadas, sino también para reducir la cantidad de creación, instalación y eliminación de la paleta necesaria.

Por último, el representador de vídeo también puede detectar que el tamaño del vídeo ha cambiado, en cuyo caso, debe enviar la notificación EC_VIDEO_SIZE_CHANGED . Una aplicación podría usar esta notificación para negociar espacio en un documento compuesto. Las dimensiones de vídeo reales están disponibles a través de la interfaz de control IBasicVideo . Los representadores directShow detectan si el vídeo ha cambiado el tamaño o no antes de enviar estos eventos.

Controlar las propiedades persistentes

Todas las propiedades establecidas a través de las interfaces IBasicVideo e IVideoWindow están diseñadas para ser persistentes entre las conexiones. Por lo tanto, la desconexión y reconexión de un representador no debe mostrar ningún efecto en el tamaño, la posición o los estilos de la ventana. Sin embargo, si las dimensiones de vídeo cambian entre conexiones, el representador debe restablecer los rectángulos de origen y destino a sus valores predeterminados. Las posiciones de origen y destino se establecen a través de la interfaz IBasicVideo .

IBasicVideo e IVideoWindow proporcionan suficiente acceso a las propiedades para permitir que una aplicación guarde y restaure todos los datos de la interfaz en un formato persistente. Esto será útil para las aplicaciones que deben guardar la configuración exacta y las propiedades de los gráficos de filtro durante una sesión de edición y restaurarlas más adelante.

Control de notificaciones de EC_REPAINT

La notificación de EC_REPAINT solo se envía cuando el representador está en pausa o detenido. Esta notificación indica al Administrador de gráficos de filtros que el representador necesita datos. Si el gráfico de filtros se detiene cuando recibe una de estas notificaciones, pausará el gráfico de filtros, esperará a que todos los filtros reciban datos (llamando a GetState) y, a continuación, los detendrá de nuevo. Cuando se detiene, un representador de vídeo debe mantenerse en la imagen para que se puedan controlar los mensajes de WM_PAINT posteriores.

Por lo tanto, si un representador de vídeo recibe un mensaje de WM_PAINT cuando está detenido o en pausa, y no tiene nada con el que pintar su ventana, debe enviar EC_REPAINT al Administrador de gráficos de filtros. Si se recibe una notificación de EC_REPAINT mientras está en pausa, el Administrador de gráficos de filtros llama a IMediaPosition::p ut_CurrentPosition con la posición actual (es decir, busca la posición actual). Esto hace que los filtros de origen vacíe el gráfico de filtros y haga que los nuevos datos se envíen a través del gráfico de filtros.

Un representador debe enviar solo una de estas notificaciones a la vez. Por lo tanto, una vez que el representador envía una notificación, debe asegurarse de que no se envíen más hasta que se entreguen algunas muestras. La forma convencional de hacerlo es tener una marca para indicar que se puede enviar un reintentos, que se desactiva después de enviar una notificación de EC_REPAINT . Esta marca debe restablecerse una vez entregados los datos o cuando se vacía la patilla de entrada, pero no si se señala el final del flujo en el pin de entrada.

Si el representador no supervisa sus notificaciones de EC_REPAINT , inundará el Administrador de gráficos de filtros con EC_REPAINT solicitudes (que son relativamente costosas de procesar). Por ejemplo, si un representador no tiene ninguna imagen que dibujar y otra ventana se arrastra a través de la ventana del representador en una operación de arrastre completo, el representador recibe varios mensajes de WM_PAINT . Solo el primero de estos debe generar una notificación de evento de EC_REPAINT desde el representador al Administrador de gráficos de filtros.

Un representador debe enviar su pin de entrada como primer parámetro a la notificación de EC_REPAINT . Al hacerlo, se consultará el pin de salida adjunto para IMediaEventSink y, si se admite, la notificación de EC_REPAINT se enviará allí primero. Esto permite que las patillas de salida controle los reintentos antes de que se deba tocar el gráfico de filtro. Esto no se realizará si se detiene el grafo de filtro, ya que no habrá búferes disponibles en el asignador de representador descommitido.

Si el pin de salida no puede controlar la solicitud y el gráfico de filtros se está ejecutando, se omite la notificación de EC_REPAINT . Un pin de salida debe devolver S_OK de IMediaEventSink::Notify para indicar que procesó la solicitud de reintentos correctamente. Se llamará al pin de salida en el subproceso de trabajo de Filter Graph Manager, lo que evita que el representador llame directamente al pin de salida y, por tanto, elimina los problemas de interbloqueo. Si el gráfico de filtros está detenido o en pausa y la salida no controla la solicitud, se realiza el procesamiento predeterminado.

Control de notificaciones en modo de Full-Screen

El distribuidor del complemento IVideoWindow (PID) del gráfico de filtros administra la reproducción de pantalla completa. Intercambiará un representador de vídeo para un representador de pantalla completa especialista, estirará una ventana de un representador a pantalla completa o hará que el representador implemente la reproducción de pantalla completa directamente. Para interactuar en protocolos de pantalla completa, un representador de vídeo debe enviar una notificación de EC_ACTIVATE cada vez que se active o desactive su ventana. Es decir, se debe enviar una notificación EC_ACTIVATE para cada mensaje WM_ACTIVATEAPP que recibe un representador.

Cuando se usa un representador en modo de pantalla completa, estas notificaciones administran el cambio en ese modo de pantalla completa y fuera de ese modo de pantalla completa. Normalmente, la desactivación de ventanas se produce cuando un usuario presiona ALT+TAB para cambiar a otra ventana, que el representador de pantalla completa directShow usa como indicación para volver al modo de representación típico.

Cuando se envía la notificación EC_ACTIVATE al Administrador de gráficos de filtros al salir del modo de pantalla completa, el Administrador de gráficos de filtros envía una notificación EC_FULLSCREEN_LOST a la aplicación de control. La aplicación puede usar esta notificación para restaurar el estado de un botón de pantalla completa, por ejemplo. DirectShow usa internamente las notificaciones de EC_ACTIVATE para administrar el cambio de pantalla completa en indicaciones de los representadores de vídeo.

Resumen de notificaciones

En esta sección se enumeran las notificaciones de gráfico de filtro que un representador puede enviar.

Notificación de evento Descripción
EC_ACTIVATE Enviado por representadores de vídeo en modo de representación de pantalla completa para cada mensaje de WM_ACTIVATEAPP recibido.
EC_COMPLETE Enviado por representadores una vez representados todos los datos.
EC_DISPLAY_CHANGED Enviado por representadores de vídeo cuando cambia un formato de presentación.
EC_PALETTE_CHANGED Se envía cada vez que un representador de vídeo detecta un cambio de paleta en la secuencia.
EC_REPAINT Enviado por representadores de vídeo detenidos o en pausa cuando se recibe un mensaje de WM_PAINT y no hay datos que mostrar. Esto hace que el Administrador de gráficos de filtros genere un marco para pintar en la pantalla.
EC_USERABORT Enviado por representadores de vídeo para indicar un cierre que el usuario solicitó (por ejemplo, un usuario que cierra la ventana de vídeo).
EC_VIDEO_SIZE_CHANGED Enviado por representadores de vídeo cada vez que se detecta un cambio en el tamaño de vídeo nativo.
EC_WINDOW_DESTROYED Enviado por representadores de vídeo cuando se quita o destruye el filtro para que los recursos que dependen del foco de ventana se puedan pasar a otros filtros.

 

Escritura de representadores de vídeo