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.
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.
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 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:
|
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;
});