as

Settings
Sign out
Notifications
Alexa
亚马逊应用商店
Ring
AWS
文档
Support
Contact Us
My Cases
新手入门
设计和开发
应用发布
参考
支持

Vega中的客户端广告插入

Vega中的客户端广告插入

在客户端广告插入 (CSAI) 中,视频播放器独立管理内容和广告播放。与服务器端广告插入 (SSAI) 不同,服务器将广告段拼接到内容清单中,CSAI保持内容流不变。客户端应用程序管理内容和广告流之间的切换。

CSAI支持动态广告插入,因为客户端使用视频广告服务模板 (VAST) 或视频多广告播放列表 (VMAP) 等协议在运行时从广告服务器获取广告。无需修改内容流,即可针对每个用户、每个会话或每个内容实时投放广告。这样无需对服务器端清单进行任何更改即可实现个性化广告体验。

CSAI为应用提供了管理广告时间、播放和过渡的完全控制权。该应用对广告生命周期拥有完全所有权:决定何时触发广告时段,控制广告播放器的初始化和播放行为,以及管理共享界面,以便在主要内容和广告之间进行精确的帧切换。

CSAI技术

Vega支持两种确定何时插入广告的方法:带外(标准)和带内(高级)。

标准CSAI:带外广告

在标准CSAI中,客户端应用依靠外部信息,而不是清单标签来确定何时插入广告。播放器接收未经修改的内容清单并单独处理广告逻辑。

广告触发方法包括:

  • VMAP: 播放器获取一个外部XML文件,该文件在特定时间戳(例如,在0秒、300秒)安排广告时段。
  • 硬编码逻辑: 应用程序根据播放器当前的时间位置以固定的间隔触发广告请求(例如,每20秒一次)。
  • 边车文件: 该应用从内容管理系统 (CMS) 下载单独的配置文件 (JSON/XML),该文件列出了特定内容的广告中断时间。
  • 广告SDK: SDK(如Google Interactive Media Ads)通过广告规则在内部管理时间,从而无需开发者管理特定的时间戳或清单标签。

在典型的带外实现中,广告标记是在用户界面层中定义的(例如,硬编码或从节目表中获取),位置侦听器直接触发插入。不会向服务层发送任何广告信息。

高级CSAI:带内广告

在高级CSAI中,播放器监控清单中是否有表明广告机会的直播中元数据标记。

常见的标记包括:

标记 协议
#EXT-X-CUE-OUT / #EXT-X-CUE-IN HTTP直播流媒体(HLS)
#EXT-X-DATERANGE HLS间隙
有线电信工程师协会 (SCTE) -35信号(转换成清单标签) HLS /HTTP上的动态自适应流媒体 (DASH)

当播放器遇到标记(例如#EXT-X-CUE-OUT)时,它会将其解释为广告利用机会,并在指定的持续时间内向广告服务器发起VAST或VMAP请求。在主要内容继续播放的同时,会同时提取和缓冲广告内容。仅当达到广告展示时间时,主要内容才会暂停。届时,预计该广告可以开始播放,以提供无缝体验。

在使用无头方法的带内实现中,用户界面可以通过sendMessage({type: 'adList', ads})将广告列表发送到服务层,以提供与清单解析所生成数据相同的数据。该服务将广告列表存储在PlayerSession上,并在getCurrentPlaybackPosition() 期间处理广告标记。当达到广告的预缓冲窗口时(startTime前5秒),该服务会向用户界面发送{type: 'adMarkers', ad}消息,然后触发广告插入。

播放同步策略

两种实现方法都支持两种策略,用于在过渡期间协调主播放器和广告播放器。

预缓冲广告(无缝)

广告播放器在过渡之前会预先初始化并缓冲。这提供了没有视觉干扰的无缝用户体验。

  1. 主要内容播放。
  2. 检测到广告标记。使用autoPlay=false初始化广告播放器。
  3. 广告播放器缓冲区。canplay事件触发 (JS) 或服务发送READY状态(无头)。
  4. 用户界面收到就绪信号。广告已准备好播放,等待预定的播放时间。
  5. 位置侦听器会检测到已达到广告开始时间。暂停主要内容。
  6. 主要内容已暂停。清除主展示位,设置广告展示位,播放广告。
  7. 广告结束。清理广告展示位,清理广告播放器,设置主展示位,恢复主要内容的播放。

特征:

  • 没有视觉干扰
  • 播放前对广告内容进行预缓冲
  • 最适合优质观看体验
  • autoPlay设置:false(手动控制)

即时自动播放广告(有间隔)

广告中断发生时,将按需初始化广告播放器。由于广告在播放时使用autoPlay=true加载,因此在广告播放器初始化期间可能会有短暂的过渡间隔。

  1. 主要内容播放。
  2. 检测到广告标记。暂停主要内容。
  3. 主要内容已暂停。清除主界面,使用autoPlay=true初始化广告播放器。
  4. 广告播放器已准备就绪。设置广告展示位,播放自动开始。
  5. 广告结束。清理广告展示位,清理广告播放器,设置主展示位,恢复主要内容的播放。

特征:

  • 过渡期间可能会出现短暂的黑屏或暂停画面
  • 按需加载广告
  • 更简单的实现
  • autoPlay设置:true(自动播放)

连播广告

当您的广告插播包含多个连续广告时,您可以根据设备功能采用三种方法:

  • 单一广告播放器(资源受限的设备): 一次只能创建一个广告播放器来播放单个广告。这可能无法提供无缝或无间隙的过渡,仅建议内存或解码器受限的设备使用。
  • 多个广告播放器(高性能设备): 使用两个广告播放器实例实现无缝的连播广告播放。提供连续广告之间的无缝过渡。需要足够的设备资源。
  • 主要内容播放器重建(替代方法): 存储主要内容的当前播放位置。在上次广告播放期间重建主播放器。这样可以在资源受限的设备上创建两个独立的广告播放器。

CSAI面临的挑战

在实施CSAI时,请考虑以下挑战:

  • 缓冲和延迟: 当按需加载广告(即时广告)时,暂停内容以加载广告可能会导致缓冲或延迟。为了解决这个问题,预缓冲的广告在实际展示时间之前缓冲广告内容,只在足够的缓冲时间后才暂停主要内容。
  • 设备兼容性: 由于硬件功能(内存、解码器、处理能力)不同,不同设备的性能不一致。
  • 单展示位渲染: 在渲染完主内容的最后一帧之后,广告必须从第一帧开始播放。在表面切换期间,不应出现丢帧或帧重叠的情况。
  • 表面清除时的黑屏闪烁问题: 在共享表面模式下,调用clearSurfaceHandle (JS) 或clearVideoView(无头)会清空显示缓冲区,导致在主内容最后一帧与广告第一帧之间出现明显的黑帧。建议使用双表面模式来规避。

表面模式

两种实现方法均支持两种表面模式,用于管理广告切换期间的视频输出。

单表面(共享)

主播放器和广告播放器共用同一个KeplerVideoSurfaceView。主播放器必须在广告播放器渲染表面前将其释放:

  • JS方法: 在主播放器上调用clearSurfaceHandle(surfaceHandle),然后在广告播放器上调用setSurfaceHandle(surfaceHandle)
  • 无头方法: 在主播放器会话中调用clearVideoView(sessionId),然后在广告播放器会话中调用setVideoView(surfaceHandle, sessionId)

双表面

每个播放器都有其专用的KeplerVideoSurfaceView。广告位通过zIndex定位,叠加在主内容区域之上。广告播放时,主播放器无需释放其表面。

表面视图布局 (JSX)

主内容区域使用zIndex: 1,广告位则使用zIndex: 2,从而使广告渲染在上方:

已复制到剪贴板。


{/* 主内容区域 */}
<View style={{ position: 'absolute', top: 0, bottom: 0, left: 0, right: 0, zIndex: 1 }}>
  <KeplerVideoSurfaceView
    scalingmode='fit'
    onSurfaceViewCreated={onSurfaceViewCreated}
    onSurfaceViewDestroyed={onSurfaceViewDestroyed}
  />
</View>

{/* 广告位 — 播放广告时叠加主内容 */}
<View style={{ position: 'absolute', top: 0, bottom: 0, left: 0, right: 0, zIndex: 2 }}>
  <KeplerVideoSurfaceView
    scalingmode='fit'
    onSurfaceViewCreated={onAdSurfaceViewCreated}
    onSurfaceViewDestroyed={onAdSurfaceViewDestroyed}
  />
</View>

无广告播放时,广告位呈透明色,会显示主内容。广告播放器渲染的帧会覆盖底下的主内容。

处理不同宽高比的广告(双表面)

当广告和主内容的宽高比相同(例如均为16:9)时,广告位会直接叠加主内容。无需额外处理。

当两者的宽高比不同时,由于尺寸不匹配,非活动表面会在活动表面后方透出来。为避免此问题,可使用屏幕外渲染,将非活动表面移到屏幕外:

已复制到剪贴板。


{/* 主内容区域:广告播放时移至屏幕外 */}
<View style={{
  position: 'absolute', top: 0, bottom: 0, right: 0,
  left: adSurfaceVisible ? -Dimensions.get('window').width * 1.2 : 0,
  zIndex: adSurfaceVisible ? -1 : 1
}}>
  <KeplerVideoSurfaceView scalingmode='fit' ... />
</View>

{/* 广告位:广告可见时在屏幕内移动 */}
<View style={{
  position: 'absolute', top: 0, bottom: 0, right: 0,
  left: adSurfaceVisible ? 0 : -Dimensions.get('window').width * 1.2,
  zIndex: adSurfaceVisible ? 2 : -1
}}>
  <KeplerVideoSurfaceView scalingmode='fit' ... />
</View>

要实现这一步,请先添加一个状态变量const [adSurfaceVisible, setAdSurfaceVisible] = useState(false),然后在广告开始时切换为true,在广告结束时切换为false

广告时长强制实施

如果广告的定义时长小于实际媒体时长,您可以通过位置或时间更新侦听器,在正确的时间强制结束广告:

已复制到剪贴板。


// JS方式:使用timeupdate事件
const updateAdTimer = () => {
  if (adVideoPlayer.current && currentAdRef.current?.duration > 0) {
    const currentTime = adVideoPlayer.current.currentTime || 0;
    const adDuration = currentAdRef.current.duration;

    if (currentTime >= adDuration) {
      onAdEnded(); // 达到定义时长后强制结束广告
    }
  }
};

在无头方式中,使用在广告播放器会话上注册的位置侦听器,将当前播放位置与预期广告时长进行比对。

选择您的实现方法

Vega为CSAI提供了两种实现方法。选择与您的应用架构相匹配的方法。

JS方法

JS方法使用直接的React Native组件集成。VideoPlayer实例在用户界面组件层中创建和管理。应用程序可以直接访问W3C媒体API,并通过直接方法调用来控制播放。

┌─────────────────────────────────────────────────────────┐
│              React原生组件层               │
│  ┌──────────────────┐        ┌──────────────────┐       │
│  │  主播放器     │        │   广告播放器      │       │
│  │  (VideoPlayer)   │        │   (VideoPlayer)  │       │
│  └──────────────────┘        └──────────────────┘       │
├─────────────────────────────────────────────────────────┤
│       KeplerVideoSurfaceView(共享或双表面)           │
└─────────────────────────────────────────────────────────┘

在以下情况下使用此方法:

  • 您的应用使用单线程React Native架构
  • 您想直接访问W3C媒体API
  • 您更偏好更简单的组件级播放器管理

程序包:@amazon-devices/react-native-w3cmedia

要实现这种方法,请参阅使用JS方法实现客户端广告插入

无头方法

无头方法使用客户端-服务器架构。用户界面层中的IPlayerClient实例使用JavaScript接口 (JSI) 通过KeplerPlayerClientTurboModule与在单独服务层中管理的VideoPlayer实例进行通信,用于高性能进程间通信 (IPC)。

┌─────────────────────────────────────────────────────────┐
│                    用户界面层 (PlayerUI)                  │
│  ┌──────────────────┐        ┌──────────────────┐       │
│  │  主播放器     │        │   广告播放器      │       │
│  │  客户端          │        │   客户端         │       │
│  │  (IPlayerClient) │        │   (IPlayerClient)│       │
│  └────────┬─────────┘        └─────────┬────────┘       │
│           │                            │                │
│           │      KeplerPlayerClientTurboModule          │
│           ▼                            ▼                │
├─────────────────────────────────────────────────────────┤
│                  IPC桥 (JSI)                       │
├─────────────────────────────────────────────────────────┤
│           │      KeplerPlayerServerTurboModule          │
│           ▼                            ▼                │
│  ┌──────────────────┐        ┌──────────────────┐       │
│  │  PlayerServer    │        │   PlayerServer   │       │
│  │  处理程序         │        │   处理程序        │       │
│  └────────┬─────────┘        └─────────┬────────┘       │
│           │                            │                │
│         服务层(PlayerService - 无头JS)     │
│  ┌──────────────────┐        ┌──────────────────┐       │
│  │  主播放器     │        │   广告播放器      │       │
│  │  会话         │        │   会话        │       │
│  │  (VideoPlayer)   │        │   (VideoPlayer)  │       │
│  └──────────────────┘        └──────────────────┘       │
├─────────────────────────────────────────────────────────┤
│       KeplerVideoSurfaceView(共享或双表面)           │
└─────────────────────────────────────────────────────────┘

在以下情况下使用此方法:

  • 您的应用使用具有独立用户界面和服务层的多线程架构
  • 您需要基于JSI的IPC的性能优势
  • 您想用PlayerSession进行基于会话的玩家管理

程序包: Vega播放器客户端Vega播放器服务器

要实现这种方法,请参阅使用无头方法实现客户端广告插入


Last updated: 2026年5月28日