故事背景: 前段时间在网上看视频, 发现一些比较好的视频想下载到本地,但是页面上是没有下载按钮。身为一名程序员,这能难倒我?于是打开审查元素,找到video标签,src中并不是所熟悉的.mp4、.avi等视频文件的类型; 在Network面板下也没发现视频文件, 但是发现很多以.ts结尾的请求, response是二进制流(如下图)。 我去,翻车了,这是啥玩意。于是各种搜索,经过一套组合拳打下来,算是有个浅显的认识了,所以有了这篇文章。
我先总结一下,网站用到的技术大概原理就是: 把一个大的媒体文件,分割成多个小文件(ts
),每次只下载一小片段
,这些切片的索引维护在.m3u8
格式的文件中。 这项技术叫HLS
,即HTTP Live Streaming
HTTP Live Streaming,缩写为HLS,是由苹果公司提出基于HTTP的流媒体网络传输协议。它的工作原理是把整个流分成一个个小的基于HTTP的文件来下载,每次只下载一些
。当媒体流正在播放时,客户端可以选择从许多不同的备用源中以不同的速率下载同样的资源,允许流媒体会话适应不同的数据速率
。在开始一个流媒体会话时,客户端会下载一个包含元数据的扩展 M3U (m3u8) 播放列表文件,用于寻找可用的媒体流。(来自: 维基百科)
HLS常应用于视频的直播和点播中
m3u8是什么?
m3u8,是HTTP Live Streaming直播的索引文件。m3u8基本上可以认为就是.m3u格式文件,区别在于,m3u8文件使用UTF-8字符编码
m3u8文件有两种应用场景:多码率适配流和单码率适配流。
多码率适配
#EXTM3U
#EXT-X-STREAM-INF: PROGRAM-ID=1, BANDWIDTH=1280000
http://example.com/low.m3u8
#EXT-X-STREAM-INF: PROGRAM-ID=1, BANDWIDTH=2560000
http://example.com/mid.m3u8
#EXT-X-STREAM-INF: PROGRAM-ID=1, BANDWIDTH=7680000
http://example.com/hi.m3u8
#EXT-X-STREAM-INF: PROGRAM-ID=1, BANDWIDTH=65000, CODECS="mp4a.40.5"
http://example.com/audio-only.m3u8
复制代码
单码率适配
#EXTM3U
#EXT-X-TARGETDURATION: 5220
#EXTINF: 5220,
http://media.example.com/entire.ts
#EXT-X-ENDLIST
复制代码
知道大概原理了,那我们如何下载这种视频文件呢? 最主要的是找到视.m3u8文件,然后基于.m3u8文件转成.mp4文件。
如何拿到一个视频网站中的m3u8?
- 借助浏览器的network面板, 一般来说比较隐秘,不大好找。
- 借助chrome插件猫抓
如何基于m3u8转成mp4?
- 网上有提供基于一些播放器转换的,但是我试了下没成功。
- 借助
ffmpeg
, 媒体转换神器
ffmpeg的安装及使用
-
安装(我的安装过程也挺曲折,mac环境推荐两种方式)
-
第一种 clone 源码
git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
然后进入源码目录,参照INSTALL.md
文件,依次执行下列命令./configure
make
make install
-
第二种方式用homebrew
brew install ffmpeg
-
-
基本使用
-
格式转换:
ffmpeg -i input.mkv -acodec copy -vcodec copy out.mp4
-
将m3u8文件转成mp4:
ffmpeg -i https://XXXX/video.m3u8 -c copy output.mp4
-
将mp4文件切片生成m3u8文件:
ffmpeg -i test.mp4 -hls_time 10 -hls_list_size 0 out\playlist.m3u8
- -hls_time 10 : 设置 m3u8 列表中切片的 duration;如上面命令行控制转码切片长度为 10 秒左右一片,该切片规则采用的方式是从关键帧处开始切片,所以时间并不是很均匀,如果先转码再进行切片,则会比较规律
- -hls_list_size 0 : 参数设置播放列表保存的最多条目,设置为0会保存有所片信息
-
因此我的目的达到了,借助ffmpeg -i https://XXXX/video.m3u8 -c copy output.mp4
这个命令下载到了我想要的资源。
那么如何把这个技术应用到我们的项目当中呢?下面做一个小demo(将视频切片然后在web页面中播放)。
- 使用ffmpeg将视频切片
ffmpeg -i test.mp4 -hls_time 20 -hls_list_size 0 out\playlist.m3u8
,上传服务器
- 在web中播放时借助
videojs
,videojs
已经做了处理可以播放m3u8文件。引入videojs
的cdn
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://vjs.zencdn.net/7.11.4/video-js.css" rel="stylesheet" />
...
<script src="https://vjs.zencdn.net/7.11.4/video.min.js"></script>
</head>
复制代码
- 在组件的video中引入服务器上的m3u8文件(我这里设置的是本地), 同时type设置为 type设置成
application/x-mpegURL
。
import React from 'react'
export default () => {
return (
<div className="App">
<video
id="my-video"
className="video-js"
controls
preload="auto"
width="640"
height="264"
poster="MY_VIDEO_POSTER.jpg"
data-setup="{}"
>
{/* 引入m3u8文件, type设置成'application/x-mpegURL' */}
<source src="/hlsfile/playlist.m3u8" type="application/x-mpegURL" />
<p className="vjs-no-js">
To view this video please enable JavaScript, and consider upgrading to a
web browser that
<a href="https://videojs.com/html5-video-support/" target="_blank"
>supports HTML5 video</a
>
</p>
</video>
</div>
);
}
export default App;
复制代码
- 成果展示
demo地址hls-client
参考文章