googlecastnew500.png

This codelab will teach you the fundamentals of building a receiver application that takes advantage of the new Break API in Cast.

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

What you'll need

Experience

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

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

Download Source Code

and unpack the downloaded zip file.

To be able to use your 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, just skip the following instructions, just remember the URL, you'll need it in the next section.

If you don't have any server available to you, don't fret. You may install node.js, the http-server and ngrok node module.

npm install -g http-server
npm install -g ngrok

Run the server

If you're using http-server, go to your console, and do the following:

cd app-done
http-server

You should then see something like the following:

Starting up http-server, serving ./
Available on:
  http://127.0.0.1:8080
  http://172.19.17.192:8080
Hit CTRL-C to stop the server

Notice the local port used and do the following in a new terminal to expose your local receiver over HTTPS using ngrok:

ngrok http 8080


This will setup an ngrok tunnel to your local HTTP server, assigning you a globally available HTTPS secured endpoint you can use in the next step (https://116ec943.eu.ngrok.io):

ngrok by @inconshreveable                                                                                                                                                                                                                                     (Ctrl+C to quit)

Session Status         online
Version                2.2.4
Web Interface          http://127.0.0.1:8080
Forwarding             http://116ec943.eu.ngrok.io -> localhost:8080
Forwarding             https://116ec943.eu.ngrok.io -> localhost:8080

You should keep both ngrok and http-server running for the duration of the codelab. Any changes you make locally will be instantly available.

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.

Click "Add new application"

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

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.

Click on "Add new Device"

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.

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:

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

  1. Select the android_studio_folder.pngapp-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.

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

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, i.e 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.

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.

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.

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

Take a look at our sample apps on GitHub: github.com/googlecast.