Como escrever um filtro de origem para o DirectShow
[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.]
Este tópico descreve como escrever um filtro de origem personalizado para o DirectShow.
Observação
Este tópico descreve apenas as fontes de push; ele não descreve fontes de pull, como o Filtro de Leitor Assíncrono ou filtros de divisor que se conectam a fontes de pull. Para obter a distinção entre fontes de push e pull, consulte Fluxo de Dados para Desenvolvedores de Filtros.
O modelo de streaming do DirectShow
Quando você escreve um filtro de origem, é importante entender que uma fonte de push não é a mesma coisa que uma fonte dinâmica. Uma fonte dinâmica obtém dados de alguma fonte externa, como uma câmera ou um fluxo de rede. Em geral, uma fonte dinâmica não pode controlar a taxa de entrada de dados. Se os filtros downstream não consumirem os dados com rapidez suficiente, a origem precisará remover amostras.
Mas uma fonte de push não precisa ser uma fonte ao vivo. Por exemplo, uma fonte de push pode ler dados de um arquivo local. Nesse caso, os filtros do renderizador downstream determinam a rapidez com que consomem os dados da origem, com base no relógio de referência e nos carimbos de data/hora de exemplo. O filtro de origem fornece amostras o mais rápido possível, mas o fluxo de dados real é limitado pelos renderizadores. Os mecanismos para gating do fluxo de dados são descritos em Fluxo de Dados para Desenvolvedores de Filtros.
Cada pino de saída no filtro de origem cria um thread chamado thread de streaming. O pino fornece exemplos no thread de streaming. Normalmente, toda a decodificação, processamento e renderização ocorrem nesse thread, embora alguns filtros downstream possam criar threads adicionais para enfileirar seus exemplos de saída.
O thread de streaming executa um loop com a seguinte estrutura:
until (stopped)
1. Get a media sample from the allocator.
2. Fill the sample with data.
3. Time stamp the sample.
4. Deliver the sample downstream.
Se nenhum exemplo estiver disponível, a etapa 1 será bloqueada até que um exemplo fique disponível. A etapa 4 também pode bloquear; por exemplo, ele pode ser bloqueado enquanto o grafo está em pausa.
O loop é executado o mais rápido possível, mas é limitado pela rapidez com que o filtro do renderizador renderiza cada amostra. Supondo que o grafo de filtro tenha um relógio de referência, a taxa é determinada pelos tempos de apresentação nos exemplos. Se não houver um relógio de referência, o renderizador consumirá amostras o mais rápido possível.
Usando CSource e CSourceStream
As classes base do DirectShow incluem duas classes que dão suporte a fontes de push: CSource e CSourceStream.
- CSource é a classe base para o filtro e implementa a interface IBaseFilter .
- CSourceStream é a classe base para os pinos de saída e implementa a interface IPin .
Pinos de saída
Um filtro de origem pode ter mais de um pino de saída. No método do construtor do filtro, crie um ou mais pinos derivados de CSourceStream (um pino por fluxo de saída). Você não precisa armazenar ponteiros para os pinos; os pinos se adicionam automaticamente ao filtro quando são criados.
Formatos de saída
O pino de saída manipula a negociação de formato com os seguintes métodos CSourceStream :
Método | Descrição |
---|---|
Getmediatype | Obtém um tipo de mídia do pino de saída. O pin deve propor pelo menos um tipo de mídia, pois o filtro downstream pode não propor nenhum tipo. Na maioria dos casos, o filtro downstream será um decodificador ou um renderizador, dependendo se o filtro de origem fornece dados compactados ou descompactados. Um filtro de renderizador geralmente requer um tipo de mídia completo, contendo todas as informações de formato necessárias para renderizar o fluxo. Para um decodificador, a quantidade de informações necessárias no tipo de mídia depende muito do formato de codificação. |
Checkmediatype | Verifica se o pino de saída aceita um determinado tipo de mídia. Substituir esse método é opcional, dependendo de como você implementa GetMediaType. |
O método GetMediaType está sobrecarregado:
- GetMediaType (1) usa um único parâmetro, um ponteiro para um objeto CMediaType .
- GetMediaType (2) usa uma variável de índice e um ponteiro para um objeto CMediaType .
Se o pin de saída do filtro de origem der suporte a exatamente um formato de mídia, você deverá substituir (1) para inicializar o objeto CMediaType com esse formato. Deixe a implementação padrão de (2) e também deixe a implementação padrão de CheckMediaType.
Se o pino der suporte a mais de um formato, substitua (2). Inicialize o objeto CMediaType de acordo com o valor da variável de índice. O pino deve retornar os formatos como uma lista ordenada. Nesse caso, você também deve substituir o CheckMediaType para marcar o tipo de mídia em relação à sua lista de formatos.
Para formatos de vídeo descompactados, lembre-se de que o filtro downstream pode propor formatos com vários valores stride. Seu filtro deve aceitar qualquer valor de passo válido. Para obter mais informações, consulte BITMAPINFOHEADER.
Você também deve substituir o método CBaseOutputPin::D ecideBufferSize puro-virtual. Use esse método para definir o tamanho dos buffers de exemplo.
Streaming
A classe CSourceStream cria o thread de streaming para o pino. O procedimento de thread é implementado no método CSourceStream::D oBufferProcessingLoop . Esse método chama o método CSourceStream::FillBuffer puro-virtual, que a classe derivada deve substituir. Esse método é onde o pino preenche o buffer com dados. Por exemplo, se o filtro entregar um vídeo descompactado, é aqui que você desenharia os quadros de vídeo.
A classe base inicia e interrompe automaticamente o loop de thread nos momentos certos, quando o filtro é pausado ou interrompido. Quando isso acontece, a classe CSourceStream chama alguns métodos para notificar sua classe derivada:
Você pode substituir esses métodos se precisar adicionar qualquer tratamento especial. Caso contrário, as implementações padrão simplesmente retornarão S_OK.
Procurando
Se você tiver um filtro de origem com um pino de saída, poderá usar a classe CSourceSeeking como ponto de partida para implementar a busca. Herde sua classe de pino de CSourceStream e CSourceSeeking.
Observação
CSourceSeeking não é recomendado para um filtro com mais de um pino de saída. O main problema é que apenas um pino deve responder à busca de solicitações. Normalmente, isso requer comunicação entre os pinos e o filtro.
A classe CSourceSeeking gerencia a taxa de reprodução, a hora de início, a hora de parada e a duração. Sua classe derivada deve definir o tempo e a duração de parada iniciais. Sempre que um desses valores é alterado, o método CSourceSeeking::ChangeRate, CSourceSeeking::ChangeStart ou CSourceSeeking::ChangeStop é chamado, conforme apropriado. Os métodos são todos métodos virtuais puros. A classe de pino derivada substitui esses métodos para fazer o seguinte:
- Chame IPin::BeginFlush no pino downstream. Isso faz com que os filtros downstream liberem amostras que estão mantendo e rejeitem novos exemplos.
- Chame CSourceStream::Stop para interromper o thread de streaming. O filtro de origem suspende a produção de novos dados.
- Chame IPin::EndFlush no pino downstream. Isso sinaliza os filtros downstream para aceitar novos dados.
- Chame IPin::NewSegment com os novos horários de início e parada e taxa.
- Defina a propriedade de descontinuidade no próximo exemplo.
Para obter mais informações, consulte Suporte à busca em um filtro de origem.
Se o filtro der suporte à busca, a posição do fluxo agora será independente do horário da apresentação. Após uma busca, os carimbos de data/hora são redefinidos para zero. A fórmula geral para carimbos de data/hora é:
- hora de início da amostra = tempo decorrido/taxa de reprodução
- hora de término de exemplo = hora de início de exemplo + (hora por quadro/taxa de reprodução)
em que o tempo decorrido é o tempo decorrido desde que o filtro começou a ser executado ou desde o último comando seek.
Formatos de hora para busca
Por padrão, os comandos seek estão em unidades de 100 nanossegundos. O filtro de origem pode dar suporte a formatos de tempo adicionais, como procurar por número de quadro. Cada vez que o formato é identificado por um GUID; consulte GUIDs de formato de hora.
Para dar suporte a formatos de tempo adicionais, você deve implementar os seguintes métodos no pin de saída:
- IMediaSeeking::ConvertTimeFormat
- IMediaSeeking::GetTimeFormat
- IMediaSeeking::IsFormatSupported
- IMediaSeeking::IsUsingTimeFormat
- IMediaSeeking::QueryPreferredFormat
- IMediaSeeking::SetTimeFormat
Se o aplicativo definir um novo formato de hora, todos os parâmetros de posição nos métodos IMediaSeeking serão interpretados em termos do novo formato de hora. Por exemplo, se o formato de hora for quadros, o método IMediaSeeking::GetDuration deverá retornar a duração em quadros.
Na prática, poucos filtros directShow dão suporte a formatos de tempo adicionais e, como resultado, poucos aplicativos DirectShow usam essa funcionalidade.