流媒体传输 - HLS 协议
HLS
全称是 HTTP Live Streaming
,是一个由 Apple
公司提出的基于 HTTP
的媒体流传输协议,用于实时音视频流的传输。目前 HLS
协议被广泛的应用于视频点播和直播领域。
概述
原理介绍
通过将整条流切割成一个小的可以通过 HTTP
下载的媒体文件,然后提供一个配套的媒体列表文件,提供给客户端,让客户端顺序地拉取这些媒体文件播放,来实现看上去是在播放一条流的效果。由于传输层协议只需要标准的 HTTP
协议,HLS 可以方便的透过防火墙或者代理服务器,而且可以很方便的利用 CDN 进行分发加速,并且客户端实现起来也很方便.
整体架构
HLS
的架构分为三部分:Server,CDN,Client 。即服务器、分发组件和客户端。
下面是 HLS
整体架构图:

服务器用于接收媒体输入流,对它们进行编码,封装成适合于分发的格式,然后准备进行分发。
分发组件为标准的 Web 服务器。它们用于接收客户端请求,传递处理过的媒体,把资源和客户端联系起来。
客户端软件决定请求何种合适的媒体,下载这些资源,然后把它们重新组装成用户可以观看的连续流。
HLS 协议分析
HLS Playlist
其实,HLS
协议的主要内容是关于 M3U8
这个文本协议的,其实生成与解析都非常简单。为了更加直接地说明这一点,我下面举两个简单的例子:
简单的 Media Playlist
:
#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:8 #EXT-X-MEDIA-SEQUENCE:2680 #EXTINF:7.975, https://priv.example.com/fileSequence2680.ts #EXTINF:7.941, https://priv.example.com/fileSequence2681.ts #EXTINF:7.975, https://priv.example.com/fileSequence2682.ts
包含多种比特率的 Master Playlist
:
#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
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
里面是同一个流的多个不同版本 (如:分辨率,码率不同)一个
Playlist
文件必须通过URI(.m3u8 或 m3u)
或者HTTP Content-Type
来识别 (application/vnd.apple.mpegurl
或audio/mpegurl
)换行符可以用
\n
或者\r\n
以
#
开头的是tag
或者注释,以#EXT
开头的是tag
, 其余的为注释,在解析时应该忽略Playlist
里面的URI
可以用绝对地址或者相对地址,如果使用相对地址,那么是相对于Playlist
文件的地址
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
来指定。支持的Media Segment
格式
HLS TAGS
Basic Tags
: 用在Media Playlist
和Master Playlist
里面EXTM3U
: 必须在文件的第一行,标识是一个Extended M3U Playlist
文件EXT-X-VERSION
: 表示Playlist
兼容的版本
Media Segment Tags
: 只能出现在Media Playlist
里面EXTINF
: 用于指定Media Segment
的duration
EXT-X-BYTERANGE
: 用于指定URI
的sub-range
EXT-X-DISCONTINUITY
: 表示不连续EXT-X-KEY
: 表示Media Segment
已加密,该值用于解密EXT-X-MAP
: 用于指定Media Initialization Section
EXT-X-PROGRAM-DATE-TIME
: 和Media Segment
的第一个sample
一起来确定时间戳EXT-X-DATERANGE
: 将一个时间范围和一组属性键值对结合到一起
Media Playlist tags
: 只能出现在Media Playlist
里面EXT-X-TARGETDURATION
: 用于指定最大的Media Segment duration
EXT-X-MEDIA-SEQUENCE
: 用于指定第一个Media Segment
的Media Sequence Number
EXT-X-DISCONTINUITY-SEQUENCE
: 用于不同Variant Stream
之间同步EXT-X-ENDLIST
: 表示结束EXT-X-PLAYLIST-TYPE
: 可选,指定整个Playlist
的类型EXT-X-I-FRAMES-ONLY
: 表示每个Media Segment
描述一个单一的I-frame
Master Playlist tags
: 只能出现在Master Playlist
中EXT-X-MEDIA
: 用于关联同一个内容的多个Media Playlist
的多种renditions
EXT-X-STREAM-INF
: 用于指定一个Variant Stream
EXT-X-I-FRAME-STREAM-INF
: 用于指定一个Media Playlist
包含媒体的I-frames
EXT-X-SESSION-DATA
: 存放一些session
数据EXT-X-SESSION-KEY
: 用于解密
Media or Master Playlist Tags
: 可以出现在Media Playlist
或者Master Playlist
中,但是如果同时出现在同一个Master Playlist
和Media Playlist
中时,必须为相同值EXT-X-INDEPENDENT-SEGMENTS
: 表示每个Media Segment
可以独立解码EXT-X-START
: 标识一个优选的点来播放这个Playlist
HLS 播放
播放未加密 HLS
我们通过 VLC
播放器播放苹果官方提供的一个例子:http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8
,并使用 Wirshark
对其中交互进行抓包。
GET /iphone/samples/bipbop/gear1/prog_index.m3u8 HTTP/1.1 Host: devimages.apple.com Accept: */* Accept-Language: zh_CN User-Agent: VLC/3.0.8 LibVLC/3.0.8 Range: bytes=0- HTTP/1.1 206 Partial Content Accept-Ranges: bytes Content-Type: audio/x-mpegurl ETag: "50117c8233644c19b5ab49551b72507f:1239907352" Last-Modified: Thu, 16 Apr 2009 18:42:32 GMT Server: AkamaiNetStorage Date: Sun, 22 Nov 2020 15:01:49 GMT Content-Range: bytes 0-7018/7019 Content-Length: 7019 X-Cache: TCP_MEM_HIT from a184-26-91-45.deploy.akamaitechnologies.com (AkamaiGHost/10.2.0.2-31441410) (-) Connection: keep-alive #EXTM3U #EXT-X-TARGETDURATION:10 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:10, no desc fileSequence0.ts #EXTINF:10, no desc fileSequence1.ts #EXTINF:10, no desc fileSequence2.ts #EXTINF:10, no desc ...... #EXTINF:1, no desc fileSequence180.ts #EXT-X-ENDLIST
我们可以看到返回的 M3U8
里面他有一个一个 ts
视频片段,这个一个一个视频片段就是我们需要的播放的视频片段。
#EXTINF
表示每个 ts
切片视频文件的时长。 #EXT-X-TARGETDURATION
指定当前视频流中的切片文件的最大时长,也就是说这些 ts
切片的时长不能大于 #EXT-X-TARGETDURATION
的值。 #EXT-X-MEDIA-SEQUENCE
第一个 ts
分片的序列号 #EXT-X-ENDLIST
这个表示视频结束,有这个标志同时也说明当前的流是一个非直播流。 #EXT-X-PLAYLIST-TYPE:VOD
的意思是当前的视频流并不是一个直播流,而是点播流,换句话说就是该视频的全部的 ts
文件已经被生成好了 #EXT-X-ALLOW-CACHE
是否允许 cache
播放加密 HLS
HLS 协议总结
优点
- 客户端支持简单,只需要支持
HTTP
请求即可,HTTP
协议无状态,只需要按顺序下载媒体片段即可。 - 使用
HTTP
协议网络兼容性好,HTTP
数据包也可以方便地通过防火墙或者代理服务器,CDN
支持良好。 Apple
的全系列产品支持,不需要安装任何插件就可以原生支持播放HLS
, 目前Android
也加入了对HLS
的支持。- 自带多码率自适应机制。
缺点
- 相比
RTMP
这类长连接协议,延时较高,难以用到互动直播场景。 - 对于点播服务来说,由于
TS
切片通常较小,海量碎片在文件分发,一致性缓存,存储等方面都有较大挑战。
改进
- 由于客户端每次请求
TS
或M3U8
有可能一个新的连接请求,无法有效的标识客户端,一旦出现问题,基本无法有效的定位问题。 - 一般工业级的服务器都会对传统的
HLS
做一些改进,常见优化是对每个M3U8
文件增加Session
来标识一条HLS
连接。 - 不管通过哪种方式,最终我们都能通过一个唯一的
id
来标识一条流,这样在排查问题时就可以根据这个id
来定位播放过程中的问题。