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_mp4toannexbH.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/




