The Web Receiver SDK features native support for ad breaks and companion ads within a given media stream. Before starting development on ad breaks, be familiar with Basic Web Receiver set up. The Cast SDK's Break APIs ensures that users have a consistent experience across all Cast-enabled devices when ads are part of the playback.
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 break clip.
This diagram shows two ad breaks, each with two ads:
Events
The following table describes the events that are triggered when breaks are encountered during playback.
Break Event | Description |
---|---|
BREAK_STARTED | Fired when the first break clip in a break starts loading. Event is cast.framework.events.BreaksEvent . |
BREAK_ENDED | Fired when the last break clip in a break ends. Event is cast.framework.events.BreaksEvent .Note: This event fires even if a user skips the last break clip in a break to end the break. |
BREAK_CLIP_LOADING | Fired when a break clip starts loading. Event is cast.framework.events.BreaksEvent . |
BREAK_CLIP_STARTED | Fired when a break clip starts. Event is cast.framework.events.BreaksEvent . |
BREAK_CLIP_ENDED | Fired when a break clip ends. Event is cast.framework.events.BreaksEvent .Note: This event fires even if a user skips a break clip. Please check the endedReason property of BreaksEvent . To figure out amount of time the break clip was watched please check getBreakClipCurrentTimeSec and getBreakClipDurationSec of PlayerManager . |
These events can be used for analytics and ad playback tracking. When VMAP (Video Multiple Ad Playlist) and VAST (Video Ad Serving Template) are used, any standard tracking events provided in the responses are automatically dispatched by the SDK.
While a break is playing, the Web Receiver SDK broadcasts
MediaStatus
with breakStatus
to all connected senders. Senders can use this information to update UIs and
suppress any seek operations.
The Web Receiver SDK provides two ways to incorporate ad breaks to the receiver: client-side and server-side stitching.
Client-side ad stitching
For client-side ad stitching, also referred to as non-embedded,
the necessary ad information should be specified with Break
and BreakClip
objects when the media is being loaded. The following snippet is an example
of a function adding pre-roll, mid-roll, and post-roll breaks, where the
references to third_party
and its methods are examples of helper functions
a developer might have in their app. The snippet also shows how tracking events
can be fired when a user watches a break clip to completion by listening for
the BREAK_CLIP_ENDED
event and checking the value of
endedReason
.
/**
* @param {!cast.framework.messages.MediaInformation} mediaInformation
*/
function addBreakToMedia(mediaInformation) {
mediaInformation.breakClips = [
{
id: 'bc1',
title: third_party.getBreakClipTitle('bc1'),
contentId: third_party.getBreakClipUrl('bc1'),
contentType: third_party.getBreakClipContentType('bc1'),
posterUrl: third_party.getBreakClipPosterUrl('bc1')
},
{
id: 'bc2'
...
},
{
id: 'bc3'
...
},
{
id: 'bc4'
...
}];
mediaInformation.breaks = [
{
id: 'b1',
breakClipIds: ['bc1', 'bc2'],
position: 0 //pre-roll position
},
{
id: 'b2',
breakClipIds: ['bc3'],
position: 10*60 //ten minutes
},{
id: 'b3',
breakClipIds: ['bc4'],
position: -1 //post-roll position (-1 is only valid client stitching; for server-side ad stitching, exact position is required)
}];
}
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
playerManager.addEventListener(cast.framework.events.EventType.BREAK_CLIP_ENDED, function(event){
if(event.endedReason === cast.framework.events.EndedReason.END_OF_STREAM){
//call your ad tracking code here for break clip watched to completion
}
});
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
loadRequestData => {
addBreakToMedia(loadRequestData.media);
return loadRequestData;
});
context.start();
VAST Ads
The Web Receiver SDK supports IAB standard VAST ads. To include VAST ads in
your media,create a
vastAdsRequest
object and assign it to the vastAdsRequest
property of a BreakClip
.
The vastAdsRequest
object must have the adsResponse
property or adTagUrl
property defined.
/**
* @param {!cast.framework.messages.MediaInformation} mediaInformation
*/
function addBreakToMedia(mediaInformation) {
mediaInformation.breakClips = [
{
id: 'bc1',
vastAdsRequest:{
adTagUrl: 'https://castsample.com/vast?rand=' + Math.floor(Math.random()* 10000)
}
}];
}
The Web Receiver SDK sends a request to the specified adTagUrl
and parses
the XML response to generate a BreakClip
object. Below is an example of a
BreakClip
object generated by our SDK.
{
"id": "GENERATED:0",
"contentId": "https://file.mp4",
"contentType": "video/mp4",
"title": "Preroll",
"duration": 10,
"clickThroughUrl": "https://click"
}
VMAP ads
The Web Receiver SDK also supports the IAB VMAP standard. When a VMAP is
provided, Cast SDK will parse the VMAP response and generate the Break objects
for the given <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 use VMAP to insert ads into your content,
create a vastAdsRequest
object and assign it to
the vmapAdsRequest
property of the
MediaInformation
object as part of your load request.
/**
* @param {!cast.framework.messages.MediaInformation} mediaInformation
*/
function addBreakToMedia(mediaInformation) {
mediaInformation.vmapAdsRequest = {
adTagUrl: 'https://castsample.com/vmap?rand=' + Math.floor(Math.random()* 10000)
};
}
Server-side ad stitching
For server-side stitching, also referred to as embedded ads, the server is
expected to provide a single stream that contains both the primary media and
ads. In this case, the developer is expected to provide
the duration
and contentType
of the BreakClip
. The contentUrl
is omitted. In addition,
the isEmbedded
must be set to true.
/**
* @param {!cast.framework.messages.MediaInformation} mediaInformation
*/
function addBreakToMedia(mediaInformation) {
mediaInformation.breakClips = [
{
id: 'bc1',
title: third_party.getBreakClipTitle('bc1'),
posterUrl: third_party.getBreakClipPosterUrl('bc1'),
duration: third_party.getBreakClipDuration('bc1')
},
{
id: 'bc2'
...
},
{
id: 'bc3'
...
},
{
id: 'bc4'
...
}];
mediaInformation.breaks = [
{
id: 'b1',
breakClipIds: ['bc1', 'bc2'],
position: 0,
isEmbedded: true
},
{
id: 'b2',
breakClipIds: ['bc3', 'bc4'],
position: 10*60,
isEmbedded: true
}];
}
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
loadRequestData => {
addBreakToMedia(loadRequestData.media);
return loadRequestData;
});
context.start();
Behavior of breaks
Default break behavior
- Once a break is started, it will be marked as
isWatched
. If a user then scrubs to a point earlier than the break, content begins playback as normal, and the break will subsequently be skipped as having been previously watched. - If a user scrubs past a break(s) without watching the break, then the
last unplayed break between
seekFrom
andseekTo
will play before content playback begins.
Custom break behavior
The default behavior
for breaks and break clips can be modified using
the setBreakClipLoadInterceptor
and setBreakSeekInterceptor
methods on the
BreakManager
object.
setBreakClipLoadInterceptor
- The
setBreakClipLoadInterceptor
is called before a break is encountered. This function is invoked once per break clip. It passes aBreakClip
object into the callback function.
For example, if there is a pre-roll break, thesetBreakClipLoadInterceptor
is called for the pre-roll break as soon as playback starts. Immediately after the pre-roll finishes playing, thesetBreakClipLoadInterceptor
for the next break is called. - If null or nothing is returned in the callback function to
setBreakClipLoadInterceptor
, the break clip is skipped over. All the interceptors for a break are called immediately. - Using
setBreakClipLoadInterceptor
, aBreakClip
object can be modified before its playback begins.
setBreakSeekInterceptor
The setBreakSeekInterceptor
is triggered after a seek operation and passes
a BreakSeekData
object to the callback function. The BreakSeekData
object contains an array of
Breaks whose positions are defined between the current playhead time and
seek destination time (i.e.
seekFrom
and
seekTo
).
After a forward seek operation, the default behavior is to play the last
unplayed break prior to the seekTo
time. Once the ads in that break have
played, content playback resumes from the seekTo
value.
This interceptor allows the BreakClip
objects in the respective Breaks to be
modified.
If you want to customize the behavior, you can implement
setBreakSeekInterceptor
to override the default
behavior
.
If setBreakSeekInterceptor
is implemented, you must be explicit about which
breaks to play.
- If a value of null or nothing is returned from
setBreakSeekInterceptor
, the break is skipped over. - If same object that is passed into the callback function is returned, then
the
default behavior
is overridden and all the breaks are played. In addition, if a user scrubs to an earlier point then they will see the breaks even if they had previously watched those breaks. IfsetBreakSeekInterceptor
is not implemented, then breaks that have already been watched are skipped.
Making break clips skippable
To make a break clip skippable, specify after how many seconds pass before the
break clip is skippable in the BreakClip
object.
/**
* @param {!cast.framework.messages.MediaInformation} mediaInformation
*/
function addBreakToMedia(media) {
media.breakClips = [
{
id: 'bc1',
...
whenSkippable: 10 //to allow user to skip break clip from sender after 10 seconds
}];
}
Auto-skip ads
Ads can be skipped automatically without any user interaction. Two ways to skip ads are:
- Setting the
isWatched
property of a Break to true will automatically skip the break.playerManager.addEventListener(cast.framework.events.category.CORE, function(event){
if(event.type === cast.framework.events.EventType.PLAYER_LOADING){
let breaks = playerManager.getBreaks();
for(let i in breaks){
breaks[i].isWatched = true;
}
}
});
- A
BreakClip
can be skipped by returning a null value in thesetBreakClipLoadInterceptor
callBack.playerManager.getBreakManager().setBreakClipLoadInterceptor(breakClip => {
return null;
});