VideoJS+HLS视频加密播放

前言

        前段时间遇到一个播放视频的项目,为了防止登录的用户下载项目的视频,所以需要对视频加密,即使用户下载也不能播放;因为前端采用videojs,最后确认方案是将mp4转m3u8文件格式,来实现视频文件加密播放。下面做一下总结。

一、什么是HLS

        HLS(HTTP Live Streaming的缩写)是一个由苹果公司提出的基于HTTP的流媒体网络传输协议。HLS协议基于HTTP协议,客户端按照顺序使用HTTP协议下载存储在服务器上的文件。HLS协议规定,视频的封装格式是TS(Transport Stream),除了TS视频文件本身,还定义了用来控制播放的M3U8文件(文本文件)。HLS协议的工作原理是把整个视频流分割成一个个小的TS格式视频文件来传输,在开始一个流媒体会话时,客户端会先下载一个包含TS文件URL地址的M3U8文件(相当于一个播放列表),给客户端用于下载TS文件。

二、M3U8格式标准介绍

        M3U8是一种常见的流媒体格式,主要以文件列表的形式存在,既支持直播又支持点播,尤其在Android、iOS等平台最为常用,下面就来看一下M3U8的最简单的例子:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.000000,
out0.ts
#EXTINF:10.000000,
out1.ts
#EXTINF:10.000000,
out2.ts
#EXTINF:10.000000,
out3.ts
#EXTINF:6.120000,
out4.ts

#EXTM3U:M3U8文件头,必须放在第一行。
#EXT-X-VERSION:M3U8文件的版本,常见的是3,其实版本已经发展了很多了。
#EXT-X-MEDIA-SEQUENCE :第一个TS分片的序列号,一般情况下是0,但是在直播场景下,这个序列号标识直播段的起始位置。
#EXT-X-TARGETDURATION:每个分片TS的最大的时长;例如:EXT-X-TARGETDURATION:10 ,表示每个分片的最大时长是10秒。
#EXT-X-ALLOW-CACHE:是否允许cache;例如:EXT-X-ALLOW-CACHE:YES 、EXT-X-ALLOW-CACHE:NO,默认情况下是YES。
#EXT-X-ENDLIST:M3U8文件结束符。
#EXTINF:extra info,分片TS的信息,如时长,带宽等;一般情况下是 #EXTINF:<duration>,[<title>] 后面可以跟其他的信息,逗号之前是当前分片的TS时长。分片时长要小于 EXT-X-TARGETDURATION 定义的值。
#EXT-X-DISCONTINUITY:该标签表明其前一个切片与下一个切片之间存在中断。
#EXT-X-PLAYLIST-TYPE :表明流媒体类型。
#EXT-X-KEY:是否加密解析。例如:#EXT-X-KEY:METHOD=AES-128,URI="https://example.com/video.key?token=xxx" 加密算法是AES-128,密钥通过请求 https://example.com/video.key?token=xxx 来获取,密钥请求回来以后存储在本地,并用于解密后续下载的TS视频文件。

上面介绍的是最常见的playlist,还有一种playlist,仅包含播放节目列表信息,在HLS中称为master playlist,也是二级索引的结构,其示例如下:

#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1000000, RESOLUTION=720x480
http://example.com/low.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=2, BANDWIDTH=2560000,AVERAGE-BANDWIDTH=2000000, RESOLUTION=1080x720
http://example.com/mid.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=3, BANDWIDTH=7680000,AVERAGE-BANDWIDTH=6000000, RESOLUTION=1920x1080
http://example.com/high.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=4, BANDWIDTH=65000,CODECS="mp4a.40.5"
http://example.com/audio-only.m3u8

#EXT-X-STREAM-INF:用于标识一个Variant Stream,这是由一系列的Redition组成的。该标签的属性列表中包含了Variant Stream的描述信息。例如:BANDWIDTH表示Variant Stream中的峰值比特率,单位bits/s。
AVERAGE-BANDWIDTH表示Variant Stream中的平均比特率,单位bits/s。
CODECS包含Variant Stream中音视频编码格式相关的信息,比如上面的"mp4a.40.5"。
RESOLUTION包含Variant Stream中对应视频流的分辨率。
FRAME-RATE表示Variant Stream中的视频帧率。

三、FFmpeg 转HLS常见参数

        FFmpeg中自带HLS的封装参数,使用HLS格式即可进行HLS的封装,但是生成HLS的时候有各种参数可以进行参考,例如设置HLS列表中切片的前置路径、生成HLS的TS切片时设置TS的分片参数、生成HLS时设置M3U8列表中保存的TS个数等,详细参数如下表。

参数 类型 说明
start number 整数 设置M3U8列表中的第一片的序列数
hls time 浮点数 设置每一片时长
hls list size 整数 设置M3U8中分片的个数
hls ts options 字符串 设置TS 切片的参数
hls wrap 整数 设置切片索引回滚的边界值
hls allow cache 整数 设置M3U8中EXT-X-ALLOW-CACHE的标签
hls base url 字符串 设置M3U8中每一片的前置路径
hls segment filename 字符串 设置切片名模板
hls key info file 字符串 设置M3U8加密的key 文件路径
hls subtitle path 字符串 设置M3U8字幕路径
hls flags 标签(整数)

设置M3U8文件列表的操作,具体如下。

singlefile:生成一个媒体文件索引与字节范围

delete_segments:删除M3U8文件中不包含的过期的TS切片文件

rounddurations:生成的M3U8切片信息的 duration 为整数

discont_start:生成M3U8的时候在列表前边加上 discontinuity 标签

omit endlist:在M3U8末尾不追加endlist标签

use localtime 布尔 设置M3U8文件序号为本地时间戳
use localtime mkdir 布尔 根据本地时间戳生成目录
hls playlist type 字符串 设置M3U8列表为事件或者点播列表
method 字符串 设置HTTP属性

四、视频切片加密

1.生成文件视频切片加密文件

基本命令:

#!/bin/sh
openssl rand 16 > file.key
echo file.key > file.keyinfo
echo file.key >> file.keyinfo
echo $(openssl rand -hex 16) >> file.keyinfo
 
备注:加密方式 ASE

参数详解:

openssl rand 16 > file.key 生成一个key文件

echo file.key > file.keyinfo 将外部访问的file.key映射到file.keyinfo文件中。

echo file.key >> file.keyinfo 将项目file.key所在的文件路径映射到fike.keyinfo文件中。

echo $(openssl rand -hex 16) >> file.keyinfo 生成IV密钥

Go实现:

# 生成 file.key
err = exec.Command("sh", "-c", "openssl rand 16 > file.key").Run()
if err != nil {
    // todo
}
# 生成 file.keyinfo
err = exec.Command("sh", "-c", "echo file.key > file.keyinfo" && echo echo file.key >> file.keyinfo && echo $(openssl rand -hex 16) >> file.keyinfo").Start()
if err != nil {
    // todo
}
 
备注:Run():启动指定的命令并等待其完成。 Start():启动指定的命令无需等待其完成。

2.FFmpeg 创建 HLS 播放清单(m3u8)

转化命令:

ffmpeg -y -i ocean.mp4 -c:v copy -hls_time 10 -hls_list_size 0 -hls_key_info_file file.keyinfo -hls_playlist_type vod -hls_segment_filename ocean%d.ts -f hls playlist.m3u8

参数详解:

-y  覆盖输出的文件

-i ocean.mp4 输入文件,源文件;

-c:v copy 表示不重新编码,在格式未改变的情况采用;

-hls_time 10 每片 TS 文件时长10秒;

-hls_list_size 0  不限制M3U8中分片的个数;

-hls_key_info_file file.keyinfo file.keyinfo  设置M3U8加密的 key 文件路径;

-hls_playlist_type vod  设置 M3U8 列表为点播;

-hls_segment_filename ocean%d.ts  设置切片名模板;

-f hls playlist.m3u8 指定输出格式hls,playlist.m3u8 输出文件名

Go实现:

cmd := exec.Command("ffmpeg",
    "-y",
    "-i", ocean.mp4,
    "-c:v", "copy",
    "-hls_time", "10",
    "-hls_list_size", "0",
    "-hls_key_info_file", file.keyinfo,
    "-hls_playlist_type", "vod",
    "-hls_segment_filename", "ocean%d.ts",
    "-f", "hls", "playlist.m3u8"))
 
err = cmd.Start()
if err != nil {
    // todo
}
 
err = cmd.Wait()
if err != nil {
    // todo
}
 
// todo
 
备注:cmd.Start():Start启动指定的命令,但不等待它完成。cmd.Wait():等待命令退出并等待任何复制到stdin或从stdout或stderr复制完成;这里用 Wait() ,是我后面有业务处理,需要确认这里成功执行。

执行结果:

3.FFmpeg 解密合成 TS

执行代码:

ffmpeg -allowed_extensions ALL -protocol_whitelist "file,http,crypto,tcp" -i playlist.m3u8 -c copy output.mp4

备注:如果解密无法访问 key.info,下载 key.info 文件,修改 playlist.m3u8 文件中"URI"位置,保证执行 ffmpeg 可访问到

五、VideoJS + M3U8 播放视频

本地搭建一个简单的Web服务,并解析一个虚拟域名"www.test.vip"(IP+端口也可以),将生成的 file.key 、playlist.m3u8 和视频切片 TS 文件放在 resource 目录下(只要能访问到即可)
前端代码和目录:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>PC HLS video</title>
    <link href="https://vjs.zencdn.net/7.20.3/video-js.css" rel="stylesheet"/>
</head>
<body style="text-align:center;">

<h1>PC 端播放 HLS(<code>.m3u8</code>) 视频</h1>
<p>借助 video.js 和 videojs-contrib-hls</p>
<p>由于 videojs-contrib-hls 需要通过 XHR 来获取解析 m3u8 文件, 因此会遭遇跨域问题, 请设置浏览器运行跨域</p>

<video id="video" class="video-js vjs-default-skin vjs-big-play-centered" controls style="margin: auto">
    <source src="resource/playlist.m3u8" type="application/x-mpegURL">
</video>
<script src="https://vjs.zencdn.net/7.20.3/video.min.js"></script>
<!-- PC 端浏览器不支持播放 hls 文件(m3u8), 需要 videojs-contrib-hls 来给我们解码 -->
<script src="https://unpkg.com/videojs-contrib-hls/dist/videojs-contrib-hls.js"></script>
<script>
    const player = videojs('video');
    player.play();
</script>
</body>
</html>

 

 运行结果:

猜你喜欢

转载自blog.csdn.net/qq_34272964/article/details/128275481