This guide shows you how to create a custom receiver app that communicates between the sender app and receiver device and allows you to integrate the IMA SDK into your Cast app.
If you'd rather follow along with a finished IMA SDK receiver, download the sample app or view it on GitHub. You can also register a custom receiver to use our sample on GitHub with your cast device.
Our receiver sample is hosted on GitHub, so you can set up your custom receiver
for testing by using the following URL in your receiver:
https://googleads.github.io/googleads-ima-cast/client_receiver/player.html
Receiver files
The receiver is made up of three parts: HTML, CSS, and Javascript. Create an HTML file, named player.html, containing the following code:
player.html
<html>
<head>
<title>IMA Example receiver</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
<script src="//imasdk.googleapis.com/js/sdkloader/ima3.js"></script>
<script type="text/javascript" src="player.js"></script>
</head>
<body>
<div id="adContainer">
<video id="mediaElement"></video>
<script>
var player = new Player(document.getElementById('mediaElement'));
</script>
</div>
</body>
</html>
This HTML references the IMA SDK libraries and the Google Cast libraries, in addition to
style.css and player.js, which are detailed below. Other than these references, it is
creating a single Player
variable, which you also define in player.js.
Create a CSS page, named style.css, containing the following code for the receiver:
style.css
video {
width: 100%;
height: 100%;
overflow-y: hidden;
}
#adContainer {
width: 100%;
height: 100%;
}
This specifies the presentation of the adContainer DIV
, created in the HTML above.
Javascript
Constructor
The main logic for the receiver goes inside the player.js file. Create a new file
with this name. Create a constructor for the Player
type. Put the following code into the file:
player.js
'use strict';
/**
* Creates new player for video and ad playback.
* @param {cast.receiver.MediaManager} mediaElement The video element.
*/
var Player = function(mediaElement) {
var namespace = 'urn:x-cast:com.google.ads.ima.cast';
this.mediaElement_ = mediaElement;
this.mediaManager_ = new cast.receiver.MediaManager(this.mediaElement_);
this.castReceiverManager_ = cast.receiver.CastReceiverManager.getInstance();
this.imaMessageBus_ = this.castReceiverManager_.getCastMessageBus(namespace);
this.castReceiverManager_.start();
this.originalOnLoad_ = this.mediaManager_.onLoad.bind(this.mediaManager_);
this.originalOnEnded_ = this.mediaManager_.onEnded.bind(this.mediaManager_);
this.originalOnSeek_ = this.mediaManager_.onSeek.bind(this.mediaManager_);
this.setupCallbacks_();
};
This is the constructor function for the Player
type. It takes a media element DOM,
which it uses to play HTML5 video. Create a
cast.receiver.MediaManager
,
which sends and receives media messages and events. Obtain a reference to the
cast.receiver.CastReceiverManager
,
which allows communication with the platform and sends/receives system messages and events.
To send and receive messages specific to your app, create a channel for your specific
namespace, and start up the CastReceiverManager
.
There are several handlers that are overriden in MediaManager
, so save the original
handlers here to call them from the new handlers.
Set up callbacks
The last thing the constructor does is call setupCallbacks_()
, so define it here:
player.js
/**
* Attaches necessary callbacks.
* @private
*/
Player.prototype.setupCallbacks_ = function() {
var self = this;
// Google Cast device is disconnected from sender app.
this.castReceiverManager_.onSenderDisconnected = function() {
window.close();
};
// Receives messages from sender app. The message is a comma separated string
// where the first substring indicates the function to be called and the
// following substrings are the parameters to be passed to the function.
this.imaMessageBus_.onMessage = function(event) {
console.log(event.data);
var message = event.data.split(',');
var method = message[0];
switch (method) {
case 'requestAd':
var adTag = message[1];
var currentTime = parseFloat(message[2]);
self.requestAd_(adTag, currentTime);
break;
case 'seek':
var time = parseFloat(message[1]);
self.seek_(time);
break;
default:
self.broadcast_('Message not recognized');
break;
}
};
// Initializes IMA SDK when Media Manager is loaded.
this.mediaManager_.onLoad = function(event) {
self.originalOnLoadEvent_ = event;
self.initIMA_();
self.originalOnLoad_(self.originalOnLoadEvent_);
};
};
/**
* Sends messages to all connected sender apps.
* @param {!string} message Message to be sent to senders.
* @private
*/
Player.prototype.broadcast_ = function(message) {
this.imaMessageBus_.broadcast(message);
};
Handle the CastReceiverManager.onSenderDisconneded
message by closing the window. Then,
define some message types that you want to respond to from the sender app on the ima namespace
bus. All of these messages are passed as a comma-separated string where the first substring is
the function name. The sender asks you to request an ad when casting is begun, so you receive a
requestAd
message in this case, with the ad tag being the second parameter and the start time
being the third.
The other sender message is seek
, which is called when casting into content (and not ads).
Override MediaManager.onLoad()
to initialize the IMA SDK before calling the original
onLoad()
. Also define a broadcast function to broadcast messages to senders.
Initialize IMA
Implement the IMA initialization code that sets up the AdsLoader
, which is used to
request ads.
player.js
/**
* Creates new AdsLoader and adds listeners.
* @private
*/
Player.prototype.initIMA_ = function() {
this.currentContentTime_ = 0;
var adDisplayContainer = new google.ima.AdDisplayContainer(
document.getElementById('adContainer'), this.mediaElement_);
adDisplayContainer.initialize();
this.adsLoader_ = new google.ima.AdsLoader(adDisplayContainer);
this.adsLoader_.addEventListener(
google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
this.onAdsManagerLoaded_.bind(this), false);
this.adsLoader_.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR,
this.onAdError_.bind(this), false);
this.adsLoader_.addEventListener(google.ima.AdEvent.Type.ALL_ADS_COMPLETED,
this.onAllAdsCompleted_.bind(this), false);
};
/**
* Once the ads have been retrieved, use AdsManager to play the ads. Sends AdsManager
* playAdsAfterTime if starting in the middle of content.
* @param {ima.AdsManagerLoadedEvent} adsManagerLoadedEvent The loaded event.
* @private
*/
Player.prototype.onAdsManagerLoaded_ = function(adsManagerLoadedEvent) {
var adsRenderingSettings = new google.ima.AdsRenderingSettings();
adsRenderingSettings.playAdsAfterTime = this.currentContentTime_;
console.log(this.mediaElement_);
// Get the ads manager.
this.adsManager_ = adsManagerLoadedEvent.getAdsManager(
this.mediaElement_, adsRenderingSettings);
// Add listeners to the required events.
this.adsManager_.addEventListener(
google.ima.AdErrorEvent.Type.AD_ERROR,
this.onAdError_.bind(this));
this.adsManager_.addEventListener(
google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED,
this.onContentPauseRequested_.bind(this));
this.adsManager_.addEventListener(
google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
this.onContentResumeRequested_.bind(this));
try {
this.adsManager_.init(this.mediaElement_.width, this.mediaElement_.height,
google.ima.ViewMode.FULLSCREEN);
this.adsManager_.start();
} catch (adError) {
// An error may be thrown if there was a problem with the VAST response.
this.broadcast_('Ads Manager Error: ' + adError.getMessage());
}
};
You've set up and initialized the AdDisplayContainer
using the adContainer DIV
and the media
element, then created an AdsLoader
with that container. You registered event listeners to your
AdsLoader
to handle ad events. Once the ads are retrieved, AdsManager
displays them.
AdsManager events and requesting ads
In the previous section, you added event listeners for AdsManager
events and
ad errors. This section goes over these in detail.
player.js
/**
* Handles errors from AdsLoader and AdsManager.
* @param {ima.AdErrorEvent} adErrorEvent error
* @private
*/
Player.prototype.onAdError_ = function(adErrorEvent) {
this.broadcast_('Ad Error: ' + adErrorEvent.getError().toString());
// Handle the error logging.
if (this.adsManager_) {
this.adsManager_.destroy();
}
this.mediaElement_.play();
};
/**
* When content is paused by AdsManager to start playing an ad.
* @private
*/
Player.prototype.onContentPauseRequested_ = function() {
this.currentContentTime_ = this.mediaElement_.currentTime;
this.broadcast_('onContentPauseRequested,' + this.currentContentTime_);
this.mediaManager_.onEnded = function(event) {};
this.mediaManager_.onSeek = function(event) {};
};
/**
* When an ad finishes playing and AdsManager resumes content.
* @private
*/
Player.prototype.onContentResumeRequested_ = function() {
this.broadcast_('onContentResumeRequested');
this.mediaManager_.onEnded = this.originalOnEnded_.bind(this.mediaManager_);
this.mediaManager_.onSeek = this.originalOnSeek_.bind(this.mediaManager_);
this.originalOnLoad_(this.originalOnLoadEvent_);
this.seek_(this.currentContentTime_);
};
/**
* Destroys AdsManager when all requested ads have finished playing.
* @private
*/
Player.prototype.onAllAdsCompleted_ = function() {
if (this.adsManager_) {
this.adsManager_.destroy();
}
};
/**
* Sets time video should seek to when content resumes and requests ad tag.
* @param {!string} adTag ad tag to be requested.
* @param {!float} currentTime time of content video to resume from.
* @private
*/
Player.prototype.requestAd_ = function(adTag, currentTime) {
if (currentTime != 0) {
this.currentContentTime_ = currentTime;
}
var adsRequest = new google.ima.AdsRequest();
adsRequest.adTagUrl = adTag;
adsRequest.linearAdSlotWidth = this.mediaElement_.width;
adsRequest.linearAdSlotHeight = this.mediaElement_.height;
adsRequest.nonLinearAdSlotWidth = this.mediaElement_.width;
adsRequest.nonLinearAdSlotHeight = this.mediaElement_.height / 3;
this.adsLoader_.requestAds(adsRequest);
};
/**
* Seeks content video.
* @param {!float} time time to seek to.
* @private
*/
Player.prototype.seek_ = function(time) {
this.currentContentTime_ = time;
this.mediaElement_.currentTime = time;
this.mediaElement_.play();
};
The onAdError()
function handles errors from AdsLoader
and AdsManager
. If the receiver
encounters any errors, it logs them and then destroy the AdsManager
and then play the content
media. Next, you have two functions that handle pausing and resuming the content, followed by a
function to clean up the AdsManager
after ads have completed.
While the receiver is playing ads, the cast functions onEnded
and onSeek
are overridden to prevent
the cast device from assuming that an ad ending means the content video is over.
Finally, there is a function that requests ads from a server using AdsLoader
and one that plays the
content video.
Next steps
At this point you should have a finished cast custom receiver app. To try it out:
- Register your receiver in order to receive an app ID that's used with API calls from the sender app.
- Create either an Android or iOS sender app.