Estados de filtro

[O recurso associado a esta página, DirectShow, é um recurso herdado. Foi substituído por MediaPlayer, IMFMediaEngine e Audio/Video Capture in Media Foundation. Esses recursos foram otimizados para Windows 10 e Windows 11. A Microsoft recomenda fortemente que o novo código use MediaPlayer, IMFMediaEngine e Audio/Video Capture in Media Foundation em vez de DirectShow, quando possível. A Microsoft sugere que o código existente que usa as APIs herdadas seja reescrito para usar as novas APIs, se possível.]

Os filtros têm três estados possíveis: parado, pausado e em execução. A finalidade do estado pausado é sinalizar dados no grafo para que um comando de execução responda imediatamente. O Gerenciador de Grafo de Filtro controla todas as transições de estado. Quando um aplicativo chama IMediaControl::Run, IMediaControl::P ause ou IMediaControl::Stop, o Gerenciador de Grafo de Filtro chama o método IMediaFilter correspondente em todos os filtros. As transições entre parada e execução sempre passam pelo estado pausado, portanto, se o aplicativo chamar Executar em um grafo parado, o Gerenciador de Grafo de Filtro pausará o grafo antes de executá-lo.

Para a maioria dos filtros, os estados em execução e pausados são idênticos. Considere o seguinte grafo de filtro:

Renderizador de Transformação > de Origem >

Suponha, por enquanto, que o filtro de origem não seja uma fonte de captura dinâmica. Quando o filtro de origem é pausado, ele cria um thread que gera novos dados e os grava em amostras de mídia o mais rápido possível. O thread "envia por push" os exemplos downstream chamando IMemInputPin::Receive no pin de entrada do filtro de transformação. O filtro de transformação recebe os exemplos no thread do filtro de origem. Ele pode usar um thread de trabalho para entregar os exemplos ao renderizador, mas normalmente ele os entrega no mesmo thread. Enquanto o renderizador está em pausa, ele aguarda para receber uma amostra. Depois de receber um, ele bloqueia e mantém essa amostra indefinidamente. Se for um renderizador de vídeo, ele exibirá o exemplo como uma imagem de pôster, repintando a imagem conforme necessário.

Neste ponto, o fluxo está totalmente preparado e pronto para renderização. Se o grafo permanecer em pausa, os exemplos serão "acumulados" no grafo por trás do primeiro exemplo, até que cada filtro seja bloqueado em Receive ou IMemAllocator::GetBuffer. No entanto, nenhum dado é perdido. Depois que o thread de origem é desbloqueado, ele simplesmente é retomado do ponto em que foi bloqueado.

O filtro de origem e o filtro de transformação ignoram a transição de pausado para em execução. Eles simplesmente continuam processando dados o mais rápido possível. Mas quando o renderizador é executado, ele começa a renderizar amostras. Primeiro, ele renderiza a amostra que mantinha enquanto estava em pausa. Em seguida, cada vez que recebe um novo exemplo, ele calcula o tempo de apresentação do exemplo. (Para obter detalhes, consulte Hora e relógios no DirectShow.) O renderizador mantém cada amostra até a hora da apresentação, momento em que renderiza o exemplo. Enquanto aguarda o tempo de apresentação, ele é bloqueado no método Receive ou recebe novos exemplos em um thread de trabalho com uma fila. Os filtros upstream do renderizador não estão envolvidos no agendamento.

Fontes dinâmicas, como dispositivos de captura, são uma exceção a essa arquitetura geral. Com uma fonte dinâmica, não é apropriado sinalizar nenhum dado com antecedência. O aplicativo pode pausar o grafo e aguardar muito tempo antes de executá-lo. O grafo não deve renderizar amostras "obsoletas". Portanto, uma fonte dinâmica não produz amostras enquanto está em pausa, somente durante a execução. Para sinalizar esse fato para o Gerenciador do Gráfico de Filtro, o método IMediaFilter::GetState do filtro de origem retorna VFW_S_CANT_CUE. Esse código de retorno indica que o filtro mudou para o estado em pausa, mesmo que o renderizador não tenha recebido nenhum dado.

Quando um filtro é interrompido, ele rejeita mais amostras entregues a ele. Os filtros de origem desligam seus threads de streaming e outros filtros desligam os threads de trabalho que eles podem ter criado. Os pinos confirmam seus alocadores.

Transições de estado

O Gerenciador de Grafo de Filtro executa todas as transições de estado em upstream ordem, começando do renderizador e trabalhando para trás até o filtro de origem. Essa ordenação é necessária para impedir que exemplos sejam descartados e para evitar que o grafo seja deadlock. As transições de estado mais cruciais estão entre pausadas e interrompidas:

  • Parado para pausado: à medida que cada filtro é pausado, ele fica pronto para receber amostras do próximo filtro. O filtro de origem é o último a ser pausado. Ele cria o thread de streaming e começa a fornecer exemplos. Como todos os filtros downstream estão em pausa, nenhum filtro rejeita nenhuma amostra. O Gerenciador de Grafo de Filtro não conclui a transição até que cada renderizador no grafo tenha recebido uma amostra (com exceção de fontes dinâmicas, conforme descrito anteriormente).
  • Pausado para parar: quando um filtro é interrompido, ele libera todos os exemplos que contém, o que desbloqueia todos os filtros de upstream aguardando no GetBuffer. Se o filtro estiver aguardando um recurso dentro do método Receive , ele interromperá a espera e retornará de Receive, o que desbloqueará o filtro de chamada. Portanto, quando o Gerenciador de Grafo de Filtro para o próximo filtro upstream, esse filtro não é bloqueado em GetBuffer ou Receive e pode responder ao comando stop. O filtro de upstream pode fornecer alguns exemplos extras antes de obter o comando stop, mas o filtro downstream simplesmente os rejeita, pois ele já parou.

Fluxo de Dados no Grafo de Filtro