Cómo controlar metadatos temporizados en transmisiones de DAI lineales

El SDK de inserción de anuncios dinámicos (DAI) de anuncios multimedia interactivos (IMA) se basa en la información de metadatos incorporada en los segmentos multimedia de la transmisión (metadatos en banda) o en el archivo de manifiesto de transmisión (metadatos en el manifiesto) para realizar un seguimiento de las posiciones de los usuarios y de los eventos de anuncios del cliente. Los metadatos se envían en diferentes formatos, según el tipo de transmisión que se reproduce.

El reproductor de video recibe metadatos temporizados en lotes. Según el reproductor, los metadatos pueden aparecer en el momento programado o en lotes. Cada cadena de metadatos tiene asociada una marca de tiempo de presentación (PTS) para determinar cuándo debe activarse.

Su aplicación es responsable de capturar metadatos y reenviarlos al SDK de IMA de DAI. El SDK ofrece los siguientes métodos para pasar esta información:

onTimedMetadata

Este método reenvía al SDK las strings de metadatos que están listas para procesarse. Solo se necesita un argumento:

  • metadata: Es un objeto que contiene una clave de TXXX con un valor de string asociado con el prefijo google_.
processMetadata

Este método programa las cadenas de metadatos para que el SDK las procese después del PTS especificado. Toma los siguientes argumentos:

  • type: Es una cadena que contiene el tipo de evento que se procesa. Los valores aceptados son ID3 para HLS o urn:google:dai:2018 para DASH.
  • data: Es un valor de cadena con el prefijo google_ o un array de bytes que se decodifica en esa cadena.
  • timestamp: Es la marca de tiempo en segundos de la que se deben procesar los datos.

Cada tipo de transmisión compatible con el SDK de IMA de DAI usa una forma única de metadatos temporizados, como se describe en las siguientes secciones.

Transmisiones HLS MPEG2TS

Las transmisiones HLS de DAI lineales que usan los segmentos MPEG2TS pasan metadatos temporizados al reproductor de video mediante etiquetas ID3 en la banda. Estas etiquetas ID3 están incorporadas en los segmentos MPEG2TS y reciben el nombre de campo TXXX (para el contenido de texto personalizado definido por el usuario).

Reproducción en Safari

Safari procesa las etiquetas ID3 automáticamente, como una pista oculta, por lo que los eventos de cambio de inserción se activan en el momento correcto para procesar cada parte de los metadatos. Está bien pasar todos los metadatos al SDK de IMA de DAI, independientemente del contenido o del tipo. Los metadatos irrelevantes se filtran automáticamente.

Por ejemplo:

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

HLS.js proporciona etiquetas ID3 en lotes a través del evento FRAG_PARSING_METADATA, como un arreglo de muestras. HLS.js no traduce los datos del ID3 de arrays de bytes a strings ni desvía los eventos a su PTS correspondiente. No es necesario decodificar los datos de muestra de un array de bytes a una cadena, ni filtrar etiquetas de ID3 irrelevantes, ya que el SDK de IMA de DAI realiza esta decodificación y filtrado automáticamente.

Por ejemplo:

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

Transmisiones HLS CMAF

Las transmisiones HLS de DAI lineales que usan el Common Media Application Framework (CMAF) pasan metadatos temporizados a través de cuadros eMSGv1 en banda siguiendo el estándar ID3 a través de CMAF. Estos cuadros eMSG están incorporados al comienzo de cada segmento multimedia, y cada eMSG ID3 contiene un PTS en relación con la última discontinuidad en la transmisión.

A partir de la versión 1.2.0 de HLS.js, ambos reproductores sugeridos pasan ID3 a través de CMAF al usuario como si fueran etiquetas ID3. Por este motivo, los siguientes ejemplos son los mismos que para las transmisiones HLS MPEG2TS. Sin embargo, este podría no ser el caso con todos los jugadores, por lo que implementar la compatibilidad con las transmisiones HLS CMAF podría requerir un código único para analizar ID3 a través de eMSG.

Reproducción en Safari

Safari trata al ID3 a través de los metadatos eMSG como seudo eventos ID3, y los proporciona en lotes, automáticamente, como un segmento oculto, de modo que los eventos cuechange se activen en el momento correcto para procesar cada metadato. Está bien pasar todos los metadatos al SDK de IMA de DAI, ya sea relevante para el tiempo o no. Todos los metadatos no relacionados con la DAI se filtran automáticamente.

Por ejemplo:

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 de la versión 1.2.0, HLS.js trata a ID3 a través de los metadatos eMSG como seudoeventos ID3, y los proporciona en lotes, a través del evento FRAG_PARSING_METADATA, como un arreglo de muestras. HLS.js no traduce los datos del ID3 de arrays de bytes a strings ni desvía los eventos a su PTS correspondiente. No es necesario decodificar los datos de muestra de un array de bytes a una cadena, ya que el SDK de DAI de IMA realiza esta decodificación automáticamente.

Por ejemplo:

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

Transmisiones DASH

Las transmisiones DASH de DAI lineal pasan metadatos como eventos de manifiesto en una transmisión de eventos con el valor schemeIdUri personalizado urn:google:dai:2018. Cada evento en estas transmisiones contiene una carga útil de texto y el PTS.

DASH.js

Dash.js proporciona controladores de eventos personalizados que llevan el nombre del valor de schemeIdUri de cada transmisión de eventos. Estos controladores personalizados se activan por lotes, lo que te permite procesar el valor PTS para cronometrar correctamente el evento. El SDK de IMA de DAI puede controlar esto por ti con el método streamManager, processMetadata().

Por ejemplo:

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

Shaka Player muestra eventos como parte de su evento timelineregionenter. Debido a una incompatibilidad de formato con Shaka Player, el valor de los metadatos debe recuperarse sin procesar a través de la propiedad de detalles eventElement.attributes['messageData'].value.

Por ejemplo:

player.addEventListener('timelineregionenter', function(event) {
  const detail = event.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;
    streamManager.processMetadata("urn:google:dai:2018", mediaId, pts);
  }
});
...

Publicación de grupos de anuncios

En la entrega de pods, existen diferentes configuraciones para pasar metadatos temporizados según los siguientes criterios:

  • Tipo de transmisión en vivo o VOD
  • Formato de transmisión HLS o DASH
  • El tipo de reproductor utilizado
  • Es el tipo de backend de DAI que se usa.

Formato de transmisión HLS (transmisiones en vivo y VOD, reproductor HLS.js)

Si usas un reproductor HLS.js, escucha el evento FRAG_PARSING_METADATA de HLS.js para obtener los metadatos de ID3 y pasarlos al SDK con StreamManager.processMetadata().

Para reproducir el video automáticamente después de que todo esté cargado y listo, escucha el evento MANIFEST_PARSED de HLS.js para activar la reproducción.

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 transmisiones DASH, tipo de transmisión en vivo y VOD)

Si usas un reproductor DASH.js, debes usar diferentes cadenas para escuchar los metadatos ID3 de las transmisiones en vivo o de VOD:

  • Transmisiones en vivo: 'https://developer.apple.com/streaming/emsg-id3'
  • Transmisiones de VOD: 'urn:google:dai:2018'

Pasa los metadatos de ID3 al SDK con StreamManager.processMetadata().

Para mostrar automáticamente los controles de video después de que todo esté cargado y listo, escucha el evento MANIFEST_LOADED de 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 con transmisiones en vivo (formato de transmisiones DASH)

Si usas Shaka Player para la reproducción de transmisiones en vivo, usa la cadena 'emsg' para escuchar eventos de metadatos. Luego, usa los datos del mensaje de evento en la llamada a 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});
}

Reproductor Shaka con transmisiones VOD (formato de transmisiones DASH)

Si usas el Shaka Player para la reproducción de transmisiones de VOD, usa la cadena 'timelineregionenter' para escuchar eventos de metadatos. Luego, usa los datos del mensaje de evento en tu llamada a StreamManager.processMetadata() con la cadena '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);
       }
}