# Ad Breaks

## Overview

The Web Receiver SDK features native support for ad breaks and companion ads
within a given media stream. It provides APIs to set the ad position, ad source,
and behavior of ad breaks and their associated break clips. In this guide, a
[`Break`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break)
refers to an interval for playback containing one or more ads or bumpers and
each ad or bumper is referred to as a
[`BreakClip`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.BreakClip).
These breaks are associated to the media that is being loaded or playing.

### Ad types

The Web Receiver SDK supports client side ad insertion (CSAI) and server
stitched ad insertion (SSAI). **Client-stitched** ads can be manually set by the
application or extracted from **VAST** and **VMAP** template files.
**Server-stitched** ads should be specified manually prior to content load as
**embedded** ads or dynamically during content playback as **embedded expanded**
ads. Implementations for each of these ad types are described in detail below.

### manual client-stitched

The **manual client-stitched ad break** is a type of ad break that is stitched
together by the client and is specified manually by the application using the
SDK APIs. This ad type is not embedded in the main content's stream. The
`BreakClip` must provide the
[`contentId`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.BreakClip#contentId)
which is a URL that points to the ad content, the
[`contentType`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.BreakClip#contentType)
describing the ad content's format, and the
[`title`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.BreakClip#title).

> [!NOTE]
> **Note:** In some cases it is required to provide additional ad content format information in the [`hlsSegmentFormat`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.BreakClip#hlsSegmentFormat) property for **HLS** ads played by MPL. Similar to the main content's [segment format support](https://developers.google.com/cast/docs/media/streaming_protocols#segment_format_support) guide, this property communicates to the player the expected container format for the underlying ad segments.

The `Break` must have
[`isEmbedded`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#isEmbedded)
and
[`expanded`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#expanded)
set to the default value `false`. The
[`position`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#position)
can be set to a *pre-roll* , *mid-roll* or *post-roll* ad break (see more in the
[break positioning](https://developers.google.com/cast/docs/web_receiver/ad_breaks#break_positioning) section). When preparing the
ad for playback, the Web Receiver SDK generates another player instance to load
and play the ad content. These breaks require a `stitched timeline` and must be
added **statically** (see more in the
[ad insertion](https://developers.google.com/cast/docs/web_receiver/ad_breaks#ad_insertion) section). The sample below shows a basic
implementation of a **manual client-stitched** ad:

    // Create the BreakClip.
    let clipClient = new cast.framework.messages.BreakClip('bc_client');
    clipClient.title = 'The Ad Title to be displayed during playback';
    clipClient.contentId = 'https://example.com/ad.m3u8';
    clipClient.contentType = 'application/vnd.apple.mpegurl';

    // Optional: Used when HLS ad container formats differ from the main content's.
    clipClient.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.FMP4;

    // Create the Break using the BreakClip id above.
    let breakPostrollClient = new cast.framework.messages.Break(
        'break_postroll_client', ['bc_client'], -1);
    breakPostrollClient.isEmbedded = false; // Optional: default is false.
    breakPostrollClient.expanded = false; // Optional: default is false.

### VAST

The Web Receiver SDK supports adding IAB standard **VAST** (Video Ad Serving
Template) ads. When provided, the XML template is parsed to generate a
subsequent **client-stitched** break clip upon entering the break.

To create a VAST ad, the receiver app must create a
[`VastAdsRequest`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.VastAdsRequest)
and specify it in the `BreakClip`
[`vastAdsRequest`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.BreakClip#vastAdsRequest)
property. The `VastAdsRequest` object must have either the `adsResponse` (a
string representation of the XML template itself) or the `adTagUrl` (the url
where the XML template is hosted) property defined. If the URL is specified, the
SDK will handle fetching the template. The encapsulating `Break` follows
conventions for **client-stitched** ads. These ads can be added along other
**manual client-stitched** ads in the same break or in separate breaks for the
same piece of content. The sample below shows a basic implementation of a VAST
ad:

    // Create the VastAdsRequest.
    let vastTemplate = new cast.framework.messages.VastAdsRequest();
    vastTemplate.adTagUrl = 'https://example.com/ads.xml'

    // Create the BreakClip.
    let clipVast = new cast.framework.messages.BreakClip('bc_vast');
    clipVast.vastAdsRequest = vastTemplate;

    // Create the Break using the BreakClip id above.
    let breakPostrollVast = new cast.framework.messages.Break(
        'break_postroll_vast', ['bc_vast'], -1);
    breakPostrollVast.isEmbedded = false; // Optional: default is false.
    breakPostrollVast.expanded = false; // Optional: default is false.

When a `Break` that contains a **VAST** `BreakClip` is entered, the Web Receiver
SDK will optionally fetch, and then parse the template. While parsing, the SDK
will generate a new `BreakClip` and populate it with the extracted values from
the template such as the `contentId`, `contentType`, `title`, `duration`,
`whenSkippable`, and `clickThroughUrl`. The `id` for the generated break clip is
set to `GENERATED:N` where `N` is an integer that increments by `1` for each new
**VAST** break clip created starting at `0`. The generated ad is then added to
the `BreakClip` array. Each **VAST** break clip's `id` in the current `Break` is
then replaced with its corresponding generated break clip's `id`. The snippets
below illustrate the changes in the
[`MEDIA_STATUS`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.MediaStatus)
messages that pertain to ads before and after entering such a break.

> [!NOTE]
> **Note:** The `BreakClip` and `Break` arrays are updated and accessible in [`BreakManager`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.breaks.BreakManager). This information is embedded in the `MEDIA_STATUS` which is broadcast to senders.

**`Break` and `BreakClip` information prior to entering a break with VAST ads.**

    "breaks": [
      {
        "id": "break_postroll_vast",
        "breakClipIds": [
          "bc_vast"
        ],
        "position": 0,
        "isWatched": false
      }
    ],
    "breakClips": [
      {
        "id": "bc_vast"
      }
    ]

**`Break` and `BreakClip` information after entering a break with VAST ads.**

    "breaks": [
      {
        "id": "break_postroll_vast",
        "breakClipIds": [
          "GENERATED:0"
        ],
        "position": 0,
        "isWatched": true
      }
    ],
    "breakClips": [
      {
        "id": "bc_vast"
      },
      {
        "id": "GENERATED:0",
        "contentId": "https://example.com/break-clip-1.mpd",
        "contentType": "application/dash+xml",
        "title": "Ad Title Extracted from Template",
        "duration": 10,
        "whenSkippable": 5,
        "clickThroughUrl": "https://example.com/ad-target"
      }
    ]

### VMAP

The Web Receiver SDK supports the IAB **VMAP** (Video Multiple Ad Playlists)
standard. When a VMAP is provided, the Web Receiver SDK will parse the VMAP
response and generate **client-stitched** `Break` objects for any `<AdBreak>`
entries in the response. It will also generate the appropriate `BreakClips` with
a `vastAdsRequest` object for each `<AdSource>` entry provided in the VMAP. To
enable VMAP for inserting ads into your content, the application must create a
[`VastAdsRequest`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.VastAdsRequest)
object and assign it to the
[`vmapAdsRequest`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.MediaInformation#vmapAdsRequest)
property of the
[`MediaInformation`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.MediaInformation)
in the
[`LoadRequestData`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.LoadRequestData).
These ads must be inserted **statically** (see more in the
[ad insertion](https://developers.google.com/cast/docs/web_receiver/ad_breaks#ad_insertion) section). Below is a snippet outlining the
creation of a VMAP request.

> [!NOTE]
> **Note:** Manually setting `Break` and `BreakClip` ads in conjunction with VMAP ads will override the VMAP ad request. As a result, the SDK will drop the VMAP ad request and will default to using the manually specified ads in the `MediaInformation`.

    // Create the VastAdsRequest.
    let vastTemplate = new cast.framework.messages.VastAdsRequest();
    vastTemplate.adTagUrl = 'https://example.com/vmap.xml'

    // Add it to the MediaInformation of the LoadRequest.
    loadRequestData.media.vmapAdsRequest = vastTemplate;

### embedded

The **embedded ad break** is a type of ad break that is stitched server side
into the main content's stream. The duration of the `Break` is **subtracted**
from the main content's duration when calculating the media time.

The `BreakClip` must provide the
[`duration`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.BreakClip#duration)
of the ad content, and the
[`title`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.BreakClip#title).
The `Break` must have
[`isEmbedded`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#isEmbedded)
set to `true` and
[`expanded`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#expanded)
set to `false`. The
[`position`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#position)
can be set to as a *pre-roll* or *mid-roll* ad break. *Post-roll* ad breaks are
supported with positive exact `position` values. See more on this in the
[break positioning](https://developers.google.com/cast/docs/web_receiver/ad_breaks#break_positioning) section. When the ad is triggered to
play, the Web Receiver SDK continues playback of the stream as the ad segments
are embedded in it. There is no additional loading mechanism for this ad type.
The relevant ad metadata is shown to the user once the playhead is within the
break time range. These breaks require an `embedded timeline` and must be added
**statically** (see more in the [ad insertion](https://developers.google.com/cast/docs/web_receiver/ad_breaks#ad_insertion) section). The
sample below shows a basic implementation of an `embedded` ad.

    // Create the BreakClip.
    let clipEmbedded = new cast.framework.messages.BreakClip('bc_embedded');
    clipEmbedded.title = 'The Ad Title to be displayed during playback';
    clipEmbedded.duration = 15;

    // Create the Break using the BreakClip id above.
    let breakPrerollEmbedded = new cast.framework.messages.Break(
        'break_preroll_embedded', ['bc_embedded'], 0);
    breakPrerollEmbedded.isEmbedded = true;
    breakPrerollEmbedded.expanded = false; // Optional: default is false.

### embedded expanded

The **embedded expanded ad break** is a type of ad break that is stitched server
side into the main content's stream. The duration of the `Break` is **included**
in the main content's duration when calculating the media time.

The `BreakClip` must provide the
[`duration`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.BreakClip#duration)
of the ad content, and the
[`title`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.BreakClip#title).
The `Break` must have
[`isEmbedded`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#isEmbedded)
set to `true` and
[`expanded`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#expanded)
set to `true`. The
[`position`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#position)
can be set to as a *pre-roll* or *mid-roll* ad break. *Post-roll* ad breaks are
supported with positive `position` values. See more on this in the
[break positioning](https://developers.google.com/cast/docs/web_receiver/ad_breaks#break_positioning) section. When the ad is triggered to
play, the Web Receiver SDK continues playback of the stream as the ad segments
are embedded in it. There is no additional loading mechanism for this ad type.
The relevant ad metadata is shown to the user once the playhead is within the
break time range. These breaks require an `embedded timeline` and can be added
either **statically** or **dynamically** (see more in the
[ad insertion](https://developers.google.com/cast/docs/web_receiver/ad_breaks#ad_insertion) section). The sample below shows a basic
implementation of an `embedded expanded` ad:

    // Create the BreakClip.
    let clipEmbeddedExpanded =
        new cast.framework.messages.BreakClip('bc_embedded_expanded');
    clipEmbeddedExpanded.title = 'The Ad Title to be displayed during playback';
    clipEmbeddedExpanded.duration = 15;

    // Create the Break using the BreakClip id above.
    let breakPrerollEmbeddedExpanded = new cast.framework.messages.Break(
        'break_preroll_embedded_expanded', ['bc_embedded_expanded'], 0);
    breakPrerollEmbeddedExpanded.isEmbedded = true;
    breakPrerollEmbeddedExpanded.expanded = true;

### Player timeline types

When creating a player instance, the Web Receiver SDK selects a timeline type to
support playing ads during content playback. Each timeline enables certain ad
break types to be added. The timeline type is determined by the
[ad types](https://developers.google.com/cast/docs/web_receiver/ad_breaks#ad_types) present during load time in the
[`MediaInformation`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.MediaInformation)
of the
[`LoadRequestData`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.LoadRequestData).
If **embedded** ad breaks are present, the `embedded` timeline is selected. If
**client-stitched** ad breaks are present, the `stitched` timeline is selected.
In the case that no ads are present, the SDK defaults to using the `embedded`
timeline. Once the timeline is selected, it cannot be changed for the current
media item. The table below provides a detailed description of each timeline.

| Timeline Type | Description |
|---|---|
| **embedded timeline** | A representation of media time that supports ads which are embedded into the main content (**embedded** and **embedded expanded** ad breaks). When an unexpanded ad break is present, the duration of it is subtracted from the total duration of the content. On the other hand, when an expanded ad break is present, its time is considered to be a part of the main content. |
| **stitched timeline** | A representation of media time which supports ads that are sourced from external media files (**manual client-stitched** , **VAST** and **VMAP** ad breaks). When added, the ad break's duration is not a part of the main content's duration. |

*Figures 1 to 3* below illustrate some content with varied ad types and their
respective timeline values. The content is configured with a *pre-roll* break
containing two break clips and *mid-roll* and *post-roll* breaks containing a
single break clip. The **wall clock time** since the start of content playback,
the **media time** of the main content, and the time of the break's currently
playing break clip are aligned below each figure.
![Timeline of client-stitched ads](https://developers.google.com/static/cast/images/ad-breaks/client-stitched-ads.svg) **Figure 1:** The timeline representing some content and its 3 **client-stitched** ad breaks.

<br />


![Timeline of server-stitched embedded ads](https://developers.google.com/static/cast/images/ad-breaks/embedded-ads.svg) **Figure 2:** The timeline representing some content and its 3 **server-stitched embedded** ad breaks.

<br />

<br />


![Timeline of server-stitched embedded expanded ads](https://developers.google.com/static/cast/images/ad-breaks/embedded-expanded-ads.svg) **Figure 3:** The timeline representing some content and its 3 **server-stitched embedded expanded** ad breaks.

<br />

### Break positioning

The Web Receiver SDK allows developers to specify where ad breaks should be
placed by setting the
[`position`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#position)
property of the `Break`. This value corresponds to the main content's media time
and can be used to create `pre-roll`, `mid-roll`, and `post-roll` ad breaks.
These are defined as follows:

| Break Position | Description |
|---|---|
| **pre-roll** | An ad break that is played before the main content. This is denoted by setting the `breakPosition` to `0` |
| **mid-roll** | An ad break that is played mid content. This is denoted by setting the `breakPosition` to a time in which the break's start is greater than the start of the main content, and the break's end time is less than the main content's end time. |
| **post-roll** | An ad break that is played after the main content. This is denoted by setting the `breakPosition` to `-1` for [stitched timelines](https://developers.google.com/cast/docs/web_receiver/ad_breaks#player_timeline_types). For [embedded timelines](https://developers.google.com/cast/docs/web_receiver/ad_breaks#player_timeline_types) the `breakPosition` should be set to the main content's duration subtracted by the duration of the break. Not supported for live content. |

### Interoperability matrix

As a quick reference point, *Table 1* shows an overview of the ad types and
their compatibility with ad related features.

| Feature Support | manual client-stitched ad | VAST | VMAP | embedded ad | embedded expanded ad |
|---|:---:|:---:|:---:|:---:|:---:|
| compatible with | VAST | manual client-stitched | N/A | embedded expanded | embedded |
| timeline | stitched | stitched | stitched | embedded | embedded |
| ad insertion | static | static | static | static | static, dynamic |
| ad removal | No | No | No | No | Yes |
| pre-roll ad | Yes | Yes | Yes | Yes | Yes |
| mid-roll ad | Yes | Yes | Yes | Yes | Yes |
| post-roll ad | Yes | Yes | Yes | Yes | Yes |
| ad skip | Yes | Yes | Yes | Yes | Yes |
| break seek interceptor | Yes | Yes | Yes | Yes | Yes |
| break clip load interceptor | Yes | Yes | Yes | No | No |
[*Table 1: Ads interoperability matrix*]

## Events

When key break events occur, the cast SDK will dispatch events of type
[`BreaksEvent`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.events.BreaksEvent).
A receiver app can subscribe to them using the `PlayerManager`
[`addEventListener`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.PlayerManager#addEventListener)
API.

These events can be used for analytics and ad playback tracking. When **VMAP**
(Video Multiple Ad Playlist) and **VAST** (Video Ad Serving Template) ads are
used, any standard tracking events provided in the responses are automatically
dispatched by the SDK.

The event types are listed in *Table 2* along with a detailed description on
when they are fired.
![Break Events Lifecycle](https://developers.google.com/static/cast/images/ad-breaks/break-lifecycle.svg) **Figure 4**: The break events lifecycle.

| Break Event | Description |
|---|---|
| `BREAK_STARTED` | Fired when the current media time of the main content is equal to the `position` of an unwatched break. |
| `BREAK_CLIP_LOADING` | Fired only when a **stitched timeline** break clip starts loading. |
| `BREAK_CLIP_STARTED` | Fired when a break clip starts playback. |
| `BREAK_CLIP_ENDED` | Fired when a break clip ends. The [`endedReason`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.events.BreaksEvent#endedReason) will be populated for the following circumstances: - A stitched timeline break clip played through entirely. - A stitched timeline break clip transitions to another break clip. - Any break clip is skipped. - The last break clip played through entirely in a post-roll embedded break. - An error occurred. |
| `BREAK_ENDED` | Fired when the last break clip in a break ends. |
[*Table 2: Break events and their descriptions.*]

## Ad insertion

The cast SDK enables applications to insert and remove ads at different moments
of a cast session. The two types of ad insertion are **static** and **dynamic** .
**Static** ad insertion requires that ads be specified in the
[`LoadRequestData`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.LoadRequestData)
prior to player creation. **Dynamic** ad insertion makes use of the
[`BreakManager`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.breaks.BreakManager)
[`addBreak`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.breaks.BreakManager#addBreak)
API to insert breaks in the already loaded content. Each type of insertion
method is compatible with certain [ad types](https://developers.google.com/cast/docs/web_receiver/ad_breaks#ad_types). A compatibility
overview is provided in the [interoperability matrix](https://developers.google.com/cast/docs/web_receiver/ad_breaks#interoperability_matrix).

### Static ad insertion

Static ad insertion is characterized by adding the relevant ad metadata prior
to player creation. This information is provided in the
[`MediaInformation`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.MediaInformation)
of the `LoadRequestData`. For example, this can be set in a connected sender's
original load request or it can be inserted by the Web Receiver application by
intercepting the `LOAD` request. Once the `LoadRequestData` is returned to the
Web Receiver SDK for processing, the player is created. See more on
[loading media](https://developers.google.com/cast/docs/web_receiver/core_features#loading_media). The sample
below shows a **manual client-stitched** ad being added in the `LOAD` request
interceptor.

    const context = cast.framework.CastReceiverContext.getInstance();
    const playerManager = context.getPlayerManager();

    playerManager.setMessageInterceptor(
        cast.framework.messages.MessageType.LOAD, loadRequestData => {

      // Create the BreakClip.
      let clipClient = new cast.framework.messages.BreakClip('bc_client');
      clipClient.title = 'The Ad Title to be displayed during playback';
      clipClient.contentId = 'https://example.com/ad.mp4';
      clipClient.contentType = 'video/mp4';

      // Create the Break using the BreakClip id above.
      let breakPostrollClient = new cast.framework.messages.Break(
          'break_postroll_client', ['bc_client'], -1);

      // Set the ad information in the load request data.
      let media = loadRequestData.media;
      media.breakClips = [clipClient];
      media.breaks = [breakPostrollClient];

      return loadRequestData;
    });

### Dynamic ad insertion

Dynamic ad insertion is characterized by setting an ad break during content
playback. This is done by obtaining an instance of `BreakManager` and calling
the
[`addBreak`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.breaks.BreakManager#addBreak)
API. This takes two parameters at minimum, an
[embedded expanded](https://developers.google.com/cast/docs/web_receiver/ad_breaks#embedded-expanded)
[`Break`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.Break) and
an array of
[`BreakClip`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.BreakClip).
An optional third property is included to force sending the changes to
connected senders through a `MediaStatus` broadcast when set to `true`. When
adding breaks and break clips, the corresponding ids must be unique. These ads
can only be added once the player has been created. The Web Receiver SDK fires
the
[`PLAYER_LOADING`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.events#.EventType)
event once the player is created. See the sample below showcasing the usage in
an event handler which responds to changes in the ID3 metadata of a stream and
creates `Break` and `BreakClip` objects to insert it into the timeline.

    const context = cast.framework.CastReceiverContext.getInstance();
    const playerManager = context.getPlayerManager();
    const breakManager = playerManager.getBreakManager();

    playerManager.addEventListener(cast.framework.events.EventType.ID3, (event) => {

      // Create the BreakClip.
      let clipEmbeddedExpanded = parseBreakClipFromData(event.segmentData);
      let breakEmbeddedExpanded = parseExpandedBreakFromData(event.segmentData);

      // Add the break and break clip.
      breakManager.addBreak(breakEmbeddedExpanded, [clipEmbeddedExpanded]);
    });

## Dynamic ad removal

To remove dynamic breaks, the application should call
[`removeBreakById`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.breaks.BreakManager#removeBreakById)
during playback. The function takes in a string identifier of the break to be
removed from the timeline. The `breakId` specified must point to an **embedded
expanded** ad break. If any other type of ad break is detected, then the break
will remain in the timeline. See the sample below which removes a break.

    const context = cast.framework.CastReceiverContext.getInstance();
    const playerManager = context.getPlayerManager();
    const breakManager = playerManager.getBreakManager();

    breakManager.removeBreakById('break_midroll_embedded_expanded');

## Behavior of breaks

The SDK defines a default behavior for when the player enters and leaves breaks
and provides for a way to customize it further using some of the APIs provided
in
[`BreakManager`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.breaks.BreakManager).

### Default break behavior

When a `Break` is entered through regular playback or by seeking over a `Break`,
the SDK will evaluate whether or not the user has already seen it by checking
the
[`isWatched`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#isWatched)
property. When created, a break's default value for this property is `false`. If
the property is `true`, the break will not be played when entered and main
content will continue playing. If the property is `false`, the break will be
played when entered.

> [!NOTE]
> **Note:** For **embedded timeline** ad breaks, the SDK will seek over the duration of a user watched break to resume main content playback.

When seeking past breaks, the default implementation obtains all of the `Break`
items whose `position` is between the seek operation's
[`seekFrom`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.breaks.BreakSeekData#seekFrom)
and
[`seekTo`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.breaks.BreakSeekData#seekTo)
values. From this list of breaks, the SDK will play the `Break` whose `position`
is closest to the `seekTo` value and whose `isWatched` property is set to
`false`. That break's `isWatched` property will then be set to `true` and the
player will commence playing its break clips. Once the break is watched,
the main content will resume playback from the `seekTo` position. If no such
break exists, then no break will be played and the main content will resume
playing at the `seekTo` position.

During break playback, the SDK will broadcast any relevant updates to connected
sender applications in the
[`MediaStatus`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.MediaStatus).
These applications will use the broadcasts to update their UI for ads by reading
the
[`breakStatus`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.MediaStatus#breakStatus)
property. This property is defined only during break playback.

> [!NOTE]
> **Note:** Receiver applications are able to subscribe to `PlayerManager` `MEDIA_STATUS` events to monitor the changes in the `MediaStatus`.

Receiver applications can also directly query information pertaining to the
position of the playhead with respect to the current time of the `BreakClip`
shown by calling `PlayerManager`
[`getBreakClipCurrentTimeSec`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.PlayerManager#getBreakClipCurrentTimeSec).
Similarly, applications can query the duration of the current `BreakClip` by
calling
[`getBreakClipDurationSec`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.PlayerManager#getBreakClipDurationSec).

### Custom break behavior

The [default behavior](https://developers.google.com/cast/docs/caf_receiver/ad_breaks#default_break_behavior)
for breaks and break clips can be modified using the
[`setBreakClipLoadInterceptor`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.breaks.BreakManager#setBreakClipLoadInterceptor)
and
[`setBreakSeekInterceptor`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.breaks.BreakManager#setBreakSeekInterceptor)
methods provided in `BreakManager`.

> [!NOTE]
> **Note:** If performing a seek operation after a break completion, ensure that the playerState of the [`MediaStatus`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.MediaStatus) object has changed to `PLAYING` before performing the seek operation.

#### Break seek interceptor

The break seek interceptor allows the app to control the behavior of seeking
over ad breaks. The function is triggered when a seek operation is requested
that seeks forward or backward over one or more breaks. When called, the
[`BreakSeekData`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.breaks.BreakSeekData)
is passed as a parameter to the callback function. The `BreakSeekData` object
contains an array of
[`Break`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break)
objects whose `position` property is set to a number between the current
playhead time defined as
[`seekFrom`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.breaks.BreakSeekData#seekFrom)
and the seek destination time
[`seekTo`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.breaks.BreakSeekData#seekTo).

This interceptor allows the `Break` objects in the respective breaks to be
modified. When implemented, the break seek interceptor must specify which ad
breaks to play by returning an optionally modified `BreakSeekData` object. The
player will proceed to play all breaks included in the return value. If a value
of `null` or nothing is returned from the break seek interceptor, the break is
skipped over.

> [!NOTE]
> **Note:** Setting the break seek interceptor overrides the default behavior. This includes features such as skipping already watched breaks. In order to customize break seek behavior and not play watched breaks, the application will need to specify that logic in the break seek interceptor by checking the `isWatched` property of a `Break`. The SDK will handle setting the `isWatched` property when a break is entered regardless of the seek break interceptor's usage.

See the sample below for a simple implementation of the interceptor which
overrides the default behavior to watch all ad breaks seeked over with the
exception of already watched breaks.

    const context = cast.framework.CastReceiverContext.getInstance();
    const playerManager = context.getPlayerManager();
    const breakManager = playerManager.getBreakManager();

    breakManager.setBreakSeekInterceptor((breakSeekData) => {

      // Filter the breaks array by removing watched breaks.
      const unwatchedBreaks =
          breakSeekData.breaks.filter(adBreak => !adBreak.isWatched);
      breakSeekData.breaks = unwatchedBreaks;

      return breakSeekData;
    });

#### Break clip load interceptor

By using the break clip load interceptor, a `BreakClip` object can be modified
before its playback begins.

The break clip load interceptor is called only for
[stitched timeline breaks](https://developers.google.com/cast/docs/web_receiver/ad_breaks#player_timeline_types)
and can be set using
[`setBreakClipLoadInterceptor`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.breaks.BreakManager#setBreakClipLoadInterceptor).
Prior to entering a `Break`, this interceptor is called once for each individual
`BreakClip` defined in that break. The SDK passes the original
[`BreakClip`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.BreakClip)
object as a parameter of the callback function. The application can then modify
this `BreakClip` and return it so that the SDK can fetch and display the break
clip with the updated configuration. If `null` or nothing is returned, the break
clip is skipped over.

> [!NOTE]
> **Note:** If there is a pre-roll break, the break clip load interceptor is called for the pre-roll break as soon as playback starts. Immediately after the pre-roll break ends, the break clip load interceptor for the next break is called if it exists.

See below for an example which modifies the `contentUrl` of the break clips with
a utility function call `getUrlFromClipId` where the `id` of the `BreakClip`
is mapped to a URL.

    const context = cast.framework.CastReceiverContext.getInstance();
    const playerManager = context.getPlayerManager();
    const breakManager = playerManager.getBreakManager();

    breakManager.setBreakClipLoadInterceptor(
        (breakClip, breakClipLoadInterceptorContext) => {

      // Obtains the URL of a break clip id from a function call.
      breakClip.contentUrl = getUrlFromClipId(breakClip.id);

      return breakClip;
    });

## Ad Skipping

The Web Receiver SDK provides APIs to skip ad breaks and individual break clips
within an ad break. The SDK also allows users to optionally skip break clips by
interacting with their sender applications or smart display devices.

### User skippable break clips

Setting break clips as skippable allows users to interact with connected sender
applications and smart display devices to optionally skip the rest of a
currently playing break clip. Setting the
[`whenSkippable`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.BreakClip#whenSkippable)
property to a non-negative number of seconds will enable this feature for the
`BreakClip` object. The player will consider the break clip skippable once the
break clip has played for that number of seconds. Setting this value to `0`
allows users to skip the break clip immediately.

    // Create the BreakClip.
    let clip = new cast.framework.messages.BreakClip('bc');
    clip.title = 'The Ad Title to be displayed during playback';
    clip.whenSkippable = 10; // Users can skip the clip after 10 seconds of playback.

This information can be set in the sender's original load request or in the
receiver app. When skipped, a break clip in a **stitched timeline** ad break
will stop playing the current break clip. The player will either load the next
break clip if present or load the main content. When skipped, a break clip in an
**embedded timeline** ad break will seek to the end of the break clip and
continue playback of the stream at that point.

> [!NOTE]
> **Note:** In addition to implementing the above snippet, **Web Sender** applications need to add [`request logic`](https://developers.google.com/cast/docs/web_sender/advanced#sending_media_messages_to_the_receiver) to allow a user to skip an ad. This is not needed on mobile sender platforms as the functionality is provided by the SDK.

### Programmatically skipping ads

Ads can also be skipped automatically without any user interaction.

To skip an **entire break** from playing, an application should set the
[`isWatched`](https://developers.google.com/cast/docs/reference/web_receiver/cast.framework.messages.Break#isWatched)
property of a `Break` to `true`. This can be done anytime during the load
sequence or content playback. The `isWatched` property is evaluated by the
player when a break's `position` is met in the main content's current time. At
that point, the player will determine whether or not a break should be entered.
See the sample below which loops through all of the breaks and modifies the
value when the player is loading.

    const context = cast.framework.CastReceiverContext.getInstance();
    const playerManager = context.getPlayerManager();
    const breakManager = playerManager.getBreakManager();

    playerManager.addEventListener(cast.framework.events.EventType.PLAYER_LOADING,
        (event) => {

      // Obtain the breaks and iterate through each item to skip all ad breaks.
      let breaks = breakManager.getBreaks();
      breaks.forEach((brk) => {
        brk.isWatched = true;
      });
    });

To skip a **specific break clip** programmatically, the
[break clip load interceptor](https://developers.google.com/cast/docs/web_receiver/ad_breaks#break_clip_load_interceptor) should be used. By
returning `null` or not returning a value in the callback function, the clip in
that break will be skipped.

> [!NOTE]
> **Note:** The break clip load interceptor is called only for **stitched timeline** breaks. If wanting to skip breaks on an **embedded timeline** , look into [dynamic ad removal](https://developers.google.com/cast/docs/web_receiver/ad_breaks#dynamic_ad_removal).

    const context = cast.framework.CastReceiverContext.getInstance();
    const playerManager = context.getPlayerManager();
    const breakManager = playerManager.getBreakManager();

    breakManager.setBreakClipLoadInterceptor(
          (breakClip, breakClipLoadInterceptorContext) => {
      return null;
    });