Acquire a licence

Acquire the licence required to play back Widevine-encrypted content.

Prerequisites

Procedure

The main steps are as follows:

  1. Create a new MediaKeyDeliverySession object by calling createDeliverySession on the MediaKeys object that you acquired when you initialised the DRM module.

    Do not create more than one MediaKeyDeliverySession object. Creating multiple instances may break encrypted playback functionality.

  2. Set up a listener for the onEncryptedListener's onEncrypted() event (which is fired when playback of an encryped stream starts).

  3. In this listener:

    1. Get the DRM scheme's type and signalization objects from the NMPContentProtectionScheme object that is passed to the listener. (If the stream supports multiple DRM schemes, the array that is passed will contain multiple such objects.)
    2. Extract the Base64-encoded signalization string from the signalization object (which is XML). 
    3. Call generateKeyRequest, passing the type object and the Base64-decoded signalization string.

    In the following code example, this happens in the acquireLicense() function.

  4. generateKeyRequest causes the onMessage() event to be fired. This is handled by the listener referenced in Provision the device. That creates the HTTP request and sends it to the licence server. (Actual code not shown because it is licence-server-dependent.)
  5. update() the MediaKeyDeliverySession object with the licence that was received from the licence server. (Also referenced in Provision the device.)

    The update() method takes a byte array containing raw licence data. Depending on what the licence server returns, you may need to extract or otherwise manipulate the data before passing it to update().

Code sample

// Example of how to acquire a licence from a Widevine license server

// For storing the DRM schemes object so we can still access it later.
private NMPContentProtectionScheme drmSchemes  = null;

// Example of encryptedListener->onEncrypted event handler.
// Note that mNmpVideoView is the NMPVideoView object that you should have instantiated to play the stream.
mNmpVideoView.setOnEncryptedListener(new NMPVideoView.OnEncryptedListener() {
  @Override
  public void onEncrypted(ArrayList<NMPContentProtectionScheme> schemes) {
    // Store the NMPContentProtectionScheme object so we can get at this data later.
    drmSchemes = schemes;

    // Query the NMPContentProtectionScheme objects and log what is returned.
    for (int i = 0; i < schemes.size(); i++) {

         NMPLog.d(TAG, "schemes.get(i).getURI() = " + schemes.get(i).getURI());
         NMPLog.d(TAG, "schemes.get(i).getSignalization() = " + schemes.get(i).getSignalization());
         NMPLog.d(TAG, "schemes.get(i).getType() = " + schemes.get(i).getType());
  
         // Get the DRM scheme names from the URI.
         NMPLog.d(TAG, "DRM Name = " + getDRMNameFromUri(schemes.get(i).getURI()));    

         if(getDRMNameFromUri(schemes.get(i).getURI()).equals("Widevine") {
            // Stream is Widevine-encrypted, so OK to acquire licence.
            acquireLicense(mMediaKeys, schemes.get(i).getType(), schemes.get(i).getSignalization());
         } 
    }
  }
}
   
private MediaKeyDeliverySession mDeliverSession = null;
   
private String getSignalizationFromInitData(byte[] xInitData, String xType) {
  String signalization = "";
  XmlPullParserFactory factory;
  String currentTag = "";
  try {
    factory = XmlPullParserFactory.newInstance();
    XmlPullParser xpp = factory.newPullParser();
  
    String xml = formatInitDataAsXml(new String(xInitData));
    xpp.setInput(new StringReader(xml));
    int eventType = xpp.getEventType();
    while (eventType != XmlPullParser.END_DOCUMENT) {
      switch (eventType) {
        case XmlPullParser.START_DOCUMENT:
          break;
        case XmlPullParser.START_TAG:
          currentTag = xpp.getName();
          break;
        case XmlPullParser.END_TAG:
          currentTag = xpp.getName();
          break;
        case XmlPullParser.TEXT:
          if (currentTag.equalsIgnoreCase(xType)) {
            signalization = xpp.getText();
          }
          NMPLog.d(TAG, "Get initData: " + signalization);
          break;
      }
      eventType = xpp.next();
    }
  } catch (XmlPullParserException xpe) {
    NMPLog.e(TAG, "Parse xml response exception: " + xpe.getMessage());
  } catch (IOException e) {
    NMPLog.e(TAG, "Read xml response exception: " + e.getMessage());
  }
  return signalization;
}
   
/**
 * Acquire widevine license from server. Parameters:
 *  - mediaKeys     - represents all the keys available to decrypt the media -- see Initialise the DRM module
 *  - xInitDataType - payload data type obtained from scheme.getType()
 *  - xInitData     - payload for server to retrieve licence obtained from scheme.getSignalization()
 */
public boolean acquireLicense(MediaKeys mediaKeys, String xInitDataType, byte[] xInitData) {
  if (mMediaKeys != mediaKeys) {
    mMediaKeys = mediaKeys;
    mDeliverSession = mediaKeys.createDeliverySession();
    mDeliverSession.setMessageListener(messageListener);
  }
  String pssh = MultiDRMHandlerHelper.getSignalizationFromInitData(xInitData, "cenc:pssh");
  // NOTE! This call should result in the messageListener onMessage event being fired
  mDeliverSession.generateKeyRequest(xInitDataType, Base64.decode(pssh.getBytes(), Base64.DEFAULT));
  return true;
}

How to test

Try playing a Widevine-encrypted stream. It should play successfully.