Enable DRM with JW Stream

Learn a simplified approach to protecting your Android content with DRM.


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

  • Several configured DRM Policies
  • DRM license generation and management for Widevine
  • License delivery services for content playback on any device

With JW Player managing the technical aspects of DRM, you can focus on the design and implementation of engaging content experiences.

📘

NOTE

You can also Play DRM-protected content if you have not enabled DRM with JW Stream.



Requirements



Tutorial

📘

GOAL

After completing this tutorial, you should be able to answer the following question:
• How can I playback DRM-protected content when using DRM with JW Stream?


  1. Create a signed content URL. Follow steps 1-4 in the Tutorial section of Enable DRM with JW Stream. The signed content URL returns a JSON object of the content metadata similar to the following example:
{
   "title":"Tears of Steel WAVE Test",
   "description":"",
   "kind":"Single Item",
   "playlist":[
      {
         "title":"Tears of Steel WAVE Test",
         "mediaid":"WlvsLi24",
         ...
         "images":[...],
         "duration":30,
         "pubdate":1603891888,
         "description":"",
         "sources":[
            {
               "drm":{
                  "widevine":{
                     "url":"{widevine_license_url}"
                  },
                  "playready":{
                     "url":"{playready_license_url}"
                  }
               },
               "file":"{signed_video_url}",
               "type":"application/dash+xml"
            },
            ...         
         ],
         "tracks":[...],
         ...
   ],
   "feed_instance_id":"{feed_instance_id}"
}

  1. From the signed content URL response, extract the file title (title), file URL (playlist[].sources[].file) and the license URL (playlist[].sources[].drm.widevine.url).

❗️

WARNING

The ordering of items within playlist[].sources[] is not static. Therefore, do not use a defined index (playlist[].sources[0]) as part of your extraction process.


  1. Use the extracted title, video URL, and license URL to set the title, file, and mediaDrmCallback().
PlaylistItem playlistItem = new PlaylistItem.Builder()
    .title("{parsed_title}")
    .file("{parsed_file_url}")
    .mediaDrmCallback(new WidevineMediaDrmCallback("{parsed_license_url}")
    .build();

List<PlaylistItem> playlist = new ArrayList<>();
playlist.add(playlistItem);
PlayerConfig config = new PlayerConfig.Builder()
    .playlist(playlist)
    .build();
mPlayerView.setup(config);

📘

NOTE

You may see a "Cannot resolve symbol" error on new WidevineMediaDrmCallback();. This error will be resolved once you have completed the following steps of this tutorial.


  1. Add a Util.java to your project. This utility is used by the MediaDrmCallback class to download data.
public class Util {

    public static byte[] executePost(String url, byte[] data, Map < String, String > requestProperties)
    throws IOException {
        HttpURLConnection urlConnection = null;
        try {
            urlConnection = (HttpURLConnection) new URL(url).openConnection();
            urlConnection.setRequestMethod("POST");
            urlConnection.setDoOutput(data != null);
            urlConnection.setDoInput(true);
            if (requestProperties != null) {
                for (Map.Entry < String, String > requestProperty: requestProperties.entrySet()) {
                    urlConnection.setRequestProperty(requestProperty.getKey(), requestProperty.getValue());
                }
            }
            // Write the request body, if there is one.
            if (data != null) {
                OutputStream out = urlConnection.getOutputStream();
                try {
                    out.write(data);
                } finally {
                    out.close();
                }
            }
            // Read and return the response body.
            InputStream inputStream = urlConnection.getInputStream();
            try {
                return toByteArray(inputStream);
            } finally {
                inputStream.close();
            }
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }
    }
}

  1. Add the following DRM callback snippet to your project. You can modify the class for your needs.
public class WidevineMediaDrmCallback implements MediaDrmCallback {

   private final String defaultUri;

   public WidevineMediaDrmCallback(String widevineUrl) {
       defaultUri = widevineUrl;
   }

   @Override
   public byte[] executeProvisionRequest(UUID uuid, ExoMediaDrm.ProvisionRequest request) throws IOException {
       String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
       return Util.executePost(url, null, null);
   }

   @Override
   public byte[] executeKeyRequest(UUID uuid, ExoMediaDrm.KeyRequest request) throws IOException {
       String url = request.getLicenseServerUrl();
       if (TextUtils.isEmpty(url)) {
           url = defaultUri;
       }
       return Util.executePost(url, request.getData(), null);
   }
}


Did this page help you?