Ejemplos y asignadores

[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.

Cuando un pin entrega datos multimedia a otro pin, no pasa un puntero directo al búfer de memoria. En su lugar, entrega un puntero a un objeto COM que administra la memoria. Este objeto, denominado ejemplo multimedia, expone la interfaz IMediaSample . El pin receptor accede al búfer de memoria llamando a métodos IMediaSample , como IMediaSample::GetPointer, IMediaSample::GetSize e IMediaSample::GetActualDataLength.

Los ejemplos siempre viajan de bajada, desde el pin de salida hasta el pin de entrada. En el modelo de inserción, el pin de salida entrega un ejemplo llamando a IMemInputPin::Receive en el pin de entrada. El pin de entrada procesará los datos de forma sincrónica (es decir, completamente dentro del método Receive ) o los procesará de forma asincrónica en un subproceso de trabajo. El pin de entrada puede bloquearse dentro del método Receive , si necesita esperar recursos.

Otro objeto COM, denominado asignador, es responsable de crear y administrar ejemplos multimedia. Los asignadores exponen la interfaz IMemAllocator . Cada vez que un filtro necesita un ejemplo multimedia con un búfer vacío, llama al método IMemAllocator::GetBuffer , que devuelve un puntero al ejemplo. Cada conexión de anclaje comparte un asignador. Cuando se conectan dos patillas, deciden qué filtro proporcionará el asignador. Las patillas también establecen propiedades en el asignador, como el número de búferes y el tamaño de cada búfer. (Para obtener más información, consulte Conexión de filtros y negociación de asignadores).

En la ilustración siguiente se muestran las relaciones entre el asignador, los ejemplos multimedia y el filtro.

muestras de medios y asignadores

Recuentos de referencias de ejemplo multimedia

Un asignador crea un grupo finito de muestras. En cualquier momento, algunos ejemplos pueden estar en uso, mientras que otros están disponibles para las llamadas GetBuffer . El asignador usa el recuento de referencias para realizar un seguimiento de las muestras. El método GetBuffer devuelve un ejemplo con un recuento de referencias de 1. Si el recuento de referencias va a cero, el ejemplo vuelve al grupo del asignador, donde se puede usar en la siguiente llamada a GetBuffer . Siempre que el recuento de referencias permanezca por encima de cero, el ejemplo no está disponible para GetBuffer. Si cada ejemplo que pertenece al asignador está en uso, el método GetBuffer se bloquea hasta que un ejemplo esté disponible.

Por ejemplo, supongamos que un pin de entrada recibe una muestra. Si procesa el ejemplo de forma sincrónica, dentro del método Receive , no incrementa el recuento de referencias. Después de que Receive devuelva, el pin de salida libera el ejemplo, el recuento de referencias va a cero y el ejemplo vuelve al grupo del asignador. Por otro lado, si el pin de entrada procesa el ejemplo en un subproceso de trabajo, incrementa el recuento de referencias antes de salir del método Receive . El recuento de referencias ahora es 2. Cuando el pin de salida libera el ejemplo, el recuento va a 1; el ejemplo aún no vuelve al grupo. Una vez que el subproceso de trabajo haya terminado con el ejemplo, llama a Release para liberar el ejemplo. Ahora el ejemplo vuelve al grupo.

Cuando un pin recibe un ejemplo, puede copiar los datos en otro ejemplo, o puede modificar el ejemplo original y entregarlo al siguiente filtro. Potencialmente, un ejemplo puede recorrer toda la longitud del gráfico, cada filtro que llama a AddRef y Release a su vez. Por lo tanto, el pin de salida nunca debe volver a usar un ejemplo después de llamar a Receive, ya que un filtro de bajada puede usar el ejemplo. El pin de salida siempre debe llamar a GetBuffer para obtener un nuevo ejemplo.

Este mecanismo reduce la cantidad de asignación de memoria, ya que los filtros vuelven a usar los mismos búferes. También impide que los filtros escriban accidentalmente sobre los datos que no se han procesado, ya que el asignador mantiene una lista de ejemplos disponibles.

Un filtro puede usar asignadores independientes para la entrada y salida. Puede hacerlo si expande los datos de entrada (por ejemplo, descomprimiéndolo). Si la salida no es mayor que la entrada, un filtro puede procesar los datos en contexto, sin copiarlos en un nuevo ejemplo. En ese caso, dos o más conexiones de patillas pueden compartir un asignador.

Confirmación y descommitción de asignadores

Cuando un filtro crea por primera vez un asignador, el asignador no ha reservado ningún búfer de memoria. En este momento, se producirá un error en las llamadas al método GetBuffer . Cuando se inicia el streaming, el pin de salida llama a IMemAllocator::Commit, que confirma el asignador, lo que hace que asigne memoria. Los pins ahora pueden llamar a GetBuffer.

Cuando se detiene el streaming, el pin llama a IMemAllocator::D ecommit, que descommite el asignador. Todas las llamadas posteriores a GetBuffer producen un error hasta que el asignador se confirma de nuevo. Además, si alguna llamada a GetBuffer está bloqueada actualmente en espera de un ejemplo, devuelve inmediatamente un código de error. El método Decommit puede liberar o no la memoria, dependiendo de la implementación. Por ejemplo, la clase CMemAllocator espera hasta que su método de destructor libere memoria.

Data Flow en el gráfico de filtros