HLS协议及TS封装
一、HLS协议
HLS协议由苹果公司提出并推广,来自维基百科的定义。
HTTP Live Streaming(缩写是HLS)是一个由苹果公司提出的基于HTTP的流媒体网络传输协议。是苹果公司QuickTime
X和iPhone软件系统的一部分。它的工作原理是把整个流分成一个个小的基于HTTP的文件来下载,每次只下载一些。当媒体流正在播放时,客户端可以选择从许多不同的备用源中以不同的速率下载同样的资源,允许流媒体会话适应不同的数据速率。在开始一个流媒体会话时,客户端会下载一个包含元数据的extended
M3U (m3u8)playlist文件,用于寻找可用的媒体流。
HLS只请求基本的HTTP报文,与实时传输协议(RTP)不同,HLS可以穿过任何允许HTTP数据通过的防火墙或者代理服务器。它也很容易使用内容分发网络来传输媒体流。
苹果公司把HLS协议作为一个互联网草案(逐步提交),在第一阶段中已作为一个非正式的标准提交到IETF。但是,即使苹果偶尔地提交一些小的更新,IETF却没有关于制定此标准的有关进一步的动作。[1]
1.1.协议简介
Apple推出的直播协议,是通过视频流切片成文件片段来直播的。客户端首先会请求一个m3u8文件,里面会有不同码率的流,或者直接是ts文件列表,通过给出的ts文件地址去依次播放。在直播的时候,客户端会不断请求m3u8文件,检查ts列表是否有新的ts切片。
HLS协议规定:
- 视频的封装格式是TS。
- 视频的编码格式为H264,音频编码格式为MP3、AAC或者AC-3。
- 除了TS视频文件本身,还定义了用来控制播放的m3u8文件(文本文件)。
获取音视频进行HLS打包,H264+AAC的流媒体切片,提供给WEB服务器进行HLS流媒体发布,切片后:一个M3U8文件 和 多个.ts文件,M3U8是一种可扩展的播放列表文件格式。它是一个包含UTF-8编码文字的m3u播放列表。m3u是包含媒体文件URL的一个事实上的播放列表标准,编码还是h264。这种格式被用来作为HTTP Live 媒体流索引文件的格式。M3u8是一种视频列表格式,里面有真正的视频链接,在其中可以再嵌套一层m3u8。
实现HLS直播:
- 采集视频源和音频源数据
- 对原始数据进行H264编码和AAC编码
- 视频和音频数据封装为MPEG-TS包
- HLS分段生成策略及m3u8索引文件
- HTTP传输协议
1.2.m3u8文件协议
m3u8文件结构:
#EXTM3U //m3u文件头,必须放在第一行
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:2 //第一个TS分片的序列号
#EXT-X-TARGETDURATION:5 //每个分片TS的最大的时长
#EXT-X-ALLOW-CACHE: //是否允许cache
#EXT-X-ENDLIST //m3u8文件结束符 表示视频已经结束 有这个标志同时也说明当前流是一个非直播流
#EXT-X-PLAYLIST-TYPE:VOD/Live //VOD表示当前视频流不是一个直播流,而是点播流(也就是视频的全部ts文件已经生成)
#EXTINF: //extra info,分片TS的信息,如时长,带宽等
一级文件:index.m3u8
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1064000
1000kbps.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=564000
500kbps.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=282000
250kbps.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2128000
2000kbps.m3u8
二级文件:2000kbps.m3u8
#EXTM3U
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:5
2000kbps-0002.ts
#EXTINF:4.089
2000kbps-0003.ts
#EXTINF:4.989
2000kbps-0004.ts
...
#EXTINF:4.979
2000kbps-0100.ts
#ZEN-TOTAL-DURATION:491.66667
#ZEN-AVERAGE-BANDWIDTH:
#ZEN-MAXIMUM-BANDWIDTH:
#EXT-X-ENDLIST
1.3.服务端与客户端逻辑
服务端逻辑:
- 1、将媒体源切片成 Media Segment, 应该优先从可以高效解码的时间点来进行切片(如: I-frame).
- 2、为每一个 Media Segment 生成 URI.
- 3、Server 需要支持 “gzip” 方式压缩文本内容.
- 4、创建一个 Media Playlist 索引文件, EXT-X-VERSION 不要高于他需要的版本, 来提供更好的兼容性.
- 5、Server 不能随便修改 Media Playlist, 除了 Append 文本到文件末尾, 按顺序移除 Media Segment URIs, 增长 EXT-X-MEDIA-SEQUENCE 和 EXT-X-DISCONTINUITY-SEQUENCE, 添加 EXT-X-ENDLIST 到文件尾.
- 6、在最后添加 EXT-X-ENDLIST tag, 来减少 Client reload Playlist 的次数.
- 7、注意点播与直播服务器不同的地方是, 直播的 m3u8 文件会不断更新, 而点播的 m3u8 文件是不会变的, 只需要客户端在开始时请求一次即可.
客户端逻辑:
- 1、客户端通过 URI 获取 Playlist. 如果是 Master Playlist, 客户端可以选择一个 Variant Stream 来播放.
- 2、客户端检查 EXT-X-VERSION 版本是否满足.
- 3、客户端应该忽略不可识别的 tags, 忽略不可识别的属性键值对.
- 4、加载 Media Playlist file.
- 5、 播放 Media Playlist file.
- 6、重加载 Media Playlist file.
- 7、决定下一次要加载的 Media Segment.
优点:
- 客户端支持简单, 只需要支持 HTTP 请求即可, HTTP 协议无状态, 只需要按顺序下载媒体片段即可.
- 使用 HTTP 协议网络兼容性好, HTTP 数据包也可以方便地通过防火墙或者代理服务器, CDN 支持良好.
- Apple 的全系列产品支持, 由于 HLS 是苹果提出的, 所以在 Apple 的全系列产品包括 iphone, ipad, safari 都不需要安装任何插件就可以原生支持播放 HLS, 现在, Android 也加入了对 HLS 的支持.
- 自带多码率自适应, Apple 在提出 HLS 时, 就已经考虑了码流自适应的问题.
缺点:
- 相比 RTMP 这类长连接协议, 延时较高, 难以用到互动直播场景.
- 对于点播服务来说, 由于 TS 切片通常较小, 海量碎片在文件分发, 一致性缓存, 存储等方面都有较大挑战.
1.4.应用
HLS目前广泛应用于点播和直播领域。
如果是直播,客户端会不停的去请求这个m3u8文件,当这个列表有新的ts文件,客户端会请求新的ts文件追加到本地播放序列。
在HTML5页面上使用HLS非常简单
<video src="index.m3u8" controls></video>
或
<video controls>
<source src="index.m3u8"></source>
</video>
二、TS封包
2.1概念
ts流最早应用于数字电视领域,其格式非常复杂包含的配置信息表多达十几个,视频格式主要是mpeg2。苹果公司发明的http live stream流媒体是基于ts文件的,不过他大大简化了传统的ts流,只需要2个最基本的配置表PAT和PMT,再加上音视频内容就可以了,hls流媒体视频编码的主要格式为h264/mpeg4,音频为aac/mp3。
关于ts的封包,ts的封装格式要比flv更复杂,主要的数据单元是ts包,每个包有pid,一个包固定大小普通没有crc的为188,主要分为三类ts包,pat,pmt,pes,pat就是第一个包,当解析的时候会在ts包列表里找pid为0x0的包,就是pat包,pat大概作用就是入口的意思,pat里面有pmt包的pid,pmt里面存储的是流的包的pid,比如指定音频包pid是0x102,视频包pid是0x101,后面的0x102和0x101的包就是pes包了,将pes包解析并合并出原始流,就能解码播放了。具体封包格式 详解参考一,参考二
ES(Elementary Stream)流是基本码流,包含音频、视频、数据的连续码流。直接从编码器出来的数据流,可以是编码过的视频数据流(H.264,MJPEG等),音频数据流(AAC),或其他编码数据流的统称。ES流经过PES打包器之后,被转换成PES包。
PES(Packet Elementary Stream 分组的ES)ES形成的分组称为PES分组,是用来传递ES的一种数据结构。PES流是ES流经过PES打包器处理后形成的数据流,在这个过程中完成了将ES流分组、打包、加入包头信息等操作(对ES流的第一次打包)。PES流的基本单位是PES包。PES包由包头和payload组成。
TS(Transport Stream)流,也叫传输流。是在pes层上加入了数据流识别和传输的必要信息。是由固定长度的188字节的包组成。含有独立是一个或者多个program,一个program又可以包含多个视频,音频和文字信息的ES流。每个ES流会有不同的PID标示。为了分析这些ES流,TS有些固定的PID来间隔发送Program和ES信息表格:PAT表和PMT表。
2.2.TS文件
ts文件分为三次:ts层(Transport Stream)、pes层(Packet Elemental Stream)、es层(Elementary Stream)。es层就是音视频数据,pes层是在音视频数据上加了时间戳等数据帧的说明信息,ts层是在pes层上加入了数据流识别和传输的必要信息。
TS流:
+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TS | = | Packet 1 | Packet 2 | Packet 3 | ... | Packet n-1| Packet n |
+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
一个Packet: 4bytes 184bytes
+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Packet | = | Packet header | Packet data |
+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.2.1.ts层
ts包(Packet)大小固定为188字节,ts层分为三个部分:ts header、adaptation field、payload。ts header固定4个字节;adaptation field可能存在也可能不存在,主要作用是给不足188字节的数据做填充;payload是pes数据。
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ts header | adaptation field | payload(pes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.2.1.1.ts header
大小 | 说明 | |
---|---|---|
sync_byte | 8bit | 同步字节,固定为0x47 |
transport_error_indicator | 1bit | 传输错误指示符,表明在ts头的adapt域后由一个无用字节,通常都为0,这个字节算在adapt域长度内 |
payload_unit_start_indicator | 1bit | 负载单元起始标示符,一个完整的数据包开始时标记为1 |
transport_priority | 1bit | 传输优先级,0为低优先级,1为高优先级,通常取0 |
pid | 13bit | pid值 |
transport_scrambling_control | 2bit | 传输加扰控制,00表示未加密 |
adaptation_field_control | 2bit | 是否包含自适应区,‘00’保留;‘01’为无自适应域,仅含有效负载;‘10’为仅含自适应域,无有效负载;‘11’为同时带有自适应域和有效负载。 |
continuity_counter | 4bit | 递增计数器,从0-f,起始值不一定取0,但必须是连续的 |
ts层的内容是通过PID值来标识的,主要内容包括:PAT表、PMT表、音频流、视频流。解析ts流要先找到PAT表,只要找到PAT就可以找到PMT,然后就可以找到音视频流了。PAT表的PID值固定为0。PAT表和PMT表需要定期插入ts流,因为用户随时可能加入ts流,这个间隔比较小,通常每隔几个视频帧就要加入PAT和PMT。PAT和PMT表是必须的,还可以加入其它表如SDT(业务描述表)等,不过hls流只要有PAT和PMT就可以播放了。
- PAT表:他主要的作用就是指明了PMT表的PID值。
- PMT表:他主要的作用就是指明了音视频流的PID值。
- 音频流/视频流:承载音视频内容。
2.2.1.2.adaptation field
大小 | 说明 | |
---|---|---|
adaptation_field_length | 1Byte | 自适应域长度,后面的字节数 |
flag | 1Byte | 取0x50表示包含PCR或0x40表示不包含PCR |
pcr | 5Byte | Program Clock Reference,节目时钟参考,用于恢复出与编码端一致的系统时序时钟STC(System Time Clock)。 |
stuffing_bytes | xByte | 填充字节,取值0xff |
自适应区的长度要包含传输错误指示符标识的一个字节。pcr是节目时钟参考,pcr、dts、pts都是对同一个系统时钟的采样值,pcr是递增的,因此可以将其设置为dts值,音频数据不需要pcr。如果没有字段,ipad是可以播放的,但vlc无法播放。打包ts流时PAT和PMT表是没有adaptation field的,不够的长度直接补0xff即可。视频流和音频流都需要加adaptation field,通常加在一个帧的第一个ts包和最后一个ts包里,中间的ts包不加。
PAT/PMT类型包(Packet)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ts header | PAT/PMT | Stuffing Bytss |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
视频/音频类型包(Packet),一帧视频/音频数据被拆分成N个Packet
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ts header | adaptation field | payload(pes 1) |-->第1个Packet
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ts header | payload(pes 2) |-->第2个Packet
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ts header | ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ts header | payload(pes n-1) |-->第n-1个Packet
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ts header | adaptation field | payload(pes n) |-->第n个Packet
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.2.1.3.PAT(Program Associate Table)格式 节目关联表
大小 | 说明 | |
---|---|---|
table_id | 8bit | PAT表固定为0x00 |
section_syntax_indicator | 1bit | 固定为二进制1 |
zero | 1bit | 固定为二进制0 |
reserved | 2bit | 固定为二进制11(3) |
section_length | 12bit | 后面数据的长度 |
transport_stream_id | 16bit | 传输流ID,固定为0x0001 |
reserved | 2bit | 固定为二进制11(3) |
version_number | 5bit | 版本号,固定为二进制00000,如果PAT有变化则版本号加1 |
current_next_indicator | 1bit | 固定为二进制1,表示这个PAT表可以用,如果为0则要等待下一个PAT表 |
section_number | 8bit | 固定为0x00 |
last_section_number | 8bit | 固定为0x00 |
开始循环 | ||
program_number | 16bit | 节目号为0x0000时表示这是NIT,节目号为0x0001时,表示这是PMT |
reserved | 3bit | 固定为二进制111(7) |
PID | 13bit | 节目号对应内容的PID值 |
结束循环 | ||
CRC32 | 32bit | 前面数据的CRC32校验码 |
2.2.1.4.PMT(Program Map Table)格式 节目映射表
大小 | 说明 | |
---|---|---|
table_id | 8bit | PMT表取值随意,0x02 |
section_syntax_indicator | 1bit | 固定为二进制1 |
zero | 1bit | 固定为二进制0 |
reserved | 2bit | 固定为二进制11(3) |
section_length | 12bit | 后面数据的长度 |
program_number | 16bit | 频道号码,表示当前的PMT关联到的频道,取值0x0001 |
reserved | 2bit | 固定为二进制11(3) |
version_number | 5bit | 版本号,固定为00000,如果PAT有变化则版本号加1 |
current_next_indicator | 1bit | 固定为二进制1 |
section_number | 8bit | 固定为0x00 |
last_section_number | 8bit | 固定为0x00 |
reserved | 3bit | 固定为二进制111(7) |
PCR_PID | 13bit | PCR(节目参考时钟)所在TS分组的PID,指定为视频PID |
reserved | 4bit | 固定为二进制1111(15) |
program_info_length | 12bit | 节目描述信息,指定为0x000表示没有 |
开始循环 | ||
stream_type | 8bit | 流类型,标志是Video还是Audio还是其他数据,h.264编码对应0x1b,aac编码对应0x0f,mp3编码对应0x03 |
reserved | 3bit | 固定为二进制111(7) |
elementary_PID | 13bit | 与stream_type对应的PID |
reserved | 4bit | 固定为二进制1111(15) |
ES_info_length | 12bit | 描述信息,指定为0x000表示没有 |
结束循环 | ||
CRC32 | 32bit | 前面数据的CRC32校验码 |
2.2.2.pes层
pes层(ts层中payload)是在每一个视频/音频帧上加入了时间戳等信息,pes包内容很多,我们只留下最常用的。
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| pes header| optional pes header | pes payload |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
6Byte 3~259Byte max 65526Byte
大小 | 说明 | |
---|---|---|
pes start code | 3Byte | 开始码,固定为0x000001 |
stream id | 1Byte | 音频取值(0xc0-0xdf),通常为0xc0 视频取值(0xe0-0xef),通常为0xe0 |
pes packet length | 2Byte | 后面pes数据的长度,0表示长度不限制,只有视频数据长度会超过0xffff |
flag | 1Byte | 通常取值0x80,表示数据不加密、无优先级、备份的数据 |
flag | 1Byte | 取值0x80表示只含有pts,取值0xc0表示含有pts和dts |
pes data length | 1Byte | 后面数据的长度,取值5或10 |
pts | 5Byte | 33bit值 |
dts | 5Byte | 33bit值 |
pts是显示时间戳、dts是解码时间戳,视频数据两种时间戳都需要,音频数据的pts和dts相同,所以只需要pts。有pts和dts两种时间戳是B帧引起的,I帧和P帧的pts等于dts。如果一个视频没有B帧,则pts永远和dts相同。从文件中顺序读取视频帧,取出的帧顺序和dts顺序相同。dts算法比较简单,初始值 + 增量即可,pts计算比较复杂,需要在dts的基础上加偏移量。
音频的pes中只有pts(同dts),视频的I、P帧两种时间戳都要有,视频B帧只要pts(同dts)。打包pts和dts就需要知道视频帧类型,但是通过容器格式我们是无法判断帧类型的,必须解析h.264内容才可以获取帧类型。
举例说明:
------------------------------>
I P B B B P
1 2 3 4 5 6 读取顺序
1 2 3 4 5 6 dts顺序
1 5 3 2 4 6 pts顺序
点播视频dts算法:
dts = 初始值 + 90000 / video_frame_rate,初始值可以随便指定,但是最好不要取0,video_frame_rate就是帧率,比如23、30。
pts和dts是以timescale为单位的,1s = 90000 time scale , 一帧就应该是90000/video_frame_rate 个timescale。
用一帧的timescale除以采样频率就可以转换为一帧的播放时长
点播音频dts算法:
dts = 初始值 + (90000 * audio_samples_per_frame) / audio_sample_rate,audio_samples_per_frame这个值与编解码相关,aac取值1024,mp3取值1158,audio_sample_rate是采样率,比如24000、41000。AAC一帧解码出来是每声道1024个sample,也就是说一帧的时长为1024/sample_rate秒。所以每一帧时间戳依次0,1024/sample_rate,...,1024*n/sample_rate秒。
直播视频的dts和pts应该直接用直播数据流中的时间,不应该按公式计算。
2.2.3.es层
es层(pes payload)指的就是音视频数据。
video:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| start code(4 byte)| nalu header(1 byte) | h264 data(x byte) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
audio:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| adts header(7 byte) | aac data(x byte) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.2.3.1.h.264视频
打包h.264数据我们必须给视频数据加上一个nalu(Network Abstraction Layer unit),nalu包括start code和nalu header,start code固定为0x00000001(帧开始)或0x000001(帧中)。h.264的数据是由slice组成的,slice的内容包括:视频、sps、pps等。nalu type决定了后面的h.264数据内容。
nalu header:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+-+-+-+-+-+-+-+-+
F: 占1bit,forbidden_zero_bit,h.264规定必须取0,禁止位,当网络发现NAL单元有比特错误时可设置该比特为1,以便接收方纠错或丢掉该单元。
NRI: 占2bit,nal_ref_idc,取值0~3,指示这个nalu的重要性,I帧、sps、pps通常取3,P帧通常取2,B帧通常取0,nal重要性指示,标志该NAL单元的重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU。
Type:占5bit, nal_unit_type:0=未使用 1=非IDR图像片,IDR指关键帧
2=片分区A 3=片分区B
4=片分区C 5=IDR图像片,即关键帧
6=补充增强信息单元(SEI) 7=SPS序列参数集
8=PPS图像参数集 9=分解符
10=序列结束 11=码流结束
12=填充
13~23=保留 24~31=未使用
打包es层数据时pes头和es数据之间要加入一个type=9的nalu,关键帧slice前必须要加入type=7和type=8的nalu,而且是紧邻
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| pes header| nalu(0x09)| 1byte | nalu | | nalu(0x67)| | nalu(0x68)| | nalu(0x65)| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
随意 其他 内容 SPS 内容 PPS 内容 I帧 内容
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| pes header| nalu(0x09)| 1byte | nalu | | nalu(0x41)| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
随意 其他 内容 P帧 内容
2.2.3.1.aac音频
打包aac音频必须加上一个adts(Audio Data Transport Stream)头,共7Byte,adts包括fixed_header和variable_header两部分,各28bit。
fixed_header
大小 | 说明 | |
---|---|---|
syncword | 12bit | 固定为0xfff |
id | 1bit | 0表示MPEG-4,1表示MPEG-2 |
layer | 2bit | 固定为00 |
protection_absent | 1bit | 固定为1 |
profile | 2bit | 取值0~3,1表示aac |
sampling_frequency_index | 4bit | 表示采样率,0: 96000 Hz,1: 88200 Hz,2: 64000 Hz,3:48000 Hz,4: 44100 Hz,5: 32000 Hz,6: 24000 Hz,7: 22050 Hz,8: 16000 Hz,9: 12000 Hz,10: 11025 Hz,11: 8000 Hz,12: 7350 Hz |
private_bit | 1bit | 固定为0 |
channel_configuration | 3bit | 取值0~7,1: 1 channel: front-center,2: 2 channels: front-left, front-right,3: 3 channels: front-center, front-left, front-right,4: 4 channels: front-center, front-left, front-right, back-center |
original_copy | 1bit | 固定为0 |
home | 1bit | 固定为0 |
variable_header
大小 | 说明 | |
---|---|---|
copyright_identification_bit | 1bit | 固定为0 |
copyright_identification_start | 1bit | 固定为0 |
aac_frame_length | 13bit | 包括adts头在内的音频数据总长度 |
adts_buffer_fullness | 11bit | 固定为0x7ff |
number_of_raw_data_blocks_in_frame | 2bit | 固定为00 |
2.2.4.ts打包流程图
一个PAT包含整个TS流的信息,其中里面有一张表,比较重要的两个属性 program_number和program_map_PID,可能出现多对,
每一对program_number表示一个节目,而与该program_number对应的program_map_PID则表示该节目对应的流信息应该放在一个PMT表中,
而该PMT表的PID应该与这里的program_map_PID相等。
一个PMT中描述了流的类型,其中0x0f表示AAC音频,而0x1b表示H264视频,除这两种之外还有其他流,例如字幕。
elementay_PID表示该流的数据应该存放在以该PID为表示的TS包中。
+-+-+-+-+-+-+-+-+-+-+-+
| PAT |
| |
| program_number 5 |___
| program_map_PID 10 | |
| | |
| program_number 6 |___|__
| program_map_PID 11 | | |
| | | |
| program_number 7 | | |
| program_map_PID 12 | | |
| | | |
| ... | | |
| | | |
+-+-+-+-+-+-+-+-+-+-+-+ | |
| |
+-+-+-+-+-+-+-+-+-+-+-+ | |
| PMT | | |
| TS Header PID = 10 |<—— |
| | |
| stream_type 0x0f |______|__________________0x0f表示AAC音频,下方AAC数据打包PID=20,
| elementary_PID 20 | |
| stream_type 0x1b |______|__________________0x1b表示H264视频,下方H264数据打包PID=22
| elementary_PID 22 | |
| | |
+-+-+-+-+-+-+-+-+-+-+-+ |
|
+-+-+-+-+-+-+-+-+-+-+-+ |
| PMT | |
| TS Header PID = 6 |<—————
| |
| stream_type 0x0f |
| elementary_PID 23 |
| stream_type 0x1b |
| elementary_PID 24 |
| |
+-+-+-+-+-+-+-+-+-+-+-+
裸ACC数据:
+-+-+-+-+-+-+-+-+-+-+-+
| AAC |
+-+-+-+-+-+-+-+-+-+-+-+
添加PES头的ACC数据:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| AAC PES | AAC |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
添加TS头将PES分割之后的TS包,假设正好分割成2个TS包,包大小固定188字节,不够用adaptation域填充一般填充0xFF:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|TS | AAC PES | AAC 1 |TS | adaptation| AAC 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|- - - - Packet 1 - - - |- - - - - Packet 2 - - - - - |
<假设 PID = 20 的TS包>
裸H264数据,一帧:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| H264 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
添加PES头之后的H264数据,一帧表示一个PES包:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| H264 PES| H264 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
将一个PES包分割之后,分别添加TS头之后的TS包,这里假设分割成3个TS包,每个包固定大小188字节(包含TS包头在内),
最后一个包不够188字节使用adaptaion域填充0xFF:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|TS | H264 PES| H264 1|TS | H264 2 |TS | adaptation| H264 3 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|- - - Packet 1 - - - |- - Packet 2 - - | - - - - Packet 3 - - - -|
<假设 PID = 22 的TS包>
参考
苹果官方文档
HLS标准协议
维基百科
https://blog.csdn.net/yuan1125/article/details/51540918
https://blog.csdn.net/max_min_go/article/details/39463675
网友评论