Insert ads with ad pod timing metadata

  • This guide explains how to insert ad breaks using the Ad pod Timing Metadata (ATM) method for precise timing and duration, including pre-roll ads.

  • To implement this, you need a livestream event set up for Pod serving redirect Dynamic Ad Insertion (DAI) type in Google Ad Manager.

  • Retrieving ad pod timing metadata requires generating and using a signed HMAC token with the ATM API.

  • Pre-roll and mid-roll ad timing metadata are requested through specific ATM API calls, requiring different parameters.

  • Ad segments are stitched into the content manifest by replacing ad markers with discontinuity tags and inserting ad and/or slate segment URLs based on the ATM API response.

This guide describes how to insert ad breaks using the Ad pod Timing Metadata (ATM) method to fetch the precise timing and duration for ad breaks, including pre-roll ads.

To insert pre-roll ads and manage the return to content after mid-roll ad breaks, we recommend calling the following APIs:

Optionally, for high concurrency livestream events, we recommend calling Early Ad Break Notification (EABN) API to schedule ad decisions before the ad break begins.

Prerequisites

To get started, you need a livestream event set up for the Pod serving redirect Dynamic Ad Insertion (DAI) type. Choose one of the following methods:

Retrieve the content stream

When a user selects a livestream event, the client app makes a stream request to Google Ad Manager. In the stream response, the app extracts the Google DAI session ID and metadata to include in the stream manifest request.

The following example passes a Google DAI session ID to a manifest manipulator:

https://MANIFEST_MANIPULATOR_URL/manifest.m3u8?DAI_stream_ID=SESSION_ID&network_code=NETWORK_CODE&DAI_custom_asset_key=CUSTOM_ASSET_KEY

When processing the video content playback request, store the Google DAI session ID and CUSTOM_ASSET_KEY from the request to prepare for ad stitching.

Retrieve ad pod timing metadata

To retrieve the ad pod timing, follow these steps:

  1. Generate an HMAC token.
  2. Call the ATM API with the HMAC token.

Request timing metadata for pre-roll ads

Verify pre-roll settings for your livestream event using the following options:

To retrieve the pre-roll ad decision results, make a request to the ATM API.

The following example makes an ATM request for pre-roll ads:

curl "https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/pod.json?stream_id=SESSION_ID&ad_break_id=preroll&auth-token=your_signed_HMAC_token"

Request timing metadata for mid-roll ads

To retrieve ad pod metadata for mid-rolls, follow these steps:

  1. Parse the livestream manifest to find the ad markers that contain the timing and duration for each mid-roll ad break.
  2. Call the ATM API endpoint to request the precise ad pod and slate duration. The API returns a JSON object with the ad pod's decision results.

The following example makes an ATM request for mid-roll ads:

curl "https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/pod.json?stream_id=SESSION_ID&ad_break_id=AD_BREAK_ID&pd=AD_BREAK_DURATION&auth-token=your_signed_HMAC_token"

If successful, you see output similar to the following JSON object:

{
  "status": "final",
  "ads": [
    {
      "duration_ms": 5046,
      "variants": {
        "devrel1428000": {
          "segment_extension": "ts",
          "segment_durations": {
            "timescale": 1000,
            "values": [
              5045
            ]
          }
        },
        "devrel1928000": {
          "segment_extension": "ts",
          "segment_durations": {
            "timescale": 1000,
            "values": [
              5045
            ]
          }
        }
      }
    }
  ],
  "slate": {
    "duration_ms": 0,
    "variants": {
      "devrel1428000": {
        "segment_extension": "ts",
        "segment_durations": {
          "timescale": 1000,
          "values": [
            5005,
            ...
            5046
          ]
        }
      },
      "devrel1928000": {
        "segment_extension": "ts",
        "segment_durations": {
          "timescale": 1000,
          "values": [
            5005,
            ...
            5046
          ]
        }
      }
    }
  }
}

Stitch ads into content manifest

The following sections walk you through how to modify the livestream manifest and add the ad segments.

Identify ad break segments and insert discontinuities

As you process each variant manifest, identify the EXT-X-CUE-IN and EXT-X-CUE-OUT tags in your stream, indicating the start and end of an ad break.

Replace the EXT-X-CUE-IN and EXT-X-CUE-OUT tags with the EXT-X-DISCONTINUITY elements for the client video player to switch between content and ads.

The following example manifest replaces the EXT-X-CUE-IN and EXT-X-CUE-OUT tags:

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:5.000,
contentorigin.com/1.ts
#EXTINF:5.000,
contentorigin.com/2.ts
#EXT-X-CUE-OUT:17.450
#EXTINF:5.000,
contentorigin.com/3.ts
#EXTINF:5.000,
contentorigin.com/4.ts
#EXTINF:5.000,
contentorigin.com/5.ts
#EXTINF:2.450,
contentorigin.com/6.ts
#EXT-X-CUE-IN
#EXTINF:5.000,
contentorigin.com/7.mp4
#EXTINF:5.000,
contentorigin.com/8.mp4

The following example shows a replaced manifest:

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:5.000,
contentorigin.com/1.ts
#EXTINF:5.000,
contentorigin.com/2.ts
#EXTINF:5.000,
#EXT-X-DISCONTINUITY
{... Insert ad segments here ...}
#EXT-X-DISCONTINUITY
#EXTINF:5.000,
contentorigin.com/7.mp4
#EXTINF:5.000,
contentorigin.com/8.mp4

Google DAI ad segments are not encrypted. If your content is encrypted, remove encryption by inserting EXT-X-KEY:METHOD=NONE element prior to the first ad segment of each ad break. At the end of the ad break, add encryption back by inserting an appropriate EXT-X-KEY.

Keep track of the start time, duration, and index of the upcoming ad break.

Build ad segment URLs

Replace the content segments between the EXT-X-DISCONTINUITY tags with URLs for each ad segment. To determine how many ad segments to insert, use the ads.segment_durations.values provided in the JSON response from the ATM API.

To return to content before the ad pod is complete, such as when detecting an EXT-X-CUE-IN tag, your manifest manipulator must add the d= parameter to the URL of the final ad segment. This parameter shortens the segment to avoid affecting the client video player's timeline.

The following example assembles a pre-roll ad segment URL to the manifest. Note that ad segments use a zero-based index:

https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/preroll/ad/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID

The following example assembles a mid-roll ad segment URL to the manifest:

https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID

The following example inserts ad segments to the manifest:

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:5.000,
contentorigin.com/1.ts
#EXTINF:5.000,
contentorigin.com/2.ts
#EXT-X-DISCONTINUITY
#EXTINF:5.000,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID
#EXTINF:5.000,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/1.ts?stream_id=SESSION_ID
#EXTINF:5.000,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/2.ts?stream_id=SESSION_ID
#EXTINF:2.450,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/3.ts?stream_id=SESSION_ID
#EXT-X-DISCONTINUITY
#EXTINF:5.000,
contentorigin.com/7.mp4
#EXTINF:5.000,
contentorigin.com/8.mp4

Build slate segments

To fill the gap between ad and content, insert slate segments. Use the slates.segment_durations.values array from the ATM API's JSON response to determine the duration of each slate segment. Loop the sequence of segment durations as needed to fill the entire ad break.

To return to content before the ad pod is complete, such as when detecting an EXT-X-CUE-IN tag, add the d= parameter to the URL of the final slate segment. This parameter shortens the segment to avoid affecting the client video player's timeline. The value for the d= parameters must be an integer, representing the duration in milliseconds to shorten the segment.

The following example assembles a slate segment:

https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/slate/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID

The slate/0 in the example represents the slate iteration number. Refer to your client video player's compatibility and caching capability to determine whether to start at 0 and increment this number for each loop of the slate, or keep it at 0 for all iterations.

Manage the return to content

After your manifest manipulator inserts all segments from the ad pod, your manifest manipulator must transition back to the content stream. To transition back to the content stream, choose one of the following methods. Each method requires a final re-alignment segment to keep the stream timeline accurate.

Fill and Re-align: Insert the slate segments and loop the slate. Fill the duration and insert EXT-X-DISCONTINUITY elements between each slate iteration. For the last segment, append the d= parameter in milliseconds to match the content start.

Immediate Return: Insert a single re-alignment slate segment by using the d= parameter and following with content.

The video player transitions back to the content without an altered timeline.

The following example creates a transition by filling the remainder of the ad break duration with slate segments.

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:5.000,
contentorigin.com/1.ts
#EXTINF:5.000,
contentorigin.com/2.ts
#EXT-X-DISCONTINUITY
#EXTINF:5.000,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID
#EXTINF:5.000,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/1.ts?stream_id=SESSION_ID
#EXTINF:5.000,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/slate/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID
#EXT-X-DISCONTINUITY
#EXTINF:2.450,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/slate/0/profile/ENCODING_PROFILE/1.ts?stream_id=SESSION_ID&d=2450
#EXT-X-DISCONTINUITY
#EXTINF:5.000,
contentorigin.com/7.mp4
#EXTINF:5.000,
contentorigin.com/8.mp4

In this method, your manifest manipulator loops the slate as needed and appends the d= URL parameter only to the last slate segment. This process aligns precisely with the scheduled start of the underlying content.

The following example shows how to use a single slate segment with the d= parameter to help keep the player timeline accurate when you don't fill the entire remaining break with looped slates.

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0

#EXTINF:5.000,
contentorigin.com/1.ts
#EXTINF:5.000,
contentorigin.com/2.ts
#EXT-X-DISCONTINUITY
#EXTINF:5.000,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID
#EXTINF:5.000,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/1.ts?stream_id=SESSION_ID
#EXTINF:5.000,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/ad/0/profile/ENCODING_PROFILE/2.ts?stream_id=SESSION_ID
#EXT-X-DISCONTINUITY
#EXTINF:2.450,
https://dai.google.com/linear/pods/v1/adv/network/NETWORK_CODE/custom_asset/CUSTOM_ASSET_KEY/ad_break_id/AD_BREAK_ID/slate/0/profile/ENCODING_PROFILE/0.ts?stream_id=SESSION_ID&d=2450
#EXT-X-DISCONTINUITY
#EXTINF:5.000,
contentorigin.com/7.mp4
#EXTINF:5.000,
contentorigin.com/8.mp4

Optional: Schedule an ad break

To enhance your fill rate, send an Early Ad Break Notification (EABN) with the ad pod duration, custom targeting parameters, and SCTE-35 signal data. For more details, see Send early ad break notifications.