- 音视频
- 前端
【免费下载链接】xgplayer
A HTML5 video player with a parser that saves traffic
xgplayer-ads 是 xgplayer 官方提供的广告插件,它的核心价值是让播放器以最小成本快速接入标准广告能力:插件内置了 Google IMA SDK 的加载与管理逻辑,并为符合 VAST、VMAP、VPAID、SIMID 等 IAB 标准的广告提供完整的播放、状态同步与事件回调。读完本文,你将掌握ad配置块的完整语义(adType、ima、controls及 IMA 六项子配置)、广告事件监听方式、SDK 自动加载与自动播放策略,以及广告 UI 与主播放器 UI 完全解耦的设计原理,能够直接在真实项目中完成从 0 到 1 的贴片广告接入。
一、插件定位:为 xgplayer 补齐标准广告接入能力
在播放器业务中,广告接入往往意味着要分别对接多家广告 SDK、适配多种广告协议。xgplayer-ads 的目标就是把这部分复杂度收敛到一个插件里。根据官方 README 的说明,该插件集成了Google IMA与Google DAI(后者标注为待开发),对外提供符合 VAST、VMAP、VPAID 等标准的广告接入方式,开发者不需要关心广告请求、渲染、进度上报等底层细节。
从源码结构看,插件的能力边界非常清晰(见 packages/xgplayer-ads/src):
plugin.js:AdsPlugin,插件入口与对外 API,注册名为ad(static get pluginName () { return 'ad' }),对外暴露play、pause、requestAds、playAds、skip、reset、updateConfig等方法和paused、currentTime、duration等只读状态;imaAdManager.js:ImaAdManager,Google IMA SDK 的具体对接层,负责 SDK 加载、AdsLoader/AdsManager 初始化、广告事件转发;baseAdManager.js:BaseAdManager,广告管理器的公共基类,维护广告播放上下文与"内容播放阻塞"标志;ui/adUIManager.js:AdUIManager,广告 UI 管理器,负责广告播控 UI 的装饰与替换;events.js:全部广告事件常量定义。
插件当前版本为 3.0.26(见 package.json),依赖can-autoplay、eventemitter3与xgplayer-streaming-shared,与xgplayer3.0.26 互为 peerDependency。
二、快速接入:最小可运行示例
在xgplayer播放器中接入广告插件只需三步:引入插件、注册到plugins、在ad配置块中填写广告参数。README 给出的最小示例:
import Player from "xgplayer" import AdPlugin, { ADEvents } from "xgplayer-ads" import "xgplayer/dist/xgplayer.min.css" const player = new Player({ id, url, autoplay: true, plugins: [AdPlugin], ad: { adType: 'ima', ima: { locale: 'zh_cn', adsRequest: createAdsRequest() } } })对这段代码做几点拆解:
- 插件注册:将
AdPlugin放入plugins数组后,播放器初始化时会在beforePlayerInit阶段创建AdUIManager,并根据adType进入对应的广告管理器初始化分支(见 plugin.js)。该阶段返回一个initPromise,播放器初始化会等待广告管理器准备就绪(IMA_READY_TO_PLAY事件触发时 resolve)。 ad配置块:这是插件读取配置的唯一入口,adType指定广告 SDK 类型,ima是 IMA 专属配置。注意插件实际接受的adType有两种写法:'ima'与'google-ima'都会被识别并走客户端广告初始化分支(见 plugin.js)。createAdsRequest():示例中它是一个返回google.ima.AdsRequest实例的工厂函数,开发者也可改用更简单的adTagUrl字符串,详见下文配置表。
此外,插件还提供 UMD 构建(见 index.umd.js),全局变量名为AdPlugin(UMD 类额外挂载了静态属性AdEvents),适合不使用打包器的场景。
三、配置项详解:Ad Config 与 IMA Config
3.1 Ad Config(顶层广告配置)
| 配置字段 | 类型 | 默认值 | 含义 |
|---|---|---|---|
| adType | google-ima|google-dai|aws-media-tailer | - | 广告 SDK 类型,目前仅支持google-ima |
| ima | object | IMA Config | 为客户端实现方案 IMA 提供的配置 |
| controls | boolean | true | 是否需要在广告期间展示播控 UI |
三个字段的职责边界很明确:
adType是路由开关。从 plugin.js 的_initClientSideAd可以看出,'ima'与'google-ima'目前都映射到_initImaAd(),google-dai、aws-media-tailer处于待开发状态,传入时不会初始化任何广告管理器;ima是 IMA 广告管理器的全部配置来源,ImaAdManager构造时直接取this.config.ima(见 plugin.js);controls决定广告播放期间是否展示播放控制条。AdUIManager在showAdUI时若检测到config.controls === false,会将控制条整块移入 DocumentFragment 暂存,广告结束再原位恢复(见 adUIManager.js 与hideControls/showControls的实现)。
3.2 IMA Config(IMA 专属配置)
| 配置字段 | 类型 | 默认值 | 含义 |
|---|---|---|---|
| debug | boolean | false | 在插件加载前,开发者可自行引入 IMA SDK;若插件检测到没有google.ima对象,会自动加载 IMA SDK,此开关为true时加载 debug 版本 SDK |
| loadSdkTimeout | number | 3000 | 由 ImaAdManager 内部加载 IMA SDK 时的加载超时时间,单位毫秒 |
| locale | string | - | IMA 界面与文案的本地化语言,如zh_cn |
| adsRequest | object | - | google.ima.AdsRequest实例或等价对象,可携带广告标签、尺寸、跳过策略等完整请求参数 |
| adsResponse | null | string | non-null Document | - | 用作广告响应的 VAST 2.0 文档内容(字符串或 DOM),直接作为响应而非请求广告服务器;当adsRequest被设置时此参数不生效 |
| adTagUrl | string | - | 广告服务器请求地址(VAST 标签 URL);当adsRequest|adsResponse被设置时此参数不生效 |
3.3 三种广告请求方式的优先级与底层实现
adsRequest、adsResponse、adTagUrl三种方式互斥,其优先级在源码中有明确体现。ImaAdManager.requestAds()(见 imaAdManager.js)的实现逻辑为:
if (adTagUrl) { adsRequest.adTagUrl = adTagUrl } else if (adsResponse) { adsRequest.adsResponse = adsResponse } else if (providedAdsRequest && typeof providedAdsRequest === 'object') { Object.keys(providedAdsRequest).forEach(key => { adsRequest[key] = providedAdsRequest[key] }) }也就是说:adTagUrl优先级最高,其次是adsResponse(直接使用本地 VAST 文档,常见于测试场景,可完全离线验证广告流程),最后才是adsRequest对象。当使用adsRequest时,插件会将其属性逐项拷贝到新建的google.ima.AdsRequest实例上,因此你也可以传入 IMA SDK 支持的任意扩展字段。
值得注意的细节:无论使用哪种方式,插件都会主动填充广告位尺寸与自动播放意图——linearAdSlotWidth/Height、nonLinearAdSlotWidth/Height取自播放器player.sizeInfo,并通过setAdWillAutoPlay(autoplayAllowed)、setAdWillPlayMuted(autoplayRequiresMuted)告知 SDK 广告的启播环境(见 imaAdManager.js),这两个标志来自后文要讲的自动播放检测结果。
四、事件系统:广告生命周期完全可观测
广告事件独立于普通视频播放事件,统一通过播放器或插件实例的on方法监听。
player.on('ad_play', ()=>{ // do something })4.1 常用广告事件
| 事件名 | 含义 |
|---|---|
| ad_play | 当广告启播时发布(含广告从暂停中恢复播放的场景) |
| ad_pause | 当广告暂停时发布 |
| ad_time_update | 当广告类型为线性贴片时,广告当前时间发生变更时触发 |
| ad_complete | 当线性广告单个完成时发布 |
| ad_all_completed | 当所有广告(广告串 pod 全部)完成时发布 |
4.2 完整事件常量
README 列出了上述 5 个常用事件,实际上插件内部还定义了更细粒度的事件常量,全部集中在 events.js,接入时建议直接引用常量而非手写字符串:
通用广告事件:AD_START(ad_start)、AD_PLAY(ad_play)、AD_PAUSE(ad_pause)、AD_TIME_UPDATE(ad_time_update)、AD_SKIPPED(ad_skipped)、AD_ERROR(ad_error)、AD_COMPLETE(ad_complete)、AD_ALL_COMPLETED(ad_all_completed)。
IMA 专属事件(前缀ima_):IMA_SDK_LOAD_START、IMA_SDK_LOAD_SUCCESS、IMA_SDK_LOAD_ERROR、IMA_AD_LOADER_READY、IMA_AD_MANAGER_READY、IMA_AD_ENDED、IMA_AD_SKIPPED、IMA_AD_COMPLETE、IMA_ALL_ADS_COMPLETED、IMA_AD_ERROR、IMA_AD_SEEKING、IMA_AD_SEEKED、IMA_AD_LOADED、IMA_AD_BREAK_READY、IMA_CONTENT_PAUSE_REQUESTED、IMA_CONTENT_RESUME_REQUESTED、IMA_READY_TO_PLAY。
其中IMA_READY_TO_PLAY是插件初始化完成的关键信号:播放器初始化流程会等待它来 resolveinitPromise(见 plugin.js);IMA_AD_LOADED事件携带{ ad, isPreroll },可用于区分前贴片(podIndex === 0)与其他广告位。
4.3 事件监听与广告状态读取
README 提供了两种等价监听方式,既可挂在播放器上,也可挂在广告插件实例上:
import Events from "xgplayer" import AdPlugin, { ADEvents } from "xgplayer-ads" player.on([ADEvents.AD_PLAY, ADEvents.AD_PAUSE], () => { // do something }) // or const adPlugin = player.getPlugin(AdPlugin.pluginName) adPlugin.on([ADEvents.AD_PLAY, ADEvents.AD_PAUSE], () => { // do something })监听回调中可读取三个广告状态属性(均为ImaAdManager同步到插件上的只读 getter,见 plugin.js):
adPlugin.paused:广告是否处于暂停态(其判定为isLinearAdRunning && paused,即仅在"线性广告正在播放中"才反映广告的暂停状态);adPlugin.currentTime:广告当前播放时间点(秒),由AD_PROGRESS事件持续更新;adPlugin.duration:广告总时长(秒),同样来自AD_PROGRESS事件携带的AdProgressData(见 imaAdManager.js)。
4.4 广告控制方法
const adPlugin = player.getPlugin(AdPlugin.pluginName) adPlugin.play() adPlugin.pause()play()与pause()直接透传给底层adsManager.resume()/adsManager.pause()(见 imaAdManager.js)。此外插件还公开了requestAds()(重新发起广告请求)、playAds()(手动启播广告)、skip()(当AdsManager.getAdSkippableState()为 true 时跳过当前广告)、reset()(销毁 adsManager 并复位状态)与updateConfig()(运行时热更新配置)。
五、Google IMA 集成细节:SDK 加载、隐私合规、本地化与 VPAID
5.1 SDK 自动加载与降级策略
README 明确说明:内置默认会自动下载 IMA SDK,同时提供开关让开发者自行引入。这里的"开关"就是ima.debug与loadSdkTimeout。ImaAdManager._loadIMASdk()(见 imaAdManager.js)的实现逻辑是:
- 若全局已存在
google.ima对象,说明开发者已自行引入,直接 resolve,不再重复加载; - 否则动态创建
<script>标签,src为https://imasdk.googleapis.com/js/sdkloader/ima3.js(debug: true时加载ima3_debug.js调试版本); - 以
loadSdkTimeout || 3000(毫秒)为超时阈值,超时或脚本加载失败则 reject。
SDK 加载过程会依次发布IMA_SDK_LOAD_START、IMA_SDK_LOAD_SUCCESS事件;一旦加载失败,插件会发布IMA_SDK_LOAD_ERROR,并立即将shouldBlockVideoContent置为false(见 imaAdManager.js)——这意味着广告 SDK 不可用时不会阻塞正片播放,属于优雅降级:广告播不了,正片照常放。
5.2 数据隐私合规(CCPA)
由于插件默认会从 Google 域名动态加载 IMA SDK,README 特别提示:使用本插件时必须遵守 IMA SDK 的数据隐私要求(CCPA)。这属于接入方的合规义务——一旦决定使用自动加载能力,请在业务侧评估并落实对应的隐私声明与用户选择机制;如果希望完全掌控 SDK 的引入时机与合规流程,可以通过前置引入google.ima的方式让插件跳过自动加载(对应debug配置的说明),自行承担 SDK 下载。
5.3 Locale 本地化
广告 UI 的文案与语言环境通过 IMA 的settings.setLocale()控制。插件在_initConfig()中读取ima.locale并调用该方法(见 imaAdManager.js),也可在业务代码中直接调用:
google.ima.settings.setLocale('zh-CN');5.4 VPAID 支持
对于需要复杂交互、可测量性的 VPAID 广告,可通过 IMA 设置开启 VPAID 模式:
google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED);VpaidMode支持DISABLED(关闭)、ENABLED(开启)、INSECURE(允许不安全协议)等取值,具体行为以 IMA SDK 文档为准。
六、广告 UI 设计原则:与主播放器完全解耦
6.1 三条设计要点
README 对广告 UI 提出了明确的设计约束,这也是本插件架构上最值得借鉴的部分:
- AD UI 完全独立于 xgplayer:广告播控 UI 不直接修改主播放器源码,而是通过继承内置 UI 插件的功能独立实现,并复写需要修改的状态/事件;
- 内置 AdUIManager 统一管理:由
AdUIManager监听广告播放状态,响应广告 UI 的展示与隐藏; - 主播放器 UI 只提供可复写能力:xgplayer 的 UI 插件为广告场景做了微调,但内部不对广告状态做特殊编码,只暴露可继承、可覆写的能力。
这样设计的收益在于:未集成广告插件时,对主包体积的影响被降到最低;集成时又不必侵入播放器核心代码。
6.2 AdUIManager 的"装饰器"替换机制
AdUIManager(见 adUIManager.js)维护了一张"装饰对照表",把主播放器的内置 UI 插件映射为广告专用版本:
| 主播放器插件 | 广告装饰插件 | 说明 |
|---|---|---|
| PlayIcon(播放按钮) | AdPlayIcon(adPlay) | 监听AD_PAUSE/AD_PLAY切换动画,点击时调用adPlugin.play()/pause() |
| TimeIcon(时间显示) | AdTimeIcon(adTime) | 展示广告的currentTime/duration |
| Progress(进度条) | AdProgress(adProgress) | 展示广告进度,且强制关闭拖拽 seek(见后文) |
| VolumeIcon(音量) | null | 广告期间不展示 |
| CssFullscreenIcon / FullscreenIcon(全屏) | null | 广告期间不展示 |
这些装饰插件(adPlay、adTime、adProgress,分别见 adPlay.js、adTime.js、adProgress.js)通过覆写父类的duration、currentTimegetter 和listenEvents实现数据源切换——同一套 UI 逻辑,数据源从正片切换到广告。例如AdProgress.afterCreate会强制写入isCloseClickSeek: true、isDraggingSeek: true、closeMoveSeek: true,从配置层面杜绝用户在广告期间拖动进度条。
showAdUI/hideAdUI的核心技巧是DocumentFragment 占位替换:广告播放时,把正片 UI 插件节点整体移入 fragment,用广告装饰插件顶替其 DOM 位置;广告结束后再逆操作还原(见 adUIManager.js)。之所以不用简单的 CSS 隐藏,是因为那会导致插件 DOM 顺序错乱、影响样式选择器命中。同时通过播放器根节点的状态类xgplayer-ad-start、xgplayer-ad-show-ui(见 adStateClass.js)驱动容器显隐,start插件在广告期间被单独隐藏以避免与广告播控冲突。
6.3 广告状态、事件、方法的实现汇总
状态:adPlugin.paused、adPlugin.currentTime、adPlugin.duration(见 4.3 节)。
事件:通过player.on([ADEvents.AD_PLAY, ADEvents.AD_PAUSE], ...)或adPlugin.on(...)监听(见 4.3 节示例)。
方法:adPlugin.play()、adPlugin.pause(),以及requestAds()、playAds()、skip()等(见 4.4 节)。
七、广告生命周期与自动播放策略(源码级原理)
理解广告请求到播放的完整时序,有助于排查"广告不播/正片被卡"类问题。把 plugin.js 与 imaAdManager.js 串起来,典型生命周期如下:
- 初始化:
beforePlayerInit创建AdUIManager与ImaAdManager;ImaAdManager.init()依次完成 SDK 加载、locale 配置、媒体事件挂载、AdDisplayContainer与AdsLoader创建、广告请求初始化; - 请求广告:若配置了
adTagUrl/adsResponse/adsRequest三者之一,先执行自动播放检测_checkAutoplaySupport(),再调用requestAds();检测结果通过setAdWillAutoPlay/setAdWillPlayMuted传给 SDK; - 等待广告就绪:
AdsLoader加载完成触发ADS_MANAGER_LOADED,_onAdsManagerLoaded创建AdsManager并挂载全部广告事件监听(包括LOADED、STARTED、PAUSED、RESUMED、COMPLETE、ALL_ADS_COMPLETED、CONTENT_PAUSE_REQUESTED、CONTENT_RESUME_REQUESTED、SKIPPED、AD_PROGRESS、FIRST_QUARTILE、MIDPOINT、THIRD_QUARTILE、CLICK等 20 余种,见 imaAdManager.js),随后发布IMA_AD_MANAGER_READY; - 启播广告:若自动播放被允许(
autoplayAllowed),立即playAds()(内部执行displayContainer.initialize()、adsManager.init(w, h, viewMode)、adsManager.start());若不允许,则通过player.useHooks('play', cb)挂载钩子,等用户手动触发正片播放时再启播广告,并最终发布IMA_READY_TO_PLAY放行播放器初始化; - 广告播放与事件转发:
onAdEvent把 IMA 事件翻译为插件级事件——STARTED/RESUMED→AD_START/AD_PLAY,PAUSED→AD_PAUSE,AD_PROGRESS→ 同步currentTime/duration并触发AD_TIME_UPDATE,COMPLETE→AD_COMPLETE(携带hasNextInPod表示广告串内是否还有下一条),ALL_ADS_COMPLETED→AD_ALL_COMPLETED; - 内容暂停/恢复:
CONTENT_PAUSE_REQUESTED时置isLinearAdRunning = true并暂停正片、给播放器根节点添加xgplayer-ads-playing类;CONTENT_RESUME_REQUESTED时复位标志并恢复正片播放(见 imaAdManager.js 与_resumeContent); - 错误处理:
_handleAdError会销毁 adsManager、解除内容阻塞、发布IMA_AD_ERROR/AD_ERROR,并尝试恢复正片,保证广告故障不影响主内容。
7.1 自动播放检测的兜底策略
_checkAutoplaySupport()(见 imaAdManager.js)使用can-autoplay库检测当前环境的自动播放能力:考虑 Safari 等平台自动启播耗时较长,将检测超时兜底值设为 800ms;对 Tizen、WebOS 等已知支持自动播放的 TV 平台直接跳过检测。检测结果有三种组合——autoplayAllowed(可自动播放)、autoplayRequiresMuted(仅可静音自动播放,广告以静音方式启播)、两者皆否(等待用户手势)。
7.2 内容播放阻塞机制
广告准备与播放期间,正片不应抢占播放。AdsPlugin._blockContentPlay()监听播放器的play事件,一旦shouldBlockVideoContent为真就立即player.pause()阻止正片启播(见 plugin.js)。该标志在 baseAdManager.js 中定义为isLinearAdRunning || _shouldBlockVideoContent,覆盖"广告准备期 + 线性广告播放期"两个阶段;同时针对 Tizen/WebOS 这类广告与正片共用 video 元素的 TV 环境,该 getter 会直接返回false,避免误阻塞。
八、总结与延伸阅读
xgplayer-ads 用一个插件完成了广告接入的"最后一公里":配置上,adType+ima+controls三个维度覆盖了 SDK 选择、请求参数与 UI 策略;能力上,广告事件与正片事件完全隔离,UI 通过装饰器替换机制与主播放器解耦;健壮性上,SDK 加载失败、广告错误、自动播放受限均有兜底路径,正片播放不受广告故障牵连。
如需进一步深入,建议按以下路径阅读仓库源码:
- 插件入口与生命周期:packages/xgplayer-ads/src/plugin.js
- IMA 对接与事件翻译:packages/xgplayer-ads/src/imaAdManager.js
- 广告管理器公共基类:packages/xgplayer-ads/src/baseAdManager.js
- UI 装饰替换机制:packages/xgplayer-ads/src/ui/adUIManager.js
- 事件常量全集:packages/xgplayer-ads/src/events.js
- 广告装饰插件示例:adPlay.js、adProgress.js、adTime.js
- 音视频
- 前端
【免费下载链接】xgplayer
A HTML5 video player with a parser that saves traffic
相关推荐
ExoPlayer IMA 扩展模块指南:基于 Interactive Media Ads SDK 的广告插入实战
ExoPlayer IMA 扩展模块指南:基于 Interactive Media Ads SDK 的广告插入实战 导读 本文介绍 ExoPlayer 仓库中的
音视频移动开发interactive_media_ads 接入实战:在 Flutter 中集成 IMA SDK 播放 VAST 视频广告
interactive_media_ads 接入实战:在 Flutter 中集成 IMA SDK 播放 VAST 视频广告 interactive_media_
跨平台移动开发UI组件开发工具ExoPlayer IMA 模块接入指南:基于 IMA SDK 的客户端与服务端广告插入
ExoPlayer IMA 模块接入指南:基于 IMA SDK 的客户端与服务端广告插入 本文以 ExoPlayer 仓库中的 extensions/ima 模
音视频移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考