VideoJS+HLS视频加密播放
Posted 寻找09之夏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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>
运行结果:
vue 使用vue-video-player播放hls格式视频
安装
import hls from ‘videojs-contrib-hls‘ import VideoPlayer from ‘vue-video-player‘; import "video.js/dist/video-js.css" import "vue-video-player/src/custom-theme.css" Vue.use(hls) Vue.use(VideoPlayer)
<video id="my-video" class="video-js vjs-default-skin" controls preload="auto"> <source src="http://ivi.bupt.edu.cn/hls/cctv1.m3u8" type="application/x-mpegURL" /> </video>
import "video.js/dist/video-js.css";
import videojs from "video.js";
import "videojs-contrib-hls";
mounted()
videojs(
"my-video",
bigPlayButton: false,
textTrackDisplay: false,
posterImage: true,
errorDisplay: false,
controlBar: true
,
function()
this.play();
);
页面中使用 第二种方式
<video-player class="video-player vjs-custom-skin" :playsinline="true" :options="‘http://ivi.bupt.edu.cn/hls/cctv1.m3u8‘|videoOption"> </video-player>
import ‘videojs-contrib-hls‘
filters:
videoOption(val)
console.log(‘val‘,val)
return
live: false,
preload: "auto",
autoplay: true,
language: "zh-CN",
aspectRatio: "16:8",
fluid: true,
sources: [
src: val //url地址
],
notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。
controlBar:
timeDivider: false,
durationDisplay: false,
remainingTimeDisplay: false,
fullscreenToggle: true //全屏按钮
,
flash:
hls:
withCredentials: false
,
以上是关于VideoJS+HLS视频加密播放的主要内容,如果未能解决你的问题,请参考以下文章
vue.js+video.js+videojs-contrib-hls支持PC端播放m3u8格式的视频
vue 使用vue-video-player播放hls格式视频