Migrate to CAF Receiver

This guide explains how to migrate a Cast Receiver v2 app to a CAF Receiver app.

The new Cast Application Framework (CAF) SDK, also known as Receiver v3, is a major upgrade from the Receiver v2 SDK. The CAF Receiver SDK provides an easy, streamlined SDK for developing media Cast receiver applications.

The CAF receiver provides an API that is more consistent with the new CAF sender APIs. It provides full integration of a player (MPL and Shaka) and full implementation and support for the Cast media and Google Assistant voice commands. CAF SDK also provides a default UI that can be easily styled using CSS, and a data binding service to simplify UI implementation.

Why Migrate to CAF?

By migrating a receiver application to CAF, a lot of code that deals with the player can be eliminated, so you can concentrate on writing application-specific business logic.

CAF seamlessly integrates MPL and Shaka players to support a wider range of content types including—HTTP Live Streaming (TS and CMAF), MPEG-DASH, Smooth Streaming and types supported by the Media Element source property (MP3, MP4, Icecast, etc...). For a complete list see Supported Media for Google Cast. Currently CAF does not support a user-provided player.

Migrating to CAF will add the support for voice control with Google Assistant. Any new Google Assistant voice command will automatically be supported when using CAF.

In addition to supporting new media commands—like "change tracks by language” and "change playback rate”—CAF also provides better queueing, built-in ads support, and better live support.

What Changed?

The CAF receiver API tries to follow the conventions that were introduced by CAF senders for Android and iOS, and is quite different from v2.

The CAF receiver is using a new namespace cast.framework instead of cast.receiver namespace for all exposed APIs. Many of the data objects that were used by v2 are the same in CAF and are exposed under the cast.framework.messages namespace (they were mostly under cast.receiver.media).

The following v2 services are replaced by corresponding CAF services:

  • CastReceiverManager class is replaced by CastReceiverContext which is a singleton that manages the cast session, senders, sending custom messages, and global system events. The CastReceiverOptions can be used to provide global application options (such as queue, receiver version, playback config, etc.) to the context.
  • MediaManager class is replaced by PlayerManager which is a property of the CastReceiverContext singleton, and it manages the media session, media requests, Google Assistant voice requests (CommandAndControlManager in v2), and fires media events. Configuration for the players (cast.player.api.Host in MPL) is provided by PlaybackConfig, which can be provided globally or per load request.

The PlayerManager also exposes the new sub-manager classes:

const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();

// Set global options.
const options = cast.framework.CastReceiverOptions();
options.versionCode = DEVELOPERS_APP_VERSION;

context.start(options);

Receiver Business Logic

Receiver v2 exposed event handlers (such as CastReceiverManager.onReady or MediaManager.onLoad) to add business logic. In CAF the event handlers are replaced by event listeners (CastReceiverContext.addEventListener) and message interceptors (PlayerManager.setMessageInterceptor). CAF Receiver can have multiple event listeners for an event (the listener does not affect the event), and one interceptor per message. The interceptor can update the request or handle it (return a modified request, a success message, or error message), and can be an async handler that returns a promise.

The load request interceptor is the most common place to add application-specific logic. For load requests from a sender, the load interceptor can convert the content ID to content URL. The load interceptor is also being called for preload and precache requests if no explicit interceptor was provided for preload or precache.

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    request => {
      // Resolve entity to content id
      if (request.media.entity && !request.media.contentId) {
        return getMediaByEntity(request.media.entity).then(
            media => {
              request.media.contentId = media.url;
              return request;
            });
      }
      return request;
    });

The v2 customized media status handler is also replaced with a message interceptor for the media status message. Receiver apps that do not want to expose the media URL in the media status can provide a URL resolver (PlayerManager.setMediaUrlResolver), which provides the media URL for a load request. That URL is used by CAF internally and is not provided in the media status.

playerManager.setMessageInterceptor(
    cast.framework.messages.MessageType.MEDIA_STATUS,
    status => {
      // Disable seek.
      status.supportedMediaCommands &=
          ~cast.framework.messages.Command.SEEK
      return status;
    });

Events

CAF Receiver provides an extensive set of events both from CastReceiverContext and PlayerManager. Receiver apps can have multiple listeners on any event, and can also provide one listener to multiple events. (See cast.framework.events.category for some groups of events.)

The events cover any user request, playback progress, player processing, and any low-level media element event (CAF does not expose the media element itself).

The receiver app can add event listeners to act upon (for example, add text tracks definition when load completes), or for analytics.

// Log all media commands
playerManager.addEventListener(
    cast.framework.events.category.REQUEST,
    event => logEvent(event.type));

Custom Message Bus

CAF does not expose the message bus in the API, instead it provides CastReceiverContext.addCustomMessageListener to add a message listener for a specific namespace (only one per namespace) and CastReceiverContext.sendCustomMessage to send a message on a namespace. All namespaces need to be declared before starting the receiver (that is, before calling CastReceiverContext.start). The namespaces can be declared by adding a message listener to them or can be provided as a start option in CastReceiverOptions.customNamespaces.

const options = cast.framework.CastReceiverOptions();
options.customNamespaces = {
    [CUSTOM_NS]: cast.framework.system.MessageType.JSON
};
context.start(options);

context.sendCustomMessage(CUSTOM_NS, {
  type: 'status'
  message: 'Playing'
});

Default UI

CAF provides a default receiver UI that displays a playback progress bar and media metadata as needed. The default UI is provided as a custom element (<cast-media-player>) that can be styled with CSS-like styling.

<style>
   cast-media-player { --splash-image: url("splash.png"); }
</style>
<cast-media-player></cast-media-player>

For more customization, a receiver app can implement its own UI. The CAF receiver provides the cast.framework.ui.PlayerDataBinder class to help bind a UI object to the receiver playback state.