Overview
The Web Receiver SDK features native support for ad breaks and companion ads within a given media stream. It provides two ways to incorporate ad breaks on the receiver: client-side and server-side stitching using breaks and break clips.
Before starting development on ad breaks, be familiar with setup for a Custom Web Receiver. The Cast SDK Ad Breaks API 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 containing 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
positions 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 a
BreakClip
object into the callback function.
For example, if there is a pre-roll break, the setBreakClipLoadInterceptor
is
called for the pre-roll break as soon as playback starts. Immediately after the
pre-roll finishes playing, the setBreakClipLoadInterceptor
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
, a BreakClip
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 (for example
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 break has finished playing,
content playback resumes from the seekTo
position.
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 explicitly specify 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 how many seconds to wait 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;
});