前言
本文对 HLS 协议进行了详细的讲解,由浅入深,一点儿点儿揭开其神秘面纱。
首先我们先使用 ffmepg 对一段视频文件进行切片,视频所在路径:D:\Work\test
切片命令行如下:
ffmpeg -i SampleVideo_1280x720_20mb.mp4 -fflags flush_packets -max_delay 2 -flags -global_header -hls_time 5 -hls_list_size 0 -vcodec libx264 -acodec aac -r 30 -g 60 -y index.m3u8
这个命令是使用FFmpeg工具进行视频转码和分段处理的操作。下面是对每个参数的详细解释:
ffmpeg
: FFmpeg命令行工具的名称,用于处理音视频文件。-i SampleVideo_1280x720_20mb.mp4
: 指定输入文件的路径和文件名。这里的输入文件是名为 “SampleVideo_1280x720_20mb.mp4” 的视频文件。-fflags flush_packets
: 强制立即刷新输出文件的数据包。-max_delay 2
: 设置最大延迟时间为2秒,以确保尽可能快地输出数据。-flags -global_header
: 禁用全局文件头,不将文件头写入每个分段文件。-hls_time 5
: 设置HLS(HTTP Live Streaming)分段的时长为5秒。这将影响生成的.m3u8文件中每个.ts分段文件的时长。-hls_list_size 0
: 设置.m3u8文件中包含的分段列表大小为0,表示将所有分段都包含在.m3u8文件中,而不生成分段列表文件。-vcodec libx264
: 指定使用libx264编码器进行视频编码。-acodec aac
: 指定使用AAC编码器进行音频编码。-r 30
: 设置输出视频的帧率为30帧/秒。-g 60
: 设置关键帧(I帧)之间的间隔为60帧。关键帧是视频编码中的重要帧,可以独立解码,而其他帧则依赖于关键帧进行解码。-y index.m3u8
: 将输出保存为名为"index.m3u8"的文件。这是HLS流的主索引文件,包含了指向各个分段文件的链接。
通过执行这个命令,FFmpeg将会对输入的视频文件进行转码和分段处理,并生成一个HLS流的主索引文件(index.m3u8)和一系列分段文件(.ts文件),用于实现视频的流式传输和播放。
在切片过程中,CPU 利用率飙升,这属于正常现象
切片后,可以在目录下看到下面的文件,ffmpeg 将源视频文件切成了 23 个子文件和一个 index.m3u8 文件
上面先有个基本的概念,下面开始我们的主题:HLS
一、HLS 协议简介
HLS 全称为 HTTP Live Streaming,是苹果公司提出的基于 HTTP 的流媒体网络传输协议。它的工作原理是把整个媒体流分成一个个小的基于 HTTP 的媒体分片来下载,每次只下载一些分片。在开始一个流媒体会话时,客户端会下载一个包含媒体分片的索引文件,即 extended M3U playlist 文件(m3u8),用于寻找可用的媒体分片。
HLS 中,索引文件可以嵌套,一般只有一级索引和二级索引; 媒体流封分片装格式只支持 MPEG-2 传输流(ts)、WebVTT[WebVTT]文件或 Packed Audio 文件。
下图为索引文件(m3u8)和媒体分片(ts)之间的关系图:一级 m3u8 套二级 m3u8,二级 m3u8 描述 ts 分片。
二、HLS 总体框架
先看下图:
- 服务器将媒体文件转换为 m3u8 及 ts 分片; 对于直播源,服务器需要实时动态更新。
- 客户端请求 m3u8 文件,根据索引获取 ts 分片;点播与直播服务器不同的地方是,直播的 m3u8 文件会不断更新, 而点播的 m3u8 文件是不会变的,只需要客户端在开始时请求一次即可。
客户端与服务器通过 HTTP 协议进行交互,以两级 m3u8 嵌套为例,客户端先 GET 请求到一级 m3u8,一级 m3u8 里面包含了服务器端可以用于传播的一个或多个不同带宽的 URL,这 URL 可以获取到二级 m3u8;二级 m3u8 包含了多个 ts 分片的 duration 及其 URL, 最后通过这个 URL 下载 ts 分片。
交互的方式如下:
三、HLS 优势及劣势
优势:
- 客户端支持简单,只需要支持 HTTP 请求即可,HTTP 协议无状态,只需要按顺序下载媒体片段即可。
- 使用 HTTP 协议网络兼容性好,HTTP 数据包也可以方便地通过防火墙或者代理服务器。
- 当媒体流正在播放时,客户端可以选择从许多不同的备用源中以不同的速率下载同样的资源(多码流自适应) ,允许流媒体会话适应不同的数据速率。
劣势:
- 因其自身的实现方式, HLS 存在延迟(最少有一个分片),对于直播等实时敏感的场景,体验不好。
四、HLS 主要的应用场景
- 跨平台:PC 主要的直播方案是 RTMP,也有一些库能播放 HLS,譬如 jwplayer,基于 osmf 的 hls 插件也一大堆。所以实际上如果选一种协议能跨 PC/Android/IOS,那就是 HLS。
- IOS 上苛刻的稳定性要求:IOS 上最稳定的当然是 HLS, 稳定性不差于 RTMP 在 PC-flash
上的表现。 - 友好的 CDN 分发方式:目前 CDN 对于 RTMP 也是基本协议,但是 HLS 分发的基础是 HTTP,所以 CDN 的接入和分发会比 RTMP 更加完善。能在各种 CDN 之间切换,RTMP 也能,只是可能需要对接测试。
- 简单:HLS 作为流媒体协议非常简单,apple 支持得也很完善。Android 对 HLS 的支持也
会越来越完善。至于 DASH/HDS,好像没有什么特别的理由,就像 linux 已经大行其道而且开放,其他的系统很难再广泛应用。
总之,SRS 支持 HLS 主要是作为输出的分发协议,直播以 RTMP+HLS 分发,满总各种应用场景。点播以 HLS 为主。
五、M3U8 详解
HLS 协议很大一部分内容即是对 M3U8 文本协议的描述。
1、简介
M3U8 即播放索引文件,也称为 Playlist,是由多个独立行组成的文本文件,必须通过 URI(.m3u8 或 .m3u)或者 HTTP Content-Type 来识别(application/vnd.apple.mpegurl 或 audio/mpegurl)。
每行由用 \n 或者 \r\n 来标识换行。每一行可以是一个 URI、空白行或是一个 以 # 号开头的字符串。
以 # 开头的是 tag 或者注释,以 #EXT 开头的是 tag, 其余的为注释, 在解析时应该忽略。URI 表示一个 ts 分片地址或是 Playlist 地址。 URI 可以用绝对地址或者相对地址,如果使用相对地址,那么是相对于当前 Playlist 的地址。有些 tag 带有属性值,多个属性用逗号分隔。
常见的 m3u8 文件如下所示:
2、一级 m3u8
#EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=700,000 http://xxx.itv.cmvideo.cn/low.m3u8?channel-id=bstvod&Contentid=4007432528 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1300,000 http://xxx.itv.cmvideo.cn/mid.m3u8?channel-id=bstvod&Contentid=4007432527 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2300,000 http://xxx.itv.cmvideo.cn/high.m3u8?channel-id=bstvod&Contentid=4007432526
bandwidth 指 定 视 频 流 的 比 特 率 , PROGRAM-ID 无 用 无 需 关 注 , 每 一 个 #EXT-X-STREAM-INF 的下一行是二级 index 文件的路径, 可以用相对路径也可以用绝对路径。
例子中用的是相对路径。这个文件中记录了不同比特率视频流的二级 index 文件路径,客户端可以自己判断自己的现行网络带宽,来决定播放哪一个视频流。
也可以在网络带宽变化的时候平滑切换到和带宽匹配的视频流。
3、二级 m3u8
#EXTM3U #EXT-X-VERSION:1 #EXT-X-TARGETDURATION:11 #EXT-X-MEDIA-SEQUENCE:19674922 #EXT-X-PROGRAM-DATE-TIME:2019-03-28T04:33:40Z #EXTINF:10, 19674922.ts? #EXT-X-PROGRAM-DATE-TIME:2019-03-28T04:33:50Z #EXTINF:10, 19674923.ts? #EXT-X-PROGRAM-DATE-TIME:2019-03-28T04:34:00Z #EXTINF:10, 19674924.ts?
4、tag 说明
①、名词说明
- Media Playlist:二级 m3u8,携带 ts 分片 url 的 m3u8;
- Master Playlist:一级 m3u8;
- Media Segment:ts 分片;
- Attribute Lists:属性列表
Attribute Lists :
- 有的 tags 的值带有 Attribute Lists。
- 一个 Attribute List 是一个用逗号分隔的 attribute/value 对列表
- 格式为:AttributeName=AttributeValue。
②、tag 分类
tag 以 #EXT 开头,主要分为以下几类:
1)Basic Tags
Basic Tags 可以用在 Media Playlist 和 Master Playlist 里面。
- EXTM3U:必须在文件的第一行,标识是一个 Extended M3U Playlist 文件。
- EXT-X-VERSION:表示 Playlist 兼容的版本。
2)Media Segment Tags
每一个 Media Segment 通过一系列的 Media Segment tags 跟一个 URI 来指定。有的 Media Segment tags 只应用于下一个 segment,有的则是应用所有下面的 segments。一个 Media Segment tag 只能出现在 Media Playlist 里面。
- EXTINF:用于指定 Media Segment 的 duration。
- EXT-X-BYTERANGE:用于指定 URI 的 sub-range。
- EXT-X-DISCONTINUITY:表示后续分片属性发生变化,如文件格式/编码/序号。
- EXT-X-KEY:表示 Media Segment 已加密,该值用于解密。
- EXT-X-MAP:表示 Media Segment 的头部信息,比如 PAT/PMT 或者 WebVTT 头。
- EXT-X-PROGRAM-DATE-TIME:和 Media Segment 的第一个 sample 一起来确定时间戳。
3)Media Playlist Tags
Media Playlist tags 描述 Media Playlist 的全局参数。同样地,Media Playlist tags 只能出现在 Media Playlist 里面。
- EXT-X-TARGETDURATION:用于指定最大的 Media Segment duration。
- EXT-X-MEDIA-SEQUENCE:用于指定第一个 Media Segment 的序号。
- EXT-X-DISCONTINUITY-SEQUENCE:用于不同 Variant Stream 之间同步。
- EXT-X-ENDLIST:表示 Media Playlist 结束。
- EXT-X-PLAYLIST-TYPE:可选,指定整个 Playlist 的类型。
- EXT-X-I-FRAMES-ONLY:表示每个 Media Segment 均为 I-frame。
4)Master Playlist Tags
Master Playlist tags 定义 Variant Streams,Renditions 和其他显示的全局参数。Master Playlist tags 只能出现在 Master Playlist 中。
- EXT-X-MEDIA:用于关联同一个内容的多个 Media Playlist 的多种翻译。
- EXT-X-STREAM-INF:用于指定下级 Media Playlist 相关属性。
- EXT-X-I-FRAME-STREAM-INF:与 EXT-X-STREAM-INF 类似,但指向的下级 Media Playlist 包含 Media Segment 均为 I-frame。
- EXT-X-SESSION-DATA:可以随意存放一些 session 数据。
5)Media or Master Playlist Tags
这里的 tags 可以出现在 Media Playlist 或者 Master Playlist 中。但是如果同时出现在同一个 Master Playlist 和 Media Playlist 中时,必须为相同值。
- EXT-X-INDEPENDENT-SEGMENTS:表示每个 Media Segment 可以独立解码。
- EXT-X-START:标识一个优选的点来播放这个 Playlist。
六、HLS 协议详解
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>
PC:video.js
1、HLS 协议规定
视频的封装格式是 TS。
视频的编码格式为 H264,音频编码格式为 MP3、 AAC 或者 AC-3。
除了 TS 视频文件本身, 还定义了用来控制播放的 m3u8 文件(文本文件) 。
2、HLS 协议说明
HLS 的 m3u8,是一个 ts 的列表,也就是告诉客户端或浏览器可以播放这些 ts 文件, 譬如:
#EXTM3U #EXT-X-VERSION:3 #EXT-X-ALLOW-CACHE:YES #EXT-X-TARGETDURATION:13 #EXT-X-MEDIA-SEQUENCE:430 #EXT-X-PLAYLIST-TYPE:VOD #EXTINF:11.800 news-430.ts #EXTINF:10.120 news-431.ts #EXT-X-DISCONTINUITY #EXTINF:11.952 news-430.ts #EXTINF:12.640 news-431.ts #EXTINF:11.160 news-432.ts #EXT-X-DISCONTINUITY #EXTINF:11.751 news-430.ts #EXTINF:2.040 news-431.ts #EXT-X-ENDLIST
- EXTM3U
- 每个 M3U 文件第一行必须是这个 tag,提供标示作用
- EXT-X-VERSION
- 用以标示协议版本。这里是 3,那么这里用的就是 HLS 协议第三个版本,此标签只能有 0 或 1 个, 不写代表使用版本 1
- EXT-X-TARGETDURATION
- 所有切片的最大时长,有些 Apple 设备这个参数不正确会无法播放。
- EXT-X-MEDIA-SEQUENCE
- 切片的开始序号。每一个切片都有唯一的序号,相邻之间序号+1。这个编号会继续增长,保证流的连续性。
- EXTINF
- ts 切片的实际时长。duration:媒体持续时间
- #EXTINF <duration>,<title>
- EXT-X-PLAYLIST-TYPE
- 类型,vod 表示点播,live 表示直播。
- EXT-X-ENDLIST
- 文件结束符号,表示不再向播放列表文件添加媒体文件。
#EXT-X-PLAYLIST-TYPE:VOD 的意思是当前的视频流并不是一个直播流,而是点播流,换句话说就是该视频的全部的 ts 文件已经被生成好了,#EXT-X-ENDLIST 这个表示视频结束,有这个标志同时也说明当前的流是一个非直播流。
3、播放模式
点播 VOD 的特点就是当前时间点可以获取到所有 index 文件和 ts 文件,二级 index 文件中记录了所有 ts 文件的地址。这种模式允许客户端访问全部内容。上面的例子中就是一个点播模式下的 m3u8 的结构。
Live 模式就是实时生成 M3u8 和 ts 文件。它的索引文件一直处于动态变化的,播放的时候需要不断下载二级 index 文件,以获得最新生成的 ts 文件播放视频。如果一个二级 index文件的末尾没有#EXT-X-ENDLIST 标志, 说明它是一个 Live 视频流。
- 客户端在播放 VOD 模式的视频时其实只需要下载一次一级 index 文件和二级 index 文件就可以得到所有 ts 文件的下载地址,除非客户端进行比特率切换,否则无需再下载任何 index文件,只需顺序下载 ts 文件并播放就可以了。
- 但是 Live 模式下略有不同,因为播放的同时,新 ts 文件也在被生成中,所以客户端实际上是下载一次二级 index 文件,然后下载 ts 文件,再下载二级 index 文件(这个时候这个二级 index 文件已经被重写,记录了新生成的 ts 文件的下载地址),再下载新 ts 文件,如此反复进行播放。
4、TS 文件
ts 文件为传输流文件(MPEG2 - tranport stream),视频编码主要格式 h264/mpeg4,音频为 acc/MP3。
ts 文件分为三层: ts 层(Transport Stream)、 pes 层(Packet Elemental Stream)、 es 层(Elementary Stream)
es 层就是音视频压缩数据,pes 层是在音视频数据 es 上加了时间戳(pts,dts)等对数据帧的说明信息,ts 层就是在 pes 层加入数据流的识别和传输必须的信息。
- ts 层
- ts 包大小固定为 188 字节,ts 层分为三个部分:ts header、adaptation field、payload。ts header 固定 4 个字节;adaptation field 可能存在也可能不存在,主要作用是给不足 188 字节的数据做填充;payload 是 pes 数据。
- pes 层
- pes 层是在每一个视频/音频帧上加入了时间戳等信息,pes 包内容很多,我们只留下最常用的。
- es 层
- es 层指的就是音视频数据, 我们只介绍 h.264 视频。
h.264 视频:打包 h.264 数据我们必须给视频数据加上一个 nalu(Network Abstraction Layer unit),nalu 包括 nalu header 和 nalu type,nalu header 固定为 0x00000001(帧开始)或 0x000001(帧中)。
h.264 的数据是由 slice 组成的, slice 的内容包括:视频、sps、pps 等。
nalu type 决定了后面的 h.264 数据内容。