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.

📘

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



Requirements



Implementation

  1. Create a signed content URL. Follow steps 1-4 in the Implementation 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).

🚧

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();
mPlayer.setup(config);

📘

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.
@TargetApi(18)
public class WidevineMediaDrmCallback implements MediaDrmCallback {

   private static final String WIDEVINE_GTS_DEFAULT_BASE_URI =
           "https://proxy.uat.widevine.com/proxy";

   private final String defaultUri;

   public WidevineMediaDrmCallback(String contentId, String provider) {
       String params = "?video_id=" + contentId + "&provider=" + provider;
       defaultUri = WIDEVINE_GTS_DEFAULT_BASE_URI + params;
   }

   protected WidevineMediaDrmCallback(Parcel in) {
       defaultUri = in.readString();
   }

   public static final Creator<WidevineMediaDrmCallback> CREATOR = new Creator<WidevineMediaDrmCallback>() {
       @Override
       public WidevineMediaDrmCallback createFromParcel(Parcel in) {
           return new WidevineMediaDrmCallback(in);
       }

       @Override
       public WidevineMediaDrmCallback[] newArray(int size) {
           return new WidevineMediaDrmCallback[size];
       }
   };

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

   @Override
   public int describeContents() {
       return 0;
   }

   @Override
   public void writeToParcel(Parcel dest, int flags) {
       dest.writeString(defaultUri);
   }
}


Did this page help you?