You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DRM视频在Google Cast(CAF)接收器上播放异常问题咨询

CAF(Cast接收器)DRM加密视频播放故障分析

故障现象

  • 1个DRM频道可触发播放流程,但仅输出正常音频,无视频画面;其余DRM频道完全无法启动播放
  • 运行时报错:[Cast.framework.media.shakaplayer] Category: 7 Code: 7000
  • 控制台网络面板仅完成MPD清单文件加载,未发起任何音视频分片的网络请求
  • 故障发生时的接收器端实现代码:
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
};

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 => {
              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";
            };
        }

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

    });
  });


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

故障根因

报错Category:7 Code:7000对应Shaka Player内置错误码LICENSE_REQUEST_FAILED,即DRM授权流程未正常完成,导致播放器解析完MPD清单后无法进入分片拉取环节,结合代码实现具体问题点如下:

  • PlaybackConfig配置不生效:context.start()调用时会一次性读取传入的playbackConfig完成播放器初始化,后续在LOAD拦截器中直接修改全局playbackConfig变量的属性不会被播放器感知,DRM授权地址、请求处理逻辑等核心配置完全没有加载到播放器实例中。
  • License响应解析逻辑错误:当前实现中licenseHandler直接将授权接口返回的完整响应转为Uint8Array返回,而你在licenseRequestHandler中已经将请求格式改为JSON,绝大多数DRM服务端对应返回的也是JSON格式响应(通常会把二进制license做base64编码放在固定字段中),直接转格式会导致播放器无法识别有效license内容,部分流音频轨道DRM校验逻辑容错性较高可正常播放,视频轨道校验严格直接失败,就会出现有音无画的现象。
  • 参数作用域风险:LOAD拦截器中声明的token、contentId为块级作用域变量,连续切换不同DRM频道时容易出现参数串用,导致授权请求带上旧频道的鉴权参数,直接鉴权失败。
  • 多密钥场景配置缺失:绝大多数Widevine加密流会对音频、视频轨道使用不同密钥加密,当前实现未配置DRM安全等级(robustness)参数,也未开启多密钥支持,会导致单轨道解密成功、另一轨道解密失败。
  • 元数据覆盖风险:拦截器中完全重建GenericMediaMetadata替换原有metadata对象,会抹除发送端传入的DRM相关自定义字段,导致部分DRM参数丢失。

修复方案

  1. 不要在context.start()前定义静态全局playbackConfig,在LOAD拦截器中根据当前请求的媒体信息动态生成playbackConfig,调用playerManager.setPlaybackConfig()实时更新到播放器实例,保证DRM配置生效。
  2. 修改licenseHandler逻辑,先解析接口返回的JSON响应,提取出真正的license二进制数据(如果服务端返回base64编码的license需要先做解码)再返回给播放器。
  3. 补充Widevine DRM的robustness配置,适配多密钥加密场景。
  4. 不要全量替换metadata对象,仅在原有metadata基础上更新title、subtitle字段,避免丢失原有参数。

修复后的核心代码参考:

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

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

playerManager.setMessageInterceptor(
  cast.framework.messages.MessageType.LOAD,
  request => {
    return new Promise((resolve) => {
      if (request.media.metadata.mediaFormatID == mediaFormatID.DASH) {
        request.media.contentType = ContentType.DASH;
        const licenseUrl = request.media.metadata.licenseUrl;
        if (licenseUrl) {
          const playbackConfig = new cast.framework.PlaybackConfig();
          playbackConfig.licenseUrl = licenseUrl;
          playbackConfig.protectionSystem = cast.framework.ContentProtection.WIDEVINE;
          // 配置DRM安全等级,适配多密钥场景
          playbackConfig.keySystemConfig = [
            {
              keySystem: cast.framework.ContentProtection.WIDEVINE,
              robustness: 'SW_SECURE_CRYPTO'
            }
          ];
          // 固定当前请求的鉴权参数,避免作用域串值
          const token = request.media.metadata.token;
          const contentId = request.media.metadata.contentId;

          playbackConfig.licenseRequestHandler = requestInfo => {
            const body = JSON.stringify({
              token: token,
              drm_info: Array.from(new Uint8Array(requestInfo.content)),
              contentId: contentId
            });
            requestInfo.content = body;
            requestInfo.headers["Content-Type"] = "application/json";
          };

          playbackConfig.licenseHandler = (responseData) => {
            return new Promise((resolve) => {
              // 按实际服务端返回结构解析,示例为提取license字段并做base64解码
              const res = JSON.parse(new TextDecoder().decode(responseData));
              const licenseBin = Uint8Array.from(atob(res.license), c => c.charCodeAt(0));
              resolve(licenseBin);
            });
          };
          // 动态更新播放器配置
          playerManager.setPlaybackConfig(playbackConfig);
        }
      } else {
        request.media.contentType = ContentType.HLS;
        request.media.hlsSegmentFormat = cast.framework.messages.HlsSegmentFormat.FMP4;
        request.media.hlsVideoSegmentFormat = cast.framework.messages.HlsVideoSegmentFormat.FMP4;
      }

      // 仅更新元数据字段,不替换整个对象
      if (request.media.metadata) {
        request.media.metadata.title = request.media.metadata.title || '';
        request.media.metadata.subtitle = request.media.metadata.subtitle || '';
      }

      resolve(request);
    });
  }
);

context.start();

内容的提问来源于stack exchange,提问作者Алексей Обронов

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 22:01:10