The Pod Serving API is currently in closed beta. If you are interested in learning more about Pod Serving or want to implement the Pod Serving API, please contact your Google account manager.

Pod Serving manifest manipulation guide

Stay organized with collections Save and categorize content based on your preferences.

The Pod Serving API provides access to adaptive-bitrate video ad pods prepared in such a way that they can be stitched directly into a user-facing HLS media playlist.

This API is intended for advanced publishers and video technology partners. Using this API at scale requires design and implementation of a sophisticated media serving workflow which is outside the scope of this documentation.

This guide will focus on proper implementation of a Pod Serving manifest manipulation server.

Implementation

Setting up encoding profiles and live events in Ad Manager

Pod Serving requires LiveStreamEvents to be configured in Ad Manager 360, either using the Ad Manager API (recommended) or the UI. Additionally, information about the encoding profiles used for the content must be configured via the API or UI.

Encoding Profile Setup

Encoding profiles must be created in each Ad Manager network, via the DAIEncodingProfile API or UI.

An Encoding Profile consists of information describing the encoding of a single content variant. It may contain only audio settings, only video settings, or both audio and video settings. It allows us to determine which particular ad transcode should be returned for a given ad segment request.

Encoding Profile
name An identifier for the Encoding Profile. Used to build the ad segment URLs for this variant. This should be unique for a network.
id Unique ID of the profile. This value is read-only and is assigned by Google.
type Either media, iFrame, or subtitles.
containerType Container type. Either TS, FMP4, or HLS Packed Audio.
Video Settings (only present if media contains video, or if an iFrame type)
codec Required. The RFC6381 codec string.
bitrate Required.
framesPerSecond Required. Double representing FPS of the video. Example: 29.97.
resolution Required. The width x height of the video. Example: 3840x2160.
Audio Settings (only present if media contains audio)
codec Required. The RFC6381 codec string.
bitrate Required if the Encoding Profile only contains audio. Optional if it also contains Video.
audioChannels Required. Integer representing number of audio channels (including low frequency channels). Example: 6
audioSampleRate Required. Audio sample rate in hertz. Example: 68000

Live Stream Event Setup

Live Stream Events must be configured via the LiveStreamEventService API or UI.

Required Fields
name Name of the live stream
customAssetKey A custom identifier to be used for this Event. Must be unique across all Events for the network.
status At creation, events are created in the LiveStreamEventStatus.PAUSED state.

Note: Set to ACTIVE status when the event is ready for serving.

adTags Master ad tag URL generated by the Ad Manager trafficking workflow
daiEncodingProfileIds A list of the encoding profile ids enabled for this Event.
dynamicAdInsertionType Set to POD_SERVING_REDIRECT
startDateTime The start date and time of this LiveStreamEvent.
endDateTime The scheduled end date and time of this LiveStreamEvent. This attribute is required if unlimitedEndDateTime is false and ignored if unlimitedEndDateTime is true.
segmentUrlAuthenticationKeyIds List of DAI Authentication keys used to sign ad segment URL requests
Recommended optional fields
prefetchEnabled Indicates whether the option to prefetch ad requests is enabled.
prefetchSettings The information needed to prefetch ad requests for an ad break.
slateCreativeId ID corresponding to the slate for this live event. If not set, the network default value will be used.
Fields not applicable
adBreakFillType Only slate is currently available as adBreakFillType.
contentUrls No content ingest occurs with Pod serving
sourceContentConfigurationIds No content ingest occurs with Pod serving
hlsSettings
streamingFormat This can be specified on stream create.
maxFillerDuration
whitelistedIpsEnabled No content ingest occurs with Pod serving
enableRelativePlaylistDelivery Pod serving does not serve the manifest
enableForceCloseAdBreaks Pod serving does not serve the manifest
segmentDropping Pod serving does not serve the manifest

Dai Authentication Key Setup

In order to sign ad segment URLs, DAI Authentication Keys must be created and added to the LiveStreamEvent using the following steps:

  1. Call DaiAuthenticationKeyService API and generate key(s) of type HMAC
  2. Add keys to the segmentUrlAuthenticationKeyIdsfield of LiveStreamEventService API.

Once you have properly configured your events and encoder profiles, you should have the following information, which will be needed by the stitching server:

  • network_code - The Ad Manager 360 network code for this network.
  • custom_asset_key - The custom live stream asset key assigned to your event.
  • profile_names - The names of the encoding profiles used by this event.
  • HMAC_authentication_key - The secret authentication key associated with your live stream event.

Handling stream manifest requests

When your server receives a stream manifest request from the client, you must store the stream_id provided in the URL, so that it can be incorporated into each segment request. Then, you must begin streaming the manifest to the client, ready to modify each defined ad pod, before sending to the client.

Identifying ad breaks

Identifying ad breaks in your unprocessed manifest will vary depending on your encoder. One common method is to wrap the ad break in #EXT-X-CUE-OUT/#EXT-CUE-IN tags that will denote the duration of the ad pod.

Regardless of how ad breaks are delineated in your stream, as you process these breaks, you will need to keep track of a few crucial values:

  • ad_pod_index - This is the index (starting at 1) of the ad pod being processed, within the stream, starting from the beginning of the stream. This ad pod index must match globally for all stream clients and must increment by 1 for each new ad break.
  • ad_pod_duration - The total duration of this ad pod, in milliseconds. In the case of the example below, the duration would be stored as 18000.
  • cust_params - Custom parameters for per break targeting, in the format described in this Ad Manager Help Center article.

Sample Manifest (original)

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

#EXTINF:5.005,
contentorigin.com/1.ts
#EXTINF:5.005,
contentorigin.com/2.ts
#EXT-X-CUE-OUT:DURATION=18
#EXTINF:5.005,
contentorigin.com/3.ts
#EXTINF:5.005,
contentorigin.com/4.ts
#EXTINF:5.005,
contentorigin.com/5.ts
#EXTINF:3.000,
contentorigin.com/6.ts
#EXT-X-CUE-IN
#EXTINF:5.005,
contentorigin.com/7.mp4
#EXTINF:5.005,
contentorigin.com/8.mp4

Insert discontinuities between ads and content

To separate Google-hosted ad breaks from your content segments, you must insert #EXT-X-DISCONTINUITY tags at the start and end of each ad break. If these discontinuity tags do not appear in the final manifest, playback will fail.

The inserted ad segment URIs will be unencrypted. If your content is encrypted, you will also need to remove encryption by specifying #EXT-X-KEY:METHOD=NONE prior to the first ad segment of each ad break and then re-add it after the ad break.

Generate HMAC Token for Pod

Each segment request within an ad pod must be signed with an HMAC-SHA256 token. This token is generated from the preceding stream variables, and an expiration timestamp in seconds. You can omit any variable that doesn't have a value, or set it to an empty string. You need to order the variables alphabetically, then add the hmac variable to the end.

The HMAC token is generated from a message string in the following format:

custom_asset_key={custom_asset_key}~cust_params={cust_params}~exp={expiration}~network_code={network_code}~pd={pod_duration}~pod_id={ad_pod_index}

The HMAC token can be generated once per adpod and shared across users for all segments in that ad pod to avoid needing to compute each time.

The secret key is an HMAC authentication key configured in Ad Manager. The key must then be added to the LiveStreamEvent as an Ad Segment Authentication Key.

Example 1

Here's an example using variables that don't have values.

Message to encode (cust_params and scte35 are set to empty strings):

custom_asset_key=iYdOkYZdQ1KFULXSN0Gi7g~cust_params=~exp=1489680000~network_code=6062~pd=180000~pod_id=5~scte35=

Secret key:

A7490591290583E4B93189DEE7E287C299FC686872ABC7ADC9F9F536443505F

HMAC signature output:

86d7e5f8c96fe4c83141d764df376ae14a0e2066f2e6b2ccfb9e1e2d3c869a88

Once generated, the output should be appended to the message to encode to obtain the final HMAC token:

custom_asset_key=iYdOkYZdQ1KFULXSN0Gi7g~cust_params=~exp=1489680000~network_code=6062~pd=180000~pod_id=5~scte35=~hmac=86d7e5f8c96fe4c83141d764df376ae14a0e2066f2e6b2ccfb9e1e2d3c869a88

The token should then be URL-encoded to be used as a query parameter:

custom_asset_key%3DiYdOkYZdQ1KFULXSN0Gi7g~cust_params%3D~exp%3D1489680000~network_code%3D6062~pd%3D180000~pod_id%3D5~scte35%3D~hmac%3D86d7e5f8c96fe4c83141d764df376ae14a0e2066f2e6b2ccfb9e1e2d3c869a88

Example 2

Here’s an example where some optional variables are omitted entirely.

Message to encode:

custom_asset_key=iYdOkYZdQ1KFULXSN0Gi7g3~exp=1489680000~network_code=6062~pd=180000~pod_id=5

Secret key:

A7490591290583E4B93189DEE7E287C299FC686872ABC7ADC9F9F536443505F

HMAC signature output:

6a8c44c72e4718ff63ad2284edf2a8b9e319600b430349d31195c99b505858c9

Once generated, the output should be appended to the message to encode to obtain the final HMAC token:

custom_asset_key%3DiYdOkYZdQ1KFULXSN0Gi7g~exp%3D1489680000~network_code%3D6062~pd%3D180000~pod_id%3D5~hmac%3D6a8c44c72e4718ff63ad2284edf2a8b9e319600b430349d31195c99b505858c9

The token should then be URL-encoded to be used as a query parameter:

custom_asset_key%3DiYdOkYZdQ1KFULXSN0Gi7g~cust_params%3D~exp%3D1489680000~network_code%3D6062~pd%3D180000~pod_id%3D5~scte35%3D~hmac%3D86d7e5f8c96fe4c83141d764df376ae14a0e2066f2e6b2ccfb9e1e2d3c869a88

Processing ad pod segments

For each segment within an ad pod, you will need to track a few additional values:

  • segment_number - Segment index within the ad pod, starting with zero
  • segment_duration - Duration of the current segment in milliseconds. This value should be the same for all segments except the last one in the pod.
  • segment_offset - Segment offset, calculated by adding the previous segment's duration to its segment offset in milliseconds.
  • last - Boolean value, identifying the last segment in an ad pod. Defaults to false.

Building ad segment urls

Replace each segment within the ad break with a url of the format:

/linear/pods/v1/seg/network/{network_code}/custom_asset/{custom_asset_key}/pod/{pod_id}/profile/{profile_name}/{segment_number}.(ts|mp4|vtt|aac|ac3|eac3)
Path Parameters
network_code The Ad Manager 360 network code for this network.
custom_asset_key The custom live stream asset key, specified in LiveStreamEventService API or on the Live Stream page in the Google Ad Manager 360 UI.
pod_id Identifier for the adbreak, should be an integer starting at one and increasing by one for each ad break.

This value must be the same across all users viewing the current event.

profile_name Identifier for the profile being requested,
segment_number The index of this segment within the current ad pod, starting at zero.
Query Parameters
stream_id Required for cookieless request. The user’s stream_id param returned from the Stream Create request.
sd Required segment_duration
so Optional segment_offset

If so is missing, , we will assume previous segments all have the same duration and calculate segment offset from the segment_num and sd.

pd Required, except for events with durationless ad breaks enabled. The duration (in milliseconds) of the ad break. Referred to above as ad_pod_duration.
cust_params Optional custom_params (as described above)
scte35 Optional Base64-encoded SCTE-35 signal. It’s the client's responsibility to ensure that the signal is correct. If incorrect, a message is sent to the X-Ad-Manager-Dai-Warning HTTP header in the response, and the signal is still propagated to create an ad break. See the supported ad markers for more information on how DAI uses the SCTE-35 signal.
auth-token Required The HMAC token for this ad pod
last Optional Boolean indicating this is the last segment in the adbreak or not. Defaults to false.

Sample Manifest (after segment replacement)

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

#EXTINF:5.005,
contentorigin.com/1.ts
#EXTINF:5.005,
contentorigin.com/2.ts
#EXT-X-DISCONTINUITY
#EXTINF:5.005,
https://dai.google.com/linear/pods/v1/seg/network/6062/custom_asset/iYdOkYZdQ1KFULXSN0Gi7g/pod/1/profile/devrel4628000/0.ts?sd=5005&so=0&pd=18015&auth-token=custom_asset_key%3DiYdOkYZdQ1KFULXSN0Gi7g~cust_params%3D~exp%3D1489680000~network_code%3D6062~pd%3D180000~pod_id%3D5~hmac%3D44bf78223c240cbc5bae3cdfd794bfc6971b6583cd296f44ef3a46944605cf9a&stream_id=fe6c9136-09a4-4ff6-862e-daee1dea0e1b:MRN2
#EXTINF:5.005,
https://dai.google.com/linear/pods/v1/seg/network/6062/custom_asset/iYdOkYZdQ1KFULXSN0Gi7g/pod/1/profile/devrel4628000/1.ts?sd=5005&so=5005&pd=18015&auth-token=custom_asset_key%3DiYdOkYZdQ1KFULXSN0Gi7g~cust_params%3D~exp%3D1489680000~network_code%3D6062~pd%3D180000~pod_id%3D5~hmac%3D44bf78223c240cbc5bae3cdfd794bfc6971b6583cd296f44ef3a46944605cf9a&stream_id=fe6c9136-09a4-4ff6-862e-daee1dea0e1b:MRN2
#EXTINF:5.005,
https://dai.google.com/linear/pods/v1/seg/network/6062/custom_asset/iYdOkYZdQ1KFULXSN0Gi7g/pod/1/profile/devrel4628000/2.ts?sd=5005&so=10010&pd=18015&auth-token=custom_asset_key%3DiYdOkYZdQ1KFULXSN0Gi7g~cust_params%3D~exp%3D1489680000~network_code%3D6062~pd%3D180000~pod_id%3D5~hmac%3D44bf78223c240cbc5bae3cdfd794bfc6971b6583cd296f44ef3a46944605cf9a&stream_id=fe6c9136-09a4-4ff6-862e-daee1dea0e1b:MRN2
#EXTINF:3.000,
https://dai.google.com/linear/pods/v1/seg/network/6062/custom_asset/iYdOkYZdQ1KFULXSN0Gi7g/pod/1/profile/devrel4628000/3.ts?sd=3000&so=15015&pd=18015&auth-token=custom_asset_key%3DiYdOkYZdQ1KFULXSN0Gi7g~cust_params%3D~exp%3D1489680000~network_code%3D6062~pd%3D180000~pod_id%3D5~hmac%3D44bf78223c240cbc5bae3cdfd794bfc6971b6583cd296f44ef3a46944605cf9a&stream_id=fe6c9136-09a4-4ff6-862e-daee1dea0e1b:MRN2&last=true
#EXT-X-DISCONTINUITY
#EXTINF:5.005,
contentorigin.com/7.mp4
#EXTINF:5.005,
contentorigin.com/8.mp4

Congratulations! You are now properly serving a live stream with ad segments provided by the DAI pod serving API.

Best practices

  • Please note that while Ad pods are indexed starting at one, the ad segments contained within should be indexed starting at zero and be the same for all users.
  • An #EXT-X-DISCONTINUITY must precede the first ad segment of an ad break and follow the last segment of an ad break.
  • The inserted ad segment URIs will be unencrypted. If your content is encrypted, you will also need to remove encryption by specifying #EXT-X-KEY:METHOD=NONE prior to the first ad segment of each ad break and then re-add it after the ad break.
  • Each ad segment url within a given variant should be the same duration (up to 6 seconds). An exception to this rule is the last ad segment in the ad pod, which can be of shorter duration. Ad segments in different variants do not need to be the same length.
  • Playlists should follow the HLS spec. Media sequence numbers, discontinuity sequence numbers, and program date time must be updated appropriately.
  • Ad pod indices should be consistent across all users watching the stream. All user’s watching the same ad break should have the same pod ID.

Additional Resources