ExoPlayer integration

Overview

This document provides an overview of the recent changes made by the Cast and ExoPlayer teams to closely integrate the two platforms. In addition to providing improved queueing support, we now provide enhancements to ease the integration of DRM solutions.

DRM enhancements

The ExoPlayer Cast Demo has been updated to utilize a structured way to pass DRM configuration using ExoPlayer’s MediaInfo to a Chromecast receiver application. The Cast sample also uses a demo receiver that includes the same code in this overview, allowing you to test out DRM support. However, if you would like to Cast DRM protected content, you should build and host your own receiver application.

Before beginning, it would be helpful to familiarize yourself with the documentation on DRM support in Google Cast and ExoPlayer. This overview will show you how to wire-up the ExoPlayer DRM configuration to a Cast receiver. For information on how to utilize DRM in ExoPlayer, see the official ExoPlayer website.

Providing the DRM configuration

The ExoPlayer demo app contains sample code that shows how to provide DRM configuration as part of a MediaItem. The four options you can configure are:

  • Headers - a dictionary of headers that are applied to the HTTPS request to retrieve the DRM license.
  • License URL - the URL used to acquire the license.
  • Protection System - the DRM protection scheme used to protect the content, e.g. Widevine, PlayReady, etc.

The DRM configuration you provide to ExoPlayer is sent to your receiver application as a property in customData on the MediaInformation object as part of a load request. By default, this property is called exoPlayerConfig, which matches the following definition.

/**
 * Extended configuration settings for ExoPlayer.
 */
ExoPlayerConfig class {
   constructor() {
    /**
     * Dictionary of headers to apply to the license request.
     * @type {!Object|undefined}
     */
    this.headers;

    /**
     * The URL for your DRM server.
     * @type {string|undefined}
     */
    this.licenseUrl;

    /**
     * Preferred protection system to use for decrypting content.
     * @type {!cast.framework.ContentProtection|undefined}
     */
    this.protectionSystem;

    /**
     * Indicates whether CORS Access-Control requests should be made using
     * credentials such as cookies or authorization headers.
     *
     * If withCredentials is set to true then Access-Control-Allow-Origin cannot
     * be set to '*'.
     * @type {boolean|undefined}
     */
    this.withCredentials;
  }
}

Initial setup

Depending on the DRM solution you use, you might need to configure a licenseRequestHandler and a mediaPlaybackInfoHandler. The licenseRequestHandler allows you to customize how CAF requests a license from your license key server. The mediaPlaybackInfoHandler lets you modify the PlaybackConfig on a per media item basis if, for example, each piece of content has to use a different license server URL.

To capture a copy of the ExoPlayerConfig from each load request object, create a load request interceptor in your Cast receiver application.

The first step is to register your handlers before starting your Cast application.

const context = cast.framework.CastReceiverContext.getInstance();
const playbackConfig = new cast.framework.PlaybackConfig();

playbackConfig.licenseRequestHandler =
    licenseRequestHandler;
context.getPlayerManager().setMediaPlaybackInfoHandler(
    mediaPlaybackInfoHandler);
context.getPlayerManager().setMessageInterceptor(
    cast.framework.messages.MessageType.LOAD,
    loadInterceptor);

// starts the Cast application
context.start({playbackConfig: playbackConfig});

Load request interceptor

The load request interceptor is a callback that allows you to view and modify a Cast load request before CAF attempts to load a media item. Importantly it is called before the license request hander and the media playback info handler.

The load request interceptor is passed a LoadRequestData object that contains the ExoPlayerConfig that was sent by your app. You can save this object as a global variable for use in your license request handler and media playback info handler.

loadInterceptor(loadRequestData) {
    // not every load request will have a customData object
    if (loadRequestData.media && loadRequestData.media.customData &&
            loadRequestData.media.customData['exoPlayerConfig']) {
        // exoPlayerConfig is a global variable here
        exoPlayerConfig =
                loadRequestData.media.customData['exoPlayerConfig'];
    }

    // you must return the loadRequestData object
    return loadRequestData;
}

License request handler

The license request handler allows you to customize the HTTPS request CAF makes to your license server. The handler is passed a NetworkRequestInfo object, which you can then use to add HTTP headers, include cookies, or even modify the URL. The handler should return this object.

If, for example, you needed to add custom headers to your license request, you could create a license request handler similar to this:

licenseRequestHandler(networkRequestInfo) {
    if (!exoPlayerConfig) {
        return networkRequestInfo;
    }

    networkRequestInfo.headers =
            exoPlayerConfig.headers ? exoPlayerConfig.headers : undefined;

    return networkRequestInfo;
}

Media playback info handler

The media playback info handler allows you to make changes to your playback configuration on a per media item basis. The handler is passed a LoadRequestData and a PlaybackConfig, you should return a playback config. The media playback info handler will be called before each item you Cast is loaded. If you had per-content license urls, can to change them and the protection system before the load.

mediaPlaybackInfoHandler(loadRequest, playbackConfig) {
    if (!exoPlayerConfig) {
        return;
    }

    playbackConfig.licenseUrl = exoPlayerConfig.licenseUrl ?
            exoPlayerConfig.licenseUrl :
            undefined;
    playbackConfig.protectionSystem = exoPlayerConfig.protectionSystem ?
            exoPlayerConfig.protectionSystem :
            undefined;

    return playbackConfig;
}

Further resources

Each DRM implementation is custom and this code is provided as a demonstration only. You should consult your DRM provider to ensure you have correctly implemented DRM in your ExoPlayer and Cast applications.

ExoPlayer’s Website features up-to-date documentation and announcements. Issues with ExoPlayer and its Cast integration can be reported at ExoPlayer’s GitHub repository.