处理定时元数据

选择平台HTML5 Roku

互动媒体广告 (IMA) 动态广告插播 (DAI) SDK 依赖于嵌入在视频流媒体片段中的元数据信息(带内元数据)或流式传输清单文件中的元数据信息(清单内元数据)来跟踪观看者的位置和客户端广告事件。元数据以不同的格式发送,具体取决于正在播放的视频流类型。

视频播放器会分批接收定时元数据。根据播放器的不同,元数据可以按计划时间显示,也可以分批显示。每个元数据字符串都有一个关联的呈现时间戳 (PTS),用于指示何时触发该字符串。

您的应用负责捕获元数据并将其转发给 IMA DAI SDK。该 SDK 提供以下方法来传递此信息:

onTimedMetadata

此方法会将准备好处理的元数据字符串转发给 SDK。它接受一个参数:

  • metadata:一个对象,其中包含一个键 TXXX,该键具有一个以 google_ 为前缀的关联字符串值。
processMetadata

此方法会安排在指定的 PTS 之后由 SDK 处理元数据字符串。它接受以下参数:

  • type:一个字符串,其中包含要处理的事件的类型。接受的值为 HLS 的 ID3 或 DASH 的 urn:google:dai:2018
  • data:一个以 google_ 为前缀的字符串值,或一个遵循以下格式的字节数组:ID3:u\0004u\000u\000u\0000TXXXu\0004u\000u\000u\0000google_xxxxxxxx
  • timestamp:应处理数据的时间戳(以秒为单位)。

IMA DAI SDK 支持的每种视频流类型都使用一种独特的定时元数据形式,如以下部分所述。

HLS MPEG2TS 视频流

使用 MPEG2TS 片段的线性 DAI HLS 视频流通过带内 ID3 标记将定时元数据传递给视频播放器。这些 ID3 标记嵌入在 MPEG2TS 片段中,并被赋予 TXXX 字段名称(用于自定义用户定义文本内容)。

在 Safari 中播放

Safari 会自动将 ID3 标记作为隐藏轨道进行处理,因此 cuechange 事件会在正确的时间触发,以处理每条元数据。您可以将所有元数据传递给 IMA DAI SDK,无论其内容或类型如何。不相关的元数据会自动过滤掉。

示例如下:

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 通过 FRAG_PARSING_METADATA 事件以样本数组的形式分批提供 ID3 标记。HLS.js 不会将 ID3 数据从字节数组转换为字符串,也不会将事件偏移到其对应的 PTS。无需将样本数据从字节数组解码为字符串,也无需过滤掉不相关的 ID3 标记,因为 IMA DAI SDK 会自动执行此解码和过滤操作。

示例如下:

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

HLS CMAF 视频流

使用通用媒体应用框架 (CMAF) 的线性 DAI HLS 视频流通过遵循 ID3 through CMAF 标准的带内 eMSGv1 框传递 定时元数据。这些 eMSG 框嵌入在每个媒体片段的开头,每个 ID3 eMSG 都包含一个相对于视频流中上次中断的 PTS。

自 HLS.js 1.2.0 版发布以来,我们建议的两个播放器都通过 CMAF 将 ID3 传递给用户,就像它们是 ID3 标记一样。因此,以下示例与 HLS MPEG2TS 视频流的示例相同。不过,并非所有播放器都是如此,因此实现对 HLS CMAF 视频流的支持可能需要使用独特的代码来解析 ID3 through eMSG。

在 Safari 中播放

Safari 会将 ID3 through eMSG 元数据视为伪 ID3 事件,并以隐藏轨道的形式自动分批提供这些事件,这样 cuechange 事件会在正确的时间触发,以处理每条元数据。您可以将所有元数据传递给 IMA DAI SDK,无论其是否与时间相关。任何与 DAI 无关的元数据都会自动过滤掉。

示例如下:

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

自 1.2.0 版起,HLS.js 会将 ID3 through eMSG 元数据视为伪 ID3 事件,并通过 FRAG_PARSING_METADATA 事件以样本数组的形式分批提供这些事件。HLS.js 不会将 ID3 数据从字节数组转换为字符串,也不会将事件偏移到其对应的 PTS。无需将样本数据从字节数组解码为字符串,因为 IMA DAI SDK 会自动执行此解码操作。

示例如下:

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

DASH 视频流

线性 DAI DASH 视频流以事件流中的清单事件的形式传递元数据,并使用自定义 schemeIdUriurn:google:dai:2018。这些视频流中的每个事件都包含文本载荷和 PTS。

DASH.js

Dash.js 提供自定义事件处理程序,这些处理程序以每个事件流的 schemeIdUri 值命名。这些自定义处理程序会分批触发,由您负责处理 PTS 值,以便正确地为事件计时。IMA DAI SDK 可以通过 streamManager 方法 processMetadata() 为您处理 此问题。

示例如下:

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 会将事件作为其 timelineregionenter 事件的一部分显示。由于与 Shaka Player 的格式不兼容,因此必须通过详细信息属性 eventNode.attributes['messageData']以原始格式检索元数据值。

示例如下:

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);
  }
});
...

广告连播投放

对于广告连播投放,根据以下条件,传递定时元数据的配置有所不同:

  • 直播或视频点播视频流类型
  • HLS 或 DASH 串流格式
  • 所使用的播放器类型
  • 所使用的 DAI 后端类型

HLS 视频流格式(直播和视频点播流,HLS.js 播放器)

如果您使用的是 HLS.js 播放器,请监听 HLS.js FRAG_PARSING_METADATA 事件以获取 ID3 元数据,并使用 SDK StreamManager.processMetadata() 将其传递。

如需在所有内容加载完毕并准备就绪后自动播放视频,请监听 HLS.js MANIFEST_PARSED 事件以触发播放。

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(DASH 串流格式,直播和视频点播串流类型)

如果您使用的是 DASH.js 播放器, 则必须使用不同的字符串来监听直播或视频点播 视频流的 ID3 元数据:

  • 直播:'https://developer.apple.com/streaming/emsg-id3'
  • VOD 视频流:'urn:google:dai:2018'

使用 StreamManager.processMetadata() 将 ID3 元数据传递给 SDK。

如需在所有内容加载完毕并准备就绪后自动显示视频控件,请监听 DASH.js MANIFEST_LOADED 事件。

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(DASH 视频流格式)

如果您使用 Shaka Player 进行 直播播放,请使用字符串 'emsg' 来监听元数据事件。 然后,在调用 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});
}

使用 VOD 视频流的 Shaka Player(DASH 视频流格式)

如果您使用 Shaka Player 进行 视频点播流播放,请使用字符串 'timelineregionenter' 来监听 元数据事件。然后,在调用时使用事件消息数据 StreamManager.processMetadata() 并使用字符串 '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);
       }
}