演员表:拦截(和调整)来自 Widevine 许可证代理的响应

Cast: Intercepting (and adjusting) response from Widevine license proxy

提问人:Ptumster 提问时间:3/11/2023 最后编辑:Ptumster 更新时间:3/31/2023 访问量:549

问:

我正在尝试通过自定义接收器将受 DRM 保护的内容投射到我的 Chromecast 设备,但我一直收到此错误:

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

根据 Shaka 文档,此错误是因为 CDM 不喜欢它从许可证服务器获得的响应:

“许可证响应被清洁发展机制拒绝。对于此 CDM,服务器的响应可能无效或格式不正确。error.data[0] 是来自浏览器的错误消息字符串。

这并不让我特别惊讶,因为我知道我的 CDN 的 Widevine 代理使用 base64(标准)编码,但大多数 Google 产品都需要 base64url 编码。因此,我需要拦截来自许可证服务器的响应,对其进行一些按摩,然后将其传递给CDM。

我该怎么做?似乎没有我可以设置的 playbackConfig.license 响应处理程序来捕获响应......

有没有人可以查看“标准”Widevine licenseRequestHandler 函数的示例?也许我基本上做错了什么?

我的 CDN 的 widevine 代理有时似乎需要对代理 URL(带有其他一些参数,包括身份验证令牌)发出初始质询请求(以获取服务器证书),其 JSON 请求正文如下所示:

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

然后,响应被传递到 CDM,CDM 构建真正的 Widevine 质询,该质询用于对 widevine 代理的第二次调用,该代理返回实际的许可证密钥。

在我的接收器代码中,我尝试使用以下处理程序捕获和预处理许可证请求:

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

我正在使用 Cactool v2 连接到我注册(但尚未发布)的 App ID,并向它传递一个与我正在使用的 releasePid 一致的 DASH 清单 (mpd)。我看到标题加载成功,但是当它尝试解决 Widevine 许可证请求时,它会生成我之前提到的 Shaka 6008 错误。

我希望我提供的初始 Widevine 质询 (CAQ=) 将导致一个质询响应,我可以解析该响应并将其传递给 CDM,以便可以生成“真正的”Widevine 质询,但 CDM 不喜欢它从我的 widevine 代理那里得到的东西。我似乎无法“看到”从 widevine 代理返回的内容,所以我无法解决任何问题。

任何帮助将不胜感激!

谷歌投射 的 DRM Widevine

评论


答:

1赞 hrgui 3/31/2023 #1

就在最近,Web Receiver 有了新的处理程序变量,可以分配给这种情况,称为 playbackConfig.licenseHandler

不过,参数类型是 uint8array,因此需要将其更改为字符串,然后更改为 JSON 对象。

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

由于返回类型是 Promise,如果我没记错的话,我相信可以在这里启动 fetch,然后可以将其用于质询。之后,如果服务器返回包装的 JSON 响应,则需要解包。

解包 JSON 响应后,只有通过 licenseRequestHandler 才能转换连续的许可证请求。