You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Shaka Player与IMA Ads SDK切换播放不同广告的问题

问题描述

搭建了一个包含两个按钮和一个<video>元素的页面,使用Shaka Player播放自适应DASH文件,结合Google IMA Ads SDK实现广告与视频的联动播放。预期效果为:点击按钮1时先播放前置广告1,随后播放视频1;点击按钮2时先播放前置广告2,随后播放视频2。但实际测试发现,点击任意按钮后广告仅能播放一次,切换按钮后广告不再播放。

现有代码

HTML代码

<html>
<head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-type"/>
    <title id="sample_app_page_title">Ad Ping</title>
    <!--for UI builds: -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/shaka-player/4.3.0/shaka-player.ui.min.js"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/shaka-player/4.3.0/controls.min.css" rel="stylesheet">
    <!-- IMA HTML5 SDK (for serving Client Side ads): -->
    <script src="https://imasdk.googleapis.com/js/sdkloader/ima3.js" type="text/javascript"></script>
</head>
<body>
<button onclick="playAd1()">Play Video with Ad1</button>
<button onclick="playAd2()">Play Video with Ad2</button>
<div>
  <div data-shaka-player-cast-receiver-id="8D8C71A7" data-shaka-player-container>
    <video data-shaka-player controls autoplay id="video" style="width:400px; height:200px" />
  </div>
</div>
</body>
</html>

JavaScript代码

const adUrl1 = "https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpreonly&ciu_szs=300x250%2C728x90&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&correlator=";
const adUrl2 = "https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpremidpost&ciu_szs=300x250&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&cmsid=496&vid=short_onecue&correlator=";
const manifestUrl1 = "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd";
const manifestUrl2 = "https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd";

let player, ui, video, controls, adManager;

async function initApp() {
  shaka.polyfill.installAll();
  if (shaka.Player.isBrowserSupported()) {
    // When using the UI, the player is made automatically by the UI object.
    video = document.getElementById('video');
    ui = video['ui'];
    controls = ui.getControls();
    player = controls.getPlayer();

    // Listen for error events.
    player.addEventListener('error', onPlayerErrorEvent);
    // controls.addEventListener('error', onUIErrorEvent);
    adManager = player.getAdManager();

    // Attach player and ui to the window to make it easy to access in the JS console.
    window.player = player;
    window.ui = ui;
    window.video = video;
    window.controls = controls;
    window.adManager = adManager;
  } else {
    console.error("Browser not supported");
  }
}


function initializeAdManager() {
  const container = ui.getControls().getClientSideAdContainer();
  adManager.initClientSide(container, video);
}

function onError(error) {
  // Log the error.
  console.error('Error code', error.code, 'object', error);
}


async function playAd1() {
  console.log('playing Video with ads 1');
  initializeAdManager();
  fetchAd(adUrl1);
  await playVideo(manifestUrl1);
}

async function playAd2() {
  console.log('playing video with ads 2');
  initializeAdManager();
  fetchAd(adUrl2);
  await playVideo(manifestUrl2);
}

async function playVideo(url) {
  try {
    await player.load(url);
  } catch (e) {
    onError(e);
  }
}

function fetchAd(url) {
  const adRequest = new google.ima.AdsRequest();
  adRequest.adTagUrl = url;
  adManager.requestClientSideAds(adRequest);
}


function onPlayerErrorEvent(errorEvent) {
  // Extract the shaka.util.Error object from the event.
  onPlayerError(errorEvent.detail);
}

function onPlayerError(error) {
  // Handle player error
  console.error('Error code', error.code, 'object', error);
}

function onUIErrorEvent(errorEvent) {
  // Extract the shaka.util.Error object from the event.
  onPlayerError(errorEvent.detail);
}

document.addEventListener('shaka-ui-loaded', initApp);
解决方案

广告仅能播放一次的核心原因有两个:

  1. 重复初始化AdManager:每次点击按钮都调用initializeAdManager(),但Shaka的AdManager重复初始化会导致内部状态混乱。
  2. 未同步广告与视频的生命周期:当前直接在请求广告后立即加载视频,会打断广告流程;且切换时未清理旧广告状态,新广告请求无法正确绑定到新流程。

修改步骤:

  1. 全局初始化AdManager:仅在initApp()中初始化一次AdManager,避免重复初始化。
  2. 切换时清理广告状态:每次发起新广告请求前,调用adManager.destroyClientSide()清理旧实例,再重新初始化。
  3. 等待广告结束后加载视频:监听adBreakEnded事件,确保广告播放完成后再加载主视频,避免流程冲突。

修改后的关键代码:

调整initApp()提前初始化AdManager

async function initApp() {
  shaka.polyfill.installAll();
  if (shaka.Player.isBrowserSupported()) {
    video = document.getElementById('video');
    ui = video['ui'];
    controls = ui.getControls();
    player = controls.getPlayer();

    player.addEventListener('error', onPlayerErrorEvent);
    adManager = player.getAdManager();
    
    // 仅初始化一次AdManager
    const container = ui.getControls().getClientSideAdContainer();
    adManager.initClientSide(container, video);

    window.player = player;
    window.ui = ui;
    window.video = video;
    window.controls = controls;
    window.adManager = adManager;
  } else {
    console.error("Browser not supported");
  }
}

修改播放函数,同步广告与视频流程

async function playAd1() {
  console.log('playing Video with ads 1');
  // 清理旧广告状态
  adManager.destroyClientSide();
  // 重新初始化AdManager
  const container = ui.getControls().getClientSideAdContainer();
  adManager.initClientSide(container, video);
  
  // 等待广告播放完成
  await new Promise((resolve) => {
    const onAdBreakEnded = () => {
      adManager.removeEventListener('adBreakEnded', onAdBreakEnded);
      resolve();
    };
    adManager.addEventListener('adBreakEnded', onAdBreakEnded);
    
    fetchAd(adUrl1);
  });
  
  await playVideo(manifestUrl1);
}

async function playAd2() {
  console.log('playing video with ads 2');
  // 清理旧广告状态
  adManager.destroyClientSide();
  // 重新初始化AdManager
  const container = ui.getControls().getClientSideAdContainer();
  adManager.initClientSide(container, video);
  
  // 等待广告播放完成
  await new Promise((resolve) => {
    const onAdBreakEnded = () => {
      adManager.removeEventListener('adBreakEnded', onAdBreakEnded);
      resolve();
    };
    adManager.addEventListener('adBreakEnded', onAdBreakEnded);
    
    fetchAd(adUrl2);
  });
  
  await playVideo(manifestUrl2);
}

移除原initializeAdManager()函数

该函数的逻辑已整合到全局初始化和播放函数中,无需保留。

内容的提问来源于stack exchange,提问作者chochim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 19:20:46