Shaka Player 广告变现集成指南:MediaTailor、Interstitial 与 IMA SDK 实战
2026/9/16 19:50:49 网站建设 项目流程

Shaka Player 广告变现集成指南:MediaTailor、Interstitial 与 IMA SDK 实战

【免费下载链接】shaka-playerJavaScript player library / DASH & HLS client / MSE-EME player项目地址: https://gitcode.com/GitHub_Trending/sh/shaka-player

Shaka Player 通过统一的shaka.extern.IAdManager接口为应用提供了完整的广告投放 API,覆盖 Interactive Media Ads 为主干,结合仓库中 lib/ads/ 的源码实现,系统讲解如何在你的播放器应用中接入这几种主流广告方案、如何监听广告事件以及如何定制广告管理器。读完本文,你将能独立完成广告容器初始化、各类广告流的请求与播放、SCTE-35 场景下的自定义插播,以及 IMA 深度集成的配置。

1. 广告能力总览与核心 API 入口

Shaka Player 的广告能力全部收敛在 Player 实例的 Ad Manager 对象上,接口定义见 externs/shaka/ads.js 中的shaka.extern.IAdManager。该接口暴露的能力包括:

  • IMA Client SiderequestClientSideAds()initClientSide()updateClientSideAdsRenderingSettings()
  • IMA DAI Server SiderequestServerSideStream()replaceServerSideAdTagParameters()
  • MediaTailorrequestMediaTailorStream()addMediaTailorTrackingUrl()
  • InterstitialsaddCustomInterstitial()addAdUrlInterstitial()(后者支持 VAST/VMAP);
  • 通用能力setContainers()getCuePoints()getStats()getCurrentAd()setLocale()等。

其中 MediaTailor 与 IMA 的 Server Side 请求方法都返回一个Promise<string>,即"已插入广告的流清单 URI",拿到后直接交给player.load(uri)即可播放,这一模式在 lib/ads/media_tailor_ad_manager.js 的streamRequest()中通过内部streamPromise_实现。

无论使用哪套广告技术,接入的第一步都是获取 Ad Manager 并设置广告容器:

const adManager = player.getAdManager(); // If you're using a non-UI build, this is the div you'll need to create // for your layout. const csContainer = controls.getClientSideAdContainer(); const ssContainer = controls.getServerSideAdContainer(); // Note: If you are using Shaka UI this call is not necessary. adManager.setContainers(csContainer, ssContainer);

说明:如果你没有使用 Shaka 的 UI 库,需要自己在<video>元素之上创建一个<div>作为广告容器;若使用 Shaka UI,控件会提供现成的客户端/服务端广告容器,上述setContainers调用可省略。

2. AWS Elemental MediaTailor 集成

Shaka Player 提供了与 AWS Elemental MediaTailor 的深度集成,支持Client SideServer Side以及overlays(浮层)三种广告插入方式,全部通过adManager.requestMediaTailorStream()一个入口完成。

先初始化服务端逻辑(与第 1 节的容器设置代码一致,此处不再重复):

const video = document.getElementById('video'); const ui = video['ui']; const controls = video.ui.getControls(); const player = controls.getPlayer(); const adManager = player.getAdManager(); // If you're using a non-UI build, this is the div you'll need to create // for your layout. const csContainer = controls.getClientSideAdContainer(); const ssContainer = controls.getServerSideAdContainer(); // Note: If you are using Shaka UI this call is not necessary. adManager.setContainers(csContainer, ssContainer);

2.1 请求 Client Side 流

Client Side 模式下,MediaTailor 会返回一个包含广告决策信息的清单(通常是 DASH MPD),广告插入发生在客户端。requestMediaTailorStream()的第二个参数adsParams可携带自定义的广告参数,例如这里的assetidpodduration

const mediaTailorUrl = 'https://d305rncpy6ne2q.cloudfront.net/v1/session/94063eadf7d8c56e9e2edd84fdf897826a70d0df/SFP-MediaTailor-VOD-HLS-DASH/out/v1/b94f3611978f419985a18335bac9d9cb/ddb73bf548a44551a0059c346226445a/eaa5485198bf497284559efb8172425e/index.mpd'; const mediaTailorAdsParams = { adsParams: { assetid: 'test2', podduration: '15', }, }; const uri = await adManager.requestMediaTailorStream(mediaTailorUrl, mediaTailorAdsParams); player.load(uri);

2.2 请求 Server Side 流

Server Side 模式下,广告在服务端被拼接进流中,客户端拿到的就是一个干净的 HLS 主清单。此时adsParams可以不传:

const mediaTailorUrl = 'https://ad391cc0d55b44c6a86d232548adc225.mediatailor.us-east-1.amazonaws.com/v1/session/d02fedbbc5a68596164208dd24e9b48aa60dadc7/singssai/master.m3u8'; const uri = await adManager.requestMediaTailorStream(mediaTailorUrl); player.load(uri);

注意:MediaTailor 的overlays(浮层广告)插入方式与 Server Side 完全一致,即同样通过requestMediaTailorStream()请求并加载返回的流。

从源码结构看,MediaTailor 的会话信息请求、广告断点(AdBreak)解析、追踪 URL 上报等逻辑都封装在 lib/ads/media_tailor_ad_manager.js 的shaka.ads.MediaTailorAdManager中,并通过 lib/ads/media_tailor_ad.js 与 lib/ads/svta_ad.js 等适配层把第三方广告对象统一映射为 Shaka 自己的shaka.extern.IAd接口。

3. Interstitial(插播)集成

Shaka Player 原生支持多种类型的 Interstitial,覆盖了从标准到自定义的完整场景:

  • HLS Interstitials(RFC 8216bis 中的EXT-X-DATERANGE插播机制)
  • DASH Media Presentation InsertionInsertPresentation/ReplacePresentation,即 MPD 级插播)
  • Custom Interstitials(应用自定义的插播条目)
  • VAST / VMAP(通过 URL 动态解析)

3.1 HLS Interstitials:零配置原生支持

HLS Interstitials 完全不需要应用介入,Shaka Player 在解析播放清单时自动识别并调度。有以下几个值得了解的细节(对应实现均可在 lib/ads/interstitial_ad_manager.js 中找到):

  • 惰性资产列表解析(Lazy asset list resolution)。当插播引用了一个资产列表(X-ASSET-LIST)时,Shaka 不会在清单解析时立即拉取,而是等到播放即将到达该插播点时再解析。这避免了集中爆发式的广告决策请求,也让广告决策更贴近真实播放时刻。提前多久解析由ads.interstitialPreloadAheadTime配置控制(默认 10 秒,见 lib/util/player_configuration.js),也可以通过下述的"逐插播偏移"机制单独覆盖。源码中resolutionAheadTime_()shouldResolveAssetListNow_()正是这套"前瞻窗口 + 延迟解析"逻辑的落地实现。
  • Preload Date Range(RFC 8216bis Appendix F)。一个CLASS="com.apple.hls.preload"EXT-X-DATERANGE通过X-TARGET-ID指向另一个 Date Range,用来告诉播放器该插播的资源可以提前多久解析。Shaka 通过addPreloadMetadata()将其换算成"逐插播的解析偏移"(resolutionTimeOffset),具体计算为Math.max(0, 目标startTime - 预加载Date Range的startTime)
  • Date Range 更新合并。后续出现的、共享同一个IDEXT-X-DATERANGE标签会被合并处理:后出现的标签可以补充之前没有的属性,最典型的是X-PLAYOUT-LIMIT——在直播(SGAI,Server-Guided Ad Insertion)流中常用它来提前截断插播、提前回到主内容。按照规范要求,已经赋过值的属性不会被更改。
  • Seek 进入直播广告插播。当开启ads.allowStartInMiddleOfInterstitial(默认值为true,见 lib/util/player_configuration.js)时,如果用户 seek 回到一个已经解析过的直播插播区间,Shaka 会以反映新播放头位置的_HLS_start_offset重新请求其资产列表,保证接续播出的正确性。

3.2 DASH Media Presentation Insertion:同样零配置

DASH 的 MPD 级插播(InsertPresentation/ReplacePresentation,即 MPD alternate)同样开箱即用,无需任何应用干预。实现会遵循earliestResolutionTimeOffset属性:它规定了可以在插播展示时间之前多少秒提前解析备选 MPD。当该属性缺失时,回退使用ads.interstitialPreloadAheadTime配置值。

3.3 Custom Interstitials:完全可控的插播编排

自定义插播对象是shaka.extern.AdInterstitial(完整字段定义见 externs/shaka/ads.js)。它接受一个可选的resolutionTimeOffset属性(单位:秒),表示在startTime之前多久可以开始解析并预加载该插播的资源——它正好对应 DASH 的earliestResolutionTimeOffset属性与 HLS 的 preload Date Range 机制。当该属性为undefined0时,使用ads.interstitialPreloadAheadTime配置值。下面示例为了简洁都省略了该字段。

基础示例——注册一个从第 10 秒开始、可跳过、带全套追踪事件的自定义插播:

const video = document.getElementById('video'); const ui = video['ui']; const controls = video.ui.getControls(); const player = controls.getPlayer(); const adManager = player.getAdManager(); // If you're using a non-UI build, this is the div you'll need to create // for your layout. const csContainer = controls.getClientSideAdContainer(); const ssContainer = controls.getServerSideAdContainer(); // Note: If you are using Shaka UI this call is not necessary. adManager.setContainers(csContainer, ssContainer); adManager.addCustomInterstitial({ id: null, groupId: null, startTime: 10, endTime: null, uri: 'YOUR_URL', mimeType: null, isSkippable: true, skipOffset: 10, skipFor: null, canJump: false, resumeOffset: null, playoutLimit: null, once: true, pre: false, post: false, timelineRange: false, loop: false, overlay: null, displayOnBackground: false, currentVideo: null, background: null, clickThroughUrl: null, tracking: { impression: ['YOUR_TRACKING_URL'], clickTracking: ['YOUR_TRACKING_URL'], start: ['YOUR_TRACKING_URL'], firstQuartile: ['YOUR_TRACKING_URL'], midpoint: ['YOUR_TRACKING_URL'], thirdQuartile: ['YOUR_TRACKING_URL'], complete: ['YOUR_TRACKING_URL'], skip: ['YOUR_TRACKING_URL'], error: ['YOUR_TRACKING_URL'], resume: ['YOUR_TRACKING_URL'], pause: ['YOUR_TRACKING_URL'], mute: ['YOUR_TRACKING_URL'], unmute: ['YOUR_TRACKING_URL'], }, });

关键字段语义(与shaka.extern.AdInterstitial定义一致):

字段含义
startTime/endTime插播在主时间轴上的起止时间(秒);endTimenull表示不确定
uri插播内容地址,可以是 Shaka 支持的任何类型(MSE 或 src= 均可)
mimeType已知时的 MIME 类型
isSkippable/skipOffset/skipFor是否可跳过、跳过按钮何时出现、跳过按钮显示多久
canJump是否允许跨过该插播(jumpable)
resumeOffset插播结束后主内容恢复播放的位置,表示相对插播计划展示时间的偏移;直播广告替换场景必须为null
playoutLimit整个插播的最长播出时长限制
once/pre/post是否只播一次、是否在片头/片尾触发
timelineRange在时间轴 UI 上显示为"单点"还是"区间"
overlay/displayOnBackground/currentVideo/background浮层插播的几何布局与背景样式,见 3.4 节
clickThroughUrl点击广告时的跳转地址
tracking各类追踪事件的 URL 列表(类型见shaka.extern.AdTrackingEvent

与 SCTE-35 结合:可以监听timelineregionadded事件,把清单中的 SCTE-35 时间线区间直接转成自定义插播。此时timelineRange在直播场景应设为true(广告在时间轴上表现为一个区间),canJump设为trueonce设为false(同一标记在直播中可能循环触发):

const video = document.getElementById('video'); const ui = video['ui']; const controls = video.ui.getControls(); const player = controls.getPlayer(); const adManager = player.getAdManager(); // If you're using a non-UI build, this is the div you'll need to create // for your layout. const csContainer = controls.getClientSideAdContainer(); const ssContainer = controls.getServerSideAdContainer(); // Note: If you are using Shaka UI this call is not necessary. adManager.setContainers(csContainer, ssContainer); player.addEventListener('timelineregionadded', (e) => { const event = e.detail; if (event.schemeIdUri != 'urn:scte:scte35:2014:xml+bin') { return; } adManager.addCustomInterstitial({ id: event.id, groupId: null, startTime: event.startTime, endTime: event.endTime, uri: 'YOUR_URL', mimeType: null, isSkippable: false, skipOffset: null, skipFor: null, canJump: true, resumeOffset: player.isLive() ? null : 0, playoutLimit: null, once: false, pre: false, post: false, timelineRange: player.isLive(), // If true, the ad will appear as a range on the timeline. loop: false, overlay: null, displayOnBackground: false, currentVideo: null, background: null, clickThroughUrl: null, tracking: null, }); });

3.4 Custom Overlay Interstitials:浮层与多视频元素布局

自定义浮层插播支持图片、视频(渐进式或清单流)以及网页浮层。浮层几何通过overlay/currentVideo两个shaka.extern.AdPositionInfo描述(viewporttopLeftsize均为像素),并由displayOnBackground决定是否把主视频缩小到背景。这类能力仅在启用多视频元素播放插播时可用。

右上象限浮层示例(主视频不动,广告出现在右上 1/4 区域):

const video = document.getElementById('video'); const ui = video['ui']; const controls = video.ui.getControls(); const player = controls.getPlayer(); const adManager = player.getAdManager(); // If you're using a non-UI build, this is the div you'll need to create // for your layout. const csContainer = controls.getClientSideAdContainer(); const ssContainer = controls.getServerSideAdContainer(); // Note: If you are using Shaka UI this call is not necessary. adManager.setContainers(csContainer, ssContainer); adManager.addCustomInterstitial({ id: null, groupId: null, startTime: 10, endTime: null, uri: 'YOUR_URL', mimeType: null, isSkippable: true, skipOffset: 10, skipFor: null, canJump: false, resumeOffset: null, playoutLimit: null, once: true, pre: false, post: false, timelineRange: false, loop: false, overlay: { // Show interstitial in upper right quadrant viewport: { x: 1920, // Pixels y: 1080, // Pixels }, topLeft: { x: 960, // Pixels y: 0, // Pixels }, size: { x: 960, // Pixels y: 540, // Pixels }, }, displayOnBackground: false, currentVideo: null, background: null, clickThroughUrl: null, tracking: null, });

L-Shape 布局示例(广告占满全屏背景,主视频缩小到左上 1/4):

const video = document.getElementById('video'); const ui = video['ui']; const controls = video.ui.getControls(); const player = controls.getPlayer(); const adManager = player.getAdManager(); // If you're using a non-UI build, this is the div you'll need to create // for your layout. const csContainer = controls.getClientSideAdContainer(); const ssContainer = controls.getServerSideAdContainer(); // Note: If you are using Shaka UI this call is not necessary. adManager.setContainers(csContainer, ssContainer); adManager.addCustomInterstitial({ id: null, groupId: null, startTime: 10, endTime: null, uri: 'YOUR_URL', mimeType: null, isSkippable: true, skipOffset: 10, skipFor: null, canJump: false, resumeOffset: null, playoutLimit: null, once: true, pre: false, post: false, timelineRange: false, loop: false, overlay: { viewport: { x: 1920, y: 1080, }, topLeft: { x: 0, y: 0, }, size: { x: 1920, y: 1080, }, }, displayOnBackground: true, currentVideo: { viewport: { x: 1920, y: 1080, }, topLeft: { x: 0, y: 0, }, size: { x: 960, y: 540, }, }, background: null, clickThroughUrl: null, tracking: null, });

双框(double box)布局示例(主视频缩小居中偏下,广告占据右上方,可自定义背景 CSS):

const video = document.getElementById('video'); const ui = video['ui']; const controls = video.ui.getControls(); const player = controls.getPlayer(); const adManager = player.getAdManager(); // If you're using a non-UI build, this is the div you'll need to create // for your layout. const csContainer = controls.getClientSideAdContainer(); const ssContainer = controls.getServerSideAdContainer(); // Note: If you are using Shaka UI this call is not necessary. adManager.setContainers(csContainer, ssContainer); adManager.addCustomInterstitial({ id: null, groupId: null, startTime: 10, endTime: null, uri: 'YOUR_URL', mimeType: null, isSkippable: true, skipOffset: 10, skipFor: null, canJump: false, resumeOffset: null, playoutLimit: null, once: true, pre: false, post: false, timelineRange: false, loop: false, overlay: { viewport: { x: 1920, // Pixels y: 1080, // Pixels }, topLeft: { x: 960, // Pixels y: 270, // Pixels }, size: { x: 960, // Pixels y: 540, // Pixels }, }, displayOnBackground: true, currentVideo: { viewport: { x: 1920, // Pixels y: 1080, // Pixels }, topLeft: { x: 160, // Pixels y: 360, // Pixels }, size: { x: 640, // Pixels y: 360, // Pixels }, }, background: 'content-box radial-gradient(crimson, skyblue)', clickThroughUrl: null, tracking: null, });

background字段接受任意合法的 CSSbackground属性值;loop字段表示浮层插播是否循环播放,仅对浮层有效,且仅在启用多视频元素播放插播时受支持。

3.5 VAST / VMAP:通过 URL 动态解析插播

addAdUrlInterstitial(url)会请求给定的 VAST/VMAP 地址,解析响应并把其中的广告转换为插播条目后排队播放。从 lib/ads/interstitial_ad_manager.js 的addAdUrlInterstitial()实现看,它使用shaka.net.NetworkingEngine.AdvancedRequestType.INTERSTITIAL_AD_URL发起请求,并用shaka.util.TXml.parseXml(response.data, 'VAST,vmap:VMAP')解析 XML;若解析失败会抛出shaka.util.Error.Code.VAST_INVALID_XML

const video = document.getElementById('video'); const ui = video['ui']; const controls = video.ui.getControls(); const player = controls.getPlayer(); const adManager = player.getAdManager(); // If you're using a non-UI build, this is the div you'll need to create // for your layout. const csContainer = controls.getClientSideAdContainer(); const ssContainer = controls.getServerSideAdContainer(); // Note: If you are using Shaka UI this call is not necessary. adManager.setContainers(csContainer, ssContainer); const url = 'https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dlinear&correlator='; adManager.addAdUrlInterstitial(url);

4. IMA SDK 集成(客户端与服务端)

Shaka Player 与 Google Interactive Media Ads(IMA)SDK 深度集成,同时支持 Client Side 与 Server Side(DAI)广告插入,两者都通过shaka.extern.IAdManager暴露。

4.1 引入 SDK 脚本

首先需要在页面中引入 Shaka UI 编译产物与 IMA 相关 SDK(Client Side 用ima3.js,Server Side 用ima3_dai.js):

<!DOCTYPE html> <html> <head> <!-- Shaka Player ui compiled library: --> <script src="dist/shaka-player.ui.js"></script> <!-- Shaka Player ui compiled library default CSS: --> <link rel="stylesheet" type="text/css" href="dist/controls.css"> <!-- IMA HTML5 SDK (for serving Client Side ads): --> <script type="text/javascript" src="https://imasdk.googleapis.com/js/sdkloader/ima3.js"></script> <!-- IMA DAI SDK (for serving Server Side ads): --> <script type="text/javascript" src="https://imasdk.googleapis.com/js/sdkloader/ima3_dai.js"></script> <!-- Your application source: --> <script src="myapp.js"></script> </head> </html>

获取 Ad Manager:

const adManager = player.getAdManager();

4.2 客户端广告插入(Client Side Ads)

客户端模式要求你先有广告标签(ad tag)URI。同样先初始化客户端逻辑并设置容器(代码同第 1 节)。初始化完成后,可以在播放过程中的任意时刻请求广告:

const adsRequest = new google.ima.AdsRequest(); // Your ad tag url should go here. We are using a sample ad tag from the // IMA HTML5 SDK implementation guide for this tutorial. adsRequest.adTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dlinear&correlator='; adManager.requestClientSideAds(adsRequest, /** adsRenderingSettings= */ null);

adsRequest的类型是google.ima.AdsRequest,其完整字段可查阅 IMA HTML5 SDK 的 AdsRequest 参考文档。

控制广告渲染initClientSide(container, video, adsRenderingSettings)可用自定义渲染设置初始化客户端广告,之后可用updateClientSideAdsRenderingSettings()动态更新渲染设置:

const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adManager.initClientSide(container, video, adsRenderingSettings); // Updates the ads rendering settings. adManager.updateClientSideAdsRenderingSettings(adsRenderingSettings);

google.ima.AdsRenderingSettings的字段含义见 IMA 官方 AdsRenderingSettings 参考文档。

4.3 服务端广告插入(Server Side Ads / DAI)

服务端模式需要 Google Ad Manager 账号,并将流托管在 Google Ad Manager 的服务器上(官方入口见 https://admanager.google.com/)。初始化逻辑同样与第 1 节一致。

VOD 流requestServerSideStream()接收google.ima.dai.api.VODStreamRequest,返回一个 Promise,resolve 出带广告的清单 URI,可直接交给player.load()

const streamRequest = new google.ima.dai.api.VODStreamRequest(); // Your stream information will go here. We are using IMA's sample stream info // in this tutorial. streamRequest.contentSourceId = '2528370'; streamRequest.videoId = 'tears-of-steel'; const uri = await adManager.requestServerSideStream(streamRequest); player.load(uri);

shaka.extern.IAdManager.requestServerSideStream()的返回类型、参数结构见 externs/shaka/ads.js 以及 IMA DAI 的 VODStreamRequest 参考文档。

LIVE 流:使用LiveStreamRequest,核心字段是assetKey

const streamRequest = new google.ima.dai.api.LiveStreamRequest(); // Your stream information will go here. We are using IMA's sample stream info // in this tutorial. streamRequest.assetKey = 'sN_IYUG8STe1ZzhIIE_ksA'; const uri = await adManager.requestServerSideStream(streamRequest); player.load(uri);

LiveStreamRequest的完整字段见 IMA DAI 的 LiveStreamRequest 参考文档。如果使用 Shaka 的 UI 库,广告 UI(跳过按钮、倒计时等)会自动接入,无需额外编码。

5. 监听广告事件

Shaka 会把 IMA 的 Client Side 与 Server Side 事件统一转换成自己的广告事件与对象,便于应用用同一套代码处理所有广告类型。完整事件列表可查阅shaka.ads.AdManagerAdBreakReadyEvent等事件文档。

注册一个最简单的监听器,广告开始时在控制台打日志:

adManager.addEventListener(shaka.ads.Utils.AD_STARTED, () => { console.log('An ad has started'); });

事件名常量定义在 lib/ads/ad_utils.js 的shaka.ads.Utils中,常用事件包括:

  • ADS_LOADED'ads-loaded'):一组广告已加载
  • AD_STARTED'ad-started'):广告即将开始
  • AD_PLAYING'ad-playing'):广告真正进入播放状态(与AD_STARTED表示"意图开始"不同,该事件保证广告媒体元素已进入playing状态、播放头真实推进)
  • AD_FIRST_QUARTILE/AD_MIDPOINT/AD_THIRD_QUARTILE:播放进度 25% / 50% / 75%
  • AD_COMPLETE'ad-complete')、AD_STOPPEDAD_SKIPPEDAD_ERROR
  • AD_VOLUME_CHANGEDAD_MUTEDAD_PAUSEDAD_RESUMED
  • AD_CLICKEDAD_PROGRESS
  • CUEPOINTS_CHANGED'ad-cue-points-changed'):插播点集合变化
  • AD_BREAK_READY'ad-break-ready')、AD_BREAK_STARTEDAD_BREAK_ENDED:广告插播(ad break)生命周期
  • IMA_AD_MANAGER_LOADED'ima-ad-manager-loaded')、IMA_STREAM_MANAGER_LOADED'ima-stream-manager-loaded'):底层 IMA 对象就绪(见第 6 节)

每个 Shaka 广告事件都附带原始 SDK 事件与广告对象(如果可用)。大多数应用用不到,但需要时可以通过事件对象取到:

// Note that unlike in the previous example, we are capturing the AD_STARTED // event object here (the "e" parameter of the lambda function) so we can access // its properties. adManager.addEventListener(shaka.ads.Utils.AD_STARTED, (e) => { const sdkAdObject = e['sdkAdObject']; const originalEvent = e['originalEvent']; });

6. 面向 IMA 高级用户的底层对象访问

如果你已有一套 IMA 集成要接入 Shaka,或者需要使用 Shaka API 未暴露的更细粒度 SDK 能力,可以监听底层 IMA 对象加载完成的事件,直接拿到 IMA 的google.ima.AdsManager(Client Side)或google.ima.dai.api.StreamManager(Server Side):

adManager.addEventListener(shaka.ads.Utils.IMA_AD_MANAGER_LOADED, (e) => { const imaAdManager = e['imaAdManager']; }); adManager.addEventListener(shaka.ads.Utils.IMA_STREAM_MANAGER_LOADED, (e) => { const imaStreamManager = e['imaStreamManager']; });

拿到对象后的用法可参考 IMA 官方的 AdsManager 参考文档 与 StreamManager 参考文档。

7. 无 Cookie 环境下的受限广告投放(Limited Ads)

服务端 IMA SDK 允许在用户未同意或拒绝 Cookie 授权时投放受限广告(limited ads)。做法是:按照 IMA 的 limited ads 指南,通过StreamRequest.adTagParameters设置ltd参数;同时为了让清单与分片请求不带 Cookie,配置合适的requestFilter。需要注意request.withCredentials默认为false,因此只有在你代码的其他部分显式开启过它时才需要做如下覆盖:

player.getNetworkingEngine().registerRequestFilter(function(type, request, context) { if (type == shaka.net.NetworkingEngine.RequestType.MANIFEST || type == shaka.net.NetworkingEngine.RequestType.SEGMENT) { request.withCredentials = false; } });

requestFilter是 ShakaNetworkingEngine的标准扩展点(见 lib/net/networking_engine.js),应用可按RequestType对清单、分片等请求做任意改写。

8. 自定义 Ad Manager 实现

Shaka 的广告架构支持完全自定义的 Ad Manager。所有广告管理器都要实现shaka.extern.IAdManager接口(定义见 externs/shaka/ads.js)。要让 Player 使用自定义实现,需要在实例化 Player之前注册工厂函数:

// myapp.CustomAdManager is a placeholder name for your ad manager implementation. shaka.Player.setAdManagerFactory(() => new myapp.CustomAdManager());

在 lib/player.js 中可以看到shaka.Player.setAdManagerFactory(factory)是静态入口;而默认实现shaka.ads.AdManager的注册同样通过该工厂机制完成(见 lib/ads/ad_manager.js),Player 内部通过getAdManager()(lib/player.js)按需实例化。这意味着你可以在不修改 Player 核心逻辑的前提下,将广告决策、插播调度甚至第三方广告 SDK 封装成自己的实现无缝接入。

9. 常见问题与最佳实践小结

  • 容器初始化:非 UI 构建必须自建<div>广告容器并调用adManager.setContainers(csContainer, ssContainer);UI 构建下该调用可省略。
  • Server Side 流程一致性:无论 MediaTailor 还是 IMA DAI,requestServerSideStream()/requestMediaTailorStream()都返回"已插入广告"的清单 URI,之后统一走player.load(uri)
  • 解析时机控制:HLS / DASH 插播默认在播放接近插播点时(ads.interstitialPreloadAheadTime,默认 10 秒)才解析资源,既避免请求突增,也保证决策贴近播放;可用逐插播的resolutionTimeOffset(自定义插播)、earliestResolutionTimeOffset(DASH)或 preload Date Range(HLS)单独微调。
  • SCTE-35 联动:监听timelineregionadded并筛选urn:scte:scte35:2014:xml+bin的区间,可把信令插播无缝接入统一的自定义插播队列。
  • 事件统一化:监听 Shaka 自有事件(如shaka.ads.Utils.AD_STARTED)即可同时覆盖 Client Side 与 Server Side;需要底层 SDK 对象时再用IMA_AD_MANAGER_LOADED/IMA_STREAM_MANAGER_LOADED获取。
  • 扩展性:通过shaka.Player.setAdManagerFactory()可在播放器创建前注入自定义 Ad Manager 实现,满足深度定制需求。

围绕广告模块的更多实现细节可继续研读仓库中的 lib/ads/(各类 AdManager 与适配器)、externs/shaka/ads.js(全部广告相关类型定义)以及 test/ads/ 下的单元与集成测试(如interstitial_ad_manager_unit.jsvast_interstitial_parser_unit.js),这些测试用例对理解各广告技术的边界行为很有帮助。

【免费下载链接】shaka-playerJavaScript player library / DASH & HLS client / MSE-EME player项目地址: https://gitcode.com/GitHub_Trending/sh/shaka-player

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询