news 2026/9/16 19:42:49

Shaka Player DRM 配置完整指南:License Server、Clear Key、Robustness 与持久化 License 复用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Shaka Player DRM 配置完整指南:License Server、Clear Key、Robustness 与持久化 License 复用

Shaka Player DRM 配置完整指南:License Server、Clear Key、Robustness 与持久化 License 复用

【免费下载链接】shaka-playerJavaScript player library / DASH & HLS client / MSE-EME player项目地址: https://gitcode.com/GitHub_Trending/sh/shaka-player

本文基于 Shaka Player 官方教程 docs/tutorials/drm-config.md,系统讲解如何在基于 MSE-EME 的播放器应用中配置 DRM(数字版权管理)。Shaka Player 默认只播放无加密的 clear 内容,要播放受保护内容,应用只需通过player.configure()告诉播放器几个关键信息:License Server 地址、密钥系统(Key System)选择策略、Robustness 安全级别,以及可选的持久化 License 复用与 HDCP 版本约束。读完本文,你将掌握从最基础的drm.servers配置,到 Clear Key 本地解密、drm.advanced高级选项、多 Robustness 回退、持久化会话复用、最低 HDCP 版本强制等一整套实战方案,并能在 lib/drm/drm_engine.js 等源码中找到对应的底层实现依据。

前置条件:EME 需要安全上下文

使用 EME(Encrypted Media Extensions,即 DRM 的底层浏览器标准)要求页面处于安全 URL 下,这意味着必须使用https,或者运行在localhost上。当前只有 Chrome 强制执行这一要求,但其他浏览器未来也会跟进。此外,由于混合内容(mixed content)限制,如果站点本身使用https,那么 manifest 和每一个分段(segment)也必须使用https,否则受保护内容将无法加载。

这一约束直接决定了 DRM 方案的整体部署架构:License Server 必须可通过 HTTPS 访问,媒体清单与分片同样需要走 HTTPS。在开发调试阶段,Chrome 提供了针对不安全来源的临时禁用开关,但仅建议用于本机测试;Firefox 也有对应的移除计划。结论:生产环境的 DRM 播放链路(页面、manifest、分段、License Server)必须全部 HTTPS 化。

配置 License Server:drm.servers

Shaka Player 对 DRM 的接入做到了"极简"——应用只需要告诉播放器一件事:License Server 的 URL。配置入口是player.configure(),字段drm.servers是一个以密钥系统 ID 为键、以服务器 URL 为值的对象。例如同时配置 Widevine 和 PlayReady:

player.configure({ drm: { servers: { 'com.widevine.alpha': 'https://foo.bar/drm/widevine', 'com.microsoft.playready': 'https://foo.bar/drm/playready' } } });

只要 manifest 中使用的是这些密钥系统的标准 UUID,以上配置就足以完成受保护内容的播放。在 lib/util/player_configuration.js 的默认配置中,serversclearKeysadvanced均初始化为空对象,注释明确指出"key is arbitrary key system ID, value must be string",即键名来自 EME 的 Key System ID(如 Widevine 的com.widevine.alpha),值为 License Server 字符串。

drm.servers之外,drm配置域还包含大量可选项(同样定义在 player_configuration.js),例如:

  • retryParameters:License 请求的网络重试参数;
  • delayLicenseRequestUntilPlayed:是否延迟到开始播放后才请求 License;
  • preferredKeySystems:首选的密钥系统列表;
  • parseInbandPsshEnabled:是否解析流内的 PSSH 数据;
  • minHdcpVersion:最低 HDCP 版本(见下文专门小节);
  • defaultAudioRobustnessForWidevine/defaultVideoRobustnessForWidevine:Widevine 的默认音频/视频 Robustness(默认分别为SW_SECURE_CRYPTOSW_SECURE_DECODE)。

密钥系统(Key System)的选择机制

Shaka 与密钥系统无关的设计

Shaka Player 是密钥系统无关(key-system-agnostic)的:它不偏向任何一家 DRM 厂商,而是通过 EME 向浏览器询问支持哪些密钥系统,不做任何假设。如果浏览器支持多个密钥系统,则使用 manifest 中第一个被支持的密钥系统。这意味着同一个 manifest 可以同时服务于 Widevine、PlayReady、FairPlay 等不同生态,由浏览器环境自动裁决。

DASH 中的 CENC 声明与keySystemsByURI

DRM 厂商正在实现的互操作加密标准称为 Common Encryption(CENC)。某些 DASH manifest 并不指明具体的密钥系统,而是声明"任意 CENC 系统均可",形式如下:

<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc"/>

如果 manifest 中只有这一条<ContentProtection>元素,Shaka 会尝试它知道的所有密钥系统,这一映射表基于shaka.extern.DashManifestConfiguration中的keySystemsByURI(该配置在 lib/dash/dash_parser.js 中通过this.config_.dash.keySystemsByURI读取使用)。你也可以通过player.configure()按 scheme URI 自定义 DASH 密钥系统映射,例如将两个 PlayReady 相关的 UUID 统一指向推荐密钥系统:

player.configure({ manifest: { dash: { keySystemsByURI: { 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95': 'com.microsoft.playready.recommendation', 'urn:uuid:79f0049a-4098-8642-ab92-e65be0885f95': 'com.microsoft.playready.recommendation', } } } });

只要浏览器支持该密钥系统,并且你为它配置了 License Server URL,Shaka 就会使用它。

密钥系统别名映射:drm.keySystemsMapping

与 manifest 侧的keySystemsByURI不同,drm.keySystemsMapping提供的是密钥系统 ID 之间的映射,适用于你明确知道某个密钥系统被广泛支持、希望将其替换为更推荐变体的场景。例如将com.microsoft.playready映射为com.microsoft.playready.recommendation

player.configure({ drm: { keySystemsMapping: { 'com.microsoft.playready': 'com.microsoft.playready.recommendation', } } });

配置之后,当 manifest(无论 HLS 还是 DASH)使用 PlayReady 时,Shaka 会改用com.microsoft.playready.recommendation这一密钥系统。从源码看,drm_engine.js 在drmInfos缺失时会依据keySystemsMapping[originalKeySystem]进行替换,且在创建MediaKeySystemAccess时也会优先使用映射后的密钥系统名(drm_engine.js)。

Clear Key:无 License Server 的本地解密

drm.clearKeys:直接提供密钥

EME 规范要求浏览器支持一种通用密钥系统 "Clear Key"。截至文档撰写时(2016 年 4 月)只有 Chrome 和 Firefox 实现了它。Clear Key 使用未加密的密钥解密 CENC 内容,非常适合用于排查问题和测试集成——它可以在没有 License Server 的情况下验证密钥是否正确。

通过drm.clearKeys配置一个"密钥 ID → 内容密钥"的映射(两者均为十六进制字符串):

player.configure({ drm: { clearKeys: { // 'key-id-in-hex': 'key-in-hex', 'deadbeefdeadbeefdeadbeefdeadbeef': '18675309186753091867530918675309', '02030507011013017019023029031037': '03050701302303204201080425098033' } } });

这将强制使用 Clear Key 进行解密,无论 manifest 中声明了什么。当需要确认密钥是否正确时,就用这个方案。其底层实现在 drm_engine.js,通过shaka.drm.DrmEngine.configureClearKey(this.config_.clearKeys, variants)将配置注入到各 variant 的 DRM 信息中;drm_engine.js 中则通过shaka.util.MapUtils.asMap()将配置的 plain object 转为Map,并借助ManifestParserUtils.createDrmInfoFromClearKeys()构造 DRM 信息。

Clear Key License Server:走标准 License 请求流程

如果 manifest 本身就声明使用 Clear Key,你同样可以走标准的 License 请求机制,根据密钥 ID 向服务器换取密钥。EME 规范为 Clear Key CDM 定义了基于 JSON 的请求格式与许可证格式。只要服务器支持这套协议,只需像普通 DRM 一样配置 License Server 即可:

player.configure({ drm: { servers: { 'org.w3.clearkey': 'http://foo.bar/drm/clearkey' } } });

注意示例中的 License Server 是http地址——这正是上面"安全上下文"章节提到的约束场景之一,实际部署时仍建议使用 HTTPS。

高级 DRM 配置:drm.advanced

drm.advanced密钥系统 ID → 高级设置对象的映射,用于访问完整的 EME 配置能力。一个典型场景是要求 Widevine 使用硬件安全:

player.configure({ drm: { servers: { 'com.widevine.alpha': 'https://foo.bar/drm/widevine' }, advanced: { 'com.widevine.alpha': { 'videoRobustness': ['HW_SECURE_ALL'], 'audioRobustness': ['HW_SECURE_ALL'] } } } });

如果不需要这些高级项,保持默认设置即可(默认videoRobustness/audioRobustness为空数组,sessionType由 externs/shaka/drm_info.js 中定义的 DRM 信息结构承载,可由 advanced 配置的sessionType参数填充)。

自定义 License 请求头

某些 License Server 要求携带自定义请求头(如鉴权 Token),可在advanced内按密钥系统配置headers

player.configure({ drm: { servers: { 'com.widevine.alpha': 'https://foo.bar/drm/widevine' }, advanced: { 'com.widevine.alpha': { 'headers': { 'customHeader1': 'value1', 'customHeader2': 'value2' } } } } });

请求 License 时这些头部会被附加到 License 请求中,是接入私有 DRM 服务端时最常用的配置之一。

Robustness:内容处理的安全级别

Robustness 描述密钥系统处理内容时的安全强度,是密钥系统特有的字符串,表示成功播放所需满足的安全要求。

关键注意点:

  • 传入的 Robustness 安全级别高于设备实际支持能力时,player.load()会以REQUESTED_KEY_SYSTEM_CONFIG_UNAVAILABLE错误失败;
  • 默认值为空数组,即密钥系统支持的最低安全级别;
  • 每个密钥系统有各自独立的 Robustness 取值。

从 drm_engine.js 的源码可以看出,Shaka 在枚举MediaKeySystemAccess时会收集 CDM 报告的minHdcpVersions与 robustness 支持信息,并据此构造可用的 DRM 配置——这解释了为什么超出能力范围的 Robustness 会直接导致加载失败。

多 Robustness 数组与自动回退

由于videoRobustnessaudioRobustness数组,可以同时配置多个取值。建立 DRM 时,第一个可用的 Robustness会被选用,从而实现"从强到弱"的安全回退。例如在 Widevine 下同时设置硬件与软件安全:

player.configure({ drm: { servers: { 'com.widevine.alpha': 'https://foo.bar/drm/widevine' }, advanced: { 'com.widevine.alpha': { 'videoRobustness': ['HW_SECURE_ALL', 'SW_SECURE_CRYPTO'], 'audioRobustness': ['HW_SECURE_ALL', 'SW_SECURE_CRYPTO'] } } } });

这样在支持硬件安全的设备上使用HW_SECURE_ALL,在不支持的设备上自动回退到SW_SECURE_CRYPTO,兼顾了安全性与兼容性。

各密钥系统的 Robustness 取值

Widevine(Chromium 源码中widevine_key_system_properties.h定义):

  • SW_SECURE_CRYPTO
  • SW_SECURE_DECODE
  • HW_SECURE_CRYPTO
  • HW_SECURE_DECODE
  • HW_SECURE_ALL

PlayReady(Microsoft 文档中的安全级别定义):

  • 3000
  • 2000
  • 150

需要特别指出的是,com.microsoft.playready密钥系统会忽略给定的 robustness,并保持在2000的解密级别。另外注意:PlayReady 不支持音频硬件 DRM(PlayReady 的限制)。

FairPlay:基于 Apple 的 FPS(FairPlay Streaming)文档,应提供空字符串作为 robustness。

其他密钥系统:其 robustness 取值目前不明确,需要参考对应 DRM 厂商的文档。

复用持久化 License 进行在线播放

部分 DRM 服务商允许下发持久化 License(persistent license)。如果满足这一前提,你可以复用已创建的 MediaKeys 会话,让下一次在线播放免去重复的 License 请求。

第一步:以persistent-license类型开启会话

默认情况下 DRM 会话类型是temporary,需要显式改为persistent-license

player.configure({ drm: { advanced: { 'com.widevine.alpha': { 'sessionType': 'persistent-license' } } } });

注意:persistent-license并非在所有设备上都可用,请谨慎使用此功能。其实现位于 drm_engine.js,当会话类型为persistent-license时会设置drmInfo.sessionType = 'persistent-license'

第二步:播放时保存会话元数据

播放开始后,可通过player.getActiveSessionsMetadata()获取当前活动的 DRM 会话元数据,从中筛选出持久化会话:

const activeDrmSessions = this.player.getActiveSessionsMetadata(); const persistentDrmSessions = activeDrmSessions.filter( ({ sessionType }) => sessionType === 'persistent-license'); // Add your own storage mechanism here, give it an unique known identifier for // the playing video

该方法由 drm_engine.js 实现,遍历内部activeSessions_并返回包含sessionIdsessionTypeinitDatainitDataType的元数据数组——你需要在应用层用自己的存储机制(如 IndexedDB、LocalStorage)将元数据与当前视频的唯一标识关联保存。

第三步:恢复持久化会话

再次播放同一视频时,从存储中取出元数据并写回配置:

player.configure({ drm: { persistentSessionOnlinePlayback: true, persistentSessionsMetadata: [{ sessionId: 'deadbeefdeadbeefdeadbeefdeadbeef', initData: new InitData(0), initDataType: 'cenc' }] } });

设置后,Shaka 会加载给定的 DRM 持久化会话,并且仅当内容缺失部分密钥时才发起 License 请求。源码佐证:默认配置中persistentSessionOnlinePlaybackfalsepersistentSessionsMetadata为空数组(player_configuration.js);drm_engine.js 在初始化时会遍历persistentSessionsMetadata恢复会话,并在 License 请求阶段根据persistentSessionOnlinePlayback决定缺失密钥时的错误严重级别(drm_engine.js)。

注意:Shaka 不提供开箱即用的会话元数据存储机制,持久化与恢复逻辑需要应用自行实现。上述示例中的new InitData(0)是文档中的示意写法,实际传入的initData应是与你内容对应的初始化数据(例如cenc类型下的 PSSH 数据)。

强制最低 HDCP 版本:minHdcpVersion

部分 CDM 支持查询最低 HDCP(高清内容保护)版本,Shaka 可以在 CDM 支持的情况下强制执行该要求:

player.configure({ drm: { minHdcpVersion: '2.3' } });

可能支持的取值包括:

  • 1.0
  • 1.1
  • 1.2
  • 1.3
  • 1.4
  • 2.0
  • 2.1
  • 2.2
  • 2.3

在源码中,minHdcpVersion默认值为空字符串''(player_configuration.js),表示不强制;drm_engine.js 在构造MediaKeySystemConfiguration时会读取该值填入minHdcpVersion,并在枚举支持能力时收集 CDM 报告的minHdcpVersions列表进行比对(drm_engine.js)。需要注意的是,并非所有设备/CDM 都支持 HDCP 版本查询,若 CDM 不支持该能力,此项约束将无法生效。

继续深入学习

DRM 配置是播放链路中的一环,接下来可参考官方教程继续深入:

  • License Server 认证(license-server-auth):学习带鉴权请求头的 License Server 接入与票据刷新;
  • FairPlay 配置(fairplay):学习苹果生态下 FairPlay 的证书、Content ID 与skd://协议处理。

同时,上述所有配置项均可在 lib/util/player_configuration.js 中找到默认值与类型注释,在 lib/drm/drm_engine.js 中找到对应的实现逻辑,是排查 DRM 问题时的第一手资料。

【免费下载链接】shaka-playerJavaScript player library / DASH & HLS client / MSE-EME player项目地址: https://gitcode.com/GitHub_Trending/sh/shaka-player

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 19:41:52

STM32 SPI通信详解:从协议原理到W25Q128 Flash驱动开发

我早期学习STM32时&#xff0c;最先搞定的通信接口是UART&#xff0c;毕竟收发打印太直观了。但一遇到SPI&#xff0c;整个人就有点懵&#xff1a;明明只有四根线&#xff0c;怎么比串口还难懂&#xff1f;当时拿着W25Q128的Flash模块&#xff0c;对着数据手册看时序图&#xf…

作者头像 李华
网站建设 2026/9/16 19:41:22

图片编辑API对接全流程:Base64编码、请求构造与高频报错排查

前阵子做业务系统集成&#xff0c;需要把“用户上传一张图、输入一句修改建议、后台返回一张改好的图”这个能力落地。技术选型时对比了好几个方案&#xff0c;最终选了Nano-Banana图片编辑API。从拿到密钥到跑通第一张成品图&#xff0c;核心请求代码用不了十行&#xff0c;但…

作者头像 李华
网站建设 2026/9/16 19:40:57

Matlab实现MMG船舶轨迹预测:从物理建模到可运行代码

1. 项目概述&#xff1a;这不是一个“画船”的Matlab动画&#xff0c;而是一次对船舶运动本质的数值解剖你在网上搜“Matlab 船舶轨迹”&#xff0c;大概率会看到一堆用plot画个箭头、加个圆圈、再用for循环让小船图标沿着预设路径“滑”过去的代码——那叫动画演示&#xff0c…

作者头像 李华
网站建设 2026/9/16 19:39:36

CSRF本质是浏览器信任机制的副产品

1. CSRF不是“跨站请求伪造”的缩写&#xff0c;而是浏览器信任机制的意外副产品很多人一看到CSRF就条件反射背出那句教科书定义&#xff1a;“Cross-Site Request Forgery&#xff0c;跨站请求伪造”。但这句话本身已经埋下了理解偏差的种子——它把CSRF描述成一种“主动攻击行…

作者头像 李华