DASH的全称是Dynamic Adaptive Streaming Over HTTP,是一种自适应比特率流媒体技术。与苹果的HTTP Live Streaming(HLS)技术相似,通过把内容分割成小的基于HTTP的文件段序列,来进行流媒体播放。MPEG(Moving Picture Experts Group,动态图像专家组)是一个针对音视频压缩的国际标准化组织,为了寻求HLS的替代方案而推出了MPEG-DASH标准,MPEG-DASH作为一种基于HTTP的自适应码率的流媒体传输解决方案,在2012年由ISO/IEC发表,正式成为国际标准。
DASH的优点:
目前在使用DASH协议的视频网站比较出名的有BiliBili、YouTube等。
DASH的缺点:
其实对于延迟高的问题,DASH与HLS两个协议都发展出了各自的低延迟解决方案,DASH协议有DASH-LL,HLS协议有LL-HLS2和LHLS,本文暂不讨论。
nginx是一个代理服务器,首先需要在Linux上搭建nginx服务,具体的流程可以参考nginx的安装流程3,搭建视频点播服务器会用到nginx-vod-module模块,按照安装流程添加该模块到nginx中,搭建点播服务使用的具体环境如下:
系统版本:CentOS Linux release 7.9 2009(Core)
nginx版本:1.20.1
nginx-vod-module版本:1.29
nginx-vod-module模块是基于nginx来提供VOD(video on demand)服务的第三方模块,它支持基于DASH、HDS、HLS、MSS的点播服务搭建。
nginx-vod-module下载地址:https://github.com/kaltura/nginx-vod-module/archive/refs/tags/1.29.tar.gz
在nginx进行安装配置的过程中,需要使用--add-module=
选项来添加第三方模块一起进行编译,相关的步骤可以参考模块的github网址:
nginx-vod-module的github网址:https://github.com/kaltura/nginx-vod-module
在配置nginx进行编译的时候使用的配置参数如下:
./configure --prefix=/web/webserver/nginx-vod-hls --with-http_stub_status_module --with-http_ssl_module --with-cc-opt=’-O0 -gstabs+3’ --with-debug --add-module=…/nginx-vod-module-1.29
配置、编译、安装都完成之后,即可在nginx的配置文件中使用nginx-vod-module模块提供的关键字了。
此处使用local模式,如果想要使用mapped模式,可以参考使用nginx搭建音视频点播服务——基于HLS协议4中的mapped模式介绍。这里假设部署nginx服务的机器ip地址为192.168.192.128。
在nginx的配置文件中,添加对应的location配置:
location /vod_dash { vod dash; # 协议使用dash模式 vod_mode local; # 访问模式指定为local模式 vod_align_segments_to_key_frames on; # 每个切片以关键帧开头 vod_dash_manifest_format segmenttemplate; # 在mpd清单文件中使用片段模板的格式 vod_dash_profiles urn:mpeg:dash:profile:isoff-live:2011; # 在mpd清单文件中指定profile标记 ## DRM相关配置 #vod_drm_enabled on; # 启用DRM #vod_drm_clear_lead_segment_count 0; # 设置所有切片都需要经过DRM处理 #vod_drm_upstream_location /php_proxy/; # 指定DRM地址 #vod_drm_request_uri /dash_clear_key.php; # 配置php地址 #跨域相关配置 add_header Access-Control-Allow-Headers '*'; add_header Access-Control-Expose-Headers 'Server,range,Content-Length,Content-Range,Date'; add_header Access-Control-Allow-Methods 'GET, HEAD, OPTIONS'; add_header Access-Control-Allow-Origin '*'; alias /media; } ## 配置指向DRM服务器地址的location #location /php_proxy/ { # proxy_pass http://192.168.192.182/; # 转发地址指向DRM服务器地址 # proxy_set_header Host $http_host; #}
conf文件修改后,nginx需要reload才能使配置生效。
./nginx -s reload
这里还需要特别注意的一点是由于DASH协议的原因,播放地址必须使用https
进行访问才能正常播放。
在nginx上配置https服务:
配置教程在网上很多,就不冗余了,略过。
服务器上对应的视频文件目录结构:
/media/example0001.mp4
URL播放地址(https):
https://192.168.192.128/vod_dash/example0001.mp4/manifest.mpd
多码率自适应指的是在视频播放中自动判断网络状态,在网络状态好的时候播放高码率视频,网络状态差的时候播放低码率的视频,多码率自适应需要在服务器上需要有2个或者2个以上对应的不同码率的视频源。
假设在服务器上对应的视频文件目录结构:
/media/example0001.mp4
/media/example0001_low.mp4
使用nginx-vod-module模块可以自动计算视频对应的带宽并完成清单文件的生成,只需要拼接好对应的URL地址即可,多码率自适应的URL播放地址(https):
https://192.168.192.128/vod_dash/,example0001,example0001_low,.mp4.urlset/manifest.mpd
DASH流媒体的播放目前在各端并不是原生支持的,还需要播放器的支持,比较主流的有shakaplayer、Dash.js等,本文使用开源的Html5播放器shakaplayer。
安装shakaplayer之前需要先安装一些工具:Git v1.9+、Python v2.7 or v3.5+、Java Runtime Environment v8+、NodeJS v10+、Apache(A local web server)。
可以参考shakaplayer官网的安装说明https://shaka-player-demo.appspot.com/docs/api/tutorial-welcome.html。
安装Git v1.9+:
用yum安装的git版本太低,需要从https://mirrors.edge.kernel.org/pub/software/scm/git/下载对应版本的源码编译安装。
安装Python v2.7 or v3.5+:
可以用yum安装,如果yum无法使用,也可以从https://www.python.org/downloads/source/下载对应版本的源码编译安装。
安装Java Runtime Environment v8+:
jre需要从https://java.com/en/download/manual.jsp下载对应的版本;
解压后建立软链接ln -s /.../jre1.8.0_321/bin/java /usr/bin/java
即可,其中/.../jre1.8.0_321/bin/java
为压缩包解压后的目录。
可以参考官网的安装说明https://java.com/en/download/help/linux_x64_install.html#install
安装NodeJS v10+:
可以使用yum安装nodejs,如果yum无法使用,也可以从https://nodejs.org/en/download/下载对应版本的源码编译安装。
nodejs的yum安装步骤: 安装centos-release-scl yum install centos-release-scl-rh 安装yum-config-manager命令 yum install yum-utils 安装RHSCL yum-config-manager --enable rhel-server-rhscl-7-rpms 安装nodejs yum install rh-nodejs10 启用nodejs scl enable rh-nodejs10 bash
可以参考官网的安装说明https://www.softwarecollections.org/en/scls/rhscl/rh-nodejs10/
安装Web Server:
这里直接用nginx即可,运行shakaplayer需要web服务器支持,因为浏览器对来自file:///url
的应用程序有限制,所以不能把shakaplayer放在本地运行。
有了上面的工具支持后,就可以安装shakaplayer了:
下载shakaplayer,地址为https://github.com/google/shaka-player/releases;
解压之后进入目录,执行python build/all.py
,成功后会在dist
目录下会生成shaka-player.compiled.js
文件,之后在web中创建shakaplayer播放器时需要依赖该文件。
在html中包含上一步生成的js文件路径,加载shakaplayer,并指定URL播放地址https://192.168.192.128/vod_dash/example0001.mp4/manifest.mpd(需要使用https),具体细节如下:
<!DOCTYPE html> <html> <head> <!-- Shaka Player compiled library: --> <script src="/html/shaka-player-3.3.0/dist/shaka-player.compiled.js"></script> </head> <body> <video id="video" width="640" controls autoplay></video> <script> // 注意这里播放的mpd清单文件需要使用https来访问 const manifestUri = 'https://192.168.192.128/vod_dash/example0001.mp4/manifest.mpd'; function initApp() { // Install built-in polyfills to patch browser incompatibilities. shaka.polyfill.installAll(); // Check to see if the browser supports the basic APIs Shaka needs. if (shaka.Player.isBrowserSupported()) { // Everything looks good! initPlayer(); } else { // This browser does not have the minimum set of APIs we need. console.error('Browser not supported!'); } } async function initPlayer() { // Create a Player instance. const video = document.getElementById('video'); const player = new shaka.Player(video); // // 配置DRM // player.configure({ // drm: { // servers: { // 'org.w3.clearkey': 'https://192.168.192.182/dash_clear_key.php' // } // } // }); // Attach player to the window to make it easy to access in the JS console. window.player = player; // Listen for error events. player.addEventListener('error', one rrorEvent); // Try to load a manifest. // This is an asynchronous process. try { await player.load(manifestUri); // This runs if the asynchronous load is successful. console.log('The video has now been loaded!'); } catch (e) { // one rror is executed if the asynchronous load fails. one rror(e); } } function one rrorEvent(event) { // Extract the shaka.util.Error object from the event. one rror(event.detail); } function one rror(error) { // Log the error. console.error('Error code', error.code, 'object', error); } document.addEventListener('DOMContentLoaded', initApp); </script> </body> </html>
这样即可在shakaplayer中播放DASH流媒体视频。
DRM即数字版权管理,用于对视频内容进行加密保护,通过DRM技术可以避免内容被下载获取并非法传播,DASH的DRM解决方案有Widevine、PlayReady、ClearKey这几种形式。
Widevine最初由Widevine Technologies开发,于2010年被Google收购,原生支持广泛的设备和浏览器,如Google浏览器,Android,Chromecast等;(商业DRM解决方案)
PlayReady是由Microsoft开发的,是可用的主要DRM系统之一,具有广泛的设备支持;(商业DRM解决方案)
ClearKey是商业解决方案的免费替代品,以下示例使用ClearKey形式的DRM方案。
这里通过部署apache配合php代码来进行DRM服务器的搭建,DRM服务器的作用是提供秘钥服务,收到请求后生成并返回加、解密需要的秘钥。
这里的DRM服务器(192.168.192.182)指的是处理秘钥的服务器,与之前保存视频内容的服务器(192.168.192.128)不是同一个,注意区分。示例中将它们分别部署在了不同的机器上,根据实际情况也可以将秘钥服务和视频内容服务放在同一个服务器上。
安装apache以及php支持:
部署教程在网上很多,就不冗余了,略过。
下面是一段可用的php的代码示例,根据不同的请求方式(POST、GET)向请求者提供加密秘钥和解密秘钥,包含了以逻辑方式(当然更简单的方式也可以直接使用明文)生成加、解密秘钥的方式,简单展示了从32位自定义字符串生成秘钥的过程:
<?php if (!function_exists('hex2bin')) { function hex2bin($str) { $sbin = ''; $len = strlen($str); for ($i = 0; $i < $len; $i += 2) { $sbin .= pack('H*', substr($str, $i, 2)); // 每两个字符就打包成一个十六进制串 } return $sbin; } } // 替换 “-” —> “+” , “_” —> “/”,并进行base64解密 function b64url_dec($s) { return base64_decode(str_replace(array('-', '_'), array('+', '/'), $s), true); } // 进行base64加密后,替换 “+” —> “-” , “/” —> “_”,去掉 “=” function b64url_enc($s) { return str_replace(array('+', '/', '='), array('-', '_', ''), base64_encode($s)); } function getKey($kid) { // TODO: update secret return md5('secret' . $kid, true); // 加上前缀secret,并进行MD5加密 } // TODO: add security controls and error validation if ($_SERVER['REQUEST_METHOD'] == 'POST') { // 请求使用POST方式获取解密秘钥 $req = file_get_contents('php://input'); $req = json_decode($req); $type = $req->type; $keys = array(); foreach ($req->kids as $kid) { $key = b64url_enc(getKey(b64url_dec($kid))); $keys[] = array('k' => $key, 'kty' => 'oct', 'kid' => $kid); } $res = array('keys' => $keys, 'type' => $type); header('access-control-allow-origin: *'); } else { // 请求使用GET方式获取加密秘钥 $kid = hex2bin('0123456789abcdef0123456789abcdef'); // TODO: allocate according to media id $res = array(array( 'key' => base64_encode(getKey($kid)), 'key_id' => base64_encode($kid), 'pssh' => array(array( 'uuid' => '1077efec-c0b2-4d02-ace3-3c1e52e2fb4b', 'data' => base64_encode(hex2bin('00000001') . $kid . hex2bin('00000000')) )) )); } // 转换成json格式输出 echo str_replace('\\/', '/', json_encode($res)); ?>
通过GET请求获取的Encryption加密秘钥:
[ { "key":"6JDXwILoGcZuYDKGNPiXgA==", "key_id":"ASNFZ4mrze8BI0VniavN7w==", "pssh":[ { "uuid":"1077efec-c0b2-4d02-ace3-3c1e52e2fb4b", "data":"AAAAAQEjRWeJq83vASNFZ4mrze8AAAAA" } ] } ]
key
表示加密秘钥,key_id
表示秘钥的id号,pssh是Protection System Specific Header的缩写,pssh是标准化委员会规定的被加密的媒体资源所应该包含相对应的信息,关于标准化委员会规定的更加细节的方面可以查看——国际标准化组织对于流格式的基本媒体文件的通用加密方案(cenc)5。示例中key
、key_id
、pssh->data
都由自定义字符串0123456789abcdef0123456789abcdef
通过hash生成,通用唯一标识码uuid
是固定值。
key值 0123456789abcdef0123456789abcdef // 自定义串 #Eg����#Eg���� // 按每2位数按照十六进制格式打包到二进制字符串中(二进制不可读) ������n`2�4��� // 加上secret前缀,再进行md5加密(二进制不可读) 6JDXwILoGcZuYDKGNPiXgA== // 进行base64加密得到key值 key_id值 0123456789abcdef0123456789abcdef // 自定义串 #Eg����#Eg���� // 按每2位数按照十六进制格式打包到二进制字符串中(二进制不可读) ASNFZ4mrze8BI0VniavN7w== // 进行base64加密得到key_id值 pssh->uuid值 1077efec-c0b2-4d02-ace3-3c1e52e2fb4b // 固定值 pssh->data值 00000001、0123456789abcdef0123456789abcdef、00000000 // 三个自定义串 、#Eg����#Eg����、空值 // 分别按每2位数按照十六进制格式打包到二进制字符串中,并首尾连接在一起(二进制不可读) AAAAAQEjRWeJq83vASNFZ4mrze8AAAAA // 进行base64加密得到data值
播放时通过POST请求携带kids信息从秘钥服务器获取对应的License解密秘钥。
POST请求携带的kids信息:
{kids: ["ASNFZ4mrze8BI0VniavN7w"], type: "temporary"}
获取的License解密秘钥:
{ "keys":[ { "k":"6JDXwILoGcZuYDKGNPiXgA", "kty":"oct", "kid":"ASNFZ4mrze8BI0VniavN7w" } ], "type":"temporary" }
k
表示加密秘钥,kid
表示秘钥标号。示例中的k
可以由kid
转换得到,实际中只要标号与秘钥一一对应即可。
ASNFZ4mrze8BI0VniavN7w // 解析请求中附带的json数据从kids中获取kid值 #Eg����#Eg���� // "-"转“+”,“_”转“/”,base64解密(二进制不可读) ������n`2�4��� // 加上前缀secret,并进行MD5加密(二进制不可读) 6JDXwILoGcZuYDKGNPiXgA // 进行base64加密,`+`转`-`,`/`转`_`,去掉`=`得到k值
当前搭建好的DRM服务器的所在的机器ip为192.168.192.182,在之后的nginx配置中会配置该ip。
在nginx中配置并启用DRM,这里添加DRM配置的作用是使nginx-vod-module模块在对原视频进行切片的时候进行加密,并向DASH的清单文件MPD中加入DRM信息。
location /vod_dash { …… # DRM相关配置 vod_drm_enabled on; # 启用DRM vod_drm_clear_lead_segment_count 0; # 设置所有切片都需要经过DRM处理 vod_drm_upstream_location /php_proxy/; # 指定DRM地址 vod_drm_request_uri /dash_clear_key.php; # 配置php地址 …… } # 配置指向DRM服务器地址的location location /php_proxy/ { proxy_pass http://192.168.192.182/; # 转发地址指向DRM服务器地址 proxy_set_header Host $http_host; }
该段代码在之前nginx的配置中注释掉了,需要使用DRM的时候放开注释即可。
配置好nginx之后,播放时被请求的DASH清单文件MPD(xml格式)中会出现相对应的用ContentProtection字段进行包装的DRM信息。
在shakaplayer上也需要进行一些配置,在player的configure中配置DRM信息,这里添加DRM配置的作用是在shakaplayer播放DASH流的每个切片的之前使用请求到的解密秘钥进行对切片解密。
player.configure({ drm: { servers: { 'org.w3.clearkey': 'https://192.168.192.182/dash_clear_key.php' } } });
需要注意DRM地址也需要使用
https
,该段代码在之前加载shakaplayer的html中注释掉了,需要使用DRM的时候放开注释即可。
把DRM秘钥服务器、视频内容服务器、播放器三个地方的DRM配置都完成之后,此时播放的媒体流视频已经是经过DRM加密的了,示例使用明文字符串进行简单hash之后进行加、解密,可以通过提高hash函数的复杂性、在URL中加上身份验证等方式继续提高安全性。
播放的媒体内容经过DRM,虽然示例中在播放上看起来并没有什么不同的感觉,但下载保存的内容切片如果不经过DRM的验证则是无法播放的。
由于多码率自适应需要有2个或者2个以上的视频来支持不同带宽下的播放,出于内容安全的考虑一般情况下会为每个视频配置各自不同的秘钥。
为了支持对不同视频使用不同秘钥,需要在DRM服务器配置多组秘钥,以下php代码配置了两组ClearKey秘钥,按照之前的规则生成了两组秘钥,秘钥1是由字符串0123456789abcdef0123456789abcdef
所生成,秘钥2是由字符串01234567890123456789abcdefabcdef
所生成,为了简单起见,,在返回时直接使用已生成好了的明文进行返回:
<?php if ($_SERVER['REQUEST_METHOD'] == 'POST') { // get liense $key1 = array('k' => '6JDXwILoGcZuYDKGNPiXgA', 'kty' => 'oct', 'kid' => 'ASNFZ4mrze8BI0VniavN7w'); $key2 = array('k' => 'XHYyt3r8i4EDVtu3HoFl1g', 'kty' => 'oct', 'kid' => 'ASNFZ4kBI0VniavN76vN7w'); $license = array('keys' => array($key1, $key2), 'type' => 'temporary'); echo str_replace('\\/', '/', json_encode($license)); header('access-control-allow-origin: *'); } else { $currentUrl = $_SERVER["REQUEST_URI"]; // get key $pssh1 = array('uuid' => '1077efec-c0b2-4d02-ace3-3c1e52e2fb4b', 'data' => 'AAAAAQEjRWeJq83vASNFZ4mrze8AAAAA'); $encrpytion1 = array('key' => '6JDXwILoGcZuYDKGNPiXgA==', 'key_id' => 'ASNFZ4mrze8BI0VniavN7w==', 'pssh' => array($pssh1)); $pssh2 = array('uuid' => '1077efec-c0b2-4d02-ace3-3c1e52e2fb4b', 'data' => 'AAAAAQEjRWeJASNFZ4mrze+rze8AAAAA'); $encrpytion2 = array('key' => 'XHYyt3r8i4EDVtu3HoFl1g==', 'key_id' => 'ASNFZ4kBI0VniavN76vN7w==', 'pssh' => array($pssh2)); if (strpos($currentUrl, 'low') !== false) // URL中包含字符串low,则使用encryption1 $encrpytion = array($encrpytion1/*, $encrpytion2*/); else $encrpytion = array($encrpytion2); echo str_replace('\\/', '/', json_encode($encrpytion)); } ?>
使用GET进行请求时,会根据URL中是否携带某个字符串(示例中为low
),返回不同的加密秘钥:
URL中带low
字符串,返回加密秘钥1:
[ { "key": "6JDXwILoGcZuYDKGNPiXgA==", "key_id": "ASNFZ4mrze8BI0VniavN7w==", "pssh": [ { "uuid": "1077efec-c0b2-4d02-ace3-3c1e52e2fb4b", "data": "AAAAAQEjRWeJq83vASNFZ4mrze8AAAAA" } ] } ]
URL中不带low
字符串,返回加密秘钥2:
[ { "key": "XHYyt3r8i4EDVtu3HoFl1g==", "key_id": "ASNFZ4kBI0VniavN76vN7w==", "pssh": [ { "uuid": "1077efec-c0b2-4d02-ace3-3c1e52e2fb4b", "data": "AAAAAQEjRWeJASNFZ4mrze+rze8AAAAA" } ] } ]
使用POST进行请求时,会统一返回解密秘钥组:
{ "keys": [ { "k": "6JDXwILoGcZuYDKGNPiXgA", "kty": "oct", "kid": "ASNFZ4mrze8BI0VniavN7w" }, { "k": "XHYyt3r8i4EDVtu3HoFl1g", "kty": "oct", "kid": "ASNFZ4kBI0VniavN76vN7w" } ], "type": "temporary" }
为了让DRM服务器能够识别不同的视频,需要在对秘钥服务器的请求URL中附带一些能够识别不同视频的参数。在nginx配置中,加入$vod_suburi
变量,将视频文件路径附加到请求的尾部。
vod_drm_request_uri /dash_clear_key.php$vod_suburi;
配合DRM秘钥服务器上对字符串(示例中的low
)的判定检测,可以识别不用码率的视频(示例中的example0001.mp4
与example0001_low.mp4
)。
对DRM服务器和视频内容服务器进行了配置之后,使用2.3.2小节提供的访问方式即可播放多码率自适应的DASH流媒体视频,此时不同码率的视频是使用不同的加密秘钥进行加密的。
最后还需要了解一下mpd清单文件,DASH的mpd文件(Media Presentation Description)是用来组织需要播放的各种内容的一个清单列表,本质上是一个xml格式的文件,结构如下:
Period(媒体时段),完整的视频可以按时间分为多个Period,一个Period可以包含一个或者多个AdaptationSet;
AdaptationSet(自适应集合),一个AdaptationSet可以包含一个或多个Representation,这些Representation可具有不同的码率和分辨率,也可能是不同的媒体类型,例如视频或者音频;
Representation(媒体文件描述),每个Representation描述了一段可以独立解码的流媒体,包含码率、分辨率、编码器等信息,一个Representation包含多个Segment;
Segment(切片),流媒体按照一定的时长被切割成的片段。
示例中使用nginx-vod-module模块通过修改nginx配置来动态生成mpd清单文件,因此并不需要手动配置该清单文件,这里贴一下引入DRM后生成的mpd清单文件完整内容如下:
<?xml version="1.0"?> <MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd" type="static" mediaPresentationDuration="PT1880.979S" minBufferTime="PT10S" profiles="urn:mpeg:dash:profile:isoff-live:2011"> <Period> <AdaptationSet id="1" segmentAlignment="true" maxWidth="1280" maxHeight="720" maxFrameRate="25"> <SegmentTemplate timescale="1000" media="https://192.168.192.128/vod_dash/example0001.mp4/fragment-$Number$-$RepresentationID$.m4s" initialization="https://192.168.192.128/vod_dash/example0001.mp4/init-$RepresentationID$.mp4" duration="10000" startNumber="1"> </SegmentTemplate> <Representation id="v1-x3" mimeType="video/mp4" codecs="avc1.640029" width="1280" height="720" frameRate="25" sar="1:1" startWithSAP="1" bandwidth="549135"> <ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc"/> <ContentProtection xmlns:cenc="urn:mpeg:cenc:2013" schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b" cenc:default_KID="01234567-89ab-cdef-0123-456789abcdef"> <cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAEBI0VniavN7wEjRWeJq83vAAAAAA==</cenc:pssh> </ContentProtection> </Representation> </AdaptationSet> <AdaptationSet id="2" segmentAlignment="true"> <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="1"/> <SegmentTemplate timescale="1000" media="https://192.168.192.128/vod_dash/example0001.mp4/fragment-$Number$-$RepresentationID$.m4s" initialization="https://192.168.192.128/vod_dash/example0001.mp4/init-$RepresentationID$.mp4" duration="10000" startNumber="1"> </SegmentTemplate> <Representation id="a1-x3" mimeType="audio/mp4" codecs="mp4a.40.2" audioSamplingRate="44100" startWithSAP="1" bandwidth="64000"> <ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc"/> <ContentProtection xmlns:cenc="urn:mpeg:cenc:2013" schemeIdUri="urn:uuid:1077efec-c0b2-4d02-ace3-3c1e52e2fb4b" cenc:default_KID="01234567-89ab-cdef-0123-456789abcdef"> <cenc:pssh>AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAEBI0VniavN7wEjRWeJq83vAAAAAA==</cenc:pssh> </ContentProtection> </Representation> </AdaptationSet> </Period> </MPD>
使用nginx搭建音视频点播服务器——基于HLS协议:https://blog.csdn.net/SeeDoubleU/article/details/121805174 ↩︎
Introducing LowLatency HLS:https://developer.apple.com/videos/play/wwdc2019/502/ ↩︎
nginx的安装流程:https://blog.csdn.net/SeeDoubleU/article/details/121727292 ↩︎
使用nginx搭建音视频点播服务——基于HLS协议:https://blog.csdn.net/SeeDoubleU/article/details/121805174 ↩︎
ISO Common Encryption (‘cenc’) Protection Scheme for ISO Base Media File Format Stream Format: https://www.w3.org/TR/eme-stream-mp4/ ↩︎