1

I'm trying to get DRM-protected content to cast to my Chromecast device via my custom receiver, and I keep getting this error:

[ERROR] Event detected: {"type":"ERROR","detailedErrorCode":200,"error":{"shakaErrorCode":6008,"shakaErrorData":["Failed to execute 'update' on 'MediaKeySession': UpdateSession failed"]}}

According to the Shaka docs, this error is because the CDM doesn't like the response it is getting from the license server:

"The license response was rejected by the CDM. The server's response may be invalid or malformed for this CDM. error.data[0] is an error message string from the browser."

This does not particularly surprise me, since I know that my CDN's Widevine proxy uses base64 (standard) encoding, but most Google products require base64url encoding. So I need to intercept the response from the license server, massage it a bit, and then pass it on to the CDM.

How can I do this? There doesn't seem to be a playbackConfig.licenseResponseHandler that I can set up to capture the response ...

Does anyone have an example of a 'standard' Widevine licenseRequestHandler function that I can look at? Maybe I'm doing something basically wrong with mine?

My CDN's widevine proxy seems to sometimes require an initial challenge request (to get the server certificate) to the proxy URL (with some other params, including an auth token), with a JSON request body like this:

{   
    "getWidevineLicense": {     
        "releasePid": "2vnevwf3AVz0",     // unique identifier for my particular video
        "widevineChallenge": "CAQ="       
    } 
}  

Then the response is passed to the CDM, which builds the real Widevine challenge, which is used in a second call to the widevine proxy, which returns the actual license key.

Within my receiver code, I attempt to capture and pre-process the license request, with the following handler:

playbackConfig.licenseRequestHandler = requestInfo => {
  castDebugLogger.warn(LOG_TAG, 'Unmodified request: ', requestInfo);
  requestInfo.url = widevineLicenseServer;
                    
  // It doesn't seem to matter if we use requestInfo.body or requestInfo.content 
  //-- it doesn't work either way.
  var wrapped = { "getWidevineLicense": {} };

  var base64_string = "CAQ=";
  castDebugLogger.warn(LOG_TAG, 'Checking for original content: ', requestInfo.content);
  if (requestInfo && requestInfo.content && requestInfo.content.length > 16) {
    castDebugLogger.warn(LOG_TAG, 'Detected original content: ', requestInfo.content);
    base64_string = btoa(requestInfo.content);
    // Shaka expects to be using base64url encoding, but Comcast (my CDN) uses base64 (standard),
    // so I have to manually adjust two characters.
    base64_string = base64_string.replace(/-/g, '+');
    base64_string = base64_string.replace(/_/g, '/');
  }
  wrapped.getWidevineLicense.widevineChallenge = base64_string;
  wrapped.getWidevineLicense.releasePid = releasePid;
  castDebugLogger.warn(LOG_TAG, 'Wrapped content: ', wrapped);
  // Encode the wrapped request as JSON.
  const wrappedJson = JSON.stringify(wrapped);
  requestInfo.content = wrappedJson;
  castDebugLogger.warn(LOG_TAG, 'Handling license request for DRM with a modified license request: ', requestInfo);
};

I am using the Cactool v2 to connect to my registered (but not yet published) App ID, and I'm passing it a DASH manifest (mpd) that aligns with the releasePid I'm using. I see the title loading successfully, but when it tries to resolve the Widevine license request, it generates that Shaka 6008 error I mentioned earlier.

I was hoping that the initial Widevine challenge I provided (CAQ=) would result in a challenge response that I could parse and pass on to the CDM so the 'real' widevine challenge could be generated, but the CDM doesn't like what it is getting back from my widevine proxy. I can't seem to "see" what is coming back from the widevine proxy, so I can't fix whatever is going wrong.

Any help would be greatly appreciated!

Ptumster
  • 21
  • 4

1 Answers1

1

Just recently, the Web Receiver has new handler variable that can be assigned to for this situation, called playbackConfig.licenseHandler

The argument type is of uint8array though, so it requires changing that to a string, then a JSON object.

(function(non-null Uint8Array, non-null cast.framework.NetworkResponseInfo) returns (non-null Promise containing non-null Uint8Array or non-null Uint8Array) or undefined)

Since the return type is a Promise, I believe a fetch can be initiated here if I am not mistaken, which then can be used for the challenge. After that, if the server returns a wrapped JSON response, then it requires unwrapping.

After the JSON response is unwrapped, only then consecutive license requests can be transformed via licenseRequestHandler.

hrgui
  • 590
  • 2
  • 5