现在看一些网站的视频,发现都是先获取 m3u8 播放源列表,然后分别下载视频片段,每个片段也就几百K大小,然后拼成一个完整的视频。那这个背后到底用到了什么技术呢?查阅资料整理如下:
HTTP Live Streaming (HLS) 是由苹果公司提出的基于 HTTP 的流媒体网络传输协议。包含:
- 一个m3u(8)的索引文件
- TS媒体分片文件
- key加密串文件(如果加密的话)
索引文件的格式如下:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:5
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:4.042667,
/m3u8/seg1.ts
#EXTINF:4.000000,
/m3u8/seg2.ts
#EXTINF:4.000000,
/m3u8/seg3.ts
#EXTINF:4.000000,
/m3u8/seg4.ts
#EXTINF:4.000000,
/m3u8/seg5.ts
#EXTINF:4.000000,
/m3u8/seg6.ts
#EXT-X-ENDLIST
其中包含一些基本信息,例如:
- #EXTM3U:文件头(必须放在第一行)
- #EXT-X-VERSION:版本号
- #EXT-X-TARGETDURATION:每个分片的最大时长(单位是秒)
- #EXT-X-MEDIA-SEQUENCE:首个分片的序列号(默认为0)
- #EXTINF:分片的信息,例如时长等
- #EXT-X-ENDLIST:文件结束符
上面的播放源表示列表中有 6 个片段,每个片段都是一个 ts 文件,按播放顺序有序排列,我们可以手动把这些小的 ts 片段下载下来,然后用下面命令将其合并成一个大的 ts 文件,然后转成一个 mp4 文件:
$ ffmpeg -f concat -safe 0 -i playlist.m3u8 -c copy output.ts
$ ffmpeg -i output.ts -acodec copy -vcodec copy output.mp4
更简单的办法是,用下面的命令直接将其保存为 mp4 文件:
$ ffmpeg -i http://cdn.zlib.cn/m3u8/test.m3u8 -c copy output.mp4
为了保护数字版权,大部分的视频网站会对 ts 片段进行加密,例如下面的数据源:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-ALLOWCACHE:1
#EXT-X-KEY:METHOD=AES-128,URI="seg1.key"
#EXTINF:4.458667,
seg1.ts.enc
#EXT-X-KEY:METHOD=AES-128,URI="seg2.key"
#EXTINF:4.010000,
seg2.ts.enc
#EXT-X-KEY:METHOD=AES-128,URI="seg3.key"
#EXTINF:4.468667,
seg3.ts.enc
#EXT-X-KEY:METHOD=AES-128,URI="seg4.key"
#EXTINF:3.893000,
seg4.ts.enc
#EXT-X-KEY:METHOD=AES-128,URI="seg5.key"
#EXTINF:4.007333,
seg5.ts.enc
#EXT-X-TARGETDURATION:5
#EXT-X-ENDLIST
发现多了一些字段,例如:
- #EXT-X-ALLOWCACHE:是否允许缓存
- #EXT-X-KEY:加密解析
EXT-X-KEY 就是表示经过加密的,基本格式形如:
#EXT-X-KEY:METHOD=AES-128,URI=”http://example.com/key",IV=0x0123456789abcdef0123456789abcdef
AES-128 且有 iv 填充的是 aes-cbc 算法,解码需要 iv (偏移量)和 key (秘钥),EXT-X-KEY 那一行记录了密钥的获取路径和偏移量的值,如果拿到正确的 key 和 iv 的话,可以用下面的命令进行解密:
$ openssl aes-128-cbc -d -in seg0.ts -out seg0.decode.ts -nosalt -iv xxx -K xxx
也可以用 ffmpeg 来进行快速处理:
$ ffmpeg -allowed_extensions ALL -i http://cdn.zlib.cn/m3u8/test.enc.m3u8 -c copy video5.mp4
但是为了防止用户下载,一般会在 js 中对 key 进行动态处理,以气球云为例,它的 m3u8 播放源文件格式如下:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:20
#EXT-X-ALLOW-CACHE:YES
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-KEY:METHOD=AES-128,URI="https://play.qiqiuyun.net/sdk_api/video/hls_clef/shd?resNo=xxx&token=xxx&ssl=1",IV=xxx
#EXTINF:10.200,
https://xxx-pub.pubssl.qiqiuyun.net/xxx/xxx?schoolId=xxx&fileGlobalId=xxx
#EXT-X-KEY:METHOD=
EXT-X-KEY 那一行拿到的结果却是:
- key 是20位(正常应该是16位)
- iv 是32位(正常应该是16位)
所以在 JS 代码中对 key 进行了解密后再给到播放器的,从而达到防下载的目的。