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:

- [Ad pod Timing Metadata (ATM)
  API](https://developers.google.com/ad-manager/dynamic-ad-insertion/api/pod-serving/reference/live#method_ad_pod_timing_metadata): query ad pod decision results, including ad and slate durations.
- [Ad segment
  endpoint](https://developers.google.com/ad-manager/dynamic-ad-insertion/api/pod-serving/reference/live#method_pod_segment): request ad or slate segments with the option to terminate the current ad pod.

Optionally, for high concurrency livestream events, we recommend calling [Early
Ad Break Notification (EABN)
API](https://developers.google.com/ad-manager/api/beta/reference/rest/v1/networks.liveStreamEventsByCustomAssetKey.adBreaks)
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:

- **Ad Manager UI:** [Set up a livestream for
  DAI](https://support.google.com/admanager/answer/7294289).
- **Ad Manager API:** Use a client library ([available
  options](https://developers.google.com/ad-manager/api/clients)) to call [`LiveStreamEventService.createLiveStreamEvents`](https://developers.google.com/ad-manager/api/reference/latest/LiveStreamEventService#createlivestreamevents) method. Set the `LiveStreamEvent.dynamicAdInsertionType` parameter to [`POD_SERVING_REDIRECT`](https://developers.google.com/ad-manager/api/reference/latest/LiveStreamEventService.DynamicAdInsertionType).

## 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 <var translate="no">CUSTOM_ASSET_KEY</var> 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](https://developers.google.com/ad-manager/dynamic-ad-insertion/api/pod-serving/live/pod-request-overview).
2. Call the ATM API with the HMAC token.

> [!NOTE]
> **Note:** The ATM API requires an HMAC token for all requests, even if you disable the "Stream create authentication keys" option.

### Request timing metadata for pre-roll ads

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

- [Pre-rolls in DAI live streams (Beta)](https://support.google.com/admanager/answer/14001609)
- [LiveStreamEvent.prerollsettings](https://developers.google.com/ad-manager/api/reference/latest/LiveStreamEventService.LiveStreamEvent#prerollsettings)

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](https://developers.google.com/ad-manager/dynamic-ad-insertion/api/pod-serving/reference/live#method_ad_pod_timing_metadata) endpoint to request the precise ad pod and slate duration. The API returns a JSON object with the ad pod's decision results.

> [!NOTE]
> **Note:** Google DAI only uses the first set of targeting parameters it receives for each unique ad break---either from an ATM request or Early Ad Break Notification (EABN)---and ignores all subsequent parameters for the same break.

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.ts
    #EXTINF:5.000,
    contentorigin.com/8.ts

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.ts
    #EXTINF:5.000,
    contentorigin.com/8.ts

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.

> [!NOTE]
> **Note:** Include an HMAC token with your requests to the ATM API, but omit the token from the URLs for each ad segment.

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 `sd=` 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\&sd=2450
    #EXT-X-DISCONTINUITY
    #EXTINF:5.000,
    contentorigin.com/7.ts
    #EXTINF:5.000,
    contentorigin.com/8.ts

### 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 `sd=` 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 `sd=` parameter 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 `sd=` parameter in milliseconds
to match the content start.

**Immediate Return:** Insert a single re-alignment slate segment by using
the `sd=` 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\&sd=2450
    #EXT-X-DISCONTINUITY
    #EXTINF:5.000,
    contentorigin.com/7.ts
    #EXTINF:5.000,
    contentorigin.com/8.ts

In this method, your manifest
manipulator loops the slate as needed and appends the `sd=` 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 `sd=`
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\&sd=2450
    #EXT-X-DISCONTINUITY
    #EXTINF:5.000,
    contentorigin.com/7.ts
    #EXTINF:5.000,
    contentorigin.com/8.ts

## 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](https://developers.google.com/ad-manager/api/beta/ad-breaks).