Streaming DRM protected content with Bitmovin Player Android SDK

As DRM (Digital Rights Management) protected content is often crucial for certain streaming infrastructures, this tutorial aims to help you get started with Bitmovin Player Android SDK DRM setup.

Briefly about DRM

In a nutshell, DRM systems provide the ability to control how people can consume your content while securing it against piracy using various encryption schemes. As the topic itself is quite complex we will not cover the details of how it works as part of this tutorial, but if you are completely new to the topic of DRM and you want to learn more you can visit our dedicated article - What is DRM and How Does it Work.

All the DRM solutions mentioned in this tutorial are self sufficient systems that can be used independently of one another.

As the device portfolio that needs to be covered for streaming across the user base is very diverse in terms of DRM systems support, it leads to a need to use a so called Common Encryption Schema (CENC). This approach allows different DRM systems to decrypt commonly encrypted content to save the infrastructure costs. This means that Multi-DRM content packaging output supports more DRM systems, so more devices can be covered.

Example of CENC present in MPEG-DASH mpd manifest file

<!-- Common Encryption -->
<ContentProtection
  schemeIdUri="urn:mpeg:dash:mp4protection:2011"
  value="cenc"
  cenc:default_KID="8D1A585A-A0B4-A942-917A-C1B659142B2A">
</ContentProtection>
<!-- PlayReady -->
<ContentProtection
  schemeIdUri="urn:uuid:9A04F079-9840-4286-AB92-E65BE0885F95">
</ContentProtection>
<!-- Widevine -->
<ContentProtection
  schemeIdUri="urn:uuid:EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED">
</ContentProtection>

This tutorial focuses on DRM systems that are most commonly used across the OTT industry. We will cover examples for Widevine (Google) and PlayReady (Microsoft) systems and we will also mention the ClearKey encryption system as a free security alternative. You can check the complete support list in our Bitmovin Player DRM support page.

Widevine

Widevine is usually very easy to setup for playback using a license server URL configured via licenseUrl property using the WidevineConfig. The license server URL can be also defined inside of the stream manifest, but player will prefer the one defined in the DrmConfig if it is present.

val sourceConfig = SourceConfig(  
    url = TODO("CONTENT_MANIFEST_URL"),  
    type = SourceType.Dash,  
    drmConfig = WidevineConfig(  
        licenseUrl = TODO("LICENSE_URL")  
    ),  
)

Preferred security level

Through the configuration it is possible to define the security level of the Widevine DRM key system. If a string specifies a higher security level than the system is able to support playback will fail. Setting of the required security level is configurable using preferredSecurityLevel.

val sourceConfig = SourceConfig(  
    url = TODO("CONTENT_MANIFEST_URL"),  
    type = SourceType.Dash,  
    drmConfig = WidevineConfig(  
        licenseUrl = TODO("LICENSE_URL")  
    ).apply {  
        preferredSecurityLevel = "L3"  
    },  
)

Preparing license message

In some workflows it may be required to prepare the license acquisition message which will be sent to the license acquisition server. As many DRM providers expect different, vendor-specific message, this can be done using the prepareMessageCallback . Also the prepareLicenseCallback option can be used for custom Widevine servers where the response is not just the license itself, but instead the license is e.g. wrapped in an JSON object.

val sourceConfig = SourceConfig(  
    url = TODO("CONTENT_MANIFEST_URL"),  
    type = SourceType.Dash,  
    drmConfig = WidevineConfig(  
        licenseUrl = TODO("LICENSE_URL")  
    ).apply {  
        prepareMessageCallback = PrepareMessageCallback { keyMessage -> keyMessage }  
        prepareLicenseCallback = PrepareLicenseCallback { licenseResponse -> licenseResponse }  
    }  
)

PlayReady

The PlayReady DRM system is supported only on Android TV devices. It is usually configured simply by providing the license server URL through the licenseUrl field, same as the way Widevine is configured.

val sourceConfig = SourceConfig(  
    url = TODO("CONTENT_MANIFEST_URL"),  
    type = SourceType.Dash,  
    drmConfig = PlayReadyConfig(  
        licenseUrl = TODO("LICENSE_URL")  
    )  
)

License request handling

If the infrastructure requires additional license request setup it is possible to use the httpHeaders property on the specific DRM system present in DrmConfig to add the appropriate headers to the license requests.

Example Of Headers Setup

val sourceConfig = SourceConfig(  
    url = TODO("CONTENT_MANIFEST_URL"),  
    type = SourceType.Dash,  
    drmConfig = PlayReadyConfig(  
        licenseUrl = TODO("LICENSE_URL")  
    ).apply {  
        httpHeaders = mutableMapOf("key" to "value")  
    }  
)

Using Bitmovin Player network API

It is also possible to use the NetworkConfig to setup the interception of the license requests or responses using preprocessHttpRequestCallback or preprocessHttpResponseCallback properties on the network configuration.

val player = Player(  
    application.applicationContext,  
    playerConfig = PlayerConfig(  
        networkConfig = NetworkConfig(  
            preprocessHttpRequestCallback = { type, request ->  
                if (type == HttpRequestType.DrmLicenseWidevine) {  
                    request.headers = mapOf("key" to "value")  
                }  
                CompletableFuture.completedFuture(request)  
            }  
        )  
    )  
)

ClearKey

To setup ClearKey content protection, ClearKeyConfig can be used.

val sourceConfig = SourceConfig(  
    url = TODO("CONTENT_MANIFEST_URL"),  
    type = SourceType.Dash,  
    drmConfig = ClearKeyConfig(  
        key = TODO("key"),  
        kid = TODO("kid"),  
    )  
)

Provider-specific Recipes

These recipes are based on the BasicDRMPlayback sample.