הגדרה באמצעות שירות Pod DAI

בחירת פלטפורמה: HTML5 Android iOS tvOS Cast Roku

במדריך הזה נסביר איך להפעיל שידור של הצגת מודעות ברצף ב-DAI לתוכן בשידור חי או לתוכן וידאו על פי דרישה (VOD), באמצעות IMA DAI SDK ל-HTML5 עם נגן וידאו שמסתמך על hls.js להפעלה. כדי לראות או לעקוב אחרי שילוב לדוגמה שהושלם, עם תמיכה גם ב-HLS.js וגם בהפעלה ב-Safari, אפשר לעיין בדוגמה להצגת Pod ב-HLS. לתמיכה ב-DASH.js, אפשר לעיין בדוגמה להצגת מודעות Pod ב-DASH. אפשר להוריד את האפליקציות לדוגמה האלה מדף הגרסאות של HTML5 DAI ב-GitHub.

סקירה כללית על הצגת מודעות ברצף ב-DAI

הטמעת Pod Serving באמצעות IMA DAI SDK כוללת שני רכיבים עיקריים, שמוצגים במדריך הזה:

  • ‫PodStreamRequest / PodVodStreamRequest: אובייקט שמגדיר בקשה לשידור לשרתי הפרסום של Google. בבקשות מציינים קוד רשת, וב-PodStreamRequest צריך גם מפתח נכס בהתאמה אישית ומפתח API אופציונלי. שניהם כוללים פרמטרים אופציונליים אחרים.

  • StreamManager: אובייקט שמטפל בתקשורת בין שידור הווידאו לבין IMA DAI SDK, למשל הפעלת פינגים למעקב והעברת אירועים בשידור לבעל האפליקציה.

דרישות מוקדמות

לפני שמתחילים, צריך:

  • שלושה קבצים ריקים:

    • dai.html
    • dai.css
    • dai.js
  • ‫Python מותקן במחשב, או שרת אינטרנט או סביבת פיתוח אחרת שמתארחת ומשמשת לבדיקה

הגדרת סביבת פיתוח

מכיוון שה-SDK טוען יחסי תלות באמצעות אותו פרוטוקול כמו הדף שממנו הוא נטען, צריך להשתמש בשרת אינטרנט כדי לבדוק את האפליקציה. דרך מהירה להפעיל שרת פיתוח מקומי היא להשתמש בשרת המובנה של Python.

  1. בשורת הפקודה, מריצים את הפקודה הבאה מהספרייה שמכילה את הקובץ index.html:

    python -m http.server 8000
  2. בדפדפן אינטרנט, עוברים אל http://localhost:8000/

    אפשר גם להשתמש בכל סביבת פיתוח או שרת אינטרנט אחרים, כמו Apache HTTP Server.

יצירת נגן וידאו

קודם כל, משנים את dai.html כדי ליצור רכיב וידאו ב-HTML5 ו-div לשימוש ברכיבים בממשק המשתמש של המודעה. צריך גם להוסיף את התגים הנדרשים כדי לטעון את הקבצים dai.css ו-dai.js, ולייבא את נגן הווידאו של hls.js.

לאחר מכן, משנים את dai.css כדי לציין את הגודל והמיקום של רכיבי הדף. לבסוף, בקובץ dai.js, מגדירים משתנים שיכילו את פרטי הבקשה לשידור ופונקציה initPlayer() שתפעל כשטעינת הדף תושלם.

הקבועים של בקשת השידור הם:

  • ‫BACKUP_STREAM: כתובת URL של שידור גיבוי שיופעל אם תהיה שגיאה חמורה בתהליך של המודעות.

  • ‫STREAM_URL: משמש רק לשידורים חיים. כתובת ה-URL של שידור הווידאו שסופקה על ידי הכלי להטמעת מודעות ותגי מעקב בקובצי פלייליסט או על ידי שותף צד שלישי שמשתמש בהצגת מודעות ברצף. לפני ששולחים בקשה, צריך להזין את מזהה השידור שסופק על ידי IMA DAI SDK. במקרה הזה, כתובת ה-URL של השידור כוללת פלייסהולדר, [[STREAMID]], שמוחלף במזהה השידור לפני שליחת הבקשה.

  • ‫NETWORK_CODE: קוד הרשת של החשבון שלכם ב-Ad Manager 360.

  • ‫CUSTOM_ASSET_KEY: משמש רק לשידורים חיים. מפתח הנכס בהתאמה אישית שמזהה את האירוע של הצגת המודעות ברצף ב-Ad Manager 360. אפשר ליצור את הפרמטר הזה באמצעות הכלי להטמעת מודעות ותגי מעקב בקובצי פלייליסט או באמצעות שותף צד שלישי להצגת מודעות ברצף.

  • ‫API_KEY: משמש רק לשידורים חיים. מפתח API אופציונלי שיכול להיות נדרש כדי לאחזר מזהה שידור מ-IMA DAI SDK.

dai.html

<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
  <script src="dai.js"></script>
  <link rel="stylesheet" href="dai.css" type="text/css">
</head>
<body onLoad="initPlayer()">
  <h2>IMA DAI SDK Demo (HLS.JS)</h2>
    <video id="video"></video>
    <div id="adUi"></div>
</body>
</html>

dai.css

#video,
#adUi {
  width: 640px;
  height: 360px;
  position: absolute;
  top: 35px;
  left: 0;
}

#adUi {
  cursor: pointer;
}

dai.js

var BACKUP_STREAM =
    'https://storage.googleapis.com/interactive-media-ads/media/bbb.m3u8'

// Stream Config.
const STREAM_URL = "";
const NETWORK_CODE = "";
const CUSTOM_ASSET_KEY = "";
const API_KEY = "";

var hls = new Hls(); // hls.js video player
var videoElement;
var adUiElement;

function initPlayer() {
  videoElement = document.getElementById('video');
  adUiElement = document.getElementById('adUi');
}

טעינת IMA DAI SDK

בשלב הבא, מוסיפים את מסגרת ה-DAI באמצעות תג סקריפט ב-dai.html, לפני התג של dai.js.

dai.html

<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
  <script type="text/javascript" src="//imasdk.googleapis.com/js/sdkloader/ima3_dai.js"></script>
  <script src="dai.js"></script>
  <link rel="stylesheet" href="dai.css" type="text/css">
</head>
...

מאתחלים את StreamManager ושולחים בקשה לשידור חי או ל-VOD

הצגת מודעות בשידור חי

כדי לבקש קבוצה של מודעות, צריך ליצור ima.dai.api.StreamManager, שאחראי לבקשה ולניהול של שידורי DAI. ה-constructor מקבל רכיב וידאו, והמופע שנוצר מקבל רכיב בממשק המשתמש של מודעה כדי לטפל באינטראקציות עם המודעה.

לאחר מכן, מגדירים פונקציה כדי לבקש את השידור החי של הצגת המודעות ברצף. הפונקציה הזו יוצרת קודם PodStreamRequest, מגדירה אותו עם הפרמטרים של streamRequest שצוינו בשלב 2, ואז קוראת ל-streamManager.requestStream() עם אובייקט הבקשה הזה.

dai.js

function initPlayer() {
  videoElement = document.getElementById('video');
  adUiElement = document.getElementById('adUi');
  streamManager = new google.ima.dai.api.StreamManager(videoElement, adUiElement)

  requestLivePodStream(NETWORK_CODE, CUSTOM_ASSET_KEY, API_KEY);
}

function requestLivePodStream(networkCode, customAssetKey, apiKey) {
  // clear HLS.js instance, if in use
  if (hls) {
    hls.destroy();
  }

  // Generate a Pod Serving live Stream Request
  const streamRequest = new google.ima.dai.api.PodStreamRequest();
  streamRequest.networkCode = networkCode;
  streamRequest.customAssetKey = customAssetKey;
  streamRequest.apiKey = apiKey;
  streamRequest.format = 'hls';
  streamManager.requestStream(streamRequest);
}

הצגת מודעות ב-VOD Pod

כדי לבקש קבוצה של מודעות, צריך ליצור ima.dai.api.StreamManager, שאחראי לבקשה ולניהול של שידורי DAI. ה-constructor מקבל רכיב וידאו, והמופע שנוצר מקבל רכיב בממשק המשתמש של מודעה כדי לטפל באינטראקציות עם המודעה.

לאחר מכן, מגדירים פונקציה כדי לבקש את שידור ה-VOD של Pod Serving. הפונקציה הזו יוצרת קודם PodVodStreamRequest, מגדירה אותו עם הפרמטרים של streamRequest שצוינו בשלב 2, ואז קוראת ל-streamManager.requestStream() עם אובייקט הבקשה הזה.

dai.js

function initPlayer() {
  videoElement = document.getElementById('video');
  adUiElement = document.getElementById('adUi');
  streamManager = new google.ima.dai.api.StreamManager(videoElement, adUiElement)

  requestVodPodStream(NETWORK_CODE);
}

function requestVodPodStream(networkCode) {
  // clear HLS.js instance, if in use
  if (hls) {
    hls.destroy();
  }

  // Generate a Pod Serving VOD Stream Request
  const streamRequest = new google.ima.dai.api.PodVodStreamRequest();
  streamRequest.networkCode = networkCode;
  streamRequest.format = 'hls';
  streamManager.requestStream(streamRequest);
}

טיפול באירועים בסטרימינג

הצגת מודעות בשידור חי

בשלב הבא, מטמיעים רכיבי event listener לאירועי וידאו מרכזיים. בדוגמה הזו, האירועים STREAM_INITIALIZED,‏ ERROR,‏ AD_BREAK_STARTED ו-AD_BREAK_ENDED מטופלים באמצעות קריאה לפונקציה onStreamEvent(). הפונקציה הזו מטפלת בטעינת השידור ובשגיאות, וגם משביתה את כפתורי הנגן בזמן שהמודעה מוצגת, כפי שנדרש על ידי ה-SDK. כשהשידור נטען, נגן הווידאו טוען ומפעיל את כתובת ה-URL שסופקה באמצעות פונקציית loadStream().

dai.js

var isAdBreak;

function initPlayer() {
  videoElement = document.getElementById('video');
  adUiElement = document.getElementById('adUi');
  streamManager = new google.ima.dai.api.StreamManager(videoElement, adUiElement);
  
  streamManager.addEventListener(
    [google.ima.dai.api.StreamEvent.Type.STREAM_INITIALIZED,
    google.ima.dai.api.StreamEvent.Type.ERROR,
    google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED,
    google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED],
    onStreamEvent,
    false);
...
function onStreamEvent(e) {
  switch (e.type) {
    case google.ima.dai.api.StreamEvent.Type.STREAM_INITIALIZED:
      console.log('Stream initialized');
      loadStream(e.getStreamData().streamId);
      break;
    case google.ima.dai.api.StreamEvent.Type.ERROR:
      console.log('Error loading stream, playing backup stream.' + e);
      loadStream('');
      break;
    case google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED:
      console.log('Ad Break Started');
      isAdBreak = true;
      videoElement.controls = false;
      adUiElement.style.display = 'block';
      break;
    case google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED:
      console.log('Ad Break Ended');
      isAdBreak = false;
      videoElement.controls = true;
      adUiElement.style.display = 'none';
      break;
    default:
      break;
  }
}

function loadStream(streamID) {
  var url;
  if(streamID) {
    url = STREAM_URL.replace('[[STREAMID]]', streamID);
  } else {
    console.log('Stream Initialization Failed');
    url = BACKUP_STREAM;
  }
  console.log('Loading:' + url);
  hls.loadSource(url);
  hls.attachMedia(videoElement);
}

הצגת מודעות ב-VOD Pod

בשלב הבא, מטמיעים רכיבי event listener לאירועי וידאו מרכזיים. בדוגמה הזו, האירועים STREAM_INITIALIZED,‏ LOADED,‏ ERROR,‏ AD_BREAK_STARTED ו-AD_BREAK_ENDED מטופלים באמצעות קריאה לפונקציית onStreamEvent(). הפונקציה הזו מטפלת בטעינת הסטרימינג ובשגיאות, וגם משביתה את כפתורי הנגן בזמן שהמודעה מוצגת, כפי שנדרש על ידי ה-SDK.

בנוסף, בסטרימינג של VOD Pod Serving צריך להפעיל את StreamManager.loadStreamMetadata() בתגובה לאירוע STREAM_INITIALIZED. אתם צריכים גם לבקש כתובת URL של שידור מהשותף הטכנולוגי שלכם לוידאו (VTP). אחרי שהקריאה ל-loadStreamMetadata() מצליחה, היא מפעילה אירוע LOADED, שבו צריך להפעיל פונקציית loadStream() עם כתובת ה-URL של השידור כדי לטעון ולהפעיל את השידור.

var isAdBreak;

function initPlayer() {
  videoElement = document.getElementById('video');
  adUiElement = document.getElementById('adUi');
  streamManager = new google.ima.dai.api.StreamManager(videoElement, adUiElement);
  
  streamManager.addEventListener(
    [google.ima.dai.api.StreamEvent.Type.STREAM_INITIALIZED,
    google.ima.dai.api.StreamEvent.Type.ERROR,
    google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED,
    google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED],
    onStreamEvent,
    false);
...
function onStreamEvent(e) {
  switch (e.type) {
    case google.ima.dai.api.StreamEvent.Type.STREAM_INITIALIZED:
      const streamId = e.getStreamData().streamId;
      // 'vtpInterface' is a place holder for your own video technology
      //  partner (VTP) API calls.
      vtpInterface.requestStreamURL({
        'streamId': streamId,
      })
      .then( (vtpStreamUrl) => {
        streamUrl = vtpStreamUrl;
        streamManager.loadStreamMetadata();
      }, (error) => {
        // Handle the error.
      });
      break;
    case google.ima.dai.api.StreamEvent.Type.LOADED:
      loadStream(streamUrl);
      break;
    case google.ima.dai.api.StreamEvent.Type.ERROR:
      console.log('Error loading stream, playing backup stream.' + e);
      loadStream();
      break;
    case google.ima.dai.api.StreamEvent.Type.AD_BREAK_STARTED:
      console.log('Ad Break Started');
      isAdBreak = true;
      videoElement.controls = false;
      adUiElement.style.display = 'block';
      break;
    case google.ima.dai.api.StreamEvent.Type.AD_BREAK_ENDED:
      console.log('Ad Break Ended');
      isAdBreak = false;
      videoElement.controls = true;
      adUiElement.style.display = 'none';
      break;
    default:
      break;
  }
}

function loadStream(url) {
  if(url) {
    console.log('Loading:' + url);
    hls.loadSource(url);
  } else {
    console.log('Stream Initialization Failed');
    hls.loadSource(BACKUP_STREAM);
  }
  hls.attachMedia(videoElement);
}

טיפול במטא-נתונים של שידור

בשלב הזה מטמיעים רכיבי event listener למטא-נתונים כדי להודיע ל-SDK כשמתרחשים אירועי מודעות. האזנה לאירועי מטא-נתונים של מודעות וידאו In-stream יכולה להשתנות בהתאם לפורמט הסטרימינג (HLS או DASH), לסוג הסטרימינג (שידור חי או וידאו על פי דרישה), לסוג הנגן ולסוג הקצה העורפי של DAI שבו משתמשים. מידע נוסף זמין במדריך בנושא מטא-נתונים מתוזמנים.

פורמט סטרימינג HLS (שידורים חיים ושידורי VOD, נגן HLS.js)

אם אתם משתמשים בנגן HLS.js, האזינו לאירוע FRAG_PARSING_METADATA של HLS.js כדי לקבל מטא-נתונים של ID3 והעבירו אותם ל-SDK באמצעות StreamManager.processMetadata().

כדי להפעיל את הסרטון באופן אוטומטי אחרי שהכול נטען ומוכן, צריך להאזין לאירוע MANIFEST_PARSED של HLS.js כדי להפעיל את ההפעלה.

function loadStream(streamID) {
  hls.loadSource(url);
  hls.attachMedia(videoElement);
  
  // Timed metadata is passed HLS stream events to the streamManager.
  hls.on(Hls.Events.FRAG_PARSING_METADATA, parseID3Events);
  hls.on(Hls.Events.MANIFEST_PARSED, startPlayback);
}

function parseID3Events(event, data) {
  if (streamManager && data) {
    // For each ID3 tag in the metadata, pass in the type - ID3, the
    // tag data (a byte array), and the presentation timestamp (PTS).
    data.samples.forEach((sample) => {
      streamManager.processMetadata('ID3', sample.data, sample.pts);
    });
  }
}

function startPlayback() {
  console.log('Video Play');
  videoElement.play();
}

‫DASH.js (פורמט של שידורי DASH, סוג שידור: בשידור חי ו-VOD)

אם אתם משתמשים בנגן DASH.js, אתם צריכים להשתמש במחרוזות שונות כדי להאזין למטא-נתונים של ID3 בשידורים חיים או בשידורי VOD:

  • שידורים חיים: 'https://developer.apple.com/streaming/emsg-id3'
  • שידורי VOD: ‏'urn:google:dai:2018'

מעבירים את המטא-נתונים של ID3 ל-SDK באמצעות StreamManager.processMetadata().

כדי להציג באופן אוטומטי את פקדי הסרטון אחרי שהכול נטען ומוכן, צריך להאזין לאירוע MANIFEST_LOADED של DASH.js.

const googleLiveSchema = 'https://developer.apple.com/streaming/emsg-id3';
const googleVodSchema = 'urn:google:dai:2018';
dashPlayer.on(googleLiveSchema, processMetadata);
dashPlayer.on(googleVodSchema, processMetadata);
dashPlayer.on(dashjs.MediaPlayer.events.MANIFEST_LOADED, loadlistener);

function processMetadata(metadataEvent) {
  const messageData = metadataEvent.event.messageData;
  const timestamp = metadataEvent.event.calculatedPresentationTime;

  // Use StreamManager.processMetadata() if your video player provides raw
  // ID3 tags, as with dash.js.
  streamManager.processMetadata('ID3', messageData, timestamp);
}

function loadlistener() {
  showControls();

  // This listener must be removed, otherwise it triggers as addional
  // manifests are loaded. The manifest is loaded once for the content,
  // but additional manifests are loaded for upcoming ad breaks.
  dashPlayer.off(dashjs.MediaPlayer.events.MANIFEST_LOADED, loadlistener);
}

Shaka Player עם שידורים חיים (פורמט סטרימינג DASH)

אם אתם משתמשים ב-Shaka player להפעלת שידור חי, השתמשו במחרוזת 'emsg' כדי להאזין לאירועי מטא-נתונים. לאחר מכן, משתמשים בנתוני הודעת האירוע בקריאה ל-StreamManager.onTimedMetadata().

shakaPlayer.addEventListener('emsg', (event) => onEmsgEvent(event));

function onEmsgEvent(metadataEvent) {
  // Use StreamManager.onTimedMetadata() if your video player provides
  // processed metadata, as with Shaka player livestreams.
  streamManager.onTimedMetadata({'TXXX': metadataEvent.detail.messageData});
}

‫Shaka Player עם שידורי VOD (פורמט שידורי DASH)

אם אתם משתמשים ב-Shaka player להפעלת סטרימינג של VOD, השתמשו במחרוזת 'timelineregionenter' כדי להאזין לאירועי מטא-נתונים. לאחר מכן, משתמשים בנתוני הודעת האירוע בקריאה ל-StreamManager.processMetadata() עם המחרוזת 'urn:google:dai:2018'.

shakaPlayer.addEventListener('timelineregionenter', (event) => onTimelineEvent(event));

function onTimelineEvent(metadataEvent) {
  const detail = metadataEvent.detail;
  if ( detail.eventElement.attributes &&
       detail.eventElement.attributes['messageData'] &&
       detail.eventElement.attributes['messageData'].value ) {
        const mediaId = detail.eventElement.attributes['messageData'].value;
        const pts = detail.startTime;
        // Use StreamManager.processMetadata() if your video player provides raw
        // ID3 tags, as with Shaka player VOD streams.
        streamManager.processMetadata('urn:google:dai:2018', mediaId, pts);
       }
}

טיפול בפעולות במשחק

מוסיפים רכיבי event listener לאירועים pause ו-start של רכיב הסרטון כדי לאפשר למשתמש להמשיך את ההפעלה כשה-SDK מושהה במהלך הפסקות למודעות.

function loadStream(streamUrl) {
  ...
  
  videoElement.addEventListener('pause', onStreamPause);
  videoElement.addEventListener('play', onStreamPlay);
}

function onStreamPause() {
  console.log('paused');
  if (isAdBreak) {
    videoElement.controls = true;
    adUiElement.style.display = 'none';
  }
}

function onStreamPlay() {
  console.log('played');
  if (isAdBreak) {
    videoElement.controls = false;
    adUiElement.style.display = 'block';
  }
}

ניקוי נכסי IMA DAI

אחרי שסיימתם לבקש ולהציג מודעות לרשת המדיה בסטרימינג של הצגת מודעות ברצף באמצעות IMA DAI SDK, מומלץ לנקות את כל המשאבים אחרי שהסשן של הצגת המודעות ברצף מסתיים. מפעילים את StreamManager.destroy() כדי להפסיק את הפעלת השידור, להפסיק את כל המעקב אחרי ביצועי המודעות ולשחרר את כל נכסי השידור שנטענו.

כדי לקבל מידע על תכונות מתקדמות יותר של ה-SDK, אפשר לעיין במדריכים האחרים או בדוגמאות ב-GitHub.