做了那么久铺垫,终于要写本项目中最为重要的直播功能了。首先介绍一下项目中用到的一些知识点和协议,下一篇来介绍如何实现直播功能。
一、音视频基础知识
(一)音频相关概念:
- 采样率(SampleRateInHz):采样率指每秒采样点个数(8000/44100Hz),采样率单位用Hz(赫兹)表示
- 声道(Sound Channel):是指声音在录制或播放时在不同空间位置采集或回放的相互独立的音频信号,所以声道数也是声音录制或者播放时的扬声器数量。 常见声道数:单声道、立体声道、4声道、5.1声道、7.1声道等
- 量化精度:量化精度表示可以将模拟信号分成多少个等级,量化精度越高,音乐的声压振幅越接近原始音乐。量化精度的单位是bit(比特),也可以理解为一个采样点用多少比特表示(8/16/24/32 bit)。在Android中,量化精度由AudioFormat.ENCODING_PCM_16BIT、ENCODING_PCM_8BIT等表示。
- 数据量(字节/秒)= (采样频率(Hz) * 采样位数(量化精度,bit) * 声道数)/ 8
(二)视频相关概念
- 帧率(Frame Rate):用于测量显示帧数的量度,测量单位是每秒显示帧数(frames per second,简称fps)。每秒显示帧数(fps)或者帧率表示图形处理器场时每秒能够更新的次数。一般而言,30fps是能够接受的帧率,而60fps则更为逼真、更为流畅,超过75fps后就不容易察觉有明显的流畅度提升。
- 分辨率:指视频成像产品所形成的图像大小或者尺寸。
- 刷新率:刷新率是指屏幕每秒画面被刷新的次数,刷新率分为垂直刷新率和水平刷新率,一般指垂直刷新率。垂直刷新率表示屏幕上图像每秒重绘多少次,也就是每秒屏幕刷新的次数,以Hz为单位。刷新率越高,图像越稳定,图像显示就越清晰。
- 码率:码率也就是比特率,比特率是单位时间播放连续的媒体的比特数量。比特率越高,带宽消耗得越多。 文件大小(b) = 码率(b/s) * 时长(s)
- 视频帧:常见的视频帧有I、P、S帧等
- I帧表示关键帧,可以理解为这一帧画面的完整保留,解码时只需要本帧数据就可以完成。
- P帧表示这一帧和之前的一个关键帧(或者P帧)的差别,解码时需要用之前的画面叠加上本帧定义的差别生成最终画面。
- B帧是双向差别帧,B帧记录的是本帧和前后帧的差别,也就是解码B帧时,不仅需要之前的缓存画面,还需要解码之后的画面,通过前后的画面和B帧进行叠加,得到最后画面。
- 数据量 = 分辨率(width * height) *每个像素所占的字节数。比如YUV420 = 分辨率 * 3 / 2
二、相关协议与库
(一)AAC音频格式
AAC音频格式有两种:ADIF和ADTS
- ADIF:Audio Data Interchange Format,音频数据交换格式,这种格式的特征是可以确定找到这个音频数据的开始,不需要在音频数据流中间开始的解码,即其解码必须在明确的位置开始进行。适用于磁盘文件。
- ADTS:Audio Data Transport Stream,音频数据传输流,这种格式的特征是它有一个同步字的比特流,解码可以在这个流中的任何位置开始。
两者的区别:ADTS可以在任意帧进行解码,每一帧都有头信息。ADIF只有一个统一的头,必须得到所有的数据后才能进行解码。
ADTS是帧序列,本身就具备流特征,在音频流的传输和处理方面更加合适,ADST帧格式如下:


- syncword :同步头 总是0xFFF, all bits must be 1,代表着一个ADTS帧的开始
- ID:MPEG Version: 0 for MPEG-4, 1 for MPEG-2
- Layer:always: '00'
- profile:表示使用哪个级别的AAC,有些芯片只支持AAC LC 。在MPEG-2 AAC中定义了3种:
0:Main profile;
1:Low Complexity profile(LC) ;
2:Scalable Sampling Rate Profile(SSR) - sampling_frequency_index:表示使用的采样率下标,通过这个下标在 Sampling Frequencies[ ]数组中查找得知采样率的值。
0: 96000 Hz
1: 88200 Hz
2: 64000 Hz
3: 48000 Hz
4: 44100 Hz
5: 32000 Hz - channel_configuration: 表示声道数
0: AOT默认设置
1: 1个声道数:前中央
2: 2个声道数:左前、右前
3: 3个声道数:左前、右前、前中央
4: 4个声道数:左前、后前、前中央、后中央
5: 5个声道数:左前、后前、前中央、左后、右后 - frame_length : 一个ADTS帧的长度包括ADTS头和AAC原始流.
- adts_buffer_fullness:0x7FF 说明是码率可变的码流
(二)H264视频编码
1. H264码流文件分为2层:
(1)VCL(Video Coding Layer):视频编码层,负责高效的视频内容表示,VCL数据即编码处理的输出,表示被压缩数据编码后的视频数据序列。 (2)NAL(Network Abstraction Layer):网络提取层,复杂以网络所要求的恰当的方式对数据进行打包和传送,是传输层,不管在本地播放还是在网络播放的传输,都需要这一层来传输。
2. H264编码格式
在VCL数据传输或者存储之前,这些编码的 VCL 数据,先被映射或封装进NAL 单元中。每个 NAL 单元包括一个原始字节序列负荷( RBSP, Raw Byte Sequence Payload)和一组对应于视频编码的 NAL 头信息。RBSP 的基本结构是:在原始编码数据的后面填加了结尾比特。一个 bit“1”若干比特“0”,以便字节对齐。

3. NAL Header
NAL头由一个字节组成,包含禁止位(1bit)、重要性位(2bit)、NALU类型(5bit)
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| TYPE |
+---------------+
位 | 0 | 1-2 | 3-7 |
---|---|---|---|
简称 | F | NRI | TYPE |
全称 | forbidden_zero_bit | nal_ref_idc | nal_unite_type |
中文 | 禁止位 | 重要性指示位 | NALU类型 |
作用 | 当网络发现NAL单元有比特错误时,可以设置该比特为1,以便接受方丢掉该单元 | 标志该NAL单元用于重建时的重要性,值越大,越重要,取值为00-11 | 1-23表示单个NAL包,24-31需要分包或者组合发送 |
NALU类型:
0:没有定义
(1-23 NAL单元,单个 NAL 单元包)
1:不分区,非IDR图像的片
2:片分区A
3:片分区B
4:片分区C
5:IDR图像中的片
6:补充增强信息单元(SEI)
7:SPS(Sequence Parameter Set序列参数集,作用于一串连续的视频图像,即视频序列)
8:PPS(Picture Parameter Set图像参数集,作用于视频序列中的一个或多个图像)
9:序列结束
10:序列结束
11:码流结束
12:填充
13-23:保留
24:STAP-A单一时间的组合包
25:STAP-B单一时间的组合包
26:MTAP16单一时间的组合包
27:MTAP24多个时间的组合包
28:FU-A分片的单元
29:FU-B分片的单元
30-32:未定义
4. H264传输
H264的编码视频序列包括一系列的NAL单元,每个单元包含一个RBSP。每个单元都按独立的NAL单元传输。NAL单元的信息头定义了RBSP的类型,NAL单元的其余部分为RBSP数据。
5. H264码流结构
这部分内容比较复杂,重点在于如果一个NALU单元内有SPS、PPS相连,那么该帧就是关键帧。
6. H264的Level和profile说明
H264的Level用来约束分辨率、帧率和码率的。Level越高所支持的分辨率、码率、帧率就越大。
H264的profile则是用来说明视频序列的帧类型:
- BP(Baseline profile)基线档次:提供I/P帧,仅支持Progressive(逐行扫描)和CAVLC。多应用于“视频会话”,如可视电话、会议电视、远程教学、视频监控等实时通信领域;
- XP(Extended profile)进阶档次:提供I/P/B/SP/SI帧,仅支持Progressive和CAVLC。多应用于流媒体领域,如视频点播、基于网络的视频监控等;
- MP(Main profile)主要档次:提供I/P/B帧,支持Progressive和Interlaced(隔行扫描),提供CAVLC和CABAC。多应用于数字电视广播、数字视频存储等领域;
- HiP(High profile)高级档次:(Fidelity Range Extensions,FRExt)在Main profile基础上新增8*8帧内预测,Custom Quant,Lossless Video Coding,更多YUV格式(4:2:2,4:4:4),像素精度提高到10位或14位。多应用于对高分辨率和高清晰度有特别要求的领域。
(三)RTMP协议
RTMP中的数据类型有Message(消息)和Chunk(消息块)。Message是RTMP协议中的基本数据单元,而RTMP协议在互联网中传输时,将Message拆分成更小的单元,这个单元就是Chunk
1. Message格式:

- Message Type 消息类型:1字节,0x04表示Ping包,0x08为Audio、0x09为video
- Payload Length 负载消息长度:3字节,表述了tag中数据段的大小
- Time Stamp 时间戳:4字节,记录每一个tag相对于第一个tag的相对时间。
- Stream ID 媒体流ID:3字节,标识消息所属的媒体流
- Message Body:音视频消息
2. 消息块chunk

- Chunk Basic Header:标识本块,1-3字节
由Header Type + Channel ID组成,其中Channel ID的大小决定了整个Chunk Basic Header的大小,而Header Type决定了Chunk Message Header的编码方式和大小。
Bits | Chunk Message Header Length |
---|---|
00 | 12 bytes |
01 | 8 bytes |
10 | 4 bytes |
11 | 1 bytes |
Channel ID | 用途 |
---|---|
02 | Ping和ByteRoad通道 |
03 | nvoke通道,connect()、publish()的数据都是在这个通道 |
04 | Audio和Video通道 |
05-07 | 服务器保留通道 |
- Chunk Message Header:标识本块负载所属消息,包含了将要发送的消息,长度由Chunk Basic header的type决定。
- Extend Timestamp:该字段发送时必须是正常的时间戳并设置成0xffffff;当正常时间戳不为0xffffff时,该字段不发送。
- Chunk Data:实际数据payload,可以是信令,也可以是媒体数据。
3. RTMP Packet
在librtmp库中,一个RTMP Packet对应一个消息块Chunk,RTMP Packet结构体定义如下:
//Chunk信息
typedef struct RTMPPacket
{
uint8_t m_headerType;//ChunkMsgHeader的类型(4种)
uint8_t m_packetType;//Message type ID(1-7协议控制;8,9音视频;10以后为AMF编码消息)
uint8_t m_hasAbsTimestamp; /* Timestamp 是绝对值还是相对值? */
int m_nChannel; //块流ID
uint32_t m_nTimeStamp; // Timestamp
int32_t m_nInfoField2; /* last 4 bytes in a long header,消息流ID */
uint32_t m_nBodySize; //消息长度
uint32_t m_nBytesRead;
RTMPChunk *m_chunk; //消息内容
char *m_body;
} RTMPPacket;
(四)FLV
FLV是一种封装格式,它可以将视频图像数据、音频数据封装在一起,形成一个新的视频。项目里,RTMP Packet中的m_body就是用来保存flv数据的。
1. FLV文件结构
FLV文件主要包括FLV Header和FLV Body两部分。

(1) Header
Header包括文件类型、流信息(是否包含视频或者音频)、Header长度等信息
(2) Body
- Body由一个个Tag组成
- Tag包含的信息主要有视频、音频或脚本信息
- Body中一般第一个Tag为脚本信息(MetaData,包含音视频的编码格式、视频的宽高信息等,该tag有且只有一个),之后的tag为Video或者audio
2. File Hader
File Header记录了FLV的类型、版本等信息,是FLV的开头,一般占9个字节空间。
字段 | 字节数 | 意义 |
---|---|---|
文件类型 | 3 | 固定为十六进制值0x46、0x4c、0x56,即字符F、L、V的ASCⅡ码 |
版本 | 1 | 一般为0x01 |
流信息 | 1 | 倒数第1位是1表示有视频,倒数第3位是1表示有音频,倒数第2、4位必须为0。 00 00 01 01,表明该文件中包含视频和音频 00 00 00 01,表明该文件中只有视频 |
长度 | 4 | 整个Header的长度,一般为9,如果大于9,则表示下面还有拓展信息 |
3. Body
Body部分是一个个Tag组成,每个Tag下面都有一块4字节的空间,用来记录这个Tag的长度,这部分可以放在后面用于逆向读取处理。
4. TAG
每个Tag由两部分组成,分别是Tag Header和Tag Data。Tag Header中存放的是当前Tag的类型、数据区(Tag Data)长度等信息。
(1) Tag Header
Tag Header格式如下:

字段 | 字节数 | 意义 |
---|---|---|
TagType | 1 | 音频为0x08,视频为0x09,脚本数据为0x12 |
DataSize | 3 | 数据区长度 |
Timestamp | 3 | 整数,单位ms,相当于第一个tag的时间戳,因此第一个tag的时间戳为0, 可以将所有Tag的时间戳全配置为0,解码器会自动处理 |
Timestamp Extended | 1 | 将时间戳拓展为4字节,代表高8位,很少使用 |
StreamID | 3 | 默认值为0 |
Data | DataSize | TagType=0x08,为AudioData;TagType=0x09,为VideoData;TagType=0x12,为ScriptData |
(2) Tag Data
数据区根据Tag类型的不同分为3中:音频数据、视频数据、脚本数据
(3) 脚本Tag Data
脚本Tag一般至少有一个,用于存放FLV的MetaData信息,比如duration、audiodatarata、creator、width等。
(4) 音频Tag Data(重点)
音频Tag Data格式如下:


AudioSpecificConfig(AAC Sequence Header)

(5) 视频Tag Data(重点)

AVCVideoPacket同样包括Packet Header和Packet Body两部分。对于AVC格式的Video,除了第一个字节的帧类型和编码ID,从第二个字节开始的具体格式如下:
字段 | 字节数 | 意义 |
---|---|---|
AVCPacketType | 1 | AVCPacketType=0x00,为AVCSequence Header; AVCPacketType=0x01,为NALU; AVCPacketType=0x02,为AVC end of sequence |
CompositionTime | 3 | 如果AVCPacketType=0x01,为相对时间戳,其他均为0 |
Data | n | 负载数据,根据AVCPacketType类型,分别是AVCDecorderConfigurationRecord、NALUs、空 |
也就是说Video Tag Data可以支持许多种视频类型,比如H.263、AVC等,如果是AVC的视频类型,那么数据内容就按照AVC数据格式来编码:使用第一个字节来判断AVC格式是文件头还是文件内容,如果是文件头,则Tag具体内容为AVCDecorderConfigurationRecord,否则为普通的视频图像数据。
AVCDecorderConfigurationRecord(AVC sequence header)

从这个定义中可以得知一个关键帧包含pps、sps数据。