Como escrever um filtro de origem para DirectShow
[O recurso associado a esta página, DirectShow, é um recurso herdado. Foi substituído por MediaPlayer, IMFMediaEnginee Audio/Video Capture in Media Foundation. Esses recursos foram otimizados para Windows 10 e Windows 11. A Microsoft recomenda vivamente que o novo código utilize MediaPlayer, IMFMediaEngine e Captura de Áudio/Vídeo no 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 fontes push; ele não descreve fontes de receção, como o Filtro de Leitor Assíncrono, ou filtros divisores que se conectam a fontes de receção. Para obter a distinção entre push e pull sources, consulte Data Flow for Filter Developers.
O modelo de streaming DirectShow
Ao escrever um filtro de fonte, é importante entender que uma fonte de push não é a mesma coisa que uma fonte ativa. Uma fonte ao vivo obtém dados de alguma fonte externa, como uma câmera ou um fluxo de rede. Geralmente, uma fonte ativa não pode controlar a taxa de entrada de dados. Se os filtros a jusante não consumirem os dados com rapidez suficiente, a fonte terá de eliminar 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 a jusante determinam a velocidade com que processam os dados da fonte, com base no relógio de referência e nos timestamps das amostras. O filtro de origem entrega amostras o mais rápido possível, mas o fluxo de dados real é limitado pelos renderizadores. Os mecanismos para limitar o fluxo de dados são descritos em Data Flow for Filter Developers.
Cada pino de saída no filtro de origem cria um thread chamado thread de streaming . O pino entrega amostras no thread de streaming. Normalmente, toda a decodificação, processamento e renderização acontece nesse thread, embora alguns filtros downstream possam criar threads adicionais para enfileirar suas amostras 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 não houver amostras disponíveis, a etapa 1 bloqueia até que uma amostra esteja disponível. O passo 4 também pode bloquear; por exemplo, ele pode bloquear enquanto o gráfico está pausado.
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 gráfico de filtro tenha um relógio de referência, a taxa é determinada pelos tempos de apresentação nas amostras. Se não houver um relógio de referência, o renderizador consome amostras o mais rápido possível.
Usando CSource e CSourceStream
As classes base DirectShow incluem duas classes que suportam fontes push: CSource e CSourceStream.
- CSource é a classe base para o filtro e implementa a interfaceIBaseFilter.
- CSourceStream é a classe base para os pinos de saída e implementa o 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 adicionam-se automaticamente ao filtro quando são criados.
Formatos de saída
O pino de saída lida com 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 pino deve propor pelo menos um tipo de mídia, porque o filtro a jusante pode não propor nenhum tipo. Na maioria dos casos, o filtro a jusante será um descodificador ou um renderizador, dependendo se o filtro de origem fornece dados comprimidos ou não comprimidos. 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 que é necessária 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. Sobrescrever este método é opcional, dependendo de como se implementa GetMediaType. |
O GetMediaType método está sobrecarregado:
- GetMediaType (1) usa um único parâmetro, um ponteiro para um objeto CMediaType.
- GetMediaType (2) recebe uma variável de índice e um ponteiro para um objeto CMediaType.
Se o pino de saída do filtro de origem suportar 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 suportar 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 verificar o tipo de mídia em relação à sua lista de formatos.
Para formatos de vídeo não comprimidos, lembre-se de que o filtro a jusante pode propor formatos com vários valores de espaçamento. O seu filtro deve aceitar qualquer valor de passo válido. Para obter mais informações, consulte BITMAPINFOHEADER.
Você também deve sobrescrever o método virtual puro CBaseOutputPin::DecideBufferSize. Use esse método para definir o tamanho dos buffers de amostra.
Transmissão
A classe CSourceStream cria a thread de streaming para o pino. O procedimento de thread é implementado no método CSourceStream::DoBufferProcessingLoop. Esse método chama o método puro virtual CSourceStream::FillBuffer, que a classe derivada deve sobrepor. Este método é onde o pino preenche o buffer com dados. Por exemplo, se o filtro fornecer vídeo não compactado, é 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 pausa ou para. 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 manipulação especial. Caso contrário, as implementações padrão simplesmente retornam S_OK.
Procura
Se você tiver um filtro de origem com um pino de saída, poderá usar a classeCSourceSeeking como ponto de partida para implementar a busca. Herde sua classe de pinos de CSourceStream e CSourceSeeking .
Observação
CSourceSeeking não é recomendado para um filtro com mais de um pino de saída. O problema principal é que apenas um pino deve responder a pedidos de busca. Normalmente, isso requer comunicação entre os pinos e o filtro.
A classe CSourceSeeking gere a taxa de reprodução, o tempo de início, o tempo de paragem e a duração. Sua classe derivada deve definir o tempo de parada inicial e a duração. Sempre que um desses valores muda, 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 pinos derivada substitui estes métodos para realizar as seguintes tarefas:
- Chame IPin::BeginFlush no pino a jusante. Isso faz com que os filtros a jusante libertem as amostras que mantêm retidas e rejeitem novas amostras.
- Chame CSourceStream::Stop para parar a tarefa de transmissão. O filtro de origem suspende a produção de novos dados.
- Chame IPin::EndFlush no pino de saída. Isso sinaliza os filtros a jusante para aceitar novos dados.
- Ligue IPin::NewSegment com os novos horários de início e término e respetiva velocidade.
- 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 suportar a procura, a posição do fluxo agora é independente do tempo de apresentação. Após uma busca, os carimbos de data/hora são zerados. A fórmula geral para carimbos de data/hora é:
- Tempo de início da amostra = tempo decorrido / taxa de reprodução
- Hora de término da amostra = Hora de início da amostra + (tempo por quadro / taxa de reprodução)
onde tempo decorrido é o tempo decorrido desde que o filtro começou a ser executado ou desde o último comando de pesquisa.
Formatos de tempo para procurar
Por padrão, os comandos de busca estão em unidades de 100 nanossegundos. Seu filtro de origem pode suportar formatos de tempo adicionais, como a busca por número de quadro. Cada formato de hora é identificado por um GUID; veja GUIDs de Formato de Hora.
Para oferecer suporte a formatos de tempo adicionais, você deve implementar os seguintes métodos no pino 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étodosIMediaSeekingserão interpretados em termos do novo formato de hora. Por exemplo, se o formato de tempo for quadros, o método IMediaSeeking::GetDuration deverá retornar a duração em quadros.
Na prática, poucos filtros DirectShow suportam formatos de tempo adicionais e, como resultado, poucos aplicativos DirectShow fazem uso desse recurso.