google-castgoogle-cast-sdkshaka

DRM video doesn`t in google cast web receiver (CAF)


I am experiencing a problem when working with CAST web receiver. DRM Video does not start on the TV (Android TV). Preloader works endlessly. How to solve this problem?

The title and description in the player is shown.

The logs show errors:

"shakaErrorCode":6007

LICENSE_REQUEST_FAILED - 6007 - The license request failed. This could be a timeout, a network failure, or a rejection by the server. error.data[0] is a shaka.util.Error from the networking engine. (https://shaka-player-demo.appspot.com/docs/api/shaka.util.Error.html)

"detailedErrorCode":905

LOAD_FAILED - 905 - A load command failed. - Verify the load request is set up properly and the media is able to play. (https://developers.google.com/cast/docs/web_receiver/error_codes?hl=en)

Error 6007 indicates problems with the license server. We checked the request parameters on another player and the DRM video starts. This does not work in cast receiver.

The problem occurs on the physical device
Sender: Redmi Note 8 Pro
Device with chromecast: Android Box
Chromecast built-in: 1.56.276477
CAST SDK CAF Version: 3.0.0103

API Request

Request URL: https://widevine-license.vudrm.tech/proxy
Request Method: POST
Status Code: 200 OK
Remote Address: XXX
Referrer Policy: strict-origin-when-cross-origin

Response Headers

access-control-allow-headers: Content-Type,X-Vudrm-Token
access-control-allow-methods: GET,POST,OPTIONS
access-control-allow-origin: *
Connection: keep-alive
Content-Length: 716
content-type: application/octet-stream
date: Mon, 13 Jun 2022 07:59:00 GMT
server: istio-envoy
x-b3-parentspanid: 3d92950cdf3d42ca
x-b3-sampled: 0
x-b3-spanid: 46ff3c9c3e815372
x-b3-traceid: XXX
x-envoy-upstream-healthchecked-cluster: widevine-license.default
x-envoy-upstream-service-time: 18
x-forwarded-for: XXX,XXX
x-request-id: XXX
x-vudrm: app=widevine-proxy; version=2.4.1

Request Headers

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US
CAST-DEVICE-CAPABILITIES: {"bluetooth_supported":false,"display_supported":true,"hi_res_audio_supported":false,"remote_control_input_supported":false,"touch_input_supported":false}
Connection: keep-alive
Content-Length: 297
content-type: application/json
Host: widevine-license.vudrm.tech
Origin: XXX
Referer: XXX
sec-ch-ua
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Linux; Android 9.0; Build/PI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000

Request Payload

{"token":"XXX","drm_info":[8,4],"contentId":"VS-ADULTSWIM"}

receiver.js

    const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
const playbackConfig = new cast.framework.PlaybackConfig();

const ContentType = {
  DASH: 'application/dash+xml',
  HLS: 'application/x-mpegurl'
};
const mediaFormatID = {
  DASH: 2,
  HLS: 4
};

// Debug Logger
const castDebugLogger = cast.debug.CastDebugLogger.getInstance();
const LOG_TAG = 'MyAPP.LOG';

// Enable debug logger and show a 'DEBUG MODE' overlay at top left corner.
castDebugLogger.setEnabled(true);

// Set verbosity level for Core events.
castDebugLogger.loggerLevelByEvents = {
  'cast.framework.events.category.CORE': cast.framework.LoggerLevel.INFO,
  'cast.framework.events.EventType.MEDIA_STATUS': cast.framework.LoggerLevel.DEBUG
};

// Set verbosity level for custom tags.
castDebugLogger.loggerLevelByTags = {
    LOG_TAG: cast.framework.LoggerLevel.DEBUG,
};

playerManager.setMessageInterceptor(
  cast.framework.messages.MessageType.LOAD,
  request => {

    return new Promise((resolve, _reject) => {

      // Configure player to parse DASH content
      if (request.media.metadata.mediaFormatID == mediaFormatID.DASH) {
        request.media.contentUrl = request.media.contentUrl;
        request.media.contentType = ContentType.DASH;

        // Customize the license url for playback
        if (request.media.metadata.licenseUrl) {

          playbackConfig.licenseUrl = request.media.metadata.licenseUrl;
          playbackConfig.protectionSystem = cast.framework.ContentProtection.WIDEVINE;

          let token = request.media.metadata.token;
          let contentId = request.media.metadata.contentId;

          playbackConfig.licenseRequestHandler = requestInfo => {
            requestInfo.withCredentials = false;

            let body = {
                token: token,
                drm_info: Array.apply(null, new Uint8Array(requestInfo.content)),
                contentId: contentId
            };

            body = JSON.stringify(body);
            requestInfo.content = body;

            requestInfo.headers["Content-Type"] = "application/json";
          };
        }

        mpegDashStreamUrl = request.media.contentUrl;
        vudrmToken = request.media.metadata.licenseUrl;

      } else {
        request.media.contentType = ContentType.HLS;
        request.media.contentUrl = request.media.contentUrl;
        request.media.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.FMP4;
        request.media.hlsVideoSegmentFormat = cast.framework.messages.HlsVideoSegmentFormat.FMP4;
      }

      // Add metadata
      let metadata = new cast.framework.messages.GenericMediaMetadata();

      metadata.title = request.media.metadata.title;
      metadata.subtitle = request.media.metadata.subtitle;

      request.media.metadata = metadata;

      resolve(request);
    });
  });

  context.start({playbackConfig: playbackConfig});

Solution

  • There are 3 handlers that are used by the Shaka response filters -

    1. PlaybackConfig#manifestHandler
    2. PlaybackConfig#licenseHandler
    3. PlaybackConfig#segmentHandler

    I added this code and the error was solved:

    playbackConfig.licenseHandler = data => {
     return new Promise((resolve, _reject) => {
        resolve(new Uint8Array(data));
     });
    };