Secuencias y subprocesos de aplicación
[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.
Cualquier aplicación DirectShow contiene al menos dos subprocesos importantes: el subproceso de aplicación y uno o varios subprocesos de streaming. Los ejemplos se entregan en los subprocesos de streaming y se producen cambios de estado en el subproceso de la aplicación. El subproceso de streaming principal se crea mediante un filtro de origen o analizador. Otros filtros pueden crear subprocesos de trabajo que entreguen ejemplos y también se consideran subprocesos de streaming.
Se llama a algunos métodos en el subproceso de aplicación, mientras que otros se llaman en un subproceso de streaming. Por ejemplo:
- Subprocesos de streaming: IMemInputPin::Receive, IMemInputPin::ReceiveMultiple, IPin::EndOfStream, IMemAllocator::GetBuffer.
- Subproceso de aplicación: IMediaFilter::P ause, IMediaFilter::Run, IMediaFilter::Stop, IMediaSeeking::SetPositions, IPin::BeginFlush, IPin::EndFlush.
- Cualquiera: IPin::NewSegment.
Tener un subproceso de streaming independiente permite que los datos fluyan a través del grafo mientras el subproceso de aplicación espera la entrada del usuario. Sin embargo, el peligro de varios subprocesos es que un filtro puede crear recursos cuando se detiene (en el subproceso de la aplicación), usarlos dentro de un método de streaming y destruirlos cuando se detiene (también en el subproceso de la aplicación). Si no tiene cuidado, el subproceso de streaming podría intentar usar los recursos después de destruirlos. La solución consiste en proteger los recursos mediante secciones críticas y sincronizar los métodos de streaming con cambios de estado.
Un filtro necesita una sección crítica para proteger el estado del filtro. La clase CBaseFilter tiene una variable miembro para esta sección crítica, CBaseFilter::m_pLock. Esta sección crítica se denomina bloqueo de filtro. Además, cada pin de entrada necesita una sección crítica para proteger los recursos usados por el subproceso de streaming. Estas secciones críticas se denominan bloqueos de streaming; debe declararlos en la clase pin derivada. Es más fácil usar la clase CCritSec , que encapsula un objeto de windows CRITICAL_SECTION y se puede bloquear mediante la clase CAutoLock . La clase CCritSec también proporciona algunas funciones de depuración útiles. Para obtener más información, vea Funciones críticas de depuración de secciones.
Cuando un filtro se detiene o vacía, debe sincronizar el subproceso de aplicación con el subproceso de streaming. Para evitar el interbloqueo, primero debe desbloquear el subproceso de streaming, lo que podría bloquearse por varias razones:
- Está esperando obtener un ejemplo dentro del método IMemAllocator::GetBuffer , porque todas las muestras del asignador están en uso.
- Espera a que otro filtro vuelva de un método de streaming, como Receive.
- Está esperando dentro de uno de sus propios métodos de streaming, para que algún recurso esté disponible.
- Es un filtro de representador que espera la hora de presentación del ejemplo siguiente.
- Es un filtro de representador que espera dentro del método Receive mientras está en pausa.
Por lo tanto, cuando el filtro se detiene o vacía, debe hacer lo siguiente:
- Libere cualquier ejemplo que contenga por cualquier motivo. Al hacerlo, se desbloquea el método GetBuffer .
- Vuelva desde cualquier método de streaming lo antes posible. Si un método de streaming está esperando un recurso, debe dejar de esperar inmediatamente.
- Comience a rechazar ejemplos en Receive para que el subproceso de streaming no tenga acceso a más recursos. (La clase CBaseInputPin controla esto automáticamente).
- El método Stop debe descommitar todos los asignadores del filtro. (La clase CBaseInputPin controla esto automáticamente).
El vaciado y la detención se producen en el subproceso de la aplicación. Un filtro se detiene en respuesta al método IMediaControl::Stop . Filter Graph Manager emite el comando stop en orden ascendente, empezando por los representadores y trabajando hacia atrás hasta los filtros de origen. El comando stop se produce completamente dentro del método CBaseFilter::Stop del filtro. Cuando el método devuelve, el filtro debe estar en un estado detenido.
Normalmente, el vaciado se produce debido a un comando seek. Un comando flush se inicia desde el filtro de origen o analizador y viaja hacia abajo. El vaciado se produce en dos fases: el método IPin::BeginFlush informa a un filtro para descartar todos los datos pendientes y entrantes; El método IPin::EndFlush indica al filtro que acepte los datos de nuevo. El vaciado requiere dos fases porque la llamada BeginFlush está en el subproceso de aplicación, durante el cual el subproceso de streaming continúa entregando datos. Por lo tanto, algunas muestras pueden llegar después de la llamada a BeginFlush . El filtro debe descartarlos. Se garantiza que las muestras que llegan después de la llamada a EndFlush son nuevas y deben entregarse.
Las secciones siguientes contienen ejemplos de código que muestran cómo implementar los métodos de filtro más importantes, como Pausar, Recibir, etc., de maneras que eviten interbloqueos y condiciones de carrera. Sin embargo, cada filtro tiene requisitos diferentes, por lo que deberá adaptar estos ejemplos a su filtro concreto.
Nota:
Las clases base CTransformFilter y CTransInPlaceFilter controlan muchos de los problemas descritos en este artículo. Si está escribiendo un filtro de transformación y el filtro no espera eventos dentro de un método de streaming o mantiene en muestras fuera de Receive, estas clases base deben ser suficientes.