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 refers to an interval for playback containing one or more ads or bumpers and each ad or bumper is referred to as a 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 which is a URL that points to the ad content, the contentType describing the ad content's format, and the title.

The Break must have isEmbedded and expanded set to the default value false. The position can be set to a pre-roll, mid-roll or post-roll ad break (see more in the 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 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 and specify it in the 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 messages that pertain to ads before and after entering such a break.

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 object and assign it to the vmapAdsRequest property of the MediaInformation in the LoadRequestData. These ads must be inserted statically (see more in the ad insertion section). Below is a snippet outlining the creation of a VMAP request.

// 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 of the ad content, and the title. The Break must have isEmbedded set to true and expanded set to false. The 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 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 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 of the ad content, and the title. The Break must have isEmbedded set to true and expanded set to true. The 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 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 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 present during load time in the MediaInformation of the 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
Figure 1: The timeline representing some content and its 3 client-stitched ad breaks.


Timeline of server-stitched embedded ads
Figure 2: The timeline representing some content and its 3 server-stitched embedded ad breaks.


Timeline of server-stitched embedded expanded ads
Figure 3: The timeline representing some content and its 3 server-stitched embedded expanded ad breaks.

Break positioning

The Web Receiver SDK allows developers to specify where ad breaks should be placed by setting the 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. For embedded timelines 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.

Table 1: Ads interoperability matrix
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
pre-roll ad
mid-roll ad
post-roll ad
ad skip
break seek interceptor
break clip load interceptor

Events

When key break events occur, the cast SDK will dispatch events of type BreaksEvent. A receiver app can subscribe to them using the 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
Figure 4: The break events lifecycle.
Table 2: Break events and their descriptions.
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 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.

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 prior to player creation. Dynamic ad insertion makes use of the BreakManager addBreak API to insert breaks in the already loaded content. Each type of insertion method is compatible with certain ad types. A compatibility overview is provided in the 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 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. 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 API. This takes two parameters at minimum, an embedded expanded Break and an array of 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 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 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.

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 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.

When seeking past breaks, the default implementation obtains all of the Break items whose position is between the seek operation's seekFrom and 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. These applications will use the broadcasts to update their UI for ads by reading the breakStatus property. This property is defined only during break playback.

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. Similarly, applications can query the duration of the current BreakClip by calling getBreakClipDurationSec.

Custom break behavior

The default behavior for breaks and break clips can be modified using the setBreakClipLoadInterceptor and setBreakSeekInterceptor methods provided in BreakManager.

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 is passed as a parameter to the callback function. The BreakSeekData object contains an array of Break objects whose position property is set to a number between the current playhead time defined as seekFrom and the seek destination time 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.

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 and can be set using 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 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.

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 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.

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 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 should be used. By returning null or not returning a value in the callback function, the clip in that break will be skipped.

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

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