Add Ad Breaks API Support to a Web Receiver

1. Overview

Google Cast logo

This codelab will teach you how to build a Custom Web Receiver app that uses the Cast Ad Breaks API.

What is Google Cast?

Google Cast allows users to cast content from a mobile device to a TV. Users can then use their mobile device as a remote control for media playback on the TV.

The Google Cast SDK lets you extend your app to control a TV or sound system. The Cast SDK allows you to add the necessary UI components based on the Google Cast Design Checklist.

The Google Cast Design Checklist is provided to make the Cast user experience simple and predictable across all supported platforms.

What are we going to be building?

When you have completed this codelab, you will have built a Cast Receiver that takes advantage of the new Break API.

What you'll learn

  • How to include VMAP and VAST breaks in content for Cast
  • How to skip break clips
  • How to customize default break behavior on seek

What you'll need

Experience

  • You will need to have previous web development knowledge.
  • Previous experience building Cast sender & receiver applications.

How will you use this tutorial?

Read it through only Read it and complete the exercises

How would you rate your experience with building web apps?

Novice Intermediate Proficient

2. Get the sample code

You can download all the sample code to your computer...

and unpack the downloaded zip file.

3. Deploying your receiver locally

To be able to use your web receiver with a Cast device, it needs to be hosted somewhere where your Cast device can reach it. Should you already have a server available to you that supports https, skip the following instructions and make note of the URL, as you'll need it in the next section.

If you don't have a server available to use, you can use Firebase Hosting or ngrok.

Run the server

Once you have the service of your choice set up, navigate to app-start and start your server.

Make note of the URL for your hosted receiver. You will be using it in the next section.

4. Register an application in the Cast Developer Console

You must register your application to be able to run a custom receiver, as built in this codelab, on Chromecast devices. After you've registered your application, you'll receive an application ID that your sender application must use to perform API calls, such as to launch a receiver application.

Image of the Google Cast SDK Developer Console with the 'Add New Application' button highlighted

Click "Add new application"

Image of the 'New Receiver Application' screen with the 'Custom Receiver' option highlighted

Select "Custom Receiver", this is what we're building.

Image of the 'New Custom Receiver' screen showing a URL that someone is typing into the 'Receiver Application URL' field

Enter the details of your new receiver, be sure to use the URL you ended up with

in the last section. Make a note of the Application ID assigned to your brand new receiver.

You must also register your Google Cast device so that it may access your receiver application before you publish it. Once you publish your receiver application, it will be available to all Google Cast devices. For the purpose of this codelab it's advised to work with an unpublished receiver application.

Image of the Google Cast SDK Developer Console with the 'Add New Device' button highlighted

Click on "Add new Device"

Image of the 'Add Cast Receiver Device' dialog

Enter the serial number printed on the back of your Cast device and give it a descriptive name. You can also find your serial number by casting your screen in Chrome when accessing Google Cast SDK Developer Console

It will take 5-15 minutes for your receiver and device to be ready for testing. After waiting 5-15 minutes you must reboot your Cast device.

5. Prepare the Start Project

Before starting this codelab, it may be helpful to review the ads developer guide which provides an overview of the new ads functionality.

We need to add support for Google Cast to the start app you downloaded. Here are some Google Cast terminology that we will be using in this codelab:

  • a sender app runs on a mobile device or laptop,
  • a receiver app runs on the Google Cast device.

Now you're ready to build on top of the starter project using your favorite text editor:

  1. Select the folder iconapp-start directory from your sample code download.
  2. Open up js/receiver.js and index.html

Note, as you're working through this codelab, http-server should be picking up changes you make. If you notice it doesn't, try killing and restarting http-server.

For our sender, we will use the CAF Receiver Debugging Tool to initiate a Cast session. The receiver is designed to automatically start playing a stream.

App Design

The receiver app initializes the Cast session and will stand-by until a LOAD request (ie. the command to playback a piece of media) from a sender arrives.

The app consists of one main view, defined in index.html and one JavaScript file called js/receiver.js containing all the logic to make our receiver work.

index.html

This html file will contain all of the UI for our receiver app. For now it's basically empty.

receiver.js

This script manages will manage all of the logic for our receiver app. Right now it contains a basic CAF receiver.

6. Add VMAP to Your Content

To begin, open the web sender in Chrome. Enter the Receiver Application ID you were given on the Cast SDK Developer Console and click ‘Set''.

In the receiver we need to add some logic to include ads in the content.

Copy the following line into your js/receiver.js file. It contains a sample VMAP tag link from DoubleClick plus some randomization.

const vmapUrl = "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator=" + Math.floor(Math.random() * Math.pow(10, 10));

In your js/receiver.js file, locate the playerManager.setMessageInterceptor function and add the following before the last return request; line in the function.

request.media.vmapAdsRequest = {
    adTagUrl: vmapUrl,
};

Note: The object assigned to vmapAdsRequest above is a shorthand version of a VastAdsRequest object.

Save your changes to js/receiver.js and initiate a Cast session on the web sender by right clicking anywhere on the page and selecting ‘Cast'. The ads stream should begin playing immediately.

7. Add VAST to Your Content

If you have implemented the VMAP code from above, please comment it out. In the following we will go through how to implement VAST ads in the content.

Copy the following into your js/receiver.js file. It contains a six VAST break clips from DoubleClick plus some randomization. These break clips are assigned to 5 breaks. Also the position of each break is specified.

const addVASTBreaksToMedia = (mediaInformation) => {
    mediaInformation.breakClips = [
        {
            id: "bc1",
            title: "bc1 (Pre-roll)",
            vastAdsRequest: {
                adTagUrl: 'https://pubads.g.doubleclick.net/gampad/ads?slotname=/124319096/external/ad_rule_samples&sz=640x480&ciu_szs=300x250&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&url=&unviewed_position_start=1&output=xml_vast3&impl=s&env=vp&gdfp_req=1&ad_rule=0&vad_type=linear&vpos=preroll&pod=1&ppos=1&lip=true&min_ad_duration=0&max_ad_duration=30000&vrid=6256&correlator=' + Math.floor(Math.random() * Math.pow(10, 10)) + '&video_doc_id=short_onecue&cmsid=496&kfa=0&tfcd=0'
            }
        },
        {
            id: "bc2",
            title: "bc2 (Mid-roll)",
            vastAdsRequest: {
                adTagUrl: 'https://pubads.g.doubleclick.net/gampad/ads?slotname=/124319096/external/ad_rule_samples&sz=640x480&ciu_szs=300x250&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&url=&unviewed_position_start=1&output=xml_vast3&impl=s&env=vp&gdfp_req=1&ad_rule=0&cue=15000&vad_type=linear&vpos=midroll&pod=2&mridx=1&rmridx=1&ppos=1&lip=true&min_ad_duration=0&max_ad_duration=30000&vrid=6256&correlator=' + Math.floor(Math.random() * Math.pow(10, 10)) + '&video_doc_id=short_onecue&cmsid=496&kfa=0&tfcd=0'
            }
        },
        {
            id: "bc3",
            title: "bc3 (Mid-roll)",
            vastAdsRequest: {
                adTagUrl: 'https://pubads.g.doubleclick.net/gampad/ads?slotname=/124319096/external/ad_rule_samples&sz=640x480&ciu_szs=300x250&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&url=&unviewed_position_start=1&output=xml_vast3&impl=s&env=vp&gdfp_req=1&ad_rule=0&cue=15000&vad_type=linear&vpos=midroll&pod=2&mridx=1&rmridx=1&ppos=1&lip=true&min_ad_duration=0&max_ad_duration=30000&vrid=6256&correlator=' + Math.floor(Math.random() * Math.pow(10, 10)) + '&video_doc_id=short_onecue&cmsid=496&kfa=0&tfcd=0'
            }
        },
        {
            id: "bc4",
            title: "bc4 (Mid-roll)",
            vastAdsRequest: {
                adTagUrl: 'https://pubads.g.doubleclick.net/gampad/ads?slotname=/124319096/external/ad_rule_samples&sz=640x480&ciu_szs=300x250&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&url=&unviewed_position_start=1&output=xml_vast3&impl=s&env=vp&gdfp_req=1&ad_rule=0&cue=15000&vad_type=linear&vpos=midroll&pod=2&mridx=1&rmridx=1&ppos=1&lip=true&min_ad_duration=0&max_ad_duration=30000&vrid=6256&correlator=' + Math.floor(Math.random() * Math.pow(10, 10)) + '&video_doc_id=short_onecue&cmsid=496&kfa=0&tfcd=0'
            }
        },
        {
            id: "bc5",
            title: "bc5 (Mid-roll)",
            vastAdsRequest: {
                adTagUrl: 'https://pubads.g.doubleclick.net/gampad/ads?slotname=/124319096/external/ad_rule_samples&sz=640x480&ciu_szs=300x250&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&url=&unviewed_position_start=1&output=xml_vast3&impl=s&env=vp&gdfp_req=1&ad_rule=0&cue=15000&vad_type=linear&vpos=midroll&pod=2&mridx=1&rmridx=1&ppos=1&lip=true&min_ad_duration=0&max_ad_duration=30000&vrid=6256&correlator=' + Math.floor(Math.random() * Math.pow(10, 10)) + '&video_doc_id=short_onecue&cmsid=496&kfa=0&tfcd=0'
            }
        },
        {
            id: "bc6",
            title: "bc6 (Post-roll)",
            vastAdsRequest: {
                adTagUrl: 'https://pubads.g.doubleclick.net/gampad/ads?slotname=/124319096/external/ad_rule_samples&sz=640x480&ciu_szs=300x250&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&url=&unviewed_position_start=1&output=xml_vast3&impl=s&env=vp&gdfp_req=1&ad_rule=0&vad_type=linear&vpos=postroll&pod=3&ppos=1&lip=true&min_ad_duration=0&max_ad_duration=30000&vrid=6256&correlator=' + Math.floor(Math.random() * Math.pow(10, 10)) + '&video_doc_id=short_onecue&cmsid=496&kfa=0&tfcd=0'
            }
        }
    ];
    mediaInformation.breaks = [
        {
            id: "b1",
            breakClipIds: ["bc1"],
            position: 0
        },
        {
            id: "b2",
            breakClipIds: ["bc2"],
            position: 15
        },
        {
            id: "b3",
            breakClipIds: ["bc3","bc4"],
            position: 60
        },
        {
            id: "b4",
            breakClipIds: ["bc5"],
            position: 100
        },
        {
            id: "b5",
            breakClipIds: ["bc6"],
            position: -1
        }
    ];
};

Note: The breakClipIds property of a break is an array. This means that multiple break clips can be assigned to each break.

In your js/receiver.js file, locate the LOAD message interceptor, in other words, the line that begins with playerManager.setMessageInterceptor, and add the following before the last return request; line in the function.

addVASTBreaksToMedia(request.media);

Save your changes to js/receiver.js and initiate a Cast session on the web sender by right clicking anywhere on the page and selecting ‘Cast'. The ads stream should begin playing immediately.

8. Skipping Ad Breaks

CAF has a new class called BreakManager which assists you with implementing custom business rules for ad behaviors. Let's assume that you want to enable customers a grace period to skip ads after a certain time.

The sender in our example does not have media controls. Let's add a start offset of 10 seconds so the stream begins playback after the pre-roll, but before the first mid-roll Break at the 15-second mark.

Find the playerManager.setMessageInterceptor and add the following line before the return request.

request.currentTime = 10;

Save the receiver.js file and initiate a Cast session. You should see the content load 10 seconds into it, and then play an ad 5 seconds later.

Now let's add a rule to skip the mid-roll at the 15-second mark.

You'll need an instance of the BreakManager to set an interceptor for the break loading. Copy the following line into your js/receiver.js file, after the lines containing the context and playerManager variables.

const breakManager = playerManager.getBreakManager();

Now, let's set up an interceptor with a rule to ignore any ad breaks that occurs before 30 seconds. This interceptor works like the LOAD interceptor on the PlayerManager, except this one is specific to loading BreakClips.

Copy the following into your js/receiver.js file.

breakManager.setBreakClipLoadInterceptor((breakClip, breakCtx) => {
  /** Below code will skip playback of break clips if the break position is less than 30 **/
  let breakObj = breakCtx.break;
  if(breakObj.position < 30)
    return null;
  else
    return breakClip;
});

Note: We return null here for the BreakClips that should be skipped.

Save your changes to js/receiver.js and initiate a Cast session on the web sender by right clicking anywhere on the page and selecting ‘Cast'.

The stream should begin playback but the ad block we previously saw at 15-seconds will be skipped.

9. Customizing Break Seek Behavior

When a user seeks forward then the last unplayed break between seekFrom and seekTo is played before content playback starts playing from the seekTo position. When a user seeks back no break is played. This is the default breaks behavior.

To customize which breaks play on a seek, we leverage BreakManager. We use the setBreakSeekInterceptor of the BreakManager to specify the custom behavior that we want. The setBreakSeekInterceptor is invoked whenever a seek operation is performed.

We pass a callback function to the setBreakSeekInterceptor. The callback function is passed an object that contains all the breaks between the seekFrom position and the seekTo position.

Now, let's set up an interceptor with a rule to play a break that hasn't been already watched between the seekFrom position and seekTo position.

Copy the following into your js/receiver.js file.

breakManager.setBreakSeekInterceptor(function(breakSeekData) {
     /**
     *
     * Below code will play an unwatched break between seekFrom and seekTo position
     * Note: If the position of a break is less than 30 then it will be skipped due to the setBreakClipLoadInterceptor code
     */

    let breakToPlay;
    for (let i = 0; i < breakSeekData.breaks.length; i++) {
        if (!breakSeekData.breaks[i].isWatched) {
            breakToPlay = breakSeekData.breaks[i];
        }
    }
    if (breakToPlay){
        breakSeekData.breaks = [breakToPlay];
        return breakSeekData;
    }
});

Note: If we return nothing/null then no break is played. If we return breakSeekData as is then all the breaks between seekFrom to seekTo will be played.

Save your changes to js/receiver.js and initiate a Cast session on the web sender by right clicking anywhere on the page and selecting ‘Cast'. The ads stream should begin playing immediately.

10. Congratulations

You now know how to add ads to your receiver application using the latest Cast Receiver SDK.

For more details, see the Ad Breaks developer guide.