Partilhar via


Escrevendo uma fonte de mídia personalizada

Este tópico descreve como implementar uma fonte de mídia personalizada no Microsoft Media Foundation. Contém as seguintes secções:

Criando o descritor de apresentação

O método IMFMediaSource::CreatePresentationDescriptor retorna uma cópia do descritor de apresentação da fonte. Para criar o descritor de apresentação, você deve saber o número de fluxos no conteúdo de origem e os formatos possíveis 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 na 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. Geralmente, você deve usar o formato com a mais alta qualidade.
  5. Opcionalmente, defina atributos no descritor de fluxo de dados. 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 na matriz de descritores de fluxo. A função devolve 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 da apresentação. Para obter uma lista de atributos que se aplicam a descritores de fluxo, consulte Atributos do descritor de apresentação.

Você deve criar o descritor de apresentação uma vez, na inicialização ou depois que a fonte tiver analisado o suficiente dos dados de origem para determinar o conteúdo. O métodoCreatePresentationDescriptor deve retornar uma cópia do descritor de apresentação. Para criar a cópia, chame IMFPresentationDescriptor::Clone. O retorno de 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, esteja ciente 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 procura uma nova posição. Uma chamada para Iniciar provoca uma operação de procura quando o estado anterior era 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 seguintes eventos.

  1. Envie um evento MENewStream para cada novo fluxo — isto é, para cada fluxo que estava 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 fonte estiver a procurar, 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 fonte estiver buscando, envie um evento de MEStreamSeeked. Caso contrário, envie o evento MEStreamStarted. O dado do evento é a hora de início. (A fonte de mídia pode enfileirar um evento no fluxo chamando o método IMFMediaEventGenerator::QueueEvent.)

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

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

Procura

Ao pesquisar, a posição inicial solicitada pode não estar precisamente num limite de amostra. Além disso, para conteúdo compactado, a posição inicial pode ficar entre quadros-chave. Um fluxo deve fornecer amostras a partir do ponto mais cedo necessário para produzir uma amostra descomprimida na posição inicial solicitada. Para vídeo, isso significa começar a partir do fotograma-chave anterior. O pipeline é responsável por descartar os quadros extras do decodificador, para que a reprodução comece no momento solicitado.

A hora de início indicada nos eventos de origem (MESourceStarted, MESourceSeeked, MEStreamStartede MEStreamSeeked) é a hora de início solicitada (o valor dado 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 mseg 66 mseg 100 mseg 133 mseg
Quadro-chave? Sim Não Não Sim

 

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

Pausando a fonte de mídia

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

Enquanto a origem está pausada, um fluxo pode criar novas amostras e armazená-las em uma fila, mas o fluxo não entrega as amostras. Aqui estão algumas exceções a esta regra:

  • As fontes em tempo real devem descartar os dados quando estiverem em pausa.
  • Se a fonte obtiver dados de uma rede, poderá pausar o servidor.

Se o cliente chamar IMFMediaStream::RequestSample enquanto a fonte estiver pausada, a solicitação também será enfileirada até que a fonte seja iniciada novamente. Os pedidos não devem ser descartados.

A pausa só é permitida a partir do estado inicial. Caso contrário, Pause deve retornar MF_E_INVALID_STATE_TRANSITION.

Geração de dados de origem

O Media Foundation usa um modelo pull, o que significa que os fluxos geram e entregam amostras em resposta a solicitações do pipeline. Um stream pode fornecer amostras quando a fonte de mídia está ativa e o stream é selecionado. Um fluxo fornece dados somente quando o cliente solicita uma nova amostra.

Solicitações de amostra

O cliente solicita um novo exemplo chamando IMFMediaStream::RequestSample. Aqui está 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 o IUnknown interface. O cliente também pode passar um ponteiro NULL em vez de um token.

  2. Se o cliente fornecer um token, o fluxo de mídia chamará AddRef no token e colocará o token em uma fila de primeiro a entrar, primeiro a sair. 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 na amostra 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 deIMFSampleda 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 de RequestSample do cliente, ele retira o token da fila e chama Release para o token, mas não envia um evento MEMediaSample.

O cliente pode usar o token para acompanhar o status da solicitação. Quando o cliente recebe o evento MEMediaSample, pode obter o token do sample e compará-lo com o pedido original. O cliente também pode usar o token para detetar se a fonte de mídia descartou 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 método RequestSample é implementado como uma operação assíncrona. Se o método for síncrono, não será necessário colocar o token de solicitação em uma fila. No entanto, se a geração de dados levar uma quantidade significativa de tempo, uma abordagem assíncrona é recomendada — por exemplo, se a fonte ler dados de um fluxo de bytes.

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

Quando o fluxo de mídia atinge o fim do fluxo, ele envia um evento MEEndOfStream após a última amostra. Após o fim de cada fluxo, a fonte de mídia envia o evento MEEndOfPresentation. Após um fluxo de mídia enviar o evento MEEndOfStream, o métodoRequestSample retorna MF_E_END_OF_STREAM até que a fonte seja reiniciada.

Atribuição de amostras

Quando o fluxo está pronto para preencher uma solicitação de amostra 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 Media Buffers.

O fluxo deve definir o carimbo de data/hora e a duração, se conhecida. A marca temporal é relativa à fonte. Na maioria das vezes, o início do conteúdo corresponde a uma marca temporal de zero. Por exemplo, se a fonte ler de um ficheiro de mídia, o início do ficheiro terá uma marca temporal de zero.

O carimbo de data/hora na amostra não é necessariamente igual ao tempo de apresentação. A Sessão de Mídia traduz desde o horário de origem até o tempo de apresentação. Para dados compactados, o fluxo deve gerar dados a partir do quadro-chave mais próximo antes do momento de início. Isso permite que o decodificador entregue o quadro que aparece na hora de início solicitada. (Caso contrário, o descodificador teria de esperar até ao seguinte quadro-chave.)

Se a velocidade de reprodução for mais rápida ou mais lenta do que 1,0, o pipeline ajusta o ritmo do relógio de apresentação. A fonte não ajusta os carimbos de data/hora nas amostras.

A fonte 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 um intervalo significativo, é recomendável que o fluxo envie um evento MEStreamTick. Este evento notifica o cliente de que uma amostra está em falta. Os dados do evento são o carimbo temporal da amostra em falta, em unidades de 100 nanossegundos (VT_I8). Esse evento pode evitar que os componentes a jusante esperem por amostras que não chegarão. O fluxo pode enviar quantos eventos MEStreamTick forem necessários para cobrir a lacuna no fluxo.

Desligando a fonte de mídia

Quando o cliente termina de usar a fonte de mídia, ele chama IMFMediaSource::Shutdown. Dentro deste método, a fonte de mídia deve interromper contagens de referências circulares. 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 ao vivo

A partir do Windows 7, o Media Foundation suporta automaticamente dispositivos de captura de áudio e vídeo. Para vídeo, o dispositivo deve fornecer um minidriver de streaming do kernel (KS) na categoria de captura de vídeo. Media Foundation usa o caminho PnP para enumerar o dispositivo. Para áudio, o Media Foundation utiliza a API Windows Multimedia Device (MMDevice) para enumerar dispositivos de extremidade de áudio. Se o dispositivo atender a esses critérios, não há necessidade de 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 ativa. Existem apenas algumas diferenças entre uma fonte ao vivo e outras fontes de mídia:

  • No método IMFMediaSource::GetCharacteristics, retorne o flag MFMEDIASOURCE_IS_LIVE.
  • A primeira amostra deve ter um carimbo de data e hora igual a zero.
  • Eventos e estados de streaming são tratados da mesma forma que fontes de mídia, com exceção do estado pausado.
  • Enquanto estiver pausado, não enfileire amostras. Solte todos os dados gerados durante a pausa.
  • As fontes ao vivo normalmente não suportam busca, reprodução reversa ou controlo de velocidade.

Fontes de mídia

Tutorial: Escrevendo uma fonte de mídia personalizada