Exemplos e alocadores
[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.]
Quando um pino fornece dados de mídia para outro pino, ele não passa um ponteiro direto para o buffer de memória. Em vez disso, ele fornece um ponteiro para um objeto COM que gerencia a memória. Esse objeto, chamado de exemplo de mídia, expõe a interface IMediaSample . O pino de recebimento acessa o buffer de memória chamando métodos IMediaSample , como IMediaSample::GetPointer, IMediaSample::GetSize e IMediaSample::GetActualDataLength.
Os exemplos sempre viajam downstream, do pino de saída ao pino de entrada. No modelo de push, o pino de saída fornece um exemplo chamando IMemInputPin::Receive no pino de entrada. O pin de entrada processará os dados de forma síncrona (ou seja, completamente dentro do método Receive ) ou os processará de forma assíncrona em um thread de trabalho. O pin de entrada tem permissão para bloquear dentro do método Receive , se precisar aguardar recursos.
Outro objeto COM, chamado de alocador, é responsável por criar e gerenciar exemplos de mídia. Os alocadores expõem a interface IMemAllocator . Sempre que um filtro precisa de um exemplo de mídia com um buffer vazio, ele chama o método IMemAllocator::GetBuffer , que retorna um ponteiro para o exemplo. Cada conexão de pino compartilha um alocador. Quando dois pinos se conectam, eles decidem qual filtro fornecerá o alocador. Os pinos também definem propriedades no alocador, como o número de buffers e o tamanho de cada buffer. (Para obter detalhes, consulte How Filters Connect and Negotiating Allocators.)
A ilustração a seguir mostra as relações entre o alocador, os exemplos de mídia e o filtro.
Contagens de referência de exemplo de mídia
Um alocador cria um pool finito de amostras. A qualquer momento, alguns exemplos podem estar em uso, enquanto outros estão disponíveis para chamadas GetBuffer . O alocador usa a contagem de referência para acompanhar os exemplos. O método GetBuffer retorna um exemplo com uma contagem de referência de 1. Se a contagem de referência for para zero, o exemplo voltará para o pool do alocador, no qual ele pode ser usado na próxima chamada getBuffer . Desde que a contagem de referência permaneça acima de zero, o exemplo não estará disponível para GetBuffer. Se cada amostra pertencente ao alocador estiver em uso, o método GetBuffer será bloqueado até que uma amostra fique disponível.
Por exemplo, suponha que um pino de entrada receba uma amostra. Se ele processar o exemplo de forma síncrona, dentro do método Receive , ele não incrementará a contagem de referência. Depois que Receive retorna, o pino de saída libera o exemplo, a contagem de referência vai para zero e o exemplo retorna ao pool do alocador. Por outro lado, se o pino de entrada processar o exemplo em um thread de trabalho, ele incrementará a contagem de referência antes de sair do método Receive . A contagem de referência agora é 2. Quando o pino de saída libera o exemplo, a contagem vai para 1; o exemplo ainda não retorna ao pool. Depois que o thread de trabalho for concluído com o exemplo, ele chamará Release para liberar o exemplo. Agora, o exemplo retorna ao pool.
Quando um pino recebe um exemplo, ele pode copiar os dados para outro exemplo ou pode modificar o exemplo original e entregar esse para o próximo filtro. Potencialmente, um exemplo pode percorrer todo o comprimento do grafo, cada filtro chamando AddRef e Release por sua vez. Portanto, o pino de saída nunca deve reutilize um exemplo depois de chamar Receive, pois um filtro downstream pode estar usando o exemplo. O pino de saída sempre deve chamar GetBuffer para obter um novo exemplo.
Esse mecanismo reduz a quantidade de alocação de memória, pois os filtros reutilizam os mesmos buffers. Ele também impede que os filtros gravem acidentalmente dados que não foram processados, pois o alocador mantém uma lista de amostras disponíveis.
Um filtro pode usar alocadores separados para entrada e saída. Ele poderá fazer isso se expandir os dados de entrada (por exemplo, descompactando-os). Se a saída não for maior que a entrada, um filtro poderá processar os dados em vigor, sem copiá-los para um novo exemplo. Nesse caso, duas ou mais conexões de pino podem compartilhar um alocador.
Confirmar e descompactar alocadores
Quando um filtro cria um alocador pela primeira vez, o alocador não reserva nenhum buffer de memória. Neste ponto, todas as chamadas para o método GetBuffer falharão. Quando o streaming é iniciado, o pino de saída chama IMemAllocator::Commit, que confirma o alocador, fazendo com que ele aloque memória. Os pinos agora podem chamar GetBuffer.
Quando o streaming é interrompido, o pin chama IMemAllocator::D ecommit, que desabilita o alocador. Todas as chamadas subsequentes para GetBuffer falham até que o alocador seja confirmado novamente. Além disso, se alguma chamada para GetBuffer estiver bloqueada no momento aguardando um exemplo, ela retornará imediatamente um código de falha. O método Decommit pode ou não liberar a memória, dependendo da implementação. Por exemplo, a classe CMemAllocator aguarda até que seu método destruidor libere memória.
Tópicos relacionados