Select platform: [HTML5](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/html5/get-started/pod-serving.md.txt "View this page for the HTML5 platform docs.") [Android](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/android/get-started/pod-serving.md.txt "View this page for the Android platform docs.") [iOS](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/ios/get-started/pod-serving.md.txt "View this page for the iOS platform docs.") [tvOS](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/tvos/get-started/pod-serving.md.txt "View this page for the tvOS platform docs.") [Cast](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/cast/get-started/pod-serving.md.txt "View this page for the Cast platform docs.") [Roku](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/roku/get-started/pod-serving.md.txt "View this page for the Roku platform docs.")

<br />

This guide demonstrates how to play a DAI Pod Serving stream for live or VOD
content, using the IMA DAI SDK for HTML5 with a video player that relies on hls.js
for playback. To view or follow a completed sample integration, with support for
both HLS.js and Safari Playback, see the
[HLS Pod Serving example](https://github.com/googleads/googleads-ima-html5-dai/tree/main/podserving/hls_js).
For DASH.js support, see the [DASH Pod Serving example](https://github.com/googleads/googleads-ima-html5-dai/tree/main/podserving/dash_js).
You can download these sample apps from the [HTML5 DAI GitHub release
page](https://github.com/googleads/googleads-ima-html5-dai/releases).


**Note:** The IMA DAI SDK for HTML5 supports both HLS and DASH streams, but playing each requires a different media player or extension like [HLS.js](https://github.com/video-dev/hls.js/) or [DASH.js](https://github.com/Dash-Industry-Forum/dash.js/), with varying implementations.


This guide explains how to configure HLS.js and IMA DAI SDK to play an HLS stream, with
important notes about implementation differences for DASH.


See the [IMA
DASH.js sample app](https://github.com/googleads/googleads-ima-html5-dai/tree/main/dash_js/simple) for a starting point to implement DASH DAI streams with IMA.

<br />

> [!IMPORTANT]
> **Key Point:** To use DAI Pod Serving, you must be working with a Pod Serving partner and must have an [Ad Manager 360
> Advanced](https://support.google.com/admanager/answer/3485387) account. If you have an Ad Manager account, contact your account manager for more details. For information about signing up for Ad Manager, visit the [Ad Manager Help
> Center](https://support.google.com/admanager/answer/7084151).


**Note:** For information on integrating with other platforms, or on using the IMA client-side SDKs, see [Interactive Media Ads SDKs](https://developers.google.com/interactive-media-ads).

<br />

## DAI Pod Serving overview

Implementing Pod Serving using the IMA DAI SDK involves two main components, which
are demonstrated in this guide:

- [`PodStreamRequest`](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/html5/reference/js/PodStreamRequest) /
  [`PodVodStreamRequest`](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/html5/reference/js/PodVodStreamRequest): An object that defines a stream request to
  Google's advertising servers. Requests specify a **Network Code** , and the
  `PodStreamRequest` also requires a **Custom Asset Key** , and an optional
  **API key**. Both include other optional parameters.

- [`StreamManager`](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/html5/reference/js/StreamManager): An object that handles communication between
  the video stream and the IMA DAI SDK, such as firing tracking pings and
  forwarding stream events to the publisher.

## Prerequisites

Before you begin, you need the following:

- Three empty files:

  - **dai.html**
  - **dai.css**
  - **dai.js**
- Python installed on your computer, or a web server or other hosted development
  environment to use for testing

## Configure a development environment

Since the SDK loads dependencies using the same protocol as the page from
which it's loaded, you need to use a web server to test your app. A quick
way to start a local development server is to use Python's built-in server.

1. Using a command line, from the directory that contains your `index.html`
   file run:

   ```bash
   python -m http.server 8000
   ```

   > [!NOTE]
   > **Note:** `http.server` is only available in Python 3.x.

2. In a web browser, go to `http://localhost:8000/`

   You can also use any other hosted development environment or web server, such
   as the [Apache HTTP Server](https://httpd.apache.org/).

## Create a video player

> [!NOTE]
> **Note:** This guide assumes that you're using the HLS.js library to playback an HLS stream. The IMA DAI SDK for HTML5 can also support DASH streams, with an appropriate library like Shaka player or DASH.js.

First, modify **dai.html** to create a HTML5 video element and a div to
use for Ad UI elements. Also add the necessary tags to load the **dai.css**
and **dai.js** files, as well as to import the `hls.js` video player.

Then, modify **dai.css** to specify the size and position of the page elements.
Finally, in **dai.js** , define variables to hold the stream request information
and an `initPlayer()` function to run when the page loads.

The stream request constants are as follows:

- `BACKUP_STREAM`: A URL for a backup stream to play in case the ads process
  encounters a fatal error.

- `STREAM_URL`: **Only used for livestreams** . The video stream URL provided by
  your manifest manipulator or third-party partner using Pod Serving. It should
  require you to insert the stream ID provided by the IMA DAI SDK, before you
  make a request. In this case, the stream URL includes a placeholder,
  `[[STREAMID]]`, which is replaced with the stream ID before making a request.

- `NETWORK_CODE`: The network code for your Ad Manager 360 account.

- `CUSTOM_ASSET_KEY`: **Only used for livestreams**. The custom asset key that
  identifies your Pod Serving event in Ad Manager 360. This can be created by
  your manifest manipulator or third-party Pod Serving partner.

- `API_KEY`: **Only used for livestreams**. An optional API key that can be
  required to retrieve a stream ID from the IMA DAI SDK.

#### dai.html

```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

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

#adUi {
  cursor: pointer;
}
```

#### dai.js

```javascript
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');
}
```

## Load the IMA DAI SDK

Next, add the DAI framework using a script tag in **dai.html** , before the tag
for **dai.js**.

> [!NOTE]
> **Note:** To receive verbose console logs, you can replace `ima3_dai.js` with `ima3_dai_debug.js`. However, `ima3_dai_debug.js` shouldn't be used in production apps.

#### dai.html

```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>
...
```

## Initialize the StreamManager and make a live or VOD stream request

### Livestream Pod Serving

In order to request a set of ads, create an `ima.dai.api.StreamManager`, which
is responsible for requesting and managing DAI streams. The constructor takes
a video element and the resulting instance takes an ad UI element to handle ad
interactions.

Then, define a function to request the Pod Serving livestream. This function
first creates a `PodStreamRequest`, configures it with the streamRequest
parameters provided in step 2, and then calls `streamManager.requestStream()`
with that request object.

> [!IMPORTANT]
> **Key Point:** Neither stream in this example is protected, so `apiKey` is not used. If your stream requires an API Key, see [Authenticate DAI video stream
> requests](https://support.google.com/admanager/answer/6381445).

#### dai.js

```javascript
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 Serving

In order to request a set of ads, create an `ima.dai.api.StreamManager`, which
is responsible for requesting and managing DAI streams. The constructor takes
a video element and the resulting instance takes an ad UI element to handle ad
interactions.

Then, define a function to request the Pod Serving VOD stream. This function
first creates a `PodVodStreamRequest`, configures it with the streamRequest
parameters provided in step 2, and then calls `streamManager.requestStream()`
with that request object.

#### dai.js

```javascript
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);
}
```

## Handle stream events

### Livestream Pod Serving

Next, implement event listeners for major video events. This example handles
the `STREAM_INITIALIZED`, `ERROR`, `AD_BREAK_STARTED` and `AD_BREAK_ENDED`
events by calling an `onStreamEvent()` function. This function handles stream
loading and errors, as well as disabling the player controls while an ad is
playing, which is required by the SDK. When the stream is loaded, the video
player loads and plays the provided URL using a `loadStream()` function.

#### dai.js

```javascript
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 Serving

Next, implement event listeners for major video events. This example handles
the `STREAM_INITIALIZED`, `LOADED`, `ERROR`, `AD_BREAK_STARTED` and
`AD_BREAK_ENDED` events by calling an `onStreamEvent()` function. This
function handles stream loading and errors, as well as disabling the player
controls while an ad is playing, which is required by the SDK.

In addition, VOD Pod Serving streams require calling
`StreamManager.loadStreamMetadata()` in response to the to the
`STREAM_INITIALIZED` event. You also need to request a stream URL from your,
video technology partner (VTP). Once the `loadStreamMetadata()` call succeeds
it triggers a `LOADED` event, where you should call a `loadStream()` function
with your stream URL to load and play the stream.

```javascript
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);
}
```

## Handle stream metadata

In this step you implement event listeners for metadata to notify the SDK when
ad events occur. Listening for in-stream metadata events can vary, depending on
the stream format (HLS or DASH), the stream type (Live or VOD stream), your
player type, and the type of DAI backend being used. See our [Timed
Metadata](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/html5/timed-metadata#ima_dash_pod_serving)
guide for more information.

### HLS stream format (Live and VOD streams, HLS.js player)

If you are using an [HLS.js](https://github.com/video-dev/hls.js) player, listen to
the HLS.js `FRAG_PARSING_METADATA` event to get ID3 metadata and pass it to the
SDK with [`StreamManager.processMetadata()`](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/html5/reference/js/StreamManager#processMetadata).

To automatically play the video after everything is loaded and ready, listen to
the HLS.js `MANIFEST_PARSED` event to trigger playback.

```javascript
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 streams format, Live and VOD stream type)

If you are using a [DASH.js](https://github.com/Dash-Industry-Forum/dash.js) player,
you have to use different strings to listen for ID3 metadata for Live or VOD
streams:

- Livestreams: `'https://developer.apple.com/streaming/emsg-id3'`
- VOD streams: `'urn:google:dai:2018'`

Pass the ID3 metadata to the SDK with [`StreamManager.processMetadata()`](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/html5/reference/js/StreamManager#processMetadata).

To automatically show the video controls after everything is loaded and ready,
listen to the DASH.js `MANIFEST_LOADED` event.

```javascript
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 with livestreams (DASH streams format)

If you are using [Shaka player](https://github.com/shaka-project/shaka-player) for
livestream playback, use the string `'emsg'` to listen for metadata events.
Then, use the event message data in your call to [`StreamManager.onTimedMetadata()`](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/html5/reference/js/StreamManager#onTimedMetadata).

```javascript
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 with VOD streams (DASH streams format)

If you are using [Shaka player](https://github.com/shaka-project/shaka-player) for
VOD stream playback, use the string `'timelineregionenter'` to listen for
metadata events. Then, use the event message data in your call to
[`StreamManager.processMetadata()`](https://developers.google.com/ad-manager/dynamic-ad-insertion/sdk/html5/reference/js/StreamManager#processMetadata)
with the string `'urn:google:dai:2018'`.

```javascript
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);
       }
}
```

## Handle player events

Add event listeners to the video element's `pause` and `start` events to allow
the user to resume playback when the SDK pauses during ad breaks.

```javascript
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';
}
}
```

## Clean up IMA DAI assets

When you have successfully finished requesting and displaying ads in a Pod
Serving stream with IMA DAI SDK, we suggest you clean up any resources after the
Pod Serving session is complete. Call `StreamManager.destroy()` to stop stream
playback, stop all ad tracking, and release all of the loaded stream assets.

To learn about more advanced SDK features, see the other guides or the
[samples on GitHub](https://github.com/googleads/googleads-ima-html5-dai).