Interactive Media Ads (IMA) Dynamic Ad Insertion (DAI) SDK 依赖于嵌入在媒体片段(带内元数据)或流式传输清单文件(清单内元数据)中的元数据信息来跟踪观看者的位置和客户端广告事件。元数据以不同的格式发送,具体取决于正在播放的视频流类型。
视频播放器会批量接收定时元数据。根据播放器的不同,元数据可以在预定时间显示,也可以分批显示。每个元数据字符串都有一个关联的呈现时间戳 (PTS),用于指示应何时触发该字符串。
您的应用负责捕获元数据并将其转发给 IMA DAI SDK。该 SDK 提供了以下方法来传递此信息:
- onTimedMetadata
此方法会将准备好要处理的元数据字符串转发给 SDK。它接受一个实参:
metadata
:一个对象,包含一个键(即TXXX
)以及一个以google_
为前缀的关联字符串值。
- processMetadata
此方法会安排在指定 PTS 之后由 SDK 处理元数据字符串。它接受以下实参:
type
:一个字符串,包含正在处理的事件的类型。接受的值为ID3
(对于 HLS)或urn:google:dai:2018
(对于 DASH)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 通过 CMAF 标准,通过带内 eMSGv1 箱传递定时元数据。这些 eMSG 框嵌入在每个媒体段的开头,每个 ID3 eMSG 都包含一个相对于信息流中最后一个不连续点的 PTS。
自 HLS.js 1.2.0 版发布以来,我们建议的两个播放器都会通过 CMAF 将 ID3 传递给用户,就像它们是 ID3 标记一样。因此,以下示例与 HLS MPEG2TS 流的示例相同。不过,并非所有播放器都是如此,因此实现对 HLS CMAF 流的支持可能需要通过 eMSG 解析 ID3 的唯一代码。
在 Safari 中播放
Safari 会将通过 eMSG 元数据传递的 ID3 视为伪 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 将通过 eMSG 元数据传递的 ID3 视为伪 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 流会以事件流中的清单事件形式传递元数据,并使用自定义 schemeIdUri
值 urn: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);
}
});
...
广告连播投放
对于 Pod 服务,传递定时元数据的配置因以下条件而异:
- 直播或 VOD 视频流类型
- HLS 或 DASH 流格式
- 所用播放器的类型
- 所用 DAI 后端的类型
HLS 视频流格式(直播和 VOD 视频流,HLS.js 播放器)
如果您使用的是 HLS.js 播放器,请监听 HLS.js FRAG_PARSING_METADATA
事件以获取 ID3 元数据,并通过 StreamManager.processMetadata()
将其传递给 SDK。
为了在所有内容加载完毕并准备就绪后自动播放视频,请监听 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 视频流格式,直播和 VOD 视频流类型)
如果您使用的是 DASH.js 播放器,则必须使用不同的字符串来监听直播或 VOD 流的 ID3 元数据:
- 直播:
'https://developer.apple.com/streaming/emsg-id3'
- 点播视频流:
'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 播放器播放直播,请使用字符串 '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 视频流(DASH 视频流格式)的 Shaka Player
如果您使用 Shaka 播放器播放 VOD 流,请使用字符串 '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);
}
}