Processar metadados cronometrados em streams da DAI linear

O SDK de inserção de anúncios dinâmicos (DAI) do Interactive Media Ads (IMA) usa informações de metadados incorporadas nos segmentos de mídia do fluxo (metadados na banda) ou no arquivo de manifesto de streaming (metadados no manifesto) para rastrear as posições dos espectadores e os eventos de anúncios do lado do cliente. Os metadados são enviados em formatos diferentes, dependendo do tipo de transmissão que está sendo reproduzida.

O player de vídeo recebe metadados cronometrados em lotes. Dependendo do player, os metadados podem ser exibidos no horário programado ou em lotes. Cada string de metadados tem um carimbo de data/hora de apresentação (PTS, na sigla em inglês) associado para quando ela precisa ser acionada.

Seu app é responsável por capturar metadados e encaminhar para o SDK do DAI do IMA. O SDK oferece os seguintes métodos para transmitir essas informações:

onTimedMetadata

Esse método encaminha strings de metadados que estão prontas para processamento ao SDK. Ele usa um único argumento:

  • metadata: um objeto que contém uma chave de TXXX com um valor de string associado com o prefixo google_.
processMetadata

Esse método programa strings de metadados para serem processadas pelo SDK após o PTS especificado. Ele usa os seguintes argumentos:

  • type: uma string que contém o tipo de evento que está sendo processado. Os valores aceites são ID3 para HLS ou urn:google:dai:2018 para DASH.
  • data: um valor de string com prefixo google_ ou uma matriz de bytes que segue este formato ID3:u\0004u\000u\000u\0000TXXXu\0004u\000u\000u\0000google_xxxxxxxx.
  • timestamp: o carimbo de data/hora em segundos em que os dados precisam ser processados.

Cada tipo de transmissão compatível com o SDK do IMA DAI usa uma forma exclusiva de metadados temporizados, conforme descrito nas seções a seguir.

Transmissões HLS MPEG2TS

Os fluxos lineares de DAI HLS que usam os segmentos MPEG2TS transmitem metadados cronometrados para o player de vídeo por meio de tags ID3 na banda. Essas tags ID3 são incorporadas aos segmentos MPEG2TS e recebem o nome de campo TXXX (para conteúdo de texto personalizado definido pelo usuário).

Reprodução no Safari

O Safari processa tags ID3 automaticamente, como uma faixa oculta, para que os eventos de mudança de sinalização sejam acionados no momento certo para processar cada parte dos metadados. É possível transmitir todos os metadados para o SDK do IMA DAI, independentemente do conteúdo ou do tipo. Os metadados irrelevantes são filtrados automaticamente.

Veja um exemplo:

videoElement.textTracks.addEventListener('addtrack', (e) => {
  const track = e.track;
  if (track.kind === 'metadata') {
    track.mode = 'hidden';
    track.addEventListener('cuechange', () => {
      for (const cue of track.activeCues) {
        const metadata = {};
        metadata[cue.value.key] = cue.value.data;
        streamManager.onTimedMetadata(metadata);
      }
    });
  }
});
...

HLS.js

O HLS.js fornece tags ID3 em lotes pelo evento FRAG_PARSING_METADATA, como uma matriz de amostras. O HLS.js não traduz os dados ID3 de matrizes de bytes para strings e não compensa eventos para o PTS correspondente. Não é necessário decodificar os dados de amostra de matriz de bytes para string ou filtrar tags ID3 irrelevantes, já que o SDK de DAI do IMA executa essa decodificação e filtragem automaticamente.

Veja um exemplo:

hls.on(Hls.Events.FRAG_PARSING_METADATA, (e, data) => {
  if (streamManager && data) {
    data.samples.forEach((sample) => {
      streamManager.processMetadata('ID3', sample.data, sample.pts);
    });
  }
});
...

Streams CMAF HLS

Os fluxos lineares de HLS do DAI que usam o Common Media Application Framework (CMAF) transmitem metadados temporizados por caixas eMSGv1 na banda, seguindo o padrão ID3 pelo CMAF. Essas caixas de eMSG são incorporadas no início de cada segmento de mídia, com cada eMSG ID3 contendo um PTS relativo à última descontinuidade no stream.

Desde a versão 1.2.0 do HLS.js, os dois players sugeridos transmitem o ID3 pelo CMAF para o usuário como se fossem tags ID3. Por esse motivo, os exemplos a seguir são iguais aos dos fluxos MPEG2TS do HLS. No entanto, isso pode não ser o caso de todos os players. Portanto, a implementação do suporte a fluxos CMAF do HLS pode exigir um código exclusivo para analisar o ID3 por meio de eMSG.

Reprodução no Safari

O Safari trata os metadados ID3 por meio de eMSG como eventos pseudo ID3, fornecendo-os em lotes, automaticamente, como uma faixa oculta, de modo que os eventos cuechange sejam acionados no momento correto para processar cada parte dos metadados. É permitido transmitir todos os metadados para o SDK do DAI do IMA, seja relevante para o tempo ou não. Todos os metadados não relacionados à DAI são filtrados automaticamente.

Veja um exemplo:

videoElement.textTracks.addEventListener('addtrack', (e) => {
  const track = e.track;
  if (track.kind === 'metadata') {
    track.mode = 'hidden';
    track.addEventListener('cuechange', () => {
      for (const cue of track.activeCues) {
        const metadata = {};
        metadata[cue.value.key] = cue.value.data;
        streamManager.onTimedMetadata(metadata);
      }
    });
  }
});
...

HLS.js

A partir da versão 1.2.0, o HLS.js trata o ID3 por meio de metadados eMSG como eventos pseudo ID3, fornecendo-os em lotes pelo evento FRAG_PARSING_METADATA como uma matriz de amostras. O HLS.js não traduz os dados ID3 de matrizes de bytes para strings e não compensa os eventos no PTS correspondente. Não é necessário decodificar os dados de amostra de matriz de bytes para string, já que o SDK de DAI do IMA realiza essa decodificação automaticamente.

Veja um exemplo:

hls.on(Hls.Events.FRAG_PARSING_METADATA, (e, data) => {
  if (streamManager && data) {
    data.samples.forEach((sample) => {
      streamManager.processMetadata('ID3', sample.data, sample.pts);
    });
  }
});
...

Transmissões DASH

As transmissões lineares de DAI DASH transmitem metadados como eventos de manifesto em um fluxo de eventos com o valor personalizado schemeIdUri urn:google:dai:2018. Cada evento nesses fluxos contém um payload de texto e o PTS.

DASH.js

O Dash.js fornece manipuladores de eventos personalizados com o nome do valor schemeIdUri de cada stream de eventos. Esses manipuladores personalizados são acionados em lotes, deixando a cargo do usuário o processamento do valor do PTS para programar corretamente o evento. O SDK do IMA DAI pode processar isso para você, com o método streamManager, processMetadata().

Veja um exemplo:

const dash = dashjs.MediaPlayer().create();
dash.on('urn:google:dai:2018', (payload) => {
  const mediaId = payload.event.messageData;
  const pts = payload.event.calculatedPresentationTime;
  streamManager.processMetadata('urn:google:dai:2018', mediaId, pts);
});
...

Shaka Player

O Shaka Player mostra eventos como parte do evento timelineregionenter. Devido a uma incompatibilidade de formatação com o Shaka Player, o valor dos metadados precisa ser recuperado em formato bruto, pela propriedade de detalhes eventNode.attributes['messageData'].

Veja um exemplo:

player.addEventListener('timelineregionenter', function(event) {
  const detail = event.detail;
  if ( detail.eventNode.attributes &&
       detail.eventNode.attributes['messageData']) {
    const mediaId = detail.eventNode.attributes['messageData'];
    const pts = detail.startTime;
    streamManager.processMetadata("urn:google:dai:2018", mediaId, pts);
  }
});
...

Veiculação de pods

Para veiculação de pods, há diferentes configurações para transmitir metadados temporizados, dependendo dos seguintes critérios:

  • Tipo de transmissão ao vivo ou VOD
  • Formato de transmissão HLS ou DASH
  • O tipo de player usado
  • O tipo de back-end da DAI que está sendo usado

Formato de transmissão HLS (transmissões ao vivo e VOD, player HLS.js)

Se você estiver usando um player HLS.js, detecte o evento FRAG_PARSING_METADATA do HLS.js para receber os metadados do ID3 e transmiti-los ao SDK com StreamManager.processMetadata().

Para reproduzir o vídeo automaticamente depois que tudo estiver carregado e pronto, ouça o evento MANIFEST_PARSED do HLS.js para acionar a reprodução.

function loadStream(streamID) {
  hls.loadSource(url);
  hls.attachMedia(videoElement);
  
  // Timed metadata is passed HLS stream events to the streamManager.
  hls.on(Hls.Events.FRAG_PARSING_METADATA, parseID3Events);
  hls.on(Hls.Events.MANIFEST_PARSED, startPlayback);
}

function parseID3Events(event, data) {
  if (streamManager && data) {
    // For each ID3 tag in the metadata, pass in the type - ID3, the
    // tag data (a byte array), and the presentation timestamp (PTS).
    data.samples.forEach((sample) => {
      streamManager.processMetadata('ID3', sample.data, sample.pts);
    });
  }
}

function startPlayback() {
  console.log('Video Play');
  videoElement.play();
}

DASH.js (formato de transmissões DASH, tipo de transmissão ao vivo e VOD)

Se você estiver usando um player DASH.js, use strings diferentes para detectar metadados ID3 para transmissões ao vivo ou VOD:

  • Transmissões ao vivo: 'https://developer.apple.com/streaming/emsg-id3'
  • Transmissões de VOD: 'urn:google:dai:2018'

Transmita os metadados ID3 para o SDK com StreamManager.processMetadata().

Para mostrar automaticamente os controles de vídeo depois que tudo estiver carregado e pronto, ouça o evento MANIFEST_LOADED do DASH.js.

const googleLiveSchema = 'https://developer.apple.com/streaming/emsg-id3';
const googleVodSchema = 'urn:google:dai:2018';
dashPlayer.on(googleLiveSchema, processMetadata);
dashPlayer.on(googleVodSchema, processMetadata);
dashPlayer.on(dashjs.MediaPlayer.events.MANIFEST_LOADED, loadlistener);

function processMetadata(metadataEvent) {
  const messageData = metadataEvent.event.messageData;
  const timestamp = metadataEvent.event.calculatedPresentationTime;

  // Use StreamManager.processMetadata() if your video player provides raw
  // ID3 tags, as with dash.js.
  streamManager.processMetadata('ID3', messageData, timestamp);
}

function loadlistener() {
  showControls();

  // This listener must be removed, otherwise it triggers as addional
  // manifests are loaded. The manifest is loaded once for the content,
  // but additional manifests are loaded for upcoming ad breaks.
  dashPlayer.off(dashjs.MediaPlayer.events.MANIFEST_LOADED, loadlistener);
}

Shaka Player com transmissões ao vivo (formato de transmissões DASH)

Se você estiver usando o player Shaka para reprodução de transmissão ao vivo, use a string 'emsg' para detectar eventos de metadados. Em seguida, use os dados da mensagem de evento na chamada para StreamManager.onTimedMetadata().

shakaPlayer.addEventListener('emsg', (event) => onEmsgEvent(event));

function onEmsgEvent(metadataEvent) {
  // Use StreamManager.onTimedMetadata() if your video player provides
  // processed metadata, as with Shaka player livestreams.
  streamManager.onTimedMetadata({'TXXX': metadataEvent.detail.messageData});
}

Shaka Player com transmissões VOD (formato de transmissões DASH)

Se você estiver usando o Shaka player para reprodução de fluxo de VOD, use a string 'timelineregionenter' para detectar eventos de metadados. Em seguida, use os dados da mensagem de evento na chamada para StreamManager.processMetadata() com a string 'urn:google:dai:2018'.

shakaPlayer.addEventListener('timelineregionenter', (event) => onTimelineEvent(event));

function onTimelineEvent(metadataEvent) {
  const detail = metadataEvent.detail;
  if ( detail.eventElement.attributes &&
       detail.eventElement.attributes['messageData'] &&
       detail.eventElement.attributes['messageData'].value ) {
        const mediaId = detail.eventElement.attributes['messageData'].value;
        const pts = detail.startTime;
        // Use StreamManager.processMetadata() if your video player provides raw
        // ID3 tags, as with Shaka player VOD streams.
        streamManager.processMetadata('urn:google:dai:2018', mediaId, pts);
       }
}