Hide

Custom Receiver Application

This document provides an overview of building a custom Google Cast receiver application. A Cast receiver is an application created using HTML, Javascript and CSS. It is loaded onto a Cast device (for example, a Chromecast) through a URL that is accessible over the network to which the Cast device is connected.

Note: Google Cast receiver applications must be hosted on secure servers supporting https when published. Publishing a Google Cast receiver application may take several hours to propagate to all Google Cast devices (up to 6 hours). If you wish to verify that your published application is working properly, you can manually restart your Google Cast device to force a load of new configuration data.

Google Cast Receiver SDK

Your receiver app accesses the Receiver API with the following reference:

	//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js

Do not self-host the cast_receiver.js resource. Updates will be applied periodically to address bug fixes and new features. Self-hosting will prevent a receiver app from benefiting from these changes.

Do not include the protocol in the URL when sourcing the cast_receiver.js resource. By not specifying http or https the resource can be fetched using the same protocol as the server hosting the receiver application. This is advantageous because it means that switching from http to https (receiver apps should be hosted on TLS capable servers) is transparent and will not require a code change.

Registration

Before developing a custom receiver application, you will need to register your app with the Google Cast SDK Developer Console. See Registration for more information. All receiver applications require sender applications to provide an app ID with the command messages they send to the receiver through the sender API. When you register your application, you will receive the app ID to include in your sender's API calls.

Application life cycle

The receiver application life cycle starts from the point at which the receiver is loaded onto the Cast device and proceeds to the point at which the application is torn down and the Cast device reverts back to its default state.

Over the life cycle of a receiver application, messages are exchanged between the receiver and any connected sender applications. A sender application will send an initial message to a Google Cast device requesting a session be created using a specific application ID. This kicks off the life cycle of the receiver as the Google Cast device will attempt to load the receiver application. Assuming there are no network issues, the receiver application will be downloaded from the network using the resolved URL associated with the application ID. Once loaded, the receiver application will perform its setup operations and indicate that it is ready to process messages from any connected sender applications.

A receiver application may tear down (end its current life cycle) under the following conditions:

  • The receiver application gets a message from a connected sender to end the application session.
  • The receiver application has been idle for a defined period of time without any connected senders and decides to end the application session.
  • The receiver encounters a fatal error during its normal life cycle (rare but possible).

Initialization & readiness

When a Google Cast receiver application starts up there is a minimum set of tasks that should be completed.

  • Get and hold the provided instance of the CastReceiverManager.
  • Override/provide any event listeners on the CastReceiverManager.
  • For media applications, identify the primary media element and connect it to a MediaManager.
  • For media applications, override/provide any event listeners on the MediaManager.

    See Media, below, for more information on the default event listeners.

  • For custom applications or media applications with custom protocol extensions (namespaces), call getCastMessageBus for the namespaces to be used.
  • Call start on the CastReceiverManager to indicate the receiver application is ready to receive messages.

Here is an example of a simple custom media application.

<html>
<head>
  <title>Example minimum receiver</title>
  <script src="//www.gstatic.com/cast/sdk/libs/receiver/2.0.0/cast_receiver.js"></script>
</head>
<body>
  <video id='media'/>
  <script>
    window.mediaElement = document.getElementById('media');
    window.mediaManager = new cast.receiver.MediaManager(window.mediaElement);
    window.castReceiverManager = cast.receiver.CastReceiverManager.getInstance();
    window.castReceiverManager.start();
  </script>
</body>
</html>

Session management

A receiver application may be interested in tracking when a sender connects or disconnects from it. This is accomplished using the CastReceiverManager singleton instance whereby the receiver application registers listener functions on the onSenderConnected and onSenderDisconneted properties.

When a receiver application is started through a sender requesting a session, and subsequently, when sender applications connect to an active receiver, the receiver is notified of these connections. Similarly, for the disconnection of senders, the receiver application is notified when a sender disconnects from its active session.

The disconnect event provides a reason field that can be used to determine why the connection to the receiver was dropped. The receiver app will be able to distinguish between explicit disconnect events when the user clicks on the disconnect button in the Cast menu and other disconnects caused by WiFi issues. The design checklist recommends that if the user explicitly disconnects from the receiver, the receiver should close if no other senders are connected. This logic can be implemented in JavaScript in the receiver:

window.castReceiverManager.onSenderDisconnected = function(event) {
  if(window.castReceiverManager.getSenders().length == 0 &&
    event.reason == cast.receiver.system.DisconnectReason.REQUESTED_BY_SENDER) {
      window.close();
  }
}

When the user selects to explicitly disconnect, the sender app should close the connection to the receiver and not stop the receiver application.

Application configuration

When a receiver application calls on the CastReceiverManager.start function, it may provide optional configuration data.

/**
 * Application config
 **/
var appConfig = new cast.receiver.CastReceiverManager.Config();

/**
 * Text that represents the application status. It should meet
 * internationalization rules as may be displayed by the sender application.
 * @type {string|undefined}
 **/
appConfig.statusText = 'Ready to play';

/**
 * Maximum time in seconds before closing an idle
 * sender connection. Setting this value enables a heartbeat message to keep
 * the connection alive. Used to detect unresponsive senders faster than
 * typical TCP timeouts. The minimum value is 5 seconds, there is no upper
 * bound enforced but practically it's minutes before platform TCP timeouts
 * come into play. Default value is 10 seconds.
 * @type {number|undefined}
 **/
// 100 minutes for testing, use default 10sec in prod by not setting this value
appConfig.maxInactivity = 6000;
/**
 * Initializes the system manager. The application should call this method when
 * it is ready to start receiving messages, typically after registering
 * to listen for the events it is interested on.
 */
window.castReceiverManager.start(appConfig);

If the receiver API detects that a sender is disconnected it will raise the senderdisconnected event. If the receiver has not been able to communicate with the sender for maxInactivity seconds, it will also raise the senderdisconnected event.

During development it is a good idea to set maxInactivity to a high value. For a published receiver application it is better to not set this value and instead rely on the default value.

Messages

Message exchange is the key interaction method for receiver applications. Sender applications can command and control a receiver application using messages. Similarly, receiver applications can keep senders informed about the state of the receiver by sending messages to connected senders.

A sender issues messages to a receiver using the sender APIs for the platform the sender is running (Android, iOS, Chrome). A receiver application can also send messages using the Google Cast Receiver APIs. A receiver can send messages to an individual sender, either in response to a received message or due to an application state change. Beyond point-to-point messaging, a receiver may also broadcast messages to all connected senders.

The event object (which is the manifestation of a message) that is passed to the event listeners has a data element (event.data) where the data takes on the properties of the specific event type. For example, in the case of the onSystemVolumeChanged event, the data is made up of all the properties of the cast.receiver.media.Volume type. So, to get the volume level, the application can query the event.data.level value.

Namespace & protocols

A namespace is a labeled protocol. That is, messages that are exchanged throughout the Google Cast ecosystem utilize namespaces to identify the protocol of the message being sent. Messages are string-based, but the encoding is specific to the namespace. A common encoding mechanism is JSON and it is used for example by the Cast media protocol (media namespace).

Namespaces are a powerful mechanism for standardizing protocols to be used by multiple sender applications. For example an application developer may decide to implement and define a library for senders that implements their own custom protocol. In this way a community of developers can create applications that know how to communicate with a common receiver application. This is the reason the cast media namespace is standardized, so a generic remote control can be created.

Receiver applications use the cast.receiver.CastMessageBus to manage messaging within a specific namespace. As well, a receiver may communicate directly with a connected sender by asking the CastMessageBus for a cast.receiver.CastChannel which will provide a context for messages to be exchanged within the namespace defined for the CastMessageBus.

A receiver application may choose to listen for messages on a specified namespace. By virtue of doing so, the receiver application is said to support that namespace protocol. It is then up to any connected senders wishing to communicate on that namespace to use the appropriate protocol.

var customMessageBus = castReceiverManager.getCastMessageBus('urn:x-cast:super.awesome.example');
customMessageBus.onMessage = function(event) {
   // Handle message
}

All namespaces are defined by a string and must begin with urn:x-cast: followed by any string. For example, urn:x-cast:com.example.cast.mynamespace.

Note: the media message namespace, urn:x-cast:com.google.cast.media is reserved.

Media messages sent using the Google Cast APIs on both the sender and receiver use the media namespace protocol by convention. See Media messages, below.

Media

Most receiver applications will utilize media in some form or another. The Google Cast APIs have built-in support for basic media operations such as load, play, pause and stop. For a complete list of supported operations refer to the Media Playback Messages. The Google Cast API object MediaManager provides automatic message handling for media namespace messages. The MediaManager object is a first-responder for media events that are issued on the cast.receiver.media.MEDIA_NAMESPACE.

var mediaElement = document.getElementById('media');  // eg. <video id='media'/>
var mediaManager = new cast.receiver.MediaManager(mediaElement);

This allows Google Cast sender applications to utilize their respective platform APIs to send media messages that will be processed automatically by the receiver’s instantiated MediaManager object. This approach is appropriate for simple media such as mp4, png, mp3, etc.

You can determine if a particular codec is supported with a call to the DOM media element method, canPlayType(). For example:

canPlayType('audio/mp4; codecs="aac51"')

If a receiver application wants to listen in and intercept media events it may do so by registering event listeners on the MediaManager instance.

In most cases (to style your UI), it is recommend to register to the events from the HTML5 media element itself, so instead of registering for the Cast PLAY command, it is better to listen for the HTML5 video element playing event. In this way the receiver code is more reusable with a web implementation. The goal of the MediaManager is not to wrap the HTML5 media element but to provide enough hooks for the management of Cast communications/messages.

mediaManager['origOnLoad'] = mediaManager.onLoad;
mediaManager.onLoad = function (event) {
   //…
   mediaManager[‘origOnLoad’](event);
}

Usually, where a receiver application overrides the default event handler it will need to call on the default handler after doing some processing of the event. The simple pattern of storing the original function as a property of the object and calling upon it later works very well in this situation.

mediaManager.onPlay = (function() {
    var origOnPlay = mediaManager.onPlay;
    return function(event) {
        // … do whatever is needed for the receiver application logic
        origOnPlay(event);
    }
}());

If your receiver needs to load and play media that utilizes Adaptive Streaming, Authentication, DRM or other advanced media transmission, you can utilize the Google Cast Media Player Library to build in this functionality. See Using the Media Player Library.

Media messages

Google Cast media control messages use the urn:x-cast:com.google.cast.media namespace protocol. The MediaManager is a first-responder to media control messages. You may add event listeners as detailed earlier.

See the Media Playback Messages reference for details on all messages.

A simplified perspective on the life cycle of media control goes like this:

  • A media load message is sent from a connected sender to the receiver (see the Chrome sender's loadMedia method, for example).
  • The receiver processes the message through the MediaManager which sets the media element’s src attribute.
  • Ignoring meta data loads etc., the MediaManager sends a media status update to the connected senders indicating that the media is ready. From there the sender can issue a play media message (see the Chrome sender's sendMessage method for example) to tell the receiver to begin playback.
  • The receiver’s MediaManager will tell the media element (for example, <video ...>) to play and broadcast the media status to all connected senders. Senders can then issue pause, stop, seek and volume messages to the receiver which will likewise be routed to the media element. For each change in media state there are corresponding broadcast messages that the receiver will issue to connected senders.

When providing event listeners to the instantiated MediaManager, remember to either store and call on the default handler function or implement equivalent logic (see the sample code in cast-custom-receiver-sample for code details).

If the receiver needs to include custom information in the media status messages returned to senders, provide a customizedStatusCallback function, which allows the receiver application to customize the status message returned.

Media events

The MediaManager receives media command messages from the sender. Based on those commands, the MediaManager interacts with the video/audio HTML5 media element. If there are state changes, the MediaManager will send status messages to the senders connected to the media session, so they can display a second screen UI.

The states sent via the Cast media protocol provide the state view that the sender needs to be aware of so it can properly display a second screen UI (for example, a progress scroll bar). The following rules apply:

  • If the player has no media loaded or has an error or the media has ended, it will be in the IDLE state. At this point there is no media Session.
  • If the player is paused, as returned by the paused property of the media element, the state is PAUSED.
  • If the player has not changed the currentTime property for one second, the state is BUFFERING.
  • Otherwise the state is PLAYING.

Receiver applications can tweak this logic by applying their own heuristics and changing the state in the customizedStatusCallback API. This API will provide the current state, and the application can return a modified state.

The receiver app can also not to go to IDLE after ended or error by overriding onEnded/onError, if this makes sense in a particular scenario. Also, the receiver UI typically should not use these sender-oriented states, as it has full access to the media element events and states which can provide more granularity, for example, buffering.

The basic media receiver state view provides visibility of the internal LOADING state that is not exposed to senders (from the sender perspective the receiver is BUFFERING during LOAD). The following diagram is only important if the application wants to override onLoad as it will need to be sure that the loadedmetadata event is triggered by the media element (this event is what the MediaManager uses to transition to PLAYING/PAUSED, depending on the value of autoplay).

Further note that if you override the onLoad() function, you must be aware of the following:

  • The transition from LOADING to PLAYING/PAUSED is decided by the MediaManager receiving the loadedmetadata event from the video/audio element. An application can override onLoad() but then it must guarantee that the loadedmetadata event will be raised (by calling load() on the video/audio element). If the loadedmetadata event is not raised (due to the internal design of the application player) then the application must call sendLoadComplete() or sendLoadError().
  • If an error happens during LOAD, and there is no error event on the video/audio element (because the error is caused by application logic), the application must take the player to the IDLE state by calling sendLoadError().

When testing your player, be sure that if you override onLoad() the callbacks, onMetadataLoaded and onLoadMetadataError are still properly called. This is the best way to guarantee that your player will behave as expected by the sender.

The sender view of the receiver state is very simple, only four states. The goal of this simplified view is to have the sender display a second screen UI, including a progress bar. If needed, applications can always customize these states by using customizedStatusCallback and include sub-states in the customData field.

Debugging

See Debugging.

Sample apps

Many Google Cast sample apps have been open sourced on GitHub. There you'll find several sample Receiver applications.