Post

HLS 协议介绍

HLS 协议

HLSHTTP Live Streaming 的缩写,是由苹果公司提出的基于 HTTP 的流媒体网络传输协议,它可以同时支持直播和点播,还支持多清晰度、音视频双轨、字幕等功能。 它的原理是将一整条视频分成多段小的视频,完整的播放是由这一个个片段拼接而成的。 HLS 协议规定:

  • 视频的封装格式是 TS
  • 视频的编码格式为 H264,音频编码格式为 MP3AAC或者 AC-3
  • 除了 TS 视频文件本身,还定义了用来控制播放的 m3u8 文件(文本文件)

优点:

  1. 解决特殊情况下的防火墙屏蔽
  2. 服务器拓展方便。RTMP 是一种有状态协议,很难对视频服务器进行平滑扩展,因为需要为每一个播放视频流的客户端维护状态。而 HLS 基于无状态协议(HTTP),客户端只是按照顺序使用下载存储在服务器的普通 TS 文件,做负责均衡如同普通的 HTTP 文件服务器的负载均衡一样简单
  3. 码率自适应

缺点:

  1. 直播场景的延迟高(录播无延迟影响)
  2. 大量的 TS 片文件,会造成服务器存储和请求的压力

采集端流程

HLSHLS直播流程

  • AVInputs

采集音视频源

  • Server

服务器组件负责获取的媒体输入流 , 然后Media编码后 MPEG-4H.264 video 和 AAC audio)格式然后用硬件打包到 MPEG-2 (MPEG-2 transport stream)的传输流中。图中显示,传输流会经过 stream segmenter, 这里的工作是 MPEG-2 传输流会被分散为小片段然后保存为一个或多个系列的 .ts 格式的媒体文件。这个过程需要借助编码工具来完成,比如 Apple stream segmenter。 视频是 fmp4 文件(新版)或 ts 文件(老版),纯音频会被编码为一些音频小片段,通常为 ADTS 头的 AACMP3、或者 AC-3 格式。

服务端可以采用硬件编码和软件编码两种形式,其功能都是按照上文描述的规则对现有的媒体文件进行切片并使用索引文件进行管理。而软件切片通常会使用 Apple 公司提供的工具或者第三方的集成工具。

  • Distribution

提供 HTTP 服务,包含了 Server 创建好的 m3u8 索引和 ts 分片文件

  • Clients 请求 m3u8 资源

文件格式

HLS 协议包含了两种类型的文件,索引文件和 ts/fMP4 文件

索引文件

有两种

  1. Index file
  2. Alternate Index file

HLS_IndexHLS Index

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.

HLS_TSHLS TS

fMP4

HLS_fMP4HLS fMP4EXT-X-VERSION 7 支持了fMP4. fMP4 是基于 MPEG-4 Part 12 的流媒体格式,与 mp4 很相似,但也有一些区别,fMP4 更好的适应流式播的需求. fMP4 可以支持 h.265,能够大大节省带宽,已经慢慢变成现在的视频主流,尤其是视频监控领域。

播放模式

  1. 点播 当前时间点可以获取到所有 index 文件和 ts 文件,二级 index 文件中记录了所有 ts 文件的地址。这种模式允许客户端访问全部内容。上面的例子中就是一个点播模式下的 m3u8 的结构。

  2. 直播 实时生成 M3U8ts 文件。它的索引文件一直处于动态变化的,播放的时候需要不断下载二级 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

流程 HLS_fair_playHLS Fair Play

推流 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

安装完发现有个 goexample HLS_go_serverHLS 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

疑难问题

  1. seek 不准确如何解决? mp4 可以 seek 到指定的时间戳, tsseek 到文件的某个 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 packetmp4seek 操作逻辑是:给定一个 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_actualts_seek 一般差别不大, seek 相对较快; 如果 ts 包含的 VBR 码流, 则 ts_actualts_seek 可能相差甚远, 则 seek 相对较慢。

HLS_seekHLS Seek

  1. 不同分辨率的 ts/fMP4 文件的兼容性问题 安卓端在播放 m3u8 视频时,遇到了花屏问题。查明原因是 ts 分辨率改变 这个是必须加上 hevc_mp4toannexb,原始协议只支持 h264_mp4toannexb H.264/5 码流分 Annex-BAVCC 两种格式, AVCC 以长度信息分割 NALU,在 mp4flv 等封装格式中使用。 Annex-Bstart code(0x000001或0x00000001) 分割 NALU,在 mpegts 流媒体文件中使用 加上 hevc_mp4toannexb 后每一帧都能解析出视频宽高,这样不会有切换花屏问题。

参考资料

  1. https://developer.apple.com/documentation/http_live_streaming/understanding_the_http_live_streaming_architecture
  2. https://en.wikipedia.org/wiki/MPEG_transport_stream
  3. 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/
This post is licensed under CC BY 4.0 by the author.