diff --git a/ads/assets/img/break-manifest/side-loading.svg b/ads/assets/img/break-manifest/side-loading.svg new file mode 100644 index 000000000000..55c387f2b779 --- /dev/null +++ b/ads/assets/img/break-manifest/side-loading.svg @@ -0,0 +1,25 @@ + + + + + + + + OptiView Ads API + + Dolby CDN + + Origin + + Customer CDN + + OptiView Player + + break manifest + + break manifest + + media stream + + media stream + diff --git a/ads/assets/img/breaks/break-lifecycle.svg b/ads/assets/img/breaks/break-lifecycle.svg new file mode 100644 index 000000000000..3938b695240a --- /dev/null +++ b/ads/assets/img/breaks/break-lifecycle.svg @@ -0,0 +1,27 @@ + + + + + + + + PREPARING + + CUED + + READY + + ERROR + + SIGNALED + + prepared, no start yet + + prepared, start set + + scheduling window missed + + punch + + announced to players + diff --git a/ads/assets/img/breaks/format-double.svg b/ads/assets/img/breaks/format-double.svg new file mode 100644 index 000000000000..69df7a7e0231 --- /dev/null +++ b/ads/assets/img/breaks/format-double.svg @@ -0,0 +1,8 @@ + + + + + CONTENT + AD + side-by-side boxes over a companion backdrop + diff --git a/ads/assets/img/breaks/format-lshape-ad.svg b/ads/assets/img/breaks/format-lshape-ad.svg new file mode 100644 index 000000000000..4aa600cc9554 --- /dev/null +++ b/ads/assets/img/breaks/format-lshape-ad.svg @@ -0,0 +1,8 @@ + + + + AD + companion + backdrop + ad window with companion backdrop + diff --git a/ads/assets/img/breaks/format-lshape-content.svg b/ads/assets/img/breaks/format-lshape-content.svg new file mode 100644 index 000000000000..f17df9c31c49 --- /dev/null +++ b/ads/assets/img/breaks/format-lshape-content.svg @@ -0,0 +1,8 @@ + + + + CONTENT + ad + backdrop + live content window with ad backdrop + diff --git a/ads/assets/img/breaks/format-overlay.svg b/ads/assets/img/breaks/format-overlay.svg new file mode 100644 index 000000000000..f275eb2b7779 --- /dev/null +++ b/ads/assets/img/breaks/format-overlay.svg @@ -0,0 +1,7 @@ + + + CONTENT + + OVERLAY + semi-transparent overlay over live content + diff --git a/ads/assets/img/breaks/format-single.svg b/ads/assets/img/breaks/format-single.svg new file mode 100644 index 000000000000..9851f4b8426b --- /dev/null +++ b/ads/assets/img/breaks/format-single.svg @@ -0,0 +1,5 @@ + + + AD + full-screen replacement + diff --git a/ads/concepts/break-manifest.mdx b/ads/concepts/break-manifest.mdx new file mode 100644 index 000000000000..9062b068ed99 --- /dev/null +++ b/ads/concepts/break-manifest.mdx @@ -0,0 +1,81 @@ +--- +sidebar_position: 7 +sidebar_label: Break Manifest +--- + +# Break Manifest + +The Break Manifest is the contract between the OptiView Ads backend and the player. It is a small JSON document that describes the ad breaks that are currently relevant for a [channel](/ads/concepts/channels), and the OptiView Player polls it to learn which breaks to prepare and play. + +## Side-loading + +The Break Manifest is **side-loaded**: it is served from its own endpoint, separately from the media manifest. The player fetches the media stream from your CDN as usual and, in parallel, polls the Break Manifest to drive ad break scheduling. + +![Side-loading diagram](../assets/img/break-manifest/side-loading.svg) + +Side-loading has some important advantages: + +- **Streaming protocol independent.** Because the Break Manifest travels next to the stream instead of inside it, features do not have to be ported into an existing streaming protocol to support your use cases. It also allows us to bring features that are not possible today due to the limitations of those protocols. +- **Not in your critical path.** OptiView Ads never modifies your media manifest, so ad insertion cannot corrupt the stream and cause an outage the way an insertion platform writing wrong data into the media manifest can. +- **Minimal requirements on the stream.** The only thing the stream needs is time metadata to schedule the breaks against. + +## Endpoint + +The Break Manifest is served per channel: + +```text +GET /manifest/v1/:orgId/channels/:channelId +``` + +The endpoint is a public read endpoint: it takes no authentication and is served with permissive CORS so that players and CDNs can fetch it directly. + +```bash +curl 'https://us.markers.optiview.dolby.com/manifest/v1/org_123/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01' +``` + +:::note Regional domains +The example uses the US region (`https://us.markers.optiview.dolby.com`). For the EU region, replace `us.` with `eu.` (`https://eu.markers.optiview.dolby.com`). +::: + +Responses carry a `Cache-Control` header aligned with the channel's active polling interval, so a cached copy is never held longer than the fastest polling cadence the channel advertises. + +## Manifest envelope + +The Break Manifest document contains the following top-level properties: + +| Property | Description | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `version` | The Break Manifest format version, following Semantic Versioning. Use it to guard against future format changes. | +| `channelId` | The identifier of the [channel](/ads/concepts/channels) this manifest serves. Players use it for reporting, analytics, and diagnostics. | +| `timebase` | How each break's `start` is expressed: `wallclock` (UTC ISO 8601 timestamp), `pts` (presentation timestamp), or `mediatime` (seconds from the start of a VOD asset). See [Channels](/ads/concepts/channels). | +| `polling` | How often the player should refresh the manifest. See [Polling](#polling). | +| `vendorConfiguration` | Session-level configuration per vendor integration — for example, the Google Ad Manager network code and custom asset key the player needs to create the stream session. | +| `breaks` | The breaks currently relevant for the channel. Each entry carries the break's schedule, controls, and variants — see [Breaks](./breaks.mdx) for what a break contains. | + +Everything inside a break entry — `start`, `duration`, `resumeOffset`, `controls`, and `variant` — is described on the [Breaks](./breaks.mdx) page. + +### Polling + +The `polling` object advertises how often the player should refresh the manifest, with two cadences: + +- **`polling.idle`** — the interval to poll at when no break is active. A slower cadence keeps request load low while nothing is happening. Default: `10` seconds. +- **`polling.active`** — the interval to poll at while a break is active. A faster cadence lets the player react quickly to duration changes, an early return, or late additions. Default: `1` second. + +Both cadences are configured on the channel through its `pollingIdleSeconds` and `pollingActiveSeconds` settings. + +### Which breaks are included + +The Break Manifest reflects the breaks that are currently relevant for delivery, not the channel's entire break history: + +- An upcoming break appears in the manifest ahead of its start, controlled by the channel's ad prefetch window (`adPrefetchMs`, default 10 seconds). This gives the player time to prepare the break before it starts. +- A past break remains included while its window still overlaps the channel's DVR window (`dvrWindowMs`, default 5 minutes), so viewers seeking back still get the break. Breaks that ended before the DVR look-back are dropped. +- Only fully prepared breaks are announced. Breaks that are still being prepared, [cued breaks](./breaks.mdx#cued-breaks) waiting to be punched, and failed breaks never appear — see the [break lifecycle](./breaks.mdx#break-lifecycle). + +## Related resources + +| Resource | Relationship | +| ------------------------------------------ | --------------------------------------------------------------------------------------------------- | +| [Getting started](/ads/getting-started/) | Integrating the OptiView Player that polls the Break Manifest. | +| [Channels](./channels.mdx) | The parent of the Break Manifest. The timebase and delivery window shape which breaks are included. | +| [Breaks](./breaks.mdx) | Scheduled or detected ad opportunities announced through the manifest. | +| [Integrations](../integrations/google.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | diff --git a/ads/concepts/breaks.mdx b/ads/concepts/breaks.mdx new file mode 100644 index 000000000000..3088d5122453 --- /dev/null +++ b/ads/concepts/breaks.mdx @@ -0,0 +1,466 @@ +--- +sidebar_position: 2 +sidebar_label: Breaks +--- + +# Breaks + +A break is the core monetization entity in OptiView Ads. It represents an ad opportunity scheduled on a channel and describes when the break starts, how long it lasts, what the viewer is allowed to do during the break, and which ad experience is rendered. + +Breaks are scoped to an organization and created for a [channel](./channels.mdx). API calls authenticate with an API key and secret using HTTP Basic authentication and identify the organization with the `X-Org-ID` header. + +:::note Regional domains +The examples below use the US region (`https://us.ads.optiview.dolby.com`). For the EU region, replace `us.` with `eu.` (`https://eu.ads.optiview.dolby.com`). +::: + +## Break identity + +Every break has an `id` that is unique within its channel. The `id` is optional when creating a break: if you omit it, OptiView Ads generates one for you. When you supply your own, we recommend using a UUID. + +## Scheduling + +### Timebase-based scheduling + +The [timebase of the channel](./channels.mdx#timebase) defines how a break is scheduled. Every break follows the timebase of its channel, and the break's `start` is expressed on that timeline: + +- **`wallclock`** — the break's `start` is a UTC ISO 8601 timestamp (for example `"2026-07-16T12:15:00.000Z"`). Players match it against the wallclock timeline carried by the stream. `start` is optional on wallclock channels: omitting it creates a [cued break](#cued-breaks). +- **`pts`** — the break's `start` is a non-negative number: a presentation timestamp on the encoder's timeline. `start` is required on PTS channels. + +`duration` is always required and is expressed in seconds. It is the **maximum** duration of the break. + +### Cued breaks + +On a wallclock channel you can create a break without a `start`. Such a break is **cued**: it is fully prepared ahead of time, but it is not announced to players yet — it waits for you to assign its start at exactly the right moment. This is ideal for live productions where you know a break is coming but not exactly when. + +See [Break punching](#break-punching) for how to fire a cued break. + +### Create a break directly + +You can create a break manually by supplying the necessary fields: the `start` (when required by the timebase), the `duration`, and the `variant` describing the ad experience. See [Break configuration](#break-configuration) for all the options. + +```bash +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01/breaks' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "id": "8c3f6a2e-5b1d-4e7a-9c48-2d6f0b1a3e57", + "start": "2026-07-16T12:15:00.000Z", + "duration": 120, + "resumeOffset": 0, + "controls": { + "skipOffset": 30, + "snapback": true + }, + "variant": { + "format": "single", + "assets": [ + { + "id": "asset-001", + "type": "static", + "mediaType": "video", + "mimeType": "video/mp4", + "uri": "https://cdn.example.com/ads/asset-001.m3u8" + } + ] + } + }' +``` + +### Create from a template + +A [template](./templates.mdx) preconfigures a break: it stores the break payload once so you can schedule consistent breaks quickly. Reference the template with `templateId` when creating the break. + +The created break stores a **snapshot** of the template: the template's content is copied onto the break at creation time. Editing the template later does not change breaks that were already created from it. + +You can override parts of the template per break. Supported overrides are `id`, `eventId`, `start`, `duration`, and `variant`: + +```bash +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01/breaks' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "templateId": "template-sports-spot", + "id": "9e4b7c1d-2a8f-4d35-b6e9-7c0a5f2d8b14", + "start": "2026-07-16T12:20:00.000Z", + "duration": 60 + }' +``` + +For templates that contain [vendor assets](#vendor-assets), you can additionally pass `assetParameters` (a string map) with the request. These are merged into the vendor assets of the template snapshot — for example to set per-break ad targeting parameters; on duplicate keys, the values you provide win. + +### Prepare your breaks under an event + +An [event](./events.mdx) is an event on your channel for which you want to prepare breaks, such as a live game or a show. Attach a break to an event by setting `eventId` when creating it. + +Preparing breaks under an event is only supported on **wallclock** channels. The event's date window defines the limits within which its breaks can be scheduled: + +- The break's start must fall within the event window (`startDate` through `endDate`). +- The entire break must fit inside the window: the break's end (`start` + `duration`) must not pass the event's `endDate`. + +A [cued break](#cued-breaks) is not checked against the window at creation, because its start is not known yet; punch it while the event is in progress so its start falls inside the window. + +### Scheduling constraints + +OptiView Ads enforces a few constraints when scheduling breaks. Each one protects the viewer experience: + +- **Breaks cannot overlap.** A scheduled break cannot overlap another break on the same channel. Players render one break at a time; overlapping breaks would make the ad timeline ambiguous for your viewers. +- **The start cannot lie in the past.** A break must start ahead of the current live position of the channel. Players need to receive the break through the [Break Manifest](./break-manifest.mdx) before its start time; a break scheduled behind the live position would never be seen. +- **Ad decisioning needs lead time.** Breaks delivered through an ad server integration such as [Google Ad Manager](../integrations/google.mdx) need a small extra margin before their start, so the ad decisioning can complete before the break begins. Scheduling closer than that margin is rejected rather than risking an empty break. +- **Event boundaries are respected.** A break attached to an event must fit entirely inside the event's date window, so all of an event's breaks stay within the occurrence they belong to. +- **One cued break at a time.** A channel holds at most one [cued break](#cued-breaks). The cued break is "the next break to fire" — allowing several at once would make it ambiguous which break a punch applies to. + +## Break Lifecycle + +A break moves through a small set of states: + +- **`PREPARING`** — the break is being prepared with the ad vendor (for example, waiting for Google Ad Manager to decision the ad pod). Breaks that use a vendor asset always begin in this state. +- **`CUED`** — the break is prepared but has no start time yet. It waits for you to [punch](#break-punching) it. A break created without a `start` enters this state (after preparation completes, or immediately when no vendor preparation is needed). +- **`READY`** — the break has a start time and is ready to be announced to players. A break created with a `start` and no vendor preparation begins in this state. +- **`SIGNALED`** — the break has been announced to players through the [Break Manifest](./break-manifest.mdx) or through [SSAI cue injection](../integrations/google.mdx). +- **`ERROR`** — the break could not be delivered, for example because it passed its scheduling window before preparation completed. + +![Break lifecycle diagram](../assets/img/breaks/break-lifecycle.svg) + +At a high level, the transitions are: + +| Transition | What happens | +| ------------------- | ----------------------------------------------------------------------------------------- | +| `PREPARING → READY` | Preparation completed and the break has a start time; it can now be announced to players. | +| `PREPARING → CUED` | Preparation completed for a break without a start time; it now waits to be punched. | +| `PREPARING → ERROR` | The break missed its scheduling window before preparation could complete. | +| `CUED → READY` | You punched the break: its start time is set and it becomes eligible for delivery. | +| `READY → SIGNALED` | The break was announced to players. | + +## Break punching + +Break punching lets you prepare a break ahead of time without yet signaling it to any player. You create the break without a `start`, OptiView Ads prepares everything (including any ad vendor decisioning), and the break waits in the `CUED` state. When the moment arrives — for example, the referee blows the half-time whistle — you **punch** the break: its start time is assigned and it is announced to players right away. + +While a break is being prepared or is waiting in the cued state, no other break can be cued on that channel: the cued break is waiting for you to punch it first. Punch (or delete) it before cueing the next one. + +Punching is only available on wallclock channels. + +```bash +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01/breaks/8c3f6a2e-5b1d-4e7a-9c48-2d6f0b1a3e57/punch' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "start": "2026-07-16T12:25:00.000Z" + }' +``` + +The body is optional: if you omit it, the break starts now. A requested start in the past is clamped to now. + +## Break configuration + +This section describes the different possibilities of a break: its general properties, event based triggers, playback controls, layouts, variants, and the asset model. + +### General + +Every break carries these general properties: + +| Property | Description | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `start` | When the break starts: a position on the channel's timebase (see [Timebase-based scheduling](#timebase-based-scheduling)), or a player event (see [Event based triggers](#event-based-triggers)). | +| `duration` | The **maximum** duration of the break, in seconds. When the ad content is longer, the player returns to the content when the duration is reached; when it is shorter, the player returns early. | +| `resumeOffset` | Where the player resumes the content after the break, in seconds relative to the break's start. `0` resumes at the point where the break started; when omitted, playback resumes after the break's duration. | +| `controls` | What the viewer is allowed to do during the break — see [Controls](#controls). | +| `variant` | The ad experience(s) to render: the layout and its assets, optionally targeted per device — see [Layouts](#layouts) and [Variants](#variants). | + +### Event based triggers + +Instead of a position on the timeline, a break's `start` can be a player event. Event-triggered breaks are described in the Break Manifest with `start: { "type": "event", "event": "", "delay": }`. + +The optional `delay` property postpones the break: it is the number of seconds (≥ 0, default `0`) that must elapse after the event before the break starts. How the delay counts depends on the event, as described below. + +#### Start + +A `start` break is a **pre-roll**: it fires when content playback begins, once per session. Use it to show an ad before (or shortly after) the viewer starts watching. With a `delay`, the break fires after the viewer has actually watched that many seconds of content — the delay counts played media time only, so pausing or seeking does not advance it. + +#### Pause + +A `pause` break is a **pause ad**: it fires every time the viewer pauses playback and is dismissed when the viewer resumes. Use it to monetize the pause screen, typically with an image overlay. With a `delay`, the break appears after the viewer has been paused for that many seconds; resuming before the delay elapses cancels the pending break. When multiple `pause` breaks are defined, each pause shows the next one in order. + +#### End + +An `end` break is a **post-roll**: it fires when playback has ended, once per session. Use it to show an ad after the content finishes. With a `delay`, the break fires that many seconds after playback ended; replaying before the delay elapses cancels the pending break. + +### Controls + +The `controls` object determines what a viewer is allowed to do during a break: + +- **`skipOffset`** — makes the break skippable. The value is the number of seconds into the break after which the viewer can skip it (for example, `skipOffset: 5` makes the break skippable after 5 seconds). When omitted, the break is not skippable. +- **`snapback`** — controls what happens when a viewer seeks over a break. When enabled, a viewer who tries to seek past the break is brought back to the start of the break; after the break finishes, playback continues at the position the viewer wanted to seek to. When omitted, viewers can seek over the break freely. + +### Layouts + +The layout (`format`) of a variant determines how the ad and your content share the screen. Click a layout to jump to its section: + +| [![Single](../assets/img/breaks/format-single.svg)](#single) | [![Double Box](../assets/img/breaks/format-double.svg)](#double-box) | [![L-shape ad](../assets/img/breaks/format-lshape-ad.svg)](#l-shape-ad) | +| :----------------------------------------------------------: | :------------------------------------------------------------------: | :---------------------------------------------------------------------: | +| [Single](#single) | [Double Box](#double-box) | [L-shape ad](#l-shape-ad) | + +| [![L-shape content](../assets/img/breaks/format-lshape-content.svg)](#l-shape-content) | [![Overlay](../assets/img/breaks/format-overlay.svg)](#overlay) | +| :------------------------------------------------------------------------------------: | :-------------------------------------------------------------: | +| [L-shape content](#l-shape-content) | [Overlay](#overlay) | + +#### Single + +![Single format](../assets/img/breaks/format-single.svg) + +`format: "single"` is the most basic layout: the ad covers the whole video area, replacing the content for the duration of the break. It requires only an `assets` array; companion assets are not allowed. + +```json +{ + "format": "single", + "assets": [ + { + "type": "static", + "mediaType": "video", + "uri": "https://cdn.example.com/ads/fullscreen.m3u8" + } + ] +} +``` + +#### Double Box + +![Double format](../assets/img/breaks/format-double.svg) + +`format: "double"` squeezes the content back into its own box alongside a second box that plays the ad. The background behind both boxes is filled by a **companion** asset, which can be an image or a video — every entry in `assets` therefore requires a `companion`. + +```json +{ + "format": "double", + "assets": [ + { + "type": "static", + "mediaType": "video", + "uri": "https://cdn.example.com/ads/spot.m3u8", + "companion": { + "type": "static", + "mediaType": "image", + "uri": "https://cdn.example.com/ads/backdrop.png" + } + } + ] +} +``` + +#### L-shape ad + +![L-shape ad format](../assets/img/breaks/format-lshape-ad.svg) + +`format: "lshape_ad"` squeezes the content into a corner and **replaces it with an ad**: the ad plays in the main window while a **companion** asset (image or video) fills the L-shaped backdrop. Like Double Box, every entry in `assets` requires a `companion`. + +```json +{ + "format": "lshape_ad", + "assets": [ + { + "type": "static", + "mediaType": "video", + "uri": "https://cdn.example.com/ads/spot.m3u8", + "companion": { + "type": "static", + "mediaType": "image", + "uri": "https://cdn.example.com/ads/lshape-backdrop.png" + } + } + ] +} +``` + +#### L-shape content + +![L-shape content format](../assets/img/breaks/format-lshape-content.svg) + +`format: "lshape_content"` is almost identical to [L-shape ad](#l-shape-ad), except that **your content keeps playing** in the main window instead of being replaced by an ad. The L-shaped backdrop itself is the advertisement, so it is a plain asset — no `companion` is needed. + +This is the key difference between the two L-shapes: with `lshape_ad` the viewer watches an ad while a companion fills the backdrop; with `lshape_content` the viewer keeps watching your content while the backdrop is the ad. + +```json +{ + "format": "lshape_content", + "assets": [ + { + "type": "static", + "mediaType": "image", + "uri": "https://cdn.example.com/ads/lshape-ad.png" + } + ] +} +``` + +#### Overlay + +![Overlay format](../assets/img/breaks/format-overlay.svg) + +`format: "overlay"` renders a non-linear ad on top of the content, which keeps playing. In addition to `assets`, an overlay requires a `position` and a `size`, and accepts an optional `opacity`. All values are fractions of the player surface from `0` through `1`: + +- `position` — at least one of `top`/`bottom` and one of `left`/`right`. +- `size` — the `width` and `height` of the overlay. +- `opacity` — the transparency of the overlay. + +```json +{ + "format": "overlay", + "assets": [ + { + "type": "static", + "mediaType": "image", + "uri": "https://cdn.example.com/ads/overlay.png" + } + ], + "position": { "top": 0.05, "right": 0.05 }, + "size": { "width": 0.3, "height": 0.2 }, + "opacity": 0.9 +} +``` + +### Variants + +Variants let one break target different devices with different experiences. Set `variant` to a list, and give each variant an optional `targeting.deviceType` (`desktop`, `tablet`, `mobile`, or `tv`): + +```json +{ + "duration": 30, + "variant": [ + { + "format": "double", + "targeting": { "deviceType": "desktop" }, + "assets": [ + { + "type": "static", + "mediaType": "video", + "uri": "https://cdn.example.com/ads/desktop.m3u8", + "companion": { + "type": "static", + "mediaType": "image", + "uri": "https://cdn.example.com/ads/desktop-companion.jpg" + } + } + ] + }, + { + "format": "single", + "targeting": { "deviceType": "mobile" }, + "assets": [ + { + "type": "static", + "mediaType": "video", + "uri": "https://cdn.example.com/ads/mobile.m3u8" + } + ] + }, + { + "format": "single", + "assets": [ + { + "type": "static", + "mediaType": "video", + "uri": "https://cdn.example.com/ads/default.m3u8" + } + ] + } + ] +} +``` + +The player picks the variant to render: + +- A variant **without** `targeting` is the **default**: it matches any device. +- The **order of the variants defines which one is chosen**. The player walks the list in order and picks the first variant it matches and supports. This also applies when multiple variants target the same device, or when multiple defaults exist — the earlier one wins if the player can render it, otherwise the player falls through to the next. + +In the example above, a desktop viewer gets the Double Box, a mobile viewer gets the mobile single, and every other device falls back to the default single. + +### Asset model + +An asset describes one piece of ad media inside a variant. Every asset shares these base properties: + +| Property | Description | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `id` | Identifier of the asset, unique within the break. Generated when omitted. | +| `type` | How the asset is retrieved: [`static`](#static-assets), [`vast`](#vast-assets), or [`vendor`](#vendor-assets). | +| `mediaType` | Whether the asset is a `video` or an `image`. | +| `mimeType` | Optional MIME type of the media, so players can fail fast when they cannot display it. | +| `duration` | Optional maximum duration of the asset, in seconds. Required when a break plays multiple assets. The break `duration` takes priority. | +| `interaction.clickThrough` | Optional URL to open when the viewer clicks or taps the asset. | + +#### Static assets + +A `static` asset is a media resource the player retrieves directly — a video or image URL served from your CDN, with no additional ad-serving logic: + +```json +{ + "type": "static", + "mediaType": "video", + "mimeType": "application/x-mpegurl", + "uri": "https://cdn.example.com/ads/spot.m3u8" +} +``` + +#### VAST assets + +A `vast` asset points to an ad server that responds with a VAST XML document describing how the ad should be played. The `uri` is the VAST tag URL, and the `mimeType` should be `application/xml` or `text/xml`: + +```json +{ + "type": "vast", + "mediaType": "video", + "mimeType": "application/xml", + "uri": "https://adserver.example.com/vast/creative-001.xml" +} +``` + +#### Vendor assets + +A `vendor` asset is delivered through an [ad vendor integration](../integrations/index.mdx), such as Google Ad Manager. The vendor decides the ad content; you identify the vendor and pass the vendor-specific parameters: + +```json +{ + "type": "vendor", + "vendor": "gam", + "mediaType": "video", + "vendorParameters": { + "type": "pod" + }, + "assetParameters": { + "airingId": "airing-001" + } +} +``` + +`vendorParameters` carries the parameters required to retrieve the asset from the vendor, and the optional `assetParameters` carry ad targeting parameters forwarded to the vendor. See [Google Ad Manager](../integrations/google.mdx) for the supported values. + +#### Asset URI targeting + +For `static` and `vast` assets, the `uri` can also be a list of `{ value, targeting }` entries, so one asset can point to different resources per device: + +```json +{ + "type": "static", + "mediaType": "image", + "uri": [ + { "value": "https://cdn.example.com/ads/overlay-tv.png", "targeting": { "deviceType": "tv" } }, + { "value": "https://cdn.example.com/ads/overlay-mobile.png", "targeting": { "deviceType": "mobile" } }, + { "value": "https://cdn.example.com/ads/overlay-default.png" } + ] +} +``` + +The selection rules match [Variants](#variants): an entry without `targeting` is the default, and when several entries match, the order in the list decides which one is used. + +## Related resources + +| Resource | Relationship | +| ------------------------------------------ | -------------------------------------------------------------------------------------- | +| [Channels](./channels.mdx) | The parent of a break. The channel's timebase defines how breaks are scheduled. | +| [Templates](./templates.mdx) | Reusable break presets that can be scheduled on the channel. | +| [Events](./events.mdx) | An event on your channel for which you want to prepare breaks. | +| [Origins](./origins.mdx) | Manifest URLs monitored for ad markers. A channel can have multiple origins. | +| [Break detection](./marker-detection.mdx) | Configure marker rules to turn ad markers in your stream into breaks. | +| [Break Manifest](./break-manifest.mdx) | The manifest that announces the channel's breaks to players. | +| [Integrations](../integrations/google.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | diff --git a/ads/concepts/channels.mdx b/ads/concepts/channels.mdx new file mode 100644 index 000000000000..036f880fd5b2 --- /dev/null +++ b/ads/concepts/channels.mdx @@ -0,0 +1,80 @@ +--- +sidebar_position: 1 +sidebar_label: Channels +--- + +# Channels + +A channel represents one live stream in OptiView Ads. It is the place where you decide how ads behave for that stream: how break start times are interpreted, which breaks are announced to players and when, and whether ad markers in the stream are detected automatically. + +Everything else in OptiView Ads hangs off a channel. Origins, marker rules, breaks, events, templates, and integrations are all created for — or looked up through — a channel. + +Channels are scoped to an organization. In the OptiView Unified Dashboard, open **Ads → Channels** to create and manage them. + +In the Dashboard, opening a channel gives you access to each of these tabs: + +| Tab | Use it for | +| ------------------------------------------ | --------------------------------------------------------------------- | +| Overview | View channel settings and Break Manifest configuration. | +| [Breaks](./breaks.mdx) | Schedule, inspect, and delete breaks for the channel. | +| [Events](./events.mdx) | Prepare breaks for an event on your live stream. | +| [Origins](./origins.mdx) | Add, enable, disable, and prioritize manifest origins. | +| [Break Detection](./marker-detection.mdx) | Configure marker rules to turn ad markers in your stream into breaks. | +| [Integrations](../integrations/google.mdx) | Manage channel-level delivery integrations. | + +## Channel identity + +Every channel has a customer-facing `id`. We recommend using a UUID as the channel `id`, and the `name` property as a human-readable name. If you omit `id` on creation, one is generated for you. + +## Timing and delivery settings + +These channel settings determine how break start times are interpreted and how breaks are delivered to players through the [Break Manifest](./break-manifest.mdx). + +### Timebase + +The `timebase` determines which timeline break start times are expressed on: + +- **`wallclock`** — breaks are scheduled with a UTC timestamp that is matched against the wallclock timeline carried by the stream itself. Use this when the stream carries wallclock timing: in HLS this comes from `EXT-X-PROGRAM-DATE-TIME` tags, in DASH from the MPD's `availabilityStartTime` combined with the segment timeline (optionally synchronized through a `UTCTiming` element). +- **`pts`** — breaks are scheduled with a presentation timestamp (PTS) on the encoder's timeline. The player retrieves the PTS value from the media segments to know where it is on that timeline. Use this when your workflow schedules breaks against encoder PTS values rather than wallclock time. + +Choose the timebase when creating the channel; all breaks on the channel use the same timebase. + +### DVR window + +_Default: `300000` ms (5 minutes)_ + +`dvrWindowMs` describes how far behind live a viewer can be while watching the channel — the time-shifted (DVR) window of the stream. + +It directly impacts the Break Manifest: a break is included in the manifest as long as it is still relevant for a viewer anywhere inside the DVR window. With a larger DVR window, breaks remain in the manifest for longer so that time-shifted viewers still receive them; with a small window, only breaks near the live edge are returned to the player. + +### Live offset + +_Default: `0` ms_ + +`liveOffsetMs` describes how far the player's playhead is behind live. Players never play exactly at the live edge — they buffer a few seconds behind it. + +OptiView Ads uses this offset mainly to allow you to schedule a break in the past: because viewers watch behind the live edge, a break aimed at the viewer's current playhead position lies slightly behind raw server time. With the offset configured, break start times are evaluated against the position viewers are actually watching instead of raw server time, so such a break is accepted and activates when the playhead reaches it. + +### Ad prefetch window + +_Default: `10000` ms (10 seconds)_ + +`adPrefetchMs` defines how far ahead of a break's start time the break is announced to the player through the Break Manifest. + +With the default of `10000` (10 seconds), a break whose start time is within the next 10 seconds is included in the manifest. This lead time gives players room to prepare and prefetch the ad content before the break actually starts. + +For creating, updating, listing, and deleting channels programmatically, see the Ads API reference. + +## Related resources + +A channel is the parent or lookup point for the rest of the OptiView Ads model: + +| Resource | Relationship | +| ------------------------------------------ | -------------------------------------------------------------------------------------- | +| [Breaks](./breaks.mdx) | Scheduled or detected ad opportunities for the channel. | +| [Templates](./templates.mdx) | Reusable break presets that can be scheduled on the channel. | +| [Events](./events.mdx) | An event on your channel for which you want to prepare breaks. | +| [Origins](./origins.mdx) | Manifest URLs monitored for ad markers. A channel can have multiple origins. | +| [Break detection](./marker-detection.mdx) | Configure marker rules to turn ad markers in your stream into breaks. | +| [Break Manifest](./break-manifest.mdx) | The manifest that announces the channel's breaks to players. | +| [Integrations](../integrations/google.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | diff --git a/ads/concepts/events.mdx b/ads/concepts/events.mdx new file mode 100644 index 000000000000..4a184e298e70 --- /dev/null +++ b/ads/concepts/events.mdx @@ -0,0 +1,60 @@ +--- +sidebar_position: 4 +sidebar_label: Events +--- + +# Events + +An event is a channel-scoped time window that groups the ad breaks belonging to one scheduled occurrence, such as a live game, a show, or a tournament. It gives you a single handle for the breaks around that occurrence: the breaks share the event's window, and deleting the event removes them together. + +Events also anchor the [break punching](./breaks.mdx#break-punching) workflow: ahead of a live occurrence you prepare breaks under the event without a start time, and during the broadcast you fire them at the exact moment. + +## Event identity + +Every event has an `id` that is unique within its channel. The `id` is optional when creating an event: if you omit it, OptiView Ads generates one for you. When you supply your own, we recommend using a UUID. For a human-readable label, use the `name` property instead — it is shown in the dashboard. + +## Time window + +An event is defined by a `startDate` and an `endDate`, both UTC ISO 8601 timestamps. `startDate` must be before `endDate`. + +Preparing breaks under an event is supported on **wallclock** channels. The window is enforced on the breaks scheduled under the event: the **entire** break must fit inside the window. + +- The break start must be at or after the event `startDate`. +- The break end (`start` + `duration`) must be at or before the event `endDate`. + +A break created **without** a start time (a [cued break](./breaks.mdx#cued-breaks)) is not range-checked at creation, because its start is not known yet. Its start is set when you punch it. + +## Breaks under an event + +A break is attached to an event by setting `eventId` to the event's `id` on the break. `eventId` is optional: a break can exist on the channel without belonging to any event. + +:::warning Deleting an event deletes its breaks +Deleting an event also removes every break attached to it. Delete an event only after confirming that none of its breaks are still needed. +::: + +## Templates for an event + +[Templates](./templates.mdx) can be linked to one or more events through their `eventIds`, so a reusable break preset can be surfaced for quick scheduling under those events. Instead of creating your breaks before the event, prepare templates ahead of time and schedule breaks from them during the event. + +## Break punching during an event + +For a live occurrence you usually do not know the exact break times in advance, but you want the break fully prepared so it can fire instantly. This is what [break punching](./breaks.mdx#break-punching) is for: + +1. Create the event with a window that covers the occurrence, for example kickoff through the final whistle. +2. Ahead of the occurrence, create the [templates](./templates.mdx) describing the breaks you want to run. +3. During the event, create a cued break from a template under the event, without a `start`. OptiView Ads prepares it (including any ad decisioning) and it waits in the `CUED` state. +4. Punch the break whenever it needs to go — for example, at half-time. Its start is set and it is announced to players right away. +5. Repeat for the next break: cue it from a template, then punch it at the right moment. + +A channel holds only one cued break at a time, so punch the current cued break before cueing the next one. Do not delete a cued break unless you have cued the wrong one. + +Because a punch uses the current time as the break's start, punch a cued break only while the event is in progress: this keeps the break inside the event's window. + +## Related resources + +| Resource | Relationship | +| ------------------------------------------ | -------------------------------------------------------------------------------------- | +| [Channels](./channels.mdx) | The parent of an event. An event always belongs to one channel. | +| [Breaks](./breaks.mdx) | Prepared under an event via `eventId` and scheduled within the event's window. | +| [Templates](./templates.mdx) | Reusable break presets that can be linked to events for quick scheduling. | +| [Integrations](../integrations/google.mdx) | Channel-level delivery integrations, such as Server-Side Ad Insertion with Google DAI. | diff --git a/ads/concepts/marker-detection.mdx b/ads/concepts/marker-detection.mdx new file mode 100644 index 000000000000..69e1e2cb8b62 --- /dev/null +++ b/ads/concepts/marker-detection.mdx @@ -0,0 +1,82 @@ +--- +sidebar_position: 6 +sidebar_label: Break Detection +--- + +# Break Detection + +A channel can automatically detect ad markers (such as SCTE-35 cues or `EXT-X-DATERANGE` tags) in its origin manifests and turn them into breaks by applying marker rules. Detection is enabled or disabled per channel. Automatic detection currently supports HLS manifests only. See [Origins](./origins.mdx) for origin selection, priority ordering, and first-online behavior. + +## How detection works + +When detection is enabled, OptiView Ads polls the channel's enabled [origins](./origins.mdx) in priority order and selects the first online origin. It parses the manifest's markers, evaluates the enabled marker rules, creates breaks for matching markers, and records the result in [detection history](#detection-history). + +## Supported markers + +Detection recognizes two marker kinds in HLS manifests: + +| Marker rule type | HLS marker | Detection behavior | +| ---------------- | ------------------ | ------------------------------------------------------------------------------------------------- | +| `CUE` | `#EXT-X-CUE-OUT` | Parses a marker start and optional duration. | +| `DATERANGE` | `#EXT-X-DATERANGE` | Requires a valid `START-DATE`. Duration comes from `DURATION`, `PLANNED-DURATION`, or `END-DATE`. | + +`DATERANGE` is not limited to Apple interstitials. Any `#EXT-X-DATERANGE` tag with a valid start is considered and can be matched by its attributes. + +## Marker rules + +A marker rule turns a detected marker into a break created from a [template](./templates.mdx). The rule's `type` must match the marker kind, and every configured condition must match the marker attributes. Attribute keys are compared case-insensitively. + +Every marker rule has an `id` that is unique within its channel. The `id` is optional when creating a rule: if you omit it, OptiView Ads generates one for you. When you supply your own, we recommend using a UUID. + +| Property | Description | +| ------------ | ---------------------------------------------------------------------------------------------------------------------------------- | +| `type` | The marker kind this rule matches: `CUE` or `DATERANGE`. | +| `conditions` | Attribute key/value pairs that must all match on the marker for the rule to fire. An empty object matches any marker of that type. | +| `templateId` | The [template](./templates.mdx) to schedule the break from when the rule matches. | +| `enabled` | Whether the rule participates in detection. | + +For example, this rule matches `DATERANGE` markers whose `CLASS` attribute is `com.example.ad`: + +```json +{ + "streamType": "HLS", + "type": "DATERANGE", + "conditions": { "CLASS": "com.example.ad" }, + "templateId": "3b8e5f0a-7c2d-4e91-a6b3-9d4f1c8e2a70", + "enabled": true +} +``` + +## Detection history + +Detection history is the audit trail of what automatic detection decided for each marker. Once an origin is enabled, every marker found in the stream is recorded — even when no marker rule exists yet. This lets you review the markers appearing in your stream and identify which ones you want to turn into breaks before configuring your rules. + +Every processed marker is recorded with one of three outcomes: + +| Action | Meaning | +| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `CREATED` | A rule matched and a break was scheduled. The record links to the matched rule and the created break. | +| `SKIPPED` | No fault: the marker was ineligible (unparseable or without a resolvable start), no rule matched, or a scheduling condition prevented creation. | +| `FAILED` | An eligible, rule-matched marker could not be scheduled for an unexpected reason. | + +Each record includes the raw manifest tag line, the origin that supplied the marker, and a `reason` explaining skips and failures. History is deduplicated per channel: repeated polling of the same marker, including seeing it on another origin, does not create duplicate records. + +## Troubleshooting + +| Symptom | Checks | +| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| No breaks are created. | Is detection enabled on the channel? Is there at least one enabled HLS origin? Is the origin reachable and returning a parseable manifest? Is there an enabled marker rule whose `type` and `conditions` match the marker? Does the rule's template exist? | +| History contains `SKIPPED` with `NO_RULES_CONFIGURED`. | An enabled origin is already being polled and markers are being detected, but the channel has no marker rules yet. Use these records to identify the markers you want to act on, then create and enable a matching marker rule. | +| History contains `SKIPPED` with `NO_RULE_MATCHED`. | Check the rule `type` and all `conditions` against the marker attributes. Attribute keys are matched case-insensitively, but values must match. | +| DASH or HESP origin is not producing breaks. | DASH and HESP origins are accepted but skipped by automatic detection. Use an enabled HLS origin. | +| History contains `SKIPPED` with a scheduling reason. | The marker was recognized, but the break was not scheduled — for example because its start would lie in the past or it would overlap another break. See [Scheduling constraints](./breaks.mdx#scheduling-constraints). | +| History contains `FAILED`. | The rule matched, but an unexpected scheduling or configuration error prevented break creation. Inspect the `reason` and verify the template and break configuration. | + +## Related resources + +| Resource | Relationship | +| ---------------------------- | ---------------------------------------------------------------------------- | +| [Channels](./channels.mdx) | Detection is enabled or disabled per channel. | +| [Origins](./origins.mdx) | Manifest URLs monitored for ad markers. A channel can have multiple origins. | +| [Templates](./templates.mdx) | Reusable break presets that marker rules schedule when a marker matches. | +| [Breaks](./breaks.mdx) | The breaks created when detection matches a marker against a marker rule. | diff --git a/ads/concepts/origins.mdx b/ads/concepts/origins.mdx new file mode 100644 index 000000000000..d140a0f60374 --- /dev/null +++ b/ads/concepts/origins.mdx @@ -0,0 +1,38 @@ +--- +sidebar_position: 5 +sidebar_label: Origins +--- + +# Origins + +An origin is a manifest URL that a channel monitors for ad markers. When [break detection](./marker-detection.mdx) is enabled, OptiView Ads fetches the channel's enabled origins and parses their manifests for markers. A channel can have multiple origins so that detection keeps working when one source goes offline. + +## Origin identity + +Every origin has an `id` that is unique within its channel. The `id` is optional when creating an origin: if you omit it, OptiView Ads generates one for you. When you supply your own, we recommend using a UUID. For a human-readable label, use the `name` property instead — it is shown in the dashboard. + +## Enabling and disabling origins + +An origin can be enabled or disabled at any time. Disabling an origin removes it from detection immediately; the origin itself is kept, so you can re-enable it later without recreating it. + +## How multiple origins are used + +Only enabled origins are considered for detection. Enabled origins are ordered by `priority`, and detection uses the **first online origin**: the first one whose manifest is fetched and parsed successfully. + +- **Lowest `priority` value first.** +- **First online wins.** An origin counts as online when its manifest can be fetched and parsed. A manifest that is reachable but currently advertises no markers still counts as online, so lower-priority origins are not consulted. If an origin cannot be fetched or parsed, detection falls back to the next enabled origin in priority order. + +This lets you configure a primary origin and one or more lower-priority backups: when the primary source is unreachable, detection automatically falls back to a backup. + +:::note Supported origin types +Origins can be `HLS`, `DASH`, or `HESP`, but automatic break detection currently parses **HLS** manifests only. Use `HLS` for origins you expect to drive automatic breaks. +::: + +## Related resources + +| Resource | Relationship | +| ----------------------------------------- | ------------------------------------------------------------------------- | +| [Channels](./channels.mdx) | The parent of an origin. An origin always belongs to one channel. | +| [Break detection](./marker-detection.mdx) | Configure marker rules to turn ad markers in your stream into breaks. | +| [Templates](./templates.mdx) | Reusable break presets that marker rules schedule when a marker matches. | +| [Breaks](./breaks.mdx) | The breaks created when detection matches a marker against a marker rule. | diff --git a/ads/concepts/templates.mdx b/ads/concepts/templates.mdx new file mode 100644 index 000000000000..493fb866b7be --- /dev/null +++ b/ads/concepts/templates.mdx @@ -0,0 +1,68 @@ +--- +sidebar_position: 3 +sidebar_label: Templates +--- + +# Templates + +A template is a reusable break preset for OptiView Ads. It stores a break configuration once so you can schedule consistent breaks quickly, either manually from the dashboard and API or automatically through marker rules. + +## Template identity + +Every template has an `id` that is unique within your organization. The `id` is optional when creating a template: if you omit it, OptiView Ads generates one for you. When you supply your own, we recommend using a UUID. For a human-readable label, use the `name` property instead — it is shown in the dashboard. + +## What a template contains + +A template holds the same configuration as a break: anything you can express on a break you can preset on a template. See [Break configuration](./breaks.mdx#break-configuration) for the full description of `variant`, `resumeOffset`, and `controls`. + +A few properties differ from a break: + +| Property | Difference | +| ------------ | --------------------------------------------------------------------------------------------------------------------------------- | +| `name` | A human-readable display name for the template, shown in the dashboard. | +| `duration` | Optional on a template (it is required on a break). When set, it is the default duration for breaks scheduled from the template. | +| `channelIds` | Channels the template is linked to, so it is surfaced when scheduling breaks on those channels. | +| `eventIds` | [Events](./events.mdx) the template is linked to. | +| `start` | A template has no `start`: a template is not scheduled itself. The start is supplied when a break is scheduled from the template. | + +## Snapshot semantics + +A template is a preset, not a live link. When a break is scheduled from a template: + +1. The template's configuration is **copied onto the new break** at creation. +2. The break records the source `templateId` as provenance. +3. There is **no synchronization afterwards**. Editing or deleting the template later does not change breaks that were already created from it — they keep their copied configuration. + +## Scheduling a break from a template + +Templates are used throughout the system to schedule breaks: + +- **API** — create a break on a channel and reference the template with `templateId`. The template configuration is snapshotted onto the break at creation. +- **Dashboard** — use the **Schedule now** action on a template to create a break on the channel immediately. +- **Marker rules** — when an in-stream ad marker is matched against a [marker rule](./marker-detection.mdx), a break is scheduled from the template that the rule references. + +An API example: + +```bash +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01/breaks' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "templateId": "3b8e5f0a-7c2d-4e91-a6b3-9d4f1c8e2a70", + "start": "2026-07-16T13:00:00.000Z" + }' +``` + +`templateId` is the only required field. You can override the snapshotted configuration per break with optional fields — `start`, `duration`, `variant`, `eventId`, and `id`. For templates that contain [vendor assets](./breaks.mdx#vendor-assets), you can additionally pass `assetParameters` (a string map) with the request; these are merged into the vendor assets of the snapshot, and on duplicate keys the values you provide win. + +Start semantics depend on the channel timebase; see [Timebase-based scheduling](./breaks.mdx#timebase-based-scheduling). + +## Related resources + +| Resource | Relationship | +| ----------------------------------------- | ------------------------------------------------------------------------------------- | +| [Channels](./channels.mdx) | Breaks scheduled from a template are created on a channel. | +| [Breaks](./breaks.mdx) | The result of scheduling a template: a break with the template's snapshotted content. | +| [Events](./events.mdx) | An event on your channel for which you want to prepare breaks. | +| [Break detection](./marker-detection.mdx) | Configure marker rules to turn ad markers in your stream into breaks. | diff --git a/ads/integrations/google.mdx b/ads/integrations/google.mdx new file mode 100644 index 000000000000..b82fc01db8a3 --- /dev/null +++ b/ads/integrations/google.mdx @@ -0,0 +1,143 @@ +--- +sidebar_position: 2 +sidebar_label: Google Ad Manager +--- + +# Google Ad Manager 360 + +Google Ad Manager 360 (GAM 360) is the first supported OptiView Ads vendor. It requires a GAM 360 account with Dynamic Ad Insertion (DAI) and pod serving enabled. + +## Organization configuration + +Google configuration is organization-level and managed by your Dolby OptiView account team. It links your organization to your Google Ad Manager network (network code and service account) and tunes the signaling behavior: + +- **Look-forward time** — how far ahead of a break's start OptiView Ads signals it to Google, so the ad decisioning can complete in time. Defaults to 5 minutes. +- **Decisioning margin** — the minimum lead time required before a break's start for decisioning. Breaks scheduled closer than this margin are rejected. Defaults to 5 seconds. + +Contact your account team to set up or change these values. + +## SGAI pod serving + +SGAI is server-guided pod serving keyed by the channel's `customAssetKey`: Google decisions the ad pods for the stream, and OptiView Ads announces the resulting breaks to players. + +On the [channel](../../concepts/channels) you configure the `customAssetKey`: the Google DAI custom asset key that identifies this live stream in Google Ad Manager. It must be unique within your organization. Set it when the channel uses Google DAI pod serving; leave it unset otherwise. + +A GAM pod break uses a vendor asset with `vendorParameters.type` set to `"pod"`: + +```json +{ + "type": "vendor", + "vendor": "gam", + "uri": "placeholder", + "vendorParameters": { + "type": "pod" + } +} +``` + +### Pod decisioning + +1. A GAM pod break is created and starts in the `PREPARING` state. +2. Ahead of the break's start (within the look-forward time), OptiView Ads announces the break to Google DAI through the channel's `customAssetKey`, and Google decisions the ad pod. +3. Once decisioned, the vendor asset's `uri` carries the resulting pod ID. The break becomes `READY` when it has a start time, or `CUED` when it is waiting to be [punched](../concepts/breaks.mdx#break-punching). +4. When the break is announced to players, it becomes `SIGNALED`. The player requests the pod manifest using the vendor asset `uri`. + +A GAM pod break can only be punched after Google has decisioned it — that is, once it has left the `PREPARING` state. + +## SSAI_DAI + +`SSAI_DAI` is a channel integration. It carries one or more Google DAI asset keys: + +```json +{ + "type": "SSAI_DAI", + "daiAssetKeys": ["sports-main-1", "sports-main-2"] +} +``` + +Each DAI asset key can be used by at most one channel integration within an organization. Duplicate keys in one request are de-duplicated. A conflict with another channel integration returns HTTP `409`: + +```text +One or more daiAssetKeys are already used by another channel integration +``` + +Create an integration with the self-serve API: + +```bash +curl -X POST 'https://us.ads.optiview.dolby.com/api/v1/channels/1f7f3a5a-9c2e-4a56-b1d4-3f8a2c9d6e01/integrations' \ + -u "$ADS_API_KEY:$ADS_API_SECRET" \ + -H 'Content-Type: application/json' \ + -H 'X-Org-ID: org_123' \ + -d '{ + "type": "SSAI_DAI", + "daiAssetKeys": [ + "sports-main-1", + "sports-main-2" + ] + }' +``` + +### Best-effort fan-out + +When a break is signaled, it is announced to each of the integration's `daiAssetKeys`. The signals are best-effort: a failure for one key does not affect the other keys. + +### SSAI cue injection + +For an HLS wallclock channel with an `SSAI_DAI` integration, OptiView Ads injects `EXT-X-DATERANGE` cues (carrying `SCTE35-OUT` and `SCTE35-IN` data) for upcoming breaks into the media playlist delivered to players. After injection, the affected breaks become `SIGNALED`. + +PTS channels, and channels without an `SSAI_DAI` integration, receive their manifests unchanged, with no ad cue injection. + +## Ad targeting parameters + +In V2, a vendor asset's optional `assetParameters` carry ad-tag and targeting parameters. OptiView Ads forwards them to Google as custom parameters during decisioning. + +For the player-side SDK `adTagParameters` usage, see [Ad tag parameters](../../how-to-guides/ad-tag-parameters). The player automatically adds `theoads_slot`. + +## Custom GAM creatives + +Dynamic backdrops and overlays require custom creative templates configured in the GAM console. See [Custom GAM creatives](../../how-to-guides/gam-custom-creatives). + +## Troubleshooting + +### Break status `ERROR` + +A break can be stored with status `ERROR` and: + +```text +Break passed its scheduling window before it could be signaled +``` + +This means the break missed its scheduling window: the remaining time before its start fell below the decisioning margin before it could be signaled. Schedule pod breaks at least the decisioning margin ahead of the live point and verify the Google configuration. + +### GAM configuration error + +Break creation returns HTTP `400` when the organization's Google configuration or the channel's custom asset key is missing: + +```text +Vendor asset of type GAM requires organization.google.networkCode, organization.google.serviceAccountPath and channel.customAssetKey to be configured +``` + +Verify that your organization's [Google configuration](#organization-configuration) is set up (contact your account team) and that the channel has a `customAssetKey`. + +### Pod break too close to live + +Break creation returns HTTP `400` when a pod break starts too close to the live point: + +```text +POD ad breaks must start at least ms after the live point to allow time for ad decisioning +``` + +The default `` is `5000`. + +### Break remains `PREPARING` + +If a break never leaves `PREPARING`, the signal to Google may be skipped because the organization's Google configuration is incomplete. Check the [organization configuration](#organization-configuration) with your account team and confirm that the channel has the required delivery key: `customAssetKey` for SGAI, or an `SSAI_DAI` integration with `daiAssetKeys`. + +## Related resources + +| Resource | Relationship | +| ------------------------------------------------ | -------------------------------------------------------------------------- | +| [Channels](../concepts/channels.mdx) | Hold the `customAssetKey` used for Google server-guided pod serving. | +| [Breaks](../concepts/breaks.mdx) | Carry the vendor asset that Google uses to make ad decisions. | +| [Break Manifest](../concepts/break-manifest.mdx) | The manifest that announces the channel's breaks to players. | +| [API reference](/ads/api) | Creating and managing channels, breaks, and integrations programmatically. | diff --git a/ads/integrations/index.mdx b/ads/integrations/index.mdx new file mode 100644 index 000000000000..cec5ea5e85aa --- /dev/null +++ b/ads/integrations/index.mdx @@ -0,0 +1,25 @@ +--- +sidebar_position: 1 +sidebar_label: Integrations +--- + +# Integrations + +An integration connects OptiView Ads to an ad decisioning or "pod" serving vendor. Vendors are supported by providing [assets of type Vendor](../concepts/breaks.mdx#vendor-assets) in a break variant: the vendor makes the ad decisions, and OptiView Ads delivers the result to players. [Google Ad Manager 360](./google) is the first supported integration. + +## Supported vendors + +| Vendor | Enum value | Delivery | +| --------------------------------- | ---------- | ----------------------------- | +| [Google Ad Manager 360](./google) | `gam` | SGAI pod serving and SSAI DAI | + +The vendor model is extensible. When another vendor is supported, its documentation will be added as a separate page in this section and listed in the Integrations sidebar. + +## Related resources + +| Resource | Relationship | +| -------------------------------------- | ------------------------------------------------------------------------------------ | +| [Channels](../concepts/channels.mdx) | Hold the channel-level `customAssetKey` used for Google server-guided pod serving. | +| [Breaks](../concepts/breaks.mdx) | Carry the vendor asset in a break variant. | +| [Templates](../concepts/templates.mdx) | Reusable break presets that can include vendor assets. | +| [Google Ad Manager](./google.mdx) | The first supported vendor: SGAI pod serving and the `SSAI_DAI` channel integration. | diff --git a/sidebarsAds.ts b/sidebarsAds.ts index c0ca37d24082..7975a313b0b4 100644 --- a/sidebarsAds.ts +++ b/sidebarsAds.ts @@ -15,6 +15,26 @@ const sidebars: SidebarsConfig = { link: { type: 'doc', id: 'getting-started/index' }, items: [{ type: 'autogenerated', dirName: 'getting-started' }], }, + { + type: 'category', + label: 'Core concepts', + description: 'Understand the resources that power OptiView Ads.', + collapsed: false, + customProps: { + icon: '📚', + }, + items: [{ type: 'autogenerated', dirName: 'concepts' }], + }, + { + type: 'category', + label: 'Integrations', + description: 'Configure integrations for server-guided and server-side ad insertion.', + customProps: { + icon: '🏷️', + }, + link: { type: 'doc', id: 'integrations/index' }, + items: [{ type: 'autogenerated', dirName: 'integrations' }], + }, { type: 'category', label: 'How-to guides',