Escrevendo uma fonte de mídia personalizada

Este tópico descreve como implementar uma fonte de mídia personalizada no Microsoft Media Foundation. Ele contém as seções a seguir:

Criando o Descritor de Apresentação

O método IMFMediaSource::CreatePresentationDescriptor retorna uma cópia do descritor de apresentação da origem. Para criar o descritor de apresentação, você deve saber o número de fluxos no conteúdo de origem e os possíveis formatos de cada fluxo. Para cada fluxo, crie um descritor de fluxo da seguinte maneira:

  1. Crie uma matriz de tipos de mídia. Cada tipo de mídia na matriz representa um formato possível para o fluxo. Para obter mais informações sobre como criar tipos de mídia, consulte Tipos de mídia.
  2. Chame MFCreateStreamDescriptor para criar o descritor de fluxo. Passe a matriz de tipos de mídia. A função retorna um ponteiro IMFStreamDescriptor .
  3. Chame IMFStreamDescriptor::GetMediaTypeHandler para obter o manipulador de tipo de mídia do descritor de fluxo.
  4. Chame IMFMediaTypeHandler::SetCurrentMediaType para definir o formato de fluxo padrão. Use um dos tipos de mídia que você criou na etapa 1. Em geral, você deve usar o formato com a mais alta qualidade.
  5. Opcionalmente, defina atributos no descritor de fluxo. Para obter uma lista de atributos que se aplicam a descritores de fluxo, consulte Atributos do Descritor de Fluxo.

Agora, crie o descritor de apresentação:

  1. Chame MFCreatePresentationDescriptor e passe a matriz de descritores de fluxo. A função retorna um ponteiro IMFPresentationDescriptor .
  2. Escolha a seleção de fluxo padrão chamando IMFPresentationDescriptor::SelectStream para selecionar um ou mais fluxos. Pelo menos um fluxo deve ser selecionado na configuração padrão.
  3. Opcionalmente, defina atributos no descritor de apresentação. Para obter uma lista de atributos que se aplicam a descritores de fluxo, consulte Atributos de descritor de apresentação.

Você deve criar o descritor de apresentação uma vez, na inicialização ou depois que a origem tiver analisado o suficiente dos dados de origem para determinar o conteúdo. O método CreatePresentationDescriptor deve retornar uma cópia do descritor de apresentação. Para criar a cópia, chame IMFPresentationDescriptor::Clone. Retornar uma cópia impede que o cliente modifique o estado do descritor de apresentação original, como os atributos ou a seleção de fluxo. No entanto, lembre-se de que Clone cria uma cópia superficial, para que o cliente possa potencialmente modificar os descritores de fluxo subjacentes.

Iniciando a fonte de mídia

O método IMFMediaSource::Start inicia a fonte de mídia ou busca uma nova posição. Uma chamada para Iniciar causa uma busca quando o estado anterior foi pausado ou em execução e uma nova hora de início é especificada. Caso contrário, o método Start causará um início. Quando a operação Iniciar for concluída, envie os eventos a seguir.

  1. Envie um evento MENewStream para cada novo fluxo, ou seja, cada fluxo que foi desmarcado anteriormente e agora está selecionado. Os dados do evento são um ponteiro para o fluxo.
  2. Envie um evento MEUpdatedStream para cada fluxo que foi selecionado anteriormente e ainda está selecionado. Os dados do evento são um ponteiro para o fluxo. (Não envie um evento para fluxos desmarcados.)
  3. Se a origem estiver procurando, envie um evento MESourceSeeked . Caso contrário, envie um evento MESourceStarted . Os dados do evento são a hora de início especificada no método Start . Para o evento MESourceStarted, se a hora de início for VT_EMPTY, defina o atributo MF_EVENT_SOURCE_ACTUAL_START no evento. O valor do atributo é a hora de início real.
  4. Para cada fluxo, se a origem estiver buscando, envie um evento MEStreamSeeked . Caso contrário, envie um evento MEStreamStarted . Os dados do evento são a hora de início. (A fonte de mídia pode enfileirar um evento no fluxo chamando o método IMFMediaEventGenerator::QueueEvent do fluxo.)

Quando um fluxo for desmarcado, desligue o fluxo. O fluxo não deve enfileirar mais eventos nesse ponto.

O formato de tempo para o método Start é dado no parâmetro pguidTimeFormat . O formato de hora padrão, indicado por GUID_NULL, é unidades de 100 nanossegundos. Uma fonte de mídia deve dar suporte a esse formato de hora.

Procurando

Ao procurar, a posição inicial solicitada pode não cair em um limite de exemplo exato. Além disso, para conteúdo compactado, a posição inicial pode ficar entre quadros-chave. Um fluxo deve fornecer amostras do ponto mais antigo necessário para produzir uma amostra descompactada na posição inicial solicitada. Para vídeo, isso significa começar do quadro-chave anterior. O pipeline é responsável por remover os quadros extras do decodificador, de modo que a reprodução comece no momento solicitado.

A hora inicial fornecida nos eventos de origem (MESourceStarted, MESourceSeeked, MEStreamStarted e MEStreamSeeked) é a hora inicial solicitada (o valor fornecido no método Start ), independentemente da posição inicial real.

Por exemplo, suponha que os primeiros quadros de um fluxo de vídeo tenham as seguintes características:

Amostra 1 2 3 4
Hora 33 msec 66 msec 100 msec 133 msec
Quadro-chave? Sim Não Não Sim

 

Se o método Start for chamado com um valor de 100 milissegundos, a origem precisará gerar o vídeo a partir do quadro 1, o primeiro quadro-chave antes desse momento. O evento inicial ainda indicará 100 milissegundos nos dados do evento.

Pausando a fonte de mídia

O método IMFMediaSource::P ause pausa a fonte de mídia.

Enquanto a origem está pausada, um fluxo pode criar novos exemplos e armazená-los em uma fila, mas o fluxo não fornece os exemplos. Aqui estão algumas exceções a esta regra:

  • As fontes dinâmicas devem remover dados durante a pausa.
  • Se a origem obtiver dados de uma rede, ela poderá pausar o servidor.

Se o cliente chamar IMFMediaStream::RequestSample enquanto a origem estiver em pausa, a solicitação também será enfileirada até que a origem seja iniciada novamente. As solicitações não devem ser descartadas.

A pausa é permitida somente do estado iniciado. Caso contrário, Pause deverá retornar MF_E_INVALID_STATE_TRANSITION.

Gerando dados de origem

O Media Foundation usa um modelo de pull, o que significa que os fluxos geram e fornecem amostras em resposta a solicitações do pipeline. Um fluxo pode fornecer exemplos quando a origem da mídia está em execução e o fluxo é selecionado. Um fluxo fornece dados somente quando o cliente solicita um novo exemplo.

Solicitações de Amostra

O cliente solicita um novo exemplo chamando IMFMediaStream::RequestSample. Esta é a sequência de operações:

  1. O cliente chama IMFMediaStream::RequestSample. O argumento é um ponteiro para um objeto de token opcional que o cliente usa para acompanhar a solicitação. O cliente implementa o token. Os tokens devem expor a interface IUnknown . O cliente também pode passar um ponteiro NULL em vez de um token.

  2. Se o cliente forneceu um token, o fluxo de mídia chamará AddRef no token e colocará o token em uma fila inicial e inicial. O método retorna e as etapas restantes ocorrem de forma assíncrona.

  3. Quando mais dados estão disponíveis, o fluxo de mídia cria um novo exemplo. (Esta etapa é descrita com mais detalhes na próxima seção.)

  4. O fluxo de mídia extrai o primeiro token da fila.

  5. Se o token não for NULL, o fluxo de mídia definirá o atributo MFSampleExtension_Token no exemplo de mídia. O valor do atributo é um ponteiro para o token.

  6. O fluxo de mídia envia um evento MEMediaSample . Os dados do evento são um ponteiro para a interface IMFSample da amostra .

  7. Se o cliente forneceu um token, o fluxo de mídia chamará Release no objeto de token.

Se o fluxo de mídia não puder atender à solicitação RequestSample do cliente, ele extrairá o token da fila e chamará Release no token, mas não enviará um evento MEMediaSample .

O cliente pode usar o token para acompanhar o status da solicitação. Quando o cliente recebe o evento MEMediaSample , ele pode obter o token do exemplo e combiná-lo com a solicitação original. O cliente também pode usar o token para detectar se a fonte de mídia retirou a solicitação. Se a contagem de referência do token cair para zero e o fluxo de mídia não enviar um evento MEMediaSample, isso significa que a solicitação foi descartada.

As etapas listadas aqui pressupõem que o método RequestSample seja implementado como uma operação assíncrona. Se o método for síncrono, você não precisará colocar o token de solicitação em uma fila. No entanto, se a geração de dados levar algum tempo significativo, uma abordagem assíncrona será recomendada, por exemplo, se a fonte ler dados de um fluxo de bytes.

O fluxo é responsável por armazenar em buffer todos os dados acumulados entre chamadas para RequestSample.

Quando o fluxo de mídia atinge o final do fluxo, ele envia um evento MEEndOfStream após o último exemplo. Depois que cada fluxo for encerrado, a fonte de mídia enviará um evento MEEndOfPresentation . Depois que um fluxo de mídia envia o evento MEEndOfStream, o método RequestSample retorna MF_E_END_OF_STREAM até que a origem seja reiniciada.

Alocando exemplos

Quando o fluxo está pronto para preencher uma solicitação de exemplo pendente, ele cria um novo exemplo e adiciona um ou mais buffers de mídia ao exemplo. Para obter mais informações sobre como criar buffers de mídia, consulte Buffers de mídia.

O fluxo deve definir o carimbo de data/hora e a duração, se conhecido. O carimbo de data/hora é relativo à origem. Na maioria dos casos, o início do conteúdo corresponde a um carimbo de data/hora zero. Por exemplo, se a origem ler de um arquivo de mídia, o início do arquivo terá um carimbo de data/hora zero.

O carimbo de data/hora no exemplo não é necessariamente igual ao tempo de apresentação. A Sessão de Mídia é convertida do tempo de origem para a hora da apresentação. Para dados compactados, o fluxo deve gerar dados a partir do quadro de chave mais próximo antes da hora de início. Isso permite que o decodificador entregue o quadro que aparece na hora de início solicitada. (Caso contrário, o decodificador precisará aguardar até o quadro-chave a seguir.)

Se a taxa de reprodução for mais rápida ou mais lenta que 1,0, o pipeline ajustará a taxa do relógio de apresentação. A origem não ajusta os carimbos de data/hora em exemplos.

A origem pode definir informações adicionais sobre o exemplo definindo atributos. Para obter uma lista de atributos de exemplo, consulte Atributos de exemplo.

Lacunas no Fluxo

Se um fluxo contiver uma lacuna de comprimento significativo, é recomendável que o fluxo envie um evento MEStreamTick . Esse evento notifica o cliente de que uma amostra está ausente. Os dados do evento são o carimbo de data/hora da amostra ausente, em unidades de 100 nanossegundos (VT_I8). Esse evento pode salvar componentes downstream de esperar por exemplos que não chegarão. O fluxo pode enviar quantos eventos MEStreamTick forem necessários para abranger a lacuna no fluxo.

Desligando a fonte de mídia

Quando o cliente terminar de usar a fonte de mídia, ele chamará IMFMediaSource::Shutdown. Dentro desse método, a fonte de mídia deve interromper qualquer contagem de referência circular. Normalmente, haverá referências circulares entre a fonte de mídia e os fluxos de mídia.

Se você estiver usando a fila de eventos para implementar IMFMediaEventGenerator, chame IMFMediaEventQueue::Shutdown na fila de eventos. Esse método desliga a fila de eventos e sinaliza qualquer chamador que esteja aguardando um evento no momento.

Após o desligamento, todos os métodos na fonte retornam MF_E_SHUTDOWN, com exceção dos métodos IUnknown .

Fontes Dinâmicas

A partir do Windows 7, o Media Foundation dá suporte automaticamente a dispositivos de captura de áudio e vídeo. Para vídeo, o dispositivo deve fornecer um minidriver de streaming de kernel (KS) na categoria de captura de vídeo. O Media Foundation usa o caminho PnP para enumerar o dispositivo. Para áudio, o Media Foundation usa a API do Dispositivo Multimídia do Windows (MMDevice) para enumerar dispositivos de ponto de extremidade de áudio. Se o dispositivo atender a esses critérios, não será necessário implementar uma fonte de mídia personalizada.

No entanto, talvez você queira implementar uma fonte de mídia personalizada para algum outro tipo de dispositivo ou outra fonte de dados dinâmica. Há apenas algumas diferenças entre uma fonte ao vivo e outras fontes de mídia:

  • No método IMFMediaSource::GetCharacteristics , retorne o sinalizador MFMEDIASOURCE_IS_LIVE .
  • O primeiro exemplo deve ter um carimbo de data/hora zero.
  • Os eventos e os estados de streaming são tratados da mesma forma que as fontes de mídia, com exceção do estado em pausa.
  • Enquanto estiver em pausa, não enfileirar amostras. Remova todos os dados gerados durante a pausa.
  • Normalmente, as fontes dinâmicas não dão suporte à busca, ao jogo reverso ou ao controle de taxa.

Fontes de mídia

Tutorial: Escrever uma fonte de mídia personalizada