参考资料:https://www.cnblogs.com/jimodetiantang/p/9133564.html
https://cloud.tencent.com/developer/article/1032541
https://blog.csdn.net/yuan1125/article/details/51540918
https://blog.csdn.net/max_min_go/article/details/39463675
https://blog.csdn.net/simongyley/article/details/34411577
https://blog.csdn.net/heyatzw/article/details/76165756
Android 源码分析之基于NuPlayer的HLS流媒体协议
1、综述
HLS 全称是 HTTP Live Streaming,是一个由 Apple 公司提出的基于 HTTP 的媒体流传输协议,用于实时音视频流的传输。目前HLS协议被广泛的应用于视频点播和直播领域。
1.1 原理介绍
HLS 跟 DASH 协议的原理非常类似。通过将整条流切割成一个小的可以通过 HTTP 下载的媒体文件,然后提供一个配套的媒体列表文件,提供给客户端,让客户端顺序地拉取这些媒体文件播放,来实现看上去是在播放一条流的效果。由于传输层协议只需要标准的 HTTP 协议,HLS 可以方便的透过防火墙或者代理服务器,而且可以很方便的利用 CDN 进行分发加速,并且客户端实现起来也很方便。
HLS 把整个流分成一个个小的基于 HTTP 的文件来下载,每次只下载一些。HLS 协议由三部分组成:HTTP、M3U8、TS
。这三部分中,HTTP 是传输协议,M3U8 是索引文件,TS 是音视频的媒体信息。
关于 HLS 的详细介绍可参考: https://tools.ietf.org/html/draft-pantos-http-live-streaming-23
在 HTML5 页面上使用 HLS 非常简单:
直接:
<video src="example.m3u8" controls></video>
或者:
<video controls>
<source src="example.m3u8"></source>
</video>
HLS 是提供一个 m3u8 地址,Apple 的 Safari 浏览器直接就能打开 m3u8 地址,譬如:http://demo.srs.com/live/livestream.m3u8
Android 不能直接打开,需要使用 html5 的 video 标签,然后在浏览器中打开这个页面即可,譬如:
<!-- livestream.html -->
<video width="640" height="360"
autoplay controls autobuffer
src="http://demo.srs.com/live/livestream.m3u8"
type="application/vnd.apple.mpegurl">
</video>
HLS 的 m3u8,是一个 ts 的列表,也就是告诉浏览器可以播放这些 ts 文件,譬如:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:64
#EXT-X-TARGETDURATION:12
#EXTINF:11.550
livestream-64.ts
#EXTINF:5.250
livestream-65.ts
#EXTINF:7.700
livestream-66.ts
#EXTINF:6.850
livestream-67.ts
有几个关键的参数,这些参数在 SRS 的配置文件中都有配置项:
- EXT-X-TARGETDURATION:所有切片的最大时长。有些 Apple 设备这个参数不正确会无法播放。SRS 会自动计算出 ts 文件的最大时长,然后更新 m3u8 时会自动更新这个值。用户不必自己配置。
- EXTINF:ts 切片的实际时长,SRS 提供配置项 hls_fragment,但实际上的 ts 时长还受 gop 影响。
- ts 文件的数目:SRS 可配置 hls_window,指定 m3u8 中保存多少个切片,SRS 会自动清理旧的切片。
- livestream-67.ts:SRS 会自动维护 ts 切片的文件名,在编码器重推之后,这个编号会继续增长,保证流的连续性。直到 SRS 重启,这个编号才重置为 0。
譬如,每个 ts 切片为 10 秒,窗口为 60 秒,那么 m3u8 中会保存 6 个 ts 切片。
每一个 .m3u8 文件,分别对应若干个 ts 文件,这些 ts 文件才是真正存放视频的数据,m3u8 文件只是存放了一些 ts 文件的配置信息和相关路径,当视频播放时,.m3u8 是动态改变的
,video 标签会解析这个文件,并找到对应的 ts 文件来播放,所以一般为了加快速度,.m3u8 放在 web 服务器上,ts 文件放在 cdn 上。
.m3u8 文件,其实就是以 utf-8 编码的 m3u 文件,这个文件本身不能播放,只是存放了播放信息的文本文件。
1.2 整体框架
HLS的架构分为三部分:Server,CDN,Client 。即服务器、分发组件和客户端。
下面是 HLS 整体架构图:
HLS 框架图
(1) Server
服务器端将视频数据流编码、封装和切割为连续的、时长很短的MPEG-TS格式的文件,通常一个ts分片大概是10s;并提供一个配套的媒体列表文件(m3u8文件)。
视频封装格式:MPEG-TS。
编码:视频编码为H.264,音频编码为AAC, MP3, AC-3或者EC-3格式。
HLS也支持纯音频格式,通常是MPEG基本音频文件(MP4封装的AAC格式)。
(2) Distribution
由标准的网络服务器组成,接收客户端的请求和分发所有的资源包括m3u8列表文件和ts分片文件。
(3) Client
客户端先通过下载m3u8文件,再通过m3u8文件的索引地址顺序地拉取ts媒体文件播放。对于直播,它的索引文件一直处于动态变化的,你需要不断的更新索引文件 playlist 然后移除旧的索引文件。
一般为了加快速度,m3u8 放在 web 服务器上,ts 文件放在 cdn 上。
把视频文件上传到服务器上,视频会被转换成HLS格式的视频(即TS和m3u8文件)。Media encoder模块负责将视频源中的视频数据转码到目标编码格式(H264)的视频数据,然后Stream Segment模块将视频切片,切片的结果就是index file(m3u8)和ts文件了。
HLS index file
HLS的index文件就是m3u8的文件,先下载一级index file(master_playlist.m3u8),它里面记录了二级索引文件的地址(Alternate-A、Alternate-B、Alternate-C)的地址,然后客户端再去下载二级索引文件,二级索引文件中又记录了TS文件的下载地址,这样客户端就可以按顺序下载TS视频文件并连续播放。
1.3 HLS协议编码格式要求
- 视频的编码格式:H264
- 音频的编码格式:AAC、MP3、AC-3
- 视频的封装格式:ts
- 保存 ts 索引的 m3u8 文件
1.4 HLS相关文章
- HLS 协议详解
- Apple Developer—HTTP Live Streaming Overview
- MPEG-2 Stream Encryption Format for HTTP Live Streaming
- 流媒体协议—HLS
DEMO:
- M3U8 golang library
- HLS downloader:读取一个 m3u8 URL,下载为 TS 文件。
- https://github.com/selsta/hlsdl
2、HLS 之 M3U8
m3u8 文件是用文本方式对媒体文件进行描述,由一系列标签组成。
m3u8 文件示例 1:单码率适配流
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:YES
#EXT-X-MEDIA-SEQUENCE:2
#EXT-X-TARGETDURATION:16
#EXTINF:14.357, no desc
livestream-2.ts
#EXTINF:15.617, no desc
livestream-3.ts
#EXTINF:14.358, no desc
livestream-4.ts
#EXTINF:15.618, no desc
livestream-5.ts
#EXTINF:11.130, no desc
livestream-6.ts
该 m3u8 文件只是一个简单的 Media Playlist。
m3u8 文件示例 2:多码率适配流
#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-MEDIA:TYPE=AUDIO,GROUP-ID="audio_low",LANGUAGE="en",NAME="English",AUTOSELECT=YES,DEFAULT=YES,URI="audio_low.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio_high",LANGUAGE="en",NAME="English",AUTOSELECT=YES,DEFAULT=YES,URI="audio_high.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=100000,CODECS="mp4a.40.2,avc1.4d401e",AUDIO="audio_low"
video.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=200000,CODECS="mp4a.40.2,avc1.4d401e",AUDIO="audio_high"
video.m3u8
包含多种比特率的 Master Playlist。该文件是一个实际使用中的顶级 m3u8 文件,该文件中又定义了 http://example.com/low.m3u8
、http://example.com/mid.m3u8
等几个二级文件。顶级 m3u8 文件主要是做码率适配的,二级 m3u8 才是真正的切片文件,客户端会默认选择码率最高的请求,如果发现码率达不到,会请求降低码率的流。客户端拿到二级 m3u8 文件后,会继续请求里面的文件,这时就可以进行播放了。
- HLS 通过 URI(RFC3986) 指向的一个 Playlist 来表示一个媒体流。
- 一个 Playlist 可以是一个 Media Playlist 或者 Master Playlist,使用 UTF-8 编码的文本文件,包含一些 URI 跟描述性的 tags。
- 一个 Media Playlist 包含一个 Media Segments 列表,当顺序播放时,能播放整个完整的流。
- 要想播放这个 Playlist,客户端需要首先下载它,然后播放里面的每一个 Media Segment。
- 更加复杂的情况是,Playlist 是一个 Master Playlist,包含一个 Variant Stream 集合,通常每个 Variant Stream 里面是同一个流的多个不同版本(如: 分辨率, 码率不同)。
2.1 HLS Media Segments
- 每一个 Media Segment 通过一个 URI 指定,可能包含一个 byte range。
- 每一个 Media Segment 的 duration 通过 EXTINF tag 指定。
- 每一个 Media Segment 有一个唯一的整数 Media Segment Number。
- 有些媒体格式需要一个 format-specific sequence 来初始化一个 parser,在 Media Segment 被 parse 之前。这个字段叫做 Media Initialization Section,通过 EXT-X-MAP tag 来指定。
2.1.1 支持的 Media Segment 格式
(1)MPEG-2 Transport Streams
- 即最常见的 TS 文件。
- RFC: ISO_13818。
- Media Initialization Section:
PAT
(Program Association Table) 跟PMT
(Program Map Table)。 - 每个 TS segment 必须包含一个 MPEG-2 Program。
- 每一个 TS segment 包含一个 PAT 和 PMT, 最好在 segment 的开始处,或者通过一个 EXT-X-MAP tag 来指定。
(2)Fragmented MPEG-4
- 即常提到的 fMP4。
- RFC: ISOBMFF。
- Media Initialization Section:
ftyp box
(包含一个高于 ios6 的 brand),moov box
必须紧跟在ftyp box
之后。moov box
必须包含一个trak box
(对于每个 fMP4 segment 里面的traf box
,包含匹配的track_ID
)。每个trak box
应该包含一个 sample table,但是它的 sample count 必须为 0。mvhd box
跟tkhd
的 duration 必须为 0。mvex box
必须跟在上一个trak box
后面。 - 不像普通的 MP4 文件包含一个
moov box
(包含 sample tables&