Apply Studio DRM with JW Platform (Web Player)

Learn how to configure your player to generate a license request and authenticate your video playback using a valid streaming key from the license server.

👍

This feature requires an Enterprise license.

JWP provides a simplified approach to protecting1 your content with industry-standard Digital Rights Management (DRM). By enabling DRM on a property from your JWP dashboard, the complex aspects of DRM management are managed by JWP on your behalf:

  • Several configured DRM Policies
  • DRM media content key generation and management for FairPlay Streaming
  • License delivery services for content playback on any Apple device

With JWP managing the technical aspects of DRM, you can focus on the design and implementation of engaging content experiences. For more information about the DRM workflow, please refer to the High-Level Workflow Overview.



1 Studio DRM blocks recording via browser extensions/add-ons or certain software programs on the application level, but is not guaranteed for all web browsers. Blocking screen recording depends on the Content Decryption Module (CDM) used in the browser. The CDM may vary with browsers updates and versions.

📘

For the following use cases, use Studio DRM Standalone with your current streaming and hosting solution:

  • Choosing not to enable Studio DRM with JW Platform
  • Implementing live stream integrations


Compatibility

The two most recent stable versions of the following listed browsers are supported.

HTML5 Browsers FairPlay PlayReady Widevine
Chrome
Firefox
Internet Explorer 11
(Windows 8.1+)
Microsoft Edge
(Windows 10+)
Opera
Safari


Requirements

ItemNotes
DRM EntitlementContact your JWP representative to learn more about having Studio DRM with JW Platformenabled for your account
Embedded playerSee: Add a player library
FairPlay Streaming Deployment PackageSee: Enabling Apple FairPlay Streaming

Enabling Apple FairPlay Streaming

If you want to use Apple FairPlay DRM, you must get a FairPlay Streaming Deployment package from Apple and upload the credentials to each DRM-enabled property in your JW dashboard.

🚧

You can use the same Fairplay Deployment package on all your properties, but you must upload the package to each property, explicitly.


Acquire FairPlay credentials

You must have the FairPlay credentials listed in the following table.

CredentialNotes
Encoded *.p12 keystore fileThe *.p12 keystore file must contain your FPS Deployment certificate and private key. This file must be password-protected (be sure to password-protect the whole file, NOT just the private key)
ASKApp secret key

To create the .p12 keystore file in OpenSSL, use the following commands:

$ openssl pkcs12 -export -out NAME.p12 -inkey PRIVATE_KEY.pem -in CERT.pem -passout pass:PASSWORD


Add FairPlay credentials to a property

  1. From the property list page, click on the name of a Studio DRM-enabled property.
  2. On the Content Protection tab, click Add FPS Credentials to add your credentials to the property. Repeat this step for all the DRM-enabled properties on which you want to enable FairPlay.


Implementation

Use the following steps to set up DRM playback in your web player:

  1. Generate a signed URL for DRM playback.
  2. Make a GET call with the signed URL. Within the sources array of the API response, the content URL with its associated DRM-specific LAURLs are returned.

🚧

Both the media URL and its associated LAURLs are valid for only 10 minutes from when they are requested. When a multi-item playlist of DRM-protected media is provided to the player, an intermediary service must be used to retrieve valid LAURLs for each media.

curl -L -X GET 'https://cdn.jwplayer.com/v2/media/{media_id}/drm/{policy_id}?token={valid_JWT}' \
- H 'Authorization: Bearer {v2_api_secret}'
"sources": [{
        "drm": {
            "widevine": {
                "url": "Widevine LAURL"
            },
            "playready": {
                "url": "Playready LAURL"
            }
        },
        "file": "MPD-URL.mpd",
        "type": "application/dash+xml"
    },
    {
        "drm": {
            "fairplay": {
                "processSpcUrl": "FairPlay LAURL",
                "certificateUrl": "FairPlay Certificate URL"
            }
        },
        "file": "M3U8-URL.m3u8",
        "type": "application/vnd.apple.mpegurl"
    }

]

  1. In the jwplayer().setup(), create a playlist[].sources[] object in which you define DASH and HLS streams for the same content.

    The following example includes the following streams: DASH and HLS.
jwplayer('myElement').setup({
    "playlist": [{
        "sources": [{
            "file": "MPD-URL.mpd",
            "type": "application/dash+xml"
        }, {
            "file": "M3U8-URL.m3u8"
            "type": "application/vnd.apple.mpegurl"
        }]
    }]
});

  1. For each file playlist[].sources[].file, add the LAURLs into a drm object.
jwplayer('myElement').setup({
    "playlist": [{
        "sources": [{
            "drm": {
          "widevine": {
              "url": "Widevine LAURL"
          },
          "playready": {
              "url": "Playready LAURL"
          }
      },
            "file": "MPD-URL.mpd",
            "type": "application/dash+xml"
        }, 
        {
            "drm": {
    "fairplay": {
        "processSpcUrl": "FairPlay LAURL",
        "certificateUrl": "FairPlay Certificate URL"
    }
},
            "file": "M3U8-URL.m3u8",
            "type": "application/vnd.apple.mpegurl"
        }]
    }]
});


Intermediary Service

One way to playback multi-item playlists is to set up an intermediary service that can generate the signed URL mentioned above and make the GET request to the Delivery API. The service will then return the relevant LAURLs and content URLs as they are needed by the player.

This intermediary service should use an appropriate form of authentication to prevent exposing the ability to retrieve valid content URLs and the LAURLs needed to achieve playback.

Once you have an intermediary service setup the player can be initialized as below.


📘

Each playlist item requires both a valid media ID and drm section to be defined. The actual values in the drm object are not important. They will be replaced just before each piece of content is loaded with signed links retrieved from the intermediary service.


jwplayer("player").setup({ 
    "playlist": [{
        "title": "DRM Item 1",
        "mediaId": "mediaId1",
        "sources": [{
            "drm": {
                "widevine": {
                    "url": "https://temp.com"
                },
                "playready": {
                    "url": "https://temp.com"
                }
            },
            "file": "https://temp.mpd",
            "type": "application/dash+xml"
        },{
            "drm": {
                "fairplay": {
                    "processSpcUrl": "https://temp.com",
                    "certificateUrl": "https://temp.com"
                },
            },
            "file": "https://temp.m3u8",
            "type": "application/vnd.apple.mpegurl"
        }]
    },{
        "title": "DRM Item 2",
        "mediaId": "mediaId2",
        "sources": [{
            "drm": {
                "widevine": {
                    "url": "https://temp.com"
                },
                "playready": {
                    "url": "https://temp.com"
                }
            },
            "file": "https://temp.mpd",
            "type": "application/dash+xml"
        },{
            "drm": {
                "fairplay": {
                    "processSpcUrl": "https://temp.com",
                    "certificateUrl": "https://temp.com"
                },
            },
            "file": "https://temp.m3u8",
            "type": "application/vnd.apple.mpegurl"
        }]
    }]
});

With this initialization, you can then use the setPlaylistItemCallback method to update each playlist item before it is loaded with the relevant signed URL.


The following functions require two global variables:

  • policyId set to a valid policy ID
  • urlServiceHost set to the host DNS of the intermediary service
jwplayer().setPlaylistItemCallback(function(item, index) {
    return new Promise(function(resolve, reject) {
        updatePlaylistItem(item);
        resolve(item);
    }); 
});

function updatePlaylistItem(item) {
    if (item.sources[0].type == "dash") {
        item.file = getManifestUrl("mpd", item.mediaId, policyId);
        item.sources[0].drm = {
            widevine: {
                url: getLaurl("widevine", item.mediaId, policyId)
            },
            playready: {
                url: getLaurl("playready", item.mediaId, policyId)
            }
        }
    } else {
        item.file = getManifestUrl("m3u8", item.mediaId, policyId);
        item.sources[0].drm = {
            fairplay: {
                processSpcUrl: getLaurl("fairplay", item.mediaId, policyId, "/laurl"),
                certificateUrl: getLaurl("fairplay", item.mediaId, policyId, "/cert")
            }
        }
    }
    item.sources[0].file = item.file;
}

function getLaurl(drm, mediaId, policyId, fairplayPostfix="") {
    console.log("getting laurl for " + mediaId);
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open("GET", `${urlServiceHost}/${drm}/${mediaId}/${policyId}${fairplayPostfix}`, false);
    xmlHttp.send(null);
    return xmlHttp.responseText;
}

function getManifestUrl(type, mediaId, policyId) {
    console.log("getting manifest for " + mediaId);
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.open("GET", `${urlServiceHost}/${type}/${mediaId}/${policyId}`, false);
    xmlHttp.send(null);
    return xmlHttp.responseText;
}



Did this page help you?