JWP Web Player (JW Platform)

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.


🚧

If you are not utilizing Studio DRM with JW Platform please reference these guides based on your use case:


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

Item Notes
DRM entitlement Contact your JWP representative for more information.
DRM-enabled property See: Enable a property
FairPlay Streaming Deployment Package See: Add FairPlay credentials to a property
Embedded player See: Add a player library


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"
        }
    
    ]
    
    
  3. 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"
            }]
        }]
    });
    
  4. 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"
            }
          ],
          "tracks": [
            {
              "kind": "captions",
              "file": "https://www.yourdomain.com/caption-file_en.vtt",
              "label": "English"
            },
            {
              "kind": "captions",
              "file": "https://www.yourdomain.com/caption-file_sp.vtt",
              "label": "Español"
            }
          ]
        }
      ] 
    });
    


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;
}