Red de conocimiento informático - Material del sitio web - Cómo obtener datos de un gráfico de filtro de Microsoft DirectShow

Cómo obtener datos de un gráfico de filtro de Microsoft DirectShow

Escribir un ejemplo de filtro Grabber

Una forma sencilla de obtener datos de un gráfico de filtro DirectShow es escribir un ejemplo de filtro "rastreador" personalizado. Conecte el filtro al flujo de datos que desea monitorear y luego ejecute el gráfico de filtro. Cuando los datos pasen por el filtro, la aplicación los procesará según sus necesidades.

Los posibles usos de estos ejemplos de filtros de captura incluyen:

Decodificar el archivo completo en un búfer de memoria.

Obtiene fotogramas pegados de un archivo de vídeo.

Obtenga imágenes fijas de transmisiones de vídeo en vivo.

Decodifica archivos de vídeo en buffers de Microsoft DirectDraw.

El SDK de DirectShow 8.0 incluye un ejemplo de filtro de captura, pero no proporciona el código fuente; el SDK de DirectShow 8.1 incluye un código fuente modificado para la muestra de filtro de captura como una muestra de SDK denominada Muestra de filtro de GrabberSample.

Primero, debes elegir si escribir un filtro de conversión o un programa de salida. Un filtro de transformación se puede conectar a otro filtro descendente, lo que le permite representar datos, escribir datos en un archivo y realizar otras operaciones. Pero dado que los filtros de conversión requieren conexiones descendentes adicionales, su implementación correcta puede resultar compleja. El filtro del programa de salida solo requiere una conexión de entrada.

Este artículo describe cómo escribir filtros de conversión, pero muchas de las ideas se aplican también a los filtros de programas de salida.

Este filtro es un filtro de "conversión in situ", lo que significa que modificará directamente los datos en el búfer recibido sin crear una copia del nuevo búfer. Utiliza la biblioteca de clases base DirectShow.

Para escribir un filtro de traducción in situ, realice los siguientes pasos:

1.

Defina una nueva clase derivada de la clase CTransInPlaceFilter.

2.

Puedes convertir el filtro en un objeto COM real, capaz de autoregistrarse. Para hacer esto, necesita un archivo IDL o un archivo de encabezado que contenga la definición CLSID, un archivo DEF que exporte las funciones DLL y un método de clase estática que cree el filtro. Para obtener más información, consulte los temas en la documentación del SDK de DirectShow sobre cómo crear una DLL y cómo registrar un filtro de DirectShow.

3.

Anule los dos métodos virtuales puros en CTransInPlaceFilter: método Transform y método CheckInputType.

La clase CTransInPlaceFilter maneja automáticamente una serie de otras tareas, como negociar conexiones de pines y buffers, reconectar pines cuando sea necesario, mover datos de pines de entrada a pines de salida y admitir subprocesos múltiples. Leer el código C para estas clases base es una buena manera de obtener los detalles de los filtros DirectShow. Si desea realizar operaciones más complejas, es posible que deba anular otros métodos de CTransInPlaceFilter.

Anular el método CheckInputType

El método CheckInputType en un filtro determina qué tipos de medios se aceptan y qué tipos de medios se rechazan. Durante la conexión del pin, el pin ascendente propondrá varios tipos de medios. Su filtro puede aceptar cualquier tipo de medio o rechazar cualquier tipo de medio. Cuando DirectShow crea un gráfico de filtros, automáticamente intenta encontrar los filtros enumerados en el registro para que la conexión pueda funcionar correctamente.

Por ejemplo, si su filtro solo acepta video sin comprimir, DirectShow insertará el descompresor de video apropiado cuando la aplicación intente conectarse a una fuente de archivo AVI.

Tipo de formato

Si su filtro solo acepta MEDIATYPE_Video con subtipo MEDIASUBTYPE_RGB24, no es necesario conectarse al tipo de formato FORMAT_VideoInfo. Debe decidir qué formatos manejará el filtro y qué tipos de formatos diferentes se aceptarán o rechazarán en consecuencia.

Bloques de formato y DIB invertidos

Para tipos de vídeo sin comprimir, es posible que el filtro ascendente desee pasar un mapa de bits independiente del dispositivo (DIB) invertido. Al conectarse, especificará lo anterior en el miembro biHeight de la estructura BITMAPINFOHEADER del bloque de formato. Por lo tanto, si su filtro requiere una orientación DIB específica (invertida o no), asegúrese de verificar el miembro biHeight y rechazar cualquier tipo que el filtro no pueda manejar.

Muchos descompresores pueden decodificar usando ambas direcciones y recomendarán usar ambos tipos. Si acepta un tipo de medio sin verificar la orientación, las clavijas se conectarán usando la primera orientación sugerida por el desempaquetador.

Establecer el tipo de medio en la aplicación

En el ejemplo del filtro raspador, tiene más sentido dejar que la aplicación controle los tipos de medio aceptados por el filtro. Una aplicación que utiliza este enfoque realiza los siguientes pasos:

1.

La aplicación llama a un método personalizado en el filtro para especificar el tipo de datos que necesita. Puede ser un formato específico o puede ser una descripción general que permita muchos formatos posibles (por ejemplo, vídeo RGB de 24 bits de cualquier tamaño).

2.

La aplicación conecta este ejemplo de capturador con otros filtros en el gráfico. Durante la negociación de PIN, el método CheckInput intenta hacer coincidir el tipo de medio sugerido con el tipo especificado por la aplicación en el primer paso.

3.

La aplicación llama a otro método personalizado para recuperar el tipo de medio real utilizado para esta conexión.

Por ejemplo, en el primer paso, la aplicación podría especificar RGB de 24 bits. En el segundo paso, los pines se conectarán utilizando dimensiones de vídeo específicas, como 320 X 240 píxeles. En el tercer paso, la aplicación recupera el tipo de medio para determinar las dimensiones del vídeo. Sin esta información, la aplicación no puede interpretar los datos recibidos.

Debe definir una interfaz COM personalizada en el filtro que contenga estos dos métodos. El ejemplo de filtro DirectShow Grabber utiliza la interfaz ISampleGrabber; puede utilizarla como guía al crear sus propios filtros.

Anular el método de conversión

Uno de los parámetros del constructor CTransInPlaceFilter es un indicador que especifica si el filtro modifica los datos que recibe. Si pasa un valor de falso, los datos no deben cambiarse de ninguna manera. De lo contrario, puede modificar los datos de cualquier forma en el método Transformar.

El método Transform recibe un puntero a la interfaz MediaSample IMediaSample. Este método se denomina método CTransInPlaceFilter::Receive. Después de que regresa el método Transform, el método Recibir llama a CBaseOutputPin::Deliver en el pin de salida para entregar la muestra.

Si el método Transform devuelve S_FALSE, la clase base señalará cambios en el control de calidad. Sin embargo, en este ejemplo, el método Recibir devuelve S_OK (en lugar de S_FALSE), por lo que el filtro ascendente continúa pasando.

Si el método Transform devuelve un código de error, la clase base señala un error de transmisión al gráfico de filtro y el gráfico de filtro se detiene. No se debe devolver ningún código de error a menos que se produzca un verdadero error de procesamiento de flujo. Si solo desea detener la transmisión, debe anular el método Recibir y devolver S_FALSE desde Recibir.

Volver al inicio

Usar subprocesos múltiples

El subproceso que ejecuta la aplicación siempre debe ser diferente del subproceso que pasa datos al filtro. Si desea sincronizar la recuperación de datos en su aplicación, debe considerar este subproceso múltiple. A continuación se ofrecen algunas sugerencias para manejar situaciones comunes:

Decodificar el archivo completo

Si desea decodificar todo el archivo comprimido y ordenar cada fragmento de datos sin comprimir, probablemente no No es necesario preocuparse por problemas de manejo de subprocesos. Puede crear un búfer global en su aplicación y escribir el método Transformar para escribir en ese búfer. Alternativamente, haga que Transform llame a un método de devolución de llamada cuando reciba un ejemplo. Escriba en el búfer global en este método de devolución de llamada. En su aplicación, configure la devolución de llamada y ejecute el gráfico hasta que se detenga, completando el proceso.

Decodificar parte del archivo

Esta situación es similar a decodificar el archivo completo, excepto que la aplicación necesita usar el método IMediaSeeking::SetPositions para establecer las posiciones de inicio y finalización. Además, hay una manera de devolver S_FALSE desde el método de recepción para indicarle al filtro de origen que deje de transmitir datos.

Decodificar partes aleatorias del archivo

El proceso de decodificación se vuelve más complicado si desea decodificar una parte del archivo y luego buscar otra ubicación para decodificar nuevamente. Cuando busca filtrar un gráfico o cambiar de un estado de gráficos a otro, la aplicación debe esperar a que el estado de los gráficos cambie a un estado estable.

Cuando busca un gráfico (usando IMediaSeeking o IMediaPosition), la llamada primero comienza desde el filtro del programa de salida y luego se mueve sincrónicamente hacia arriba hasta llegar al filtro de origen. El filtro de origen dejará de enviar datos de forma asincrónica, enviará una actualización en sentido descendente, buscará la nueva ubicación y luego comenzará a enviar datos nuevamente.

Para obtener un único marco de datos, anule el método de recepción para devolver S_FALSE.

En la aplicación, pausa el gráfico y busca el tiempo requerido. La fuente responderá con una búsqueda y luego enviará una muestra.

Utilice eventos si desea que su aplicación procese ejemplos de forma sincrónica en lugar de asincrónica. Configure el evento en el método Transform y luego espere a que ocurra el evento en su aplicación. Por ejemplo, puedes usar un bucle como este:

mientras (no completado)

Busca el gráfico de filtro.

Esperar señal de evento.

Este ejemplo supone que procesas los datos completamente en el método Transform o en el método de devolución de llamada. Si desea procesar datos en este bucle de aplicación, necesita un segundo evento.

Mientras (Sin terminar)

Buscar gráficos de filtro.

Esperar a que se señalice el evento 1.

Datos del proceso.

Evento de señal 2. Escriba el método Transformar del filtro de la siguiente manera:

Transformar:

Emitir señal del evento 1.

Espera al evento 2.

Devuelve S_FALSE.

Si no hay un segundo evento, el método Transform regresará inmediatamente porque se está ejecutando en otro hilo. Luego, otros filtros pueden escribir datos nuevos en la muestra mientras la aplicación todavía está procesando los datos antiguos.

Tenga en cuenta que otro enfoque es llamar a AddRef en la muestra en el método Transform y luego llamar a Release en la muestra en la aplicación. Esto evita que la muestra regrese a la lista "disponible" al conservar el número de referencia. en la muestra. Sin embargo, esto no impide que los ejemplos posteriores modifiquen este ejemplo. Para obtener más información sobre el recuento de referencias y la interfaz IMediaSample, consulte el tema "Muestras y distribuidores" en la documentación del SDK.

Volver al principio

Ejemplo de código de aplicación

El siguiente código es una aplicación de consola que utiliza el ejemplo de filtro raspador:

#include "stdafx.h"

#include lt;atlbase.hgt;

# include lt;streams.hgt;

# include lt;streams.

#include lt;qedit.hgt; // para renderizador nulo

#include lt;filfuncs.hgt; // para GetOutPin, GetInPin

#include lt; filfuncs.cppgt; // para GetOutPin, GetInPin

#include "SampleGrabber.h"

int test(int argc, char* argv[]);

p>

int main(int argc, char* argv[])

{

CoInitialize( NULL

int i = prueba( argc); , argv );

CoUninitialize();

devolver i;

}

HANDLE gWaitEvent = NULL;

HRESULT Callback(IMediaSample* pSample, REFERENCE_TIME* StartTime,

REFERENCE_TIME* StopTime)

{

// Nota: antes de llamar

//No podemos hacer nada con este ejemplo hasta que filtremos GetConnectedMediaType para aprender el formato.

DbgLog((LOG_TRACE, 0, "Devolución de llamada con muestra lx para el tiempo ld",

pSample, long(*StartTime / 10000)));

SetEvent( gWaitEvent);

return S_FALSE; // Indica a la fuente de señal que deje de transmitir muestras.

}

int test( int argc, char * argv[] )

{

// Crea un evento cuando señalamos cuando se obtiene una muestra.

gWaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

// No hay un capturador de muestra en el registro, así que use "nuevo" para crearlo.

HRESULT hr = S_OK;

CSampleGrabber *pGrab = new CSampleGrabber(NULL, amp;hr, FALSE);

pGrab-gt;AddRef();

//Establece la función de devolución de llamada del filtro. CMediaType mt;

mt.SetType(y MEDIATYPE_Video);

mt.SetSubtype(y MEDIATYPE_Video);