You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
varvideo=document.querySelector('video');varconfig=[{initDataTypes: ['webm'],videoCapabilities: [{contentType: 'video/webm; codecs="vp9"'}]}];if(!video.mediaKeys){navigator.requestMediaKeySystemAccess('org.w3.clearkey',config).then(function(keySystemAccess){varpromise=keySystemAccess.createMediaKeys();promise.catch(console.error.bind(console,'Unable to create MediaKeys'));promise.then(function(createdMediaKeys){returnvideo.setMediaKeys(createdMediaKeys);}).catch(console.error.bind(console,'Unable to set MediaKeys'));promise.then(function(createdMediaKeys){varinitData=newUint8Array([...]);varkeySession=createdMediaKeys.createSession();keySession.addEventListener('message',handleMessage,false);returnkeySession.generateRequest('webm',initData);}).catch(console.error.bind(console,'Unable to create or initialize key session'));});}functionhandleMessage(event){varkeySession=event.target;varlicense=newUint8Array([...]);keySession.update(license).catch(console.error.bind(console,'update() failed'));}
// Define a key: hardcoded in this example// – this corresponds to the key used for encryptionvarKEY=newUint8Array([0xeb,0xdd,0x62,0xf1,0x68,0x14,0xd2,0x7b,0x68,0xef,0x12,0x2a,0xfc,0xe4,0xae,0x3c]);varconfig=[{initDataTypes: ['webm'],videoCapabilities: [{contentType: 'video/webm; codecs="vp8"'}]}];varvideo=document.querySelector('video');video.addEventListener('encrypted',handleEncrypted,false);navigator.requestMediaKeySystemAccess('org.w3.clearkey',config).then(function(keySystemAccess){returnkeySystemAccess.createMediaKeys();}).then(function(createdMediaKeys){returnvideo.setMediaKeys(createdMediaKeys);}).catch(function(error){console.error('Failed to set up MediaKeys',error);});functionhandleEncrypted(event){varsession=video.mediaKeys.createSession();session.addEventListener('message',handleMessage,false);session.generateRequest(event.initDataType,event.initData).catch(function(error){console.error('Failed to generate a license request',error);});}functionhandleMessage(event){// If you had a license server, you would make an asynchronous XMLHttpRequest// with event.message as the body. The response from the server, as a// Uint8Array, would then be passed to session.update().// Instead, we will generate the license synchronously on the client, using// the hard-coded KEY at the top.varlicense=generateLicense(event.message);varsession=event.target;session.update(license).catch(function(error){console.error('Failed to update the session',error);});}// Convert Uint8Array into base64 using base64url alphabet, without padding.functiontoBase64(u8arr){returnbtoa(String.fromCharCode.apply(null,u8arr)).replace(/\+/g,'-').replace(/\//g,'_').replace(/=*$/,'');}// This takes the place of a license server.// kids is an array of base64-encoded key IDs// keys is an array of base64-encoded keysfunctiongenerateLicense(message){// Parse the clearkey license request.varrequest=JSON.parse(newTextDecoder().decode(message));// We only know one key, so there should only be one key ID.// A real license server could easily serve multiple keys.console.assert(request.kids.length===1);varkeyObj={kty: 'oct',alg: 'A128KW',kid: request.kids[0],k: toBase64(KEY)};returnnewTextEncoder().encode(JSON.stringify({keys: [keyObj]}));}
reader.onload=function(e){sourceBuffer.appendBuffer(newUint8Array(e.target.result));if(i===NUM_CHUNKS-1){mediaSource.endOfStream();}else{if(video.paused){// start playing after first chunk is appendedvideo.play();}readChunk_(++i);}};
原文链接: EME WTF?
翻译日期: 2018年6月8日
翻译人员: Gnip
目录
加密媒体扩展提供了一个API,允许web应用与内容保护系统交互,允许播放加密的音频和视频。
EME被设计来保证相同的应用和加密文件可以在任何浏览器环境使用,不管底层保护系统。
前者是通过标准API和规则实现,而后者是由通用加密
Common Encryption
概念实现。EME得名来自对HTMLMediaElement规范的扩展。
作为一个“扩展”意味着浏览器支持EME:如果浏览器不支持加密媒体,它将无法播放加密媒体,但EME对于HTML规范的依赖不是必须需的。
从EME的规范来看:
这个提议扩展HTMLMediaElement提供api来控制播放受保护的内容。
API支持从简单的密钥解密到高价值的视频(给出一个适当的用户代理实现)的情况。
许可/密钥交换是由应用程序控制,促进开发健壮的播放应用程序支持一系列内容解密和保护技术。
本规范没有定义内容保护或数字版权管理系统。相反,它定义了一个通用的API,可以用来发现、选择或者与这些系统以及简单的内容加密系统交互。数字版权管理的实现不需要遵守此规范:只有Clear Key系统需要实现为一个共同的基准。
通用的API支持一组简单的内容加密功能,而把一些应用程序的功能,比如身份验证和授权留给页面作者。这是通过获取由页面分发的内容保护系统的特的消息而不是假设带外之间的通信加密系统或者许可证或其他服务器的通信。
EME的实现使用以下外部组件:
Key System: 内容保护(DRM)机制。EME不定义key system本身,除了clear key(下面详细说明)。
Content Decryption Module (CDM): 客户端软件或硬件机制,来保证播放加密媒体。
和key system一样,EME不定义任何CDMs,但提供了一个接口与CDMs应用程序进行交互。
License (Key) server: 与CDM进行交互,提供密钥解密媒体。与许可服务器交涉是主要责任。
Packaging service: 编码和加密媒体分布/消费
注意应用程序使用EME与一个许可证服务器交互获取密钥来解密,但用户标识和身份验证并不是EME的一部分。
检索键,使媒体播放(可选)之后对用户进行身份验证。
这种服务,例如Netflix必须验证用户在他们的web应用程序:当用户登录应用程序,应用程序决定了用户的身份和特权。
EME如何工作?
EME组件如何交互的,对应下面的代码示例:
initData
)触发一个加密的事件。MediaKeys
对象与媒体元素关联,首先选择一个可用的密钥系统通过使用navigator.requestMediaKeySystemAccess()
检查哪些系统可用,然后通过MediaKeySystemAccess
为密钥系统创建为一个可用的MediaKeys
对象。注意,MediaKeys对象的初始化应该在第一个加密事件之前。通过选择一个可用的密钥系统,获得许可证服务器的URL是一个独立应用程序。MediaKeys对象代表了所有可用的密钥来解密音频或视频的媒体元素。它代表了CDM实例并提供访问CDM,专门用于创建密钥会话,用于获取密钥从许可证书服务器。MediaKeys
对象被创建,将其分配给媒体元素:setMediaKeys()
分配给HTMLMediaElement
元素,所以可以使用播放期间使用密钥,例如解码的时候。MediaKeySession
通过调用MediaKeys
上的createSession()
方法。MediaKeySession
代表了证书和它密钥的生命周期。CDM
l来生成许可证请求。通过MediaKeySession
调用generateRequest()
方法。CDM
出发一个消息事件:从证书服务器获取密钥的请求。MediaKeySession
对象接收到消息事件然后应用程序通过(例如xhr)发送消息到证书服务器。CDM
使用MediaKeySession
的update()
方法。CDM
解密媒体使用证书中的密钥。一个有效的密钥可能被使用在MediaKeys
关联的媒体元素任何会话中。CDM
会访问有密钥id索引的密钥和策略。请注意,CDM和许可证服务器之间可能存在多个消息,并且此过程中的所有通信对浏览器和应用程序都是不透明的:消息只能由CDM和许可证服务器理解,但应用程序层可以看到什么类型的消息CDM正在发送。许可证请求包含CDM有效性(和信任关系)以及在生成的许可证中加密内容密钥时使用的密钥。
...但CDM实际上做了什么?
EME实现本身并不提供解密媒体的方式:它只是为Web应用提供API来与内容解密模块进行交互。
CDM实际做的不是由EME规范定义的,CDM可以处理媒体的解码(解压缩)以及解密。至少从最强大的角度来看,CDM功能有几种可能的选择:
<video>
元素。有多种方式可以为Web应用程序提供CDM:
CDM如何提供并不是由EME规范定义的,但在所有情况下,浏览器负责审核和公开CDM。
EME没有要求特定的关键系统; 在当前的桌面和移动浏览器中,Chrome支持Widevine,IE11支持PlayReady。
从许可证服务器获取密钥
可在线使用,Web客户端就可以从许可证服务器获取密钥(包含在许可证中),并使用该密钥来启用内容的解密和播放。
以下代码(根据规范示例进行了调整)显示了应用程序如何选择适当的密钥系统并从许可证服务器获取密钥。
通用加密
通用加密解决方案允许内容提供商对每个容器/编解码器的内容进行加密和打包,并将其与各种关键系统,CDM和客户端一起使用:即支持通用加密的任何CDM。例如,使用Playready打包的视频可以使用Widevine CDM在浏览器中播放,从Widevine许可证服务器获取密钥。
这与传统的解决方案形成鲜明对比,传统解决方案只能使用完整的垂直堆栈,包括通常还包含应用程序运行时的单个客户端。
通用加密(CENC)是ISO标准,用于定义ISO BMFF的保护方案; 类似的概念适用于WebM。
清除密钥
尽管EME没有定义DRM功能,但该规范目前要求所有支持EME的浏览器必须实现Clear Key。使用这个系统,媒体可以用一个密钥加密,然后通过提供该密钥简单地回放。清除密钥可以内置到浏览器中:它不需要使用单独的解密模块。
虽然不太可能用于许多类型的商业内容,但Clear Key可在支持EME的所有浏览器中完全互操作。对于测试EME实现和使用EME的应用程序,无需从许可证服务器请求内容密钥也很方便。simpl.info/ck上有一个简单的Clear Key示例。下面是代码的,和上述步骤相似,虽然没有许可证服务器的交互。
要测试此代码,您需要加密的视频才能播放。根据webm_crypt说明,可以为WebM完成对Clear Key使用的视频加密。商业服务也是可用的(至少对于ISO BMFF / MP4)并且正在开发其他解决方案。
相关技术#1:
媒体源扩展(MSE)
HTMLMediaElement是简约和美好的。
我们可以简单地通过提供一个src URL来加载,解码和播放媒体:
The Media Source API
是HTMLMediaElement的扩展,通过允许JavaScript构建用于从视频“块”进行播放的流,实现对媒体源的更精细控制。这反过来又使诸如自适应流式传输和时移的技术成为可能。为什么MSE对EME很重要?因为除了分发受保护的内容之外,商业内容提供商必须能够根据网络条件和其他要求调整内容交付。例如,Netflix随着网络条件的变化而动态改变码流比特率。EME适用于MSE实施提供的媒体流的回放,就像通过src属性提供的媒体一样。
如何分块和播放以不同比特率编码的媒体?请参阅下面的DASH部分。
您可以在simpl.info/mse中查看MSE的实际操作; 就本示例而言,使用
File API
将WebM视频分成五个块。在生产应用程序中,视频块将通过Ajax检索。首先创建一个SourceBuffer:
然后通过使用
appendBuffer()
方法附加每个块,将整个电影“流式传输”到视频元素:在HTML5 Rocks文章中了解有关MSE的更多信息。
相关技术#2:
基于HTTP的动态自适应流媒体(DASH)
多设备,多平台,移动 - 无论您怎么称呼它,Web都经常在可变连接条件下体验。动态的自适应交付对于应对多设备领域的带宽限制和可变性至关重要。
DASH(也称为MPEG-DASH)旨在在片状世界中实现尽可能最佳的媒体传输,以实现流媒体和下载。其他一些技术可以做类似的事情 - 例如Apple的HTTP实时流媒体(HLS)和微软的平滑流媒体 - 但DASH是通过基于开放标准的HTTP进行自适应比特率流传输的唯一方法。DASH已被YouTube等网站使用。
这与EME和MSE有什么关系?基于MSE的DASH实现可以解析manifest,以适当的比特率下载视频片段,并在饥饿时将它们提供给视频元素 - 使用现有的HTTP基础架构。
换句话说,DASH使商业内容提供商能够对受保护内容进行自适应流式传输。
DASH做什么:
BBC已经开始使用DASH提供测试流:
总结:
作为视频分割过程的一部分,以编程方式构建称为媒体演示描述(MPD)的XML清单。这描述了适应集和表示形式,带有持续时间和URL。MPD看起来像这样:
(此XML取自用于YouTube DASH演示播放器的.mpd文件。)
根据DASH规范,MPD文件理论上可以用作src视频。然而,为了给予网络开发者更多的灵活性,浏览器厂商选择使用MSE(例如dash.js)将DASH支持留给JavaScript库。在JavaScript中实现DASH允许自适应算法在不需要浏览器更新的情况下发展。使用MSE还可以实现替代清单格式和传送机制,而无需更改浏览器。Google的Shaka Player实现了一个支持EME的DASH客户端。
Mozilla开发者网络有关于如何使用WebM工具和FFmpeg来分割视频和构建MPD的说明。
结论
利用网络提供付费视频和音频的速度正在加快。看起来,每个新设备,无论是平板电脑,游戏机,连接电视还是机顶盒,都能够通过HTTP从主要内容提供商流媒体。目前,超过85%的移动和桌面浏览器支持
进一步阅读
规格和标准
参考文章
Demos
The text was updated successfully, but these errors were encountered: