概览
Web Receiver SDK 为给定媒体流中的广告插播时间点和随播广告提供原生支持。它提供了用于设置广告插播时间点的广告来源、广告来源和行为及其关联的广告插播时间点的 API。在本指南中,Break
是指包含一个或多个广告或开始播放的时间间隔,每个广告或导视广告都称为 BreakClip
。这些广告插播时间点与正在加载或播放的媒体相关联。
广告类型
Web Receiver SDK 支持客户端广告插播 (CSAI) 和服务器拼接广告插播 (SSAI)。客户端拼接广告可以由应用手动设置,也可以从 VAST 和 VMAP 模板文件中提取。您应在内容加载之前手动将服务器拼接广告指定为嵌入式广告,或在内容播放期间以嵌入式展开式广告的形式动态指定此类广告。下文详细介绍了每种广告类型的实现方式。
人工拼接
客户端拼接的手动广告插播时间点是一种由客户端拼接在一起的广告插播时间点,由应用使用 SDK API 手动指定。此类广告不会嵌入到主要内容的信息流中。BreakClip
必须提供 contentId
(指向广告内容的网址)、contentType
(用于说明广告内容的格式)以及 title
。
Break
必须将 isEmbedded
和 expanded
设置为默认值 false
。position
可以设置为前贴片、中贴片广告或后贴片广告插播时间点(如需了解详情,请参阅广告插播时间点部分)。当准备播放广告时,Web Receiver SDK 会生成另一个播放器实例,用于加载和播放广告内容。这些广告插播时间点需要 stitched timeline
,并且必须静态添加(如需了解详情,请参阅广告插播部分)。以下示例展示了客户拼接式广告的基本实现:
// Create the BreakClip.
let clipClient = new cast.framework.messages.BreakClip('bc_client');
clipClient.title = 'The Ad Title to be displayed during playback';
clipClient.contentId = 'https://example.com/ad.m3u8';
clipClient.contentType = 'application/vnd.apple.mpegurl';
// Optional: Used when HLS ad container formats differ from the main content's.
clipClient.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.FMP4;
// Create the Break using the BreakClip id above.
let breakPostrollClient = new cast.framework.messages.Break(
'break_postroll_client', ['bc_client'], -1);
breakPostrollClient.isEmbedded = false; // Optional: default is false.
breakPostrollClient.expanded = false; // Optional: default is false.
VAST
Web Receiver SDK 支持添加 IAB 标准 VAST(视频广告投放模板)广告。如果提供,系统会解析 XML 模板,以在进入广告插播时间点时生成后续的客户端拼接中断片段。
如需创建 VAST 广告,接收方应用必须创建 VastAdsRequest
并在 BreakClip
vastAdsRequest
属性中指定该广告。VastAdsRequest
对象必须定义 adsResponse
(XML 模板本身的字符串表示法)或 adTagUrl
(托管 XML 模板的网址)属性。如果指定了网址,SDK 会处理模板提取操作。封装 Break
遵循客户端拼接广告的规范。可以将这些广告与其他手动拼接的广告一起添加到同一内容的不同广告插播时间点或不同插播时间点。以下示例展示了 VAST 广告的基本实现:
// Create the VastAdsRequest.
let vastTemplate = new cast.framework.messages.VastAdsRequest();
vastTemplate.adTagUrl = 'https://example.com/ads.xml'
// Create the BreakClip.
let clipVast = new cast.framework.messages.BreakClip('bc_vast');
clipVast.vastAdsRequest = vastTemplate;
// Create the Break using the BreakClip id above.
let breakPostrollVast = new cast.framework.messages.Break(
'break_postroll_vast', ['bc_vast'], -1);
breakPostrollVast.isEmbedded = false; // Optional: default is false.
breakPostrollVast.expanded = false; // Optional: default is false.
在输入包含 VAST BreakClip
的 Break
时,Web Receiver SDK 会选择提取模板,然后解析该模板。解析时,SDK 将生成新的 BreakClip
,并在其中填充从模板提取的值,例如 contentId
、contentType
、title
、duration
、whenSkippable
和 clickThroughUrl
。生成的广告插播剪辑的 id
设置为 GENERATED:N
,其中 N
是一个整数,递增自从 0
开始创建的每个新的 VAST 广告插播剪辑。1
然后,生成的广告会添加到 BreakClip
数组中。然后,系统会将当前 Break
中的每个 VAST 广告插播时间点的 id
替换为其对应的广告插播时间点剪辑的 id
。以下代码段说明了与进入此类广告插播时间点之前和之后的广告相关的 MEDIA_STATUS
消息中的变化。
在暂停投放 VAST 广告之前,Break
和 BreakClip
信息。
"breaks": [
{
"id": "break_postroll_vast",
"breakClipIds": [
"bc_vast"
],
"position": 0,
"isWatched": false
}
],
"breakClips": [
{
"id": "bc_vast"
}
]
在进入 VAST 广告之后输入广告插播时间点之后,显示 Break
和 BreakClip
信息。
"breaks": [
{
"id": "break_postroll_vast",
"breakClipIds": [
"GENERATED:0"
],
"position": 0,
"isWatched": true
}
],
"breakClips": [
{
"id": "bc_vast"
},
{
"id": "GENERATED:0",
"contentId": "https://example.com/break-clip-1.mpd",
"contentType": "application/dash+xml",
"title": "Ad Title Extracted from Template",
"duration": 10,
"whenSkippable": 5,
"clickThroughUrl": "https://example.com/ad-target"
}
]
VMAP
Web Receiver SDK 支持 IAB VMAP(视频多广告播放列表)标准。如果提供了 VMAP,Web Receiver SDK 将解析 VMAP 响应,并为响应中的任何 <AdBreak>
条目生成客户端拼接的 Break
对象。此外,还会为 VMAP 中提供的每个 <AdSource>
条目生成包含 vastAdsRequest
对象的相应 BreakClips
。若要启用 VMAP 以将内容插入您的内容,应用必须创建一个 VastAdsRequest
对象,并将其分配给 LoadRequestData
中 MediaInformation
的 vmapAdsRequest
属性。
这些广告必须静态插入(如需了解详情,请参阅广告插播部分)。以下代码段简要说明了如何创建 VMAP 请求。
// Create the VastAdsRequest.
let vastTemplate = new cast.framework.messages.VastAdsRequest();
vastTemplate.adTagUrl = 'https://example.com/vmap.xml'
// Add it to the MediaInformation of the LoadRequest.
loadRequestData.media.vmapAdsRequest = vastTemplate;
嵌入式播放器
嵌入的广告插播时间点是一种在服务器端通过主要内容流拼接起来的广告插播时间点。在计算媒体时间时,从主内容的时长中减去 Break
的时长。
BreakClip
必须提供广告内容的 duration
和 title
。必须将 Break
的 isEmbedded
设为 true
,将 expanded
设为 false
。position
可以设置为前贴片或中贴片广告广告插播时间点。后贴片广告插播时间点支持正值 position
值。如需了解详情,请参阅广告插播定位部分。当广告播放时,Web Receiver SDK 会在广告片段嵌入到其中时继续播放视频流。此类广告没有额外的加载机制。
一旦进度条指针位于中断时间范围内,系统就会向用户显示相关广告元数据。这些广告插播时间点需要 embedded timeline
,并且必须静态添加(如需了解详情,请参阅广告插播部分)。以下示例展示了 embedded
广告的基本实现。
// Create the BreakClip.
let clipEmbedded = new cast.framework.messages.BreakClip('bc_embedded');
clipEmbedded.title = 'The Ad Title to be displayed during playback';
clipEmbedded.duration = 15;
// Create the Break using the BreakClip id above.
let breakPrerollEmbedded = new cast.framework.messages.Break(
'break_preroll_embedded', ['bc_embedded'], 0);
breakPrerollEmbedded.isEmbedded = true;
breakPrerollEmbedded.expanded = false; // Optional: default is false.
嵌入式展开式
嵌入式广告插播时间点是一种广告插播时间点,可被拼接到主要内容的视频流中。计算媒体时间时,Break
的时长会包含在主内容的时长中。
BreakClip
必须提供广告内容的 duration
和 title
。必须将 Break
的 isEmbedded
设置为 true
,将 expanded
设置为 true
。position
可以设置为前贴片或中贴片广告广告插播时间点。后贴片广告插播时间点采用 position
正值。如需了解详情,请参阅广告插播定位部分。当广告播放时,Web Receiver SDK 会在广告片段嵌入到其中时继续播放视频流。此类广告没有额外的加载机制。
一旦进度条指针位于中断时间范围内,系统就会向用户显示相关广告元数据。这些插播时间点需要 embedded timeline
,可以静态或动态添加(如需了解详情,请参阅广告插播部分)。以下示例展示了 embedded expanded
广告的基本实现:
// Create the BreakClip.
let clipEmbeddedExpanded =
new cast.framework.messages.BreakClip('bc_embedded_expanded');
clipEmbeddedExpanded.title = 'The Ad Title to be displayed during playback';
clipEmbeddedExpanded.duration = 15;
// Create the Break using the BreakClip id above.
let breakPrerollEmbeddedExpanded = new cast.framework.messages.Break(
'break_preroll_embedded_expanded', ['bc_embedded_expanded'], 0);
breakPrerollEmbeddedExpanded.isEmbedded = true;
breakPrerollEmbeddedExpanded.expanded = true;
玩家时间轴类型
创建播放器实例时,Web Receiver SDK 会选择时间轴类型,以支持在内容播放期间播放广告。每个时间轴都允许添加特定的广告插播类型。时间轴类型由加载期间 LoadRequestData
的 MediaInformation
中显示的广告类型决定。如果存在嵌入广告插播时间点,系统会选择 embedded
时间轴。如果存在客户端拼接广告插播时间点,则选择 stitched
时间轴。如果没有广告,SDK 会默认使用 embedded
时间轴。选择时间轴后,便无法更改其当前媒体项的时间线。下表详细说明了各个时间表。
时间轴类型 | 说明 |
---|---|
嵌入式时间轴 | 表示支持在主要内容中嵌入广告的媒体时间(嵌入和嵌入式展开广告插播时间点)。如果存在未展开的广告插播时间点,系统会从内容的总时长中减去其时长。另一方面,如果存在展开的广告插播时间点,该时段就会被视为主要内容的一部分。 |
拼接时间轴 | 媒体时间的表示法,支持来自外部媒体文件的广告(手动客户端拼接、VAST 和 VMAP 广告插播时间点)。添加后,广告插播时长不属于主要内容的时长。 |
图 1 至图 3 展示了一些具有各种广告类型及其各自时间轴值的内容。内容配置的是包含两个广告插播时间点的前贴片广告插播时间点,以及包含一个广告插播时间点剪辑的中贴片广告和后贴片广告插播时间点。内容开始播放后的挂钟时间、主要内容的媒体时间以及当前播放的广告插播时间点的时长会在每个数字下方对齐。
广告插播定位
借助 Web Receiver SDK,开发者可以通过设置 Break
的 position
属性来指定广告插播时间点。此值对应于主要内容的媒体时间,可用于创建 pre-roll
、mid-roll
和 post-roll
广告插播时间点。其定义如下:
广告插播位置 | 说明 |
---|---|
前贴片广告 | 在主要内容之前播放的广告插播时间点。可通过将 breakPosition 设置为 0 来指明 |
中贴片广告 | 在内容中间播放的广告插播时间点。通过以下方式表示:将 breakPosition 设置为广告插播时间点的开始时间大于主要内容的开始时间,并且广告插播时间点的结束时间小于主要内容的结束时间。 |
后贴片广告 | 在主要内容之后播放的广告插播时间点。通过将拼接时间表的 breakPosition 设置为 -1 ,可表示此时间。对于嵌入式时间轴,breakPosition 应设置为主要内容的时长减去广告插播时间点的时长。不支持直播内容。 |
互操作性矩阵
快速参考表是广告类型的概览及其与广告相关功能的兼容性。
功能支持 | 客户拼接广告 | VAST | VMAP | 嵌入式广告 | 嵌入式展开式广告 |
---|---|---|---|---|---|
兼容 | VAST | 人工拼接 | 不适用 | 嵌入式展开式 | 嵌入式播放器 |
时间轴 | 缝线装订 | 缝线装订 | 缝线装订 | 嵌入式播放器 | 嵌入式播放器 |
广告插播 | 静态 | 静态 | 静态 | 静态 | 静态、动态 |
移除广告 | |||||
前贴片广告 | |||||
中贴片广告 | |||||
后贴片广告 | |||||
跳过广告 | |||||
中断搜寻拦截器 | |||||
中断片段加载拦截器 |
活动
当发生按键停止事件时,转换 SDK 将分派 BreaksEvent
类型的事件。接收器应用可以使用 PlayerManager
addEventListener
API 订阅它们。
这些事件可用于分析和广告播放跟踪。使用 VMAP(视频多广告播放列表)和 VAST(视频广告投放模板)广告时,SDK 会自动发送响应中提供的任何标准跟踪事件。
表 2 中列出了各种事件类型,并详细说明了何时触发这些事件。
广告插播事件 | 说明 |
---|---|
BREAK_STARTED |
在主要内容的当前媒体时间等于未观看插播时间点的 position 时触发。 |
BREAK_CLIP_LOADING |
仅当拼接时间轴广告插播剪辑开始加载时触发。 |
BREAK_CLIP_STARTED |
在广告插播时间点开始播放时触发。 |
BREAK_CLIP_ENDED |
在广告插播剪辑结束时触发。在以下情况下,将填充
endedReason
:
|
BREAK_ENDED |
在广告插播时间点的最后一个广告插播时间点结束时触发。 |
插入广告
借助投放 SDK,应用可以在投放会话的不同时刻插入和移除广告。广告插播分为两种类型:静态和动态。静态广告插播要求在创建玩家之前在 LoadRequestData
中指定广告。动态广告插播利用 BreakManager
addBreak
API 在已加载的内容中插入广告插播时间点。每种类型的插入方法都兼容特定的广告类型。互操作性矩阵中提供了兼容性概览。
静态广告插播
静态广告插播的特征是,在创建播放器之前添加相关广告元数据。此信息由 LoadRequestData
的 MediaInformation
提供。例如,可以在已连接的发送者的原始加载请求中进行设置,也可以通过网络接收器应用通过拦截 LOAD
请求进行插入。将 LoadRequestData
返回给 Web Receiver SDK 以进行处理后,系统就会创建播放器。详细了解如何加载媒体。以下示例展示了如何在 LOAD
请求拦截器中添加手动客户端拼接广告。
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD, loadRequestData => {
// Create the BreakClip.
let clipClient = new cast.framework.messages.BreakClip('bc_client');
clipClient.title = 'The Ad Title to be displayed during playback';
clipClient.contentId = 'https://example.com/ad.mp4';
clipClient.contentType = 'video/mp4';
// Create the Break using the BreakClip id above.
let breakPostrollClient = new cast.framework.messages.Break(
'break_postroll_client', ['bc_client'], -1);
// Set the ad information in the load request data.
let media = loadRequestData.media;
media.breakClips = [clipClient];
media.breaks = [breakPostrollClient];
return loadRequestData;
});
动态广告插播
动态广告插播是指在内容播放期间设置广告插播时间点。具体方法是获取 BreakManager
的实例并调用 addBreak
API。它至少需要两个参数,一个是嵌入式展开
Break
,另一个是 BreakClip
。加入了可选的第三个属性,该属性设置为 true
时强制通过 MediaStatus
广播向已连接的发送者发送更改。添加广告插播时间点和广告插播时间点片段时,相应的 ID 必须是唯一的。这些广告只能在播放器创建后添加。Web Receiver SDK 会在创建播放器后触发 PLAYER_LOADING
事件。以下示例展示了事件处理脚本中的用法,该处理脚本会响应数据流的 ID3 元数据的变化,并创建 Break
和 BreakClip
对象以将其插入时间轴。
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const breakManager = playerManager.getBreakManager();
playerManager.addEventListener(cast.framework.events.EventType.ID3, (event) => {
// Create the BreakClip.
let clipEmbeddedExpanded = parseBreakClipFromData(event.segmentData);
let breakEmbeddedExpanded = parseExpandedBreakFromData(event.segmentData);
// Add the break and break clip.
breakManager.addBreak(breakEmbeddedExpanded, [clipEmbeddedExpanded]);
});
动态广告移除
如需移除动态广告插播时间点,应用应在播放期间调用 removeBreakById
。此函数接受要从时间轴中移除的广告插播时间点的字符串标识符。指定的 breakId
必须指向嵌入式展开式广告插播时间点。如果检测到任何其他类型的广告插播时间点,广告插播时间点会保留在时间轴中。请参阅以下示例,移除中断。
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const breakManager = playerManager.getBreakManager();
breakManager.removeBreakById('break_midroll_embedded_expanded');
广告插播时间点的行为
该 SDK 定义了在玩家进入和离开插播时间点时的默认行为,并提供了一种使用 BreakManager
中提供的某些 API 进一步自定义的方式。
默认的广告插播行为
当通过常规播放或定位 Break
输入 Break
时,SDK 将通过检查 isWatched
属性来评估用户是否已经看到了它。创建后,此属性的广告插播时间点的默认值为 false
。如果该属性为 true
,输入后将不会播放广告插播时间点,主要内容将继续播放。如果该属性为 false
,输入时将播放广告插播时间点。
如果寻找过去的广告插播时间点,默认实现会获取 position
介于搜索操作的 seekFrom
值和 seekTo
值之间的所有 Break
项。在此断点列表中,SDK 将播放 position
最接近 seekTo
值且 isWatched
属性设置为 false
的 Break
。该广告插播时间点的 isWatched
属性将设置为 true
,播放器将会开始播放其广告插播时间点剪辑。观察完广告插播时间点后,主要内容将从 seekTo
位置继续播放。如果没有此类广告插播时间点,则不会播放任何中断,并且主要内容将在 seekTo
位置继续播放。
在中断播放期间,SDK 将在 MediaStatus
中向已连接的发送器应用广播所有相关更新。这些应用将通过读取 breakStatus
属性使用广播来更新其界面。此属性仅在中断播放期间定义。
接收器应用还可以通过调用 PlayerManager
getBreakClipCurrentTimeSec
直接查询与进度条指针的位置有关的信息,即 BreakClip
的当前时间。同样,应用也可以通过调用 getBreakClipDurationSec
来查询当前 BreakClip
的时长。
自定义广告插播时间点行为
您可以使用 BreakManager
中提供的 setBreakClipLoadInterceptor
和 setBreakSeekInterceptor
方法修改广告插播时间点和广告插播时间点的默认行为。
中断跳转拦截器
广告插播时间点拦截器允许应用控制在广告插播时间点处跳转的行为。当函数请求在一个或多个间隙向前或向后跳转时,会触发该函数。当被调用时,BreakSeekData
将作为参数传递给回调函数。BreakSeekData
对象包含一个 Break
对象数组,其 position
属性设置为介于当前进度条指针时间(定义为 seekFrom
)到跳转目的地时间 seekTo
之间的数字。
此拦截器允许修改相应断点中的 Break
对象。实现后,广告插播时间点拦截器必须通过返回选择性修改的 BreakSeekData
对象来指定播放哪个广告插播时间点。播放器会继续播放返回值中的所有中断。如果中断定位拦截器未返回 null
值或未返回任何内容,则系统会跳过相应断点。
请参阅以下示例,了解简单的拦截器实现,该实现会覆盖默认行为,以监控寻找的所有广告插播时间点(已观看的广告插播时间点除外)。
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const breakManager = playerManager.getBreakManager();
breakManager.setBreakSeekInterceptor((breakSeekData) => {
// Filter the breaks array by removing watched breaks.
const unwatchedBreaks =
breakSeekData.breaks.filter(adBreak => !adBreak.isWatched);
breakSeekData.breaks = unwatchedBreaks;
return breakSeekData;
});
中断片段加载拦截器
使用广告插播时间点加载拦截器,可以在播放之前修改 BreakClip
对象。
只能针对拼接时间轴间断调用广告插播时间点加载拦截器,并且可以使用 setBreakClipLoadInterceptor
进行设置。在输入 Break
之前,系统会针对该断点中定义的每个 BreakClip
调用此拦截器一次。SDK 将原始 BreakClip
对象作为回调函数的参数进行传递。然后,应用可以修改并返回此 BreakClip
,以便 SDK 可以使用更新后的配置提取并显示广告插播时间点。如果返回 null
或未返回任何内容,则系统会跳过广告插播剪辑。
以下示例展示了使用可调用函数 getUrlFromClipId
修改广告插播时间点剪辑的 contentUrl
,其中 BreakClip
的 id
映射到网址。
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const breakManager = playerManager.getBreakManager();
breakManager.setBreakClipLoadInterceptor(
(breakClip, breakClipLoadInterceptorContext) => {
// Obtains the URL of a break clip id from a function call.
breakClip.contentUrl = getUrlFromClipId(breakClip.id);
return breakClip;
});
广告跳过
Web Receiver SDK 提供了用于跳过广告插播时间点的广告插播时间点和具体广告插播时间点的 API。借助该 SDK,用户还可选择与其发送方应用或智能显示屏设备互动,从而跳过广告插播时间点。
用户可跳过的广告插播时间点片段
将广告插播剪辑设为可跳过后,用户与关联的发送者应用和智能显示设备互动,则可以选择跳过当前播放的广告插播剪辑的其余部分。将 whenSkippable
属性设置为非负数(以秒为单位)将为 BreakClip
对象启用此功能。一旦广告插播剪辑播放了该秒数,播放器就会将广告插播时间点视为可跳过。将此值设为 0
可让用户立即跳过广告插播时间点。
// Create the BreakClip.
let clip = new cast.framework.messages.BreakClip('bc');
clip.title = 'The Ad Title to be displayed during playback';
clip.whenSkippable = 10; // Users can skip the clip after 10 seconds of playback.
这些信息可以在发送者的原始加载请求或接收者应用中进行设置。跳过时,拼接时间轴广告插播时间点的间隙片段将停止播放当前的间隙片段。播放器将会加载下一个广告插播剪辑(如果存在),或者加载主要内容。如果跳过,嵌入式时间轴广告插播时间点中的某个广告插播时间点将跳转到广告插播时间点的末尾,并在此时继续播放视频流。
以程序化方式跳过广告
广告也可以自动跳过,无需任何用户互动。
如需跳过整个播放,应用应将 Break
的 isWatched
属性设置为 true
。此操作可以在加载序列或内容播放期间随时完成。当主要内容的当前时间达到广告插播时间点的 position
时,玩家会评估 isWatched
属性。届时,播放器会确定是否应输入广告插播时间点。请参阅以下示例,播放器会在加载过程中循环播放所有断点,并修改该值。
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const breakManager = playerManager.getBreakManager();
playerManager.addEventListener(cast.framework.events.EventType.PLAYER_LOADING,
(event) => {
// Obtain the breaks and iterate through each item to skip all ad breaks.
let breaks = breakManager.getBreaks();
breaks.forEach((brk) => {
brk.isWatched = true;
});
});
如需以编程方式跳过特定的广告插播剪辑,应使用广告插播剪辑加载拦截器。如果返回 null
或未在回调函数中返回值,该跳转的片段将被跳过。
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const breakManager = playerManager.getBreakManager();
breakManager.setBreakClipLoadInterceptor(
(breakClip, breakClipLoadInterceptorContext) => {
return null;
});