HLS 协议介绍
HLS 协议
HLS
是 HTTP Live Streaming
的缩写,是由苹果公司提出的基于 HTTP
的流媒体网络传输协议,它可以同时支持直播和点播,还支持多清晰度、音视频双轨、字幕等功能。 它的原理是将一整条视频分成多段小的视频,完整的播放是由这一个个片段拼接而成的。 HLS
协议规定:
- 视频的封装格式是
TS
。 - 视频的编码格式为
H264
,音频编码格式为MP3
、AAC
或者AC-3
。 - 除了
TS
视频文件本身,还定义了用来控制播放的m3u8
文件(文本文件)
优点:
- 解决特殊情况下的防火墙屏蔽
- 服务器拓展方便。
RTMP
是一种有状态协议,很难对视频服务器进行平滑扩展,因为需要为每一个播放视频流的客户端维护状态。而HLS
基于无状态协议(HTTP
),客户端只是按照顺序使用下载存储在服务器的普通TS
文件,做负责均衡如同普通的HTTP
文件服务器的负载均衡一样简单 - 码率自适应
缺点:
- 直播场景的延迟高(录播无延迟影响)
- 大量的
TS
片文件,会造成服务器存储和请求的压力
采集端流程
- AVInputs
采集音视频源
- Server
服务器组件负责获取的媒体输入流 , 然后Media编码后 MPEG-4
(H.264
video 和 AAC
audio)格式然后用硬件打包到 MPEG-2
(MPEG-2 transport stream)的传输流中。图中显示,传输流会经过 stream segmenter
, 这里的工作是 MPEG-2
传输流会被分散为小片段然后保存为一个或多个系列的 .ts
格式的媒体文件。这个过程需要借助编码工具来完成,比如 Apple stream segmenter
。 视频是 fmp4
文件(新版)或 ts
文件(老版),纯音频会被编码为一些音频小片段,通常为 ADTS
头的 AAC
、MP3
、或者 AC-3
格式。
服务端可以采用硬件编码和软件编码两种形式,其功能都是按照上文描述的规则对现有的媒体文件进行切片并使用索引文件进行管理。而软件切片通常会使用 Apple
公司提供的工具或者第三方的集成工具。
- Distribution
提供 HTTP
服务,包含了 Server
创建好的 m3u8
索引和 ts
分片文件
- Clients 请求
m3u8
资源
文件格式
HLS
协议包含了两种类型的文件,索引文件和 ts/fMP4
文件
索引文件
有两种
- Index file
- Alternate Index file
ts 文件
每个 ts
文件都由若干个 ts packet
组成,每个 ts packet
包是 188
字节。 原因:为了适配 ATM(Asynchronous Transfer Mode)
系统
is motivated by the fact that the payload of the ATM Adaptation Layer-1 (AAL-1) cell is 47 bytes. Therefore, four AAL-1 cells can accommodate a single TS packet.
fMP4
HLS fMP4 在
EXT-X-VERSION 7
支持了fMP4
. fMP4
是基于 MPEG-4 Part 12
的流媒体格式,与 mp4
很相似,但也有一些区别,fMP4
更好的适应流式播的需求. fMP4
可以支持 h.265
,能够大大节省带宽,已经慢慢变成现在的视频主流,尤其是视频监控领域。
播放模式
点播 当前时间点可以获取到所有
index
文件和ts
文件,二级index
文件中记录了所有ts
文件的地址。这种模式允许客户端访问全部内容。上面的例子中就是一个点播模式下的m3u8
的结构。直播 实时生成
M3U8
和ts
文件。它的索引文件一直处于动态变化的,播放的时候需要不断下载二级index
文件,以获得最新生成的ts
文件播放视频。如果一个二级index
文件的末尾没有#EXT-X-ENDLIST
标志,说明它是一个Live
视频流
安全性
加密信息:#EXT-X-KEY:METHOD=AES-128,URI=”xx.key”,IV=xxx
FairPlay Streaming
FairPlay Streaming is:
- A secure key delivery mechanism Content Key is protected on the network and on the client during playback
- Key delivery is transport agnostic Easy to integrate with existing key server infrastructure
- Requires protected HDMI for external output
推流 Demo
Conceptually, HTTP Live Streaming consists of three parts: the server component, the distribution component, and the client software. In a typical configuration, a hardware encoder takes audio-video input, encodes it as HEVC video and AC-3 audio, and outputs a fragmented MPEG-4 file or an MPEG-2 transport stream. A software stream segmenter then breaks the stream into a series of short media files, which are placed on a web server. The segmenter also creates and maintains an index file containing a list of the media files. The URL of the index file is published on the web server. Client software reads the index, then requests the listed media files in order and displays them without any pauses or gaps between segments.
下载工具
下载地址: https://developer.apple.com/download/all/?q=HLS
安装完发现有个 go
的 example
HLS Go Server
brew install go
,安装 go
,然后启动该服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# tony @ tonyMBP in ~/Desktop/hls_server [14:48:18]
$ go run ll-hls-origin-example.go
ll-hls-origin-example.go:43:2: no required module provides package github.com/fsnotify/fsnotify: go.mod file not found in current directory or any parent directory; see 'go help modules'
# tony @ tonyMBP in ~/Desktop/hls_server [14:48:25] C:1
$ go mod init hls_server
go: creating new go.mod: module hls_server
go: to add module requirements and sums:
go mod tidy
# tony @ tonyMBP in ~/Desktop/hls_server [14:49:23]
$ go build
ll-hls-origin-example.go:43:2: no required module provides package github.com/fsnotify/fsnotify; to add it:
go get github.com/fsnotify/fsnotify
# tony @ tonyMBP in ~/Desktop/hls_server [14:49:28] C:1
$ go get github.com/fsnotify/fsnotify
go: downloading github.com/fsnotify/fsnotify v1.5.4
go: downloading golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
go: added github.com/fsnotify/fsnotify v1.5.4
go: added golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
# tony @ tonyMBP in ~/Desktop/hls_server [14:49:48]
$ go run ll-hls-origin-example.go
Listening on http://:8443/
启动 mediastreamsegmenter 服务
1
$ mediastreamsegmenter -w 499 -t 1 224.0.0.50:9121 -s 16 -D -T -f ~/Desktop/hls_server/hls
使用 ffmpeg 推流
既可以使用系统内置摄像头采集音视频,也可以指定本地视频文件。
使用 mac 内置采集设备
1
2
3
4
5
6
7
8
9
10
11
12
$ ffmpeg -f avfoundation -list_devices true -i ""
[AVFoundation indev @ 0x7f924d904400] AVFoundation video devices:
[AVFoundation indev @ 0x7f924d904400] [0] FaceTime高清摄像头(内建)
[AVFoundation indev @ 0x7f924d904400] [1] Capture screen 0
[AVFoundation indev @ 0x7f924d904400] AVFoundation audio devices:
[AVFoundation indev @ 0x7f924d904400] [0] LarkAudioDevice
[AVFoundation indev @ 0x7f924d904400] [1] 外置麦克风
[AVFoundation indev @ 0x7f924d904400] [2] MacBook Pro麦克风
$ ffmpeg -f avfoundation -framerate 30 -pixel_format uyvy422 -i "0:" -c:v h264 -fflags nobuffer -tune zerolatency -f mpegts udp://192.168.1.5:9121
使用指定文件
1
$ ffmpeg -re -i "/Users/tony/Downloads/sample.mp4" -c:v h264 -fflags nobuffer -tune zerolatency -f mpegts udp://192.168.1.5:9121
疑难问题
seek
不准确如何解决?mp4
可以seek
到指定的时间戳,ts
是seek
到文件的某个position
,而不能直接seek
到指定的时间点。 在ffplay.c
中的event_loop
函数中包含了seek
的相关代码。 对于ts
,具体seek
操作调用函数关系为avformat_seek_file()=> av_seek_frame() => seek_frame_internal() => seek_frame_byte()
对于mp4
,具体seek
操作调用函数关系为avformat_seek_file()=> av_seek_frame() => seek_frame_internal() =>mov_read_seek()
ts seek
逻辑是: 给定一个文件位置,直接将文件指针指向该位置。接下来调用 read_packet()
读取一个 ts
包(188字节)时,由于之前进行了 seek
操作,文件指针很可能没有指到一个 ts packet
的包头位置(包头以 0x47 byte
打头的),这时候需要调用 mpegts_resync()
进行重新同步找到包头,然后再重新读取一个完整 ts packet
。 mp4
的 seek
操作逻辑是:给定一个 seek
的目标时间戳(timestamp),根据 mp4
里每个包的索引信息,找到时间戳对应的包就可以了。根据下面的 mp4
的文件组织结构,利用Sample Table
,可以快速找到任意给定时间戳的 video audio
数据包。
结论
- 对
mp4
文件来说,由于有索引表,可以快速找到某个时间戳所对应的数据,所以seek
操作可以快速完成。 ts
文件没有时间戳和数据包位置的对应关系,所以对播放器来说,给定seek
的时间戳ts_seek
,首先应该根据文件的码率估算一个位置 pos,然后获取该位置的数据包的时间戳ts_actual
,如果ts_actual
<ts_seek
,则需要继续往后读取数据包;如果t s_actual> ts_seek,则需要往前读取数据包,直到读到ts_seek
对应的数据包。所以ts
文件的操作可能更加耗时; 如果ts
包含的是CBR
码流,则ts_actual
与ts_seek
一般差别不大,seek
相对较快; 如果ts
包含的VBR
码流, 则ts_actual
与ts_seek
可能相差甚远, 则seek
相对较慢。
- 不同分辨率的
ts/fMP4
文件的兼容性问题 安卓端在播放m3u8
视频时,遇到了花屏问题。查明原因是ts
分辨率改变 这个是必须加上hevc_mp4toannexb
,原始协议只支持h264_mp4toannexb
H.264/5
码流分Annex-B
和AVCC
两种格式,AVCC
以长度信息分割NALU
,在mp4
和flv
等封装格式中使用。Annex-B
以start code(0x000001或0x00000001)
分割NALU
,在mpegts
流媒体文件中使用 加上hevc_mp4toannexb
后每一帧都能解析出视频宽高,这样不会有切换花屏问题。
参考资料
- https://developer.apple.com/documentation/http_live_streaming/understanding_the_http_live_streaming_architecture
- https://en.wikipedia.org/wiki/MPEG_transport_stream
- http://anddymao.com/2021/08/03/2021-08-03-%E4%B8%80%E7%A7%8D%E4%B8%87%E8%83%BDhls%E5%8D%8F%E8%AE%AE%E8%A7%A3%E6%9E%90%E6%96%B9%E6%B3%95/