# ExoPlayer Integration

This document provides an overview of the queueing and DRM integration
support.

## DRM enhancements

> [!WARNING]
> **Warning:** The code in this section is provided as an example only. If you require content protection in your receiver application, it is recommended you create a custom receiver and consult with your DRM vendor for best practices on securing your content.

> [!NOTE]
> **Note:** Web Receivers automatically handle DASH or HLS manifests that contain the DRM license and key URL.

The [ExoPlayer Cast Demo](https://github.com/google/ExoPlayer/tree/release-v2/demos/cast)
has been updated to utilize a structured way to pass DRM configuration using
ExoPlayer's `MediaInfo` to a 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
[Web Receiver](https://developers.google.com/cast/docs/caf_receiver/).

Before beginning, it would be helpful to familiarize yourself with the
documentation on DRM support in
[Google Cast](https://developers.google.com/cast/docs/mpl/streaming_protocols#drm)
and [ExoPlayer](https://exoplayer.dev/drm.html). This overview will show you how
to wire-up the ExoPlayer DRM configuration to a Web Receiver. For information
on how to utilize DRM in ExoPlayer, see the official
[ExoPlayer website](https://exoplayer.dev).

### 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, for example, Widevine.

The DRM configuration you provide to ExoPlayer is sent to your receiver
application as a property in `customData` on the [`MediaInformation`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.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`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.PlaybackConfig#licenseRequestHandler)
and a [`mediaPlaybackInfoHandler`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.PlayerManager#setMediaPlaybackInfoHandler). The `licenseRequestHandler` allows you to customize
how CAF requests a license from your license key server. The
`mediaPlaybackInfoHandler` lets you modify the
[`PlaybackConfig`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.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 Web Receiver SDK 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`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.LoadRequestData)
object that contains the Exo Player Config 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 Web
Receiver makes to your license server. The handler is passed a [`NetworkRequestInfo`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.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`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.messages.LoadRequestData)
and a [`PlaybackConfig`](https://developers.google.com/cast/docs/reference/caf_receiver/cast.framework.PlaybackConfig.html),
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](https://exoplayer.dev/) features up-to-date documentation
and announcements. Issues with ExoPlayer and its Cast integration can be
reported at [ExoPlayer's GitHub](https://github.com/google/ExoPlayer)
repository.