通信与流媒体编程实践

Asterisk播放mp4(2)——音频封装

2020-08-06  本文已影响0人  全栈顾问

Asterisk现有版本不支持播放视频文件(支持视频通话),无法满足发送视频通知、视频IVR等场景。本系列文章,通过学习音视频的相关知识和工具,尝试实现一个通过Asterisk播放mp4视频文件的应用。

在音视频媒体领域中,要区分容器格式和编码格式。我理解,容器(container)是包含了描述媒体流的信息和媒体流数据的介质(文件或网络通道),其中可以包含多路媒体流。编码格式描述的是媒体流中如何记录数据,例如:声音采样,一帧图片等。本文学习音频流处理时常用的容器,包括4个部分:WAV格式,MP3格式,RTP音频传输和制作样本数据。

WAV格式

WAV是微软开发的一种音频文件格式,符合RIFF(资源互换文件格式)规范。RIFF,全称Resource Interchange File Format,是一种按照标记区块存储数据的通用文件存储格式。符合RIFF规范的文件中,,最小存储单位为“块”(Chunk),每个块(Chunk)包含三个部分:块标识(4字节,如果不够4字节,在右边以空格填充),块长度(4字节,数据块的长度,不包括块标识和块长度的8个字节,采用小子节序)和数据。RIFF格式规定,只有 RIFF及LIST块可以含有子块,其它的块不允许包含子块。WAV文件至少由3个块构成,分别是RIFF、fmt 和Data。所有基于压缩编码的WAV文件必须含有fact块。此外所有其它块都是可选的。块fmt,Data及fact均为RIFF块的子块。

image.png

MP3格式

MP3截掉了大量的冗余信号和无关的信号,编码器通过混合滤波器组将原始声音变换到频率域,利用心理声学模型,估算刚好能被察觉到的噪声水平,再经过量化,转换成Huffman编码,形成MP3位流。MP3是一种有损压缩,压缩比在1:10到1:12之间。

理解MP3,首先需要搞清版本(version)和层(layer)的概念,版本包括:MPEG-1(ISO/IEC 11172-3),MPEG-2(ISO/IEC 13818-3)和MPEG-2.5(不是官方标准);层包括:Layer-1,Layer-2和Layer-3。MP3的版本和层不是迭代升级的关系,而是为了适应不同使用场景,最主要的指标就是针对不同的采样率。

MPEG-1: Specifies the coded representation of high quality audio for storage media and the method for decoding of high quality audio signals. Is intended for application to digital storage media providing a total continuous transfer rate of about 1,5 Mbit/s for both audio and video bitstreams, such as CD, DAT and magnetic hard disc, and for sampling rates of 32 kHz, 44,1 kHz, and 48 kHz.

MPEG-2: In order to achieve better audio quality at very low bit rates (<64 kbit/s per audio channel), in particular if compared with ITU-T (formerly CCITT) Recommendation G.722 performance, three additional sampling frequencies are provided for ISO/IEC 11172-3 layers I, II and III. The additional sampling frequencies (Fs) are 16 kHz, 22,05 kHz and 24 kHz.

MPEG-2.5: 由于MPEG-2定义的最低的采样率为16kHz,于是Fraunhofer(是个德国的研究所)便对此进行扩展,将原来MPEG-2所支持的低采样率再除以2,得到: 8, 11.025, 和 12 kHz ,称为 "MPEG 2.5"。

Layer-1的编码器最为简单,编码器的输出数据率为384 kb/s,主要用于小型数字盒式磁带(digital compact cassette,DCC)。

Layer-2的编码器的复杂程度属中等,编码器的输出数据率为256 kb/s~192 kb/s,其应用包括数字广播声音(digital broadcast audio,DBA)、数字音乐、CD-I(compact disc-interactive)和VCD(video compact disc)等。

Layer-3的编码器最为复杂,编码器的输出数据率为64 kb/s,主要应用于ISDN上的声音传输。

MP3文件结构

MP3文件大体上分为三个部分:ID3v2(不影响对音频的解析,放音时可以跳过) + 音频数据(MP3编码的帧序列,每个帧包含帧头和数据两部分,帧和帧相互独立) + ID3v1(文件尾部128字节,可选)。

ID3v2

ID3v2标签用于记录mp3文件的元数据,标签头中标记是否包含扩展头(Extended Header),填充(Padding)等部分。ID3v2标签至少包含1个标签帧(frame),每帧描述了文件的某个信息,例如:作者,专辑等等。ID3v2标签内部采用大字节序。

image.png

音频数据

每个音频数据帧由4个部分组成:帧头(4字节,必须),CRC(2字节,可选),边信息(9,17或32字节,必须),VBR头(8-120字节,可选),音频编码数据(必须)。

Side Infomation(边信息): If joint stereo is used in M/S (middle/side) mode, the left and right channels aren't encoded separately. Instead, a "middle" channel is encoded as the sum of the left and right channels, while a " side" channel is stored as the difference between the left and the right. During the decoding process, side information is read back out of the frame and applied to the bitstream so that the original signal can be reconstructed as accurately as possible. The side information is essentially a set of instructions on how the whole puzzle should be re-assembled on the other end.

MP3有两种编码方式:1,CBR(固定位率),帧的大小在整个文件中都是是固定的;2,VBR(可变位率),VBR是XING公司推出的算法,所以在MP3的FRAME里会有“Xing"这个关键字(也有用"Info"来标识的),它存放在MP3文件中的第一个有效帧的数据区里,它标识了这个MP3文件是VBR的。同时第一个帧里存放了MP3文件的帧的总个数,这就很容易获得播放总时间,同时还有100个字节存放了播放总时间的100个时间分段的帧索引,假设4分钟的MP3歌曲,240S,分成100段,每两个相邻INDEX的时间差就是2.4S,所以通过这个INDEX,只要前后处理少数的FRAME,就能快速找出我们需要快进的帧头。其实这第一帧就相当于文件头了。不过现在有些编码器在编码CBR文件时也像VBR那样将信息记入第一帧,比如著名的lame,它使用"Info"来做CBR的标记。

RTP音频传输

RTP全名是Real-time Transport Protocol(实时传输协议)。RTP用来为IP网上的语音、图像、传真等多种需要实时传输的多媒体数据提供端到端的实时传输服务。RTP为端到端的实时传输提供时间信息和流同步,但并不保证服务质量,服务质量由RTCP来提供。我理解RTP就是一种容器,核心是解决通过网络实时传递数据的问题。

RTP帧头

12个字节是固定的RTP帧头,每个帧都会携带;贡献源列表(CSRC List):0~15项(实际的数量由CC字段指定),每项32比特,用来标志对一个RTP混合器产生的新包有贡献的所有RTP包的源;再后面就是携带的数据(payload)。

RTP头中各个字段的信息将在后续的文章中详细解析,这里先关注一下PT(payload type)字段。PT指明了RTP传递的数据类型,值0-95是有预定义的(RFC3551),例如:8是pcma,0是pcmu等,96-127是动态指定的。通过asterisk建立通话的过程中,要通过sip传递sdpsdp中指定了要建立的rtp通道的相关参数,例如:端口,媒体类型和媒体参数等,sdp中指定的媒体类和rtp头中的PT字段相对应。

rfc3551

除了关注Payload type外,还要注意MTU(最大传输单元,Maximum Transmission Unit),它是数据链路层的概念,用于限制数据包的大小(例如:IP包),如果数据包大于这个值就会拆包或丢弃,取值一般是1500(或1492)字节。一个RTP包中,IP+UDP+RTP头=20+8+12=40字节。这样一个RTP最大的载荷数据为1460字节。

制作样本数据

WAV格式

生成一段10秒钟音频文件,编码格式为pcm_s16le,容器格式为wav:

ffmpeg -lavfi sine -t 10 -f wav -c:a pcm_s16le sine-10s-s16le.wav

生成的文件大小为882078字节,我们知道10秒钟44.1k采样率单声道pcm_s16le编码的裸流大小为882000,那么wav容器编码就是增加78字节的文件头。下面我们看看这78字节的内容:

image.png
子节位置 字节数 字段 数值 解析
0-3 4 块标识 5249 4646 ASCII码,大写RIFF,指明块是RIFF块。
4-7 4 块大小 9675 0d00 小字节序,882070,数据块大小。
8-11 4 格式 5741 5645 ASCII码,大写WAVE,说明文件格式

文件的大小是882078,要减去12个字节,还剩882066字节需要解析。

子节位置 字节数 字段 数值 解析
12-15 4 块标识 666d 7420 ASCII码,小写fmt,注意最后1个字节填充的是空格(20),指明块是fmt块。
16-19 4 块大小 1000 0000 小字节序,16,数据块大小。不同音频编码格式数据块的长度并不相同,详细内容看参考链接。
20-21 2 音频格式(AudioFormat) 0100 小字节序,1,PCM/非压缩格式。(其他格式的定义查看参考资料)
22-23 2 声道数(NumChannels) 0100 小字节序,1,单声道。有几个声道就是几。
24-27 4 采样率(SampleRate) 44ac 0000 小字节序,44100。
28-31 4 传输速率(ByteRate) 8858 0100 小字节序,88200,每个采样2个字节,所以每秒要传输88200字节。
32-33 2 数据块对齐单位(BlockAlign) 0200 小字节序,2,该数值为:声道数×位数/8。
34-35 2 采样位数(BitsPerSample) 0010 小字节序,8,每个采样8位二进制。

fmt子块一共24个字节,882066减24,还有882042个字节需要解析。

子节位置 字节数 字段 数值 解析
36-39 4 块标识 4c49 5354 ASCII码,大写LIST,指明块是LIST块。
40-43 4 块大小 1a00 0000 小字节序,26,数据块大小。
44-47 4 块类型 494e 464f ASCII码,大写INFO。这个块是用来描述文件信息的,并不影响解析,大体知道就行。
48-51 4 块标识 4953 4654 ASCII码,大写ISFT,创建文件的软件。
52-55 4 块大小 0e00 0000 小字节序,14,数据块大小。
56-69 14 块数据 4c61-3000 Lavf58.29.100

LIST子块一共34个字节,882042减34,还有882008个字节需要解析。

子节位置 字节数 字段 数值 解析
70-73 4 块标识 6461 7461 ASCII码,小写data,指明块是data块。
74-77 4 块大小 5075 0d00 小字节序,882000,数据块大小,和裸流字节数一致。

至此,78个字节的文件头解析完毕。由此可以进一步理解:容器是媒体流的描述信息和数据流,只有提供了描述信息,解析程序才知道如何读取数据流。

MP3格式

ffmpeg -lavfi sine -t 10 -f mp3 -c:a mp3 sine-10s.mp3

image.png

生成的mp3文件是单声道,采样率44.1k。文件大小80475字节,是WAV格式882078字节的1/11,极大减少了存储空间。

ID3v2头

image.png
子节位置 字节数 字段 数值 解析
0-2 3 标签头标识 4944 33 ASCII码,ID3,必须以此开头,说明是ID3v2标签。
3-4 2 版本号 04 00 4,0,表示为ID3v2.4.0版本。
5 1 标识字节 00 具体解释查看参考资料。其中第6位(左边第2位)指明是否包含扩展头,第4位(左边第4位)指明是否包含尾(footer)。
6-9 4 标签大小 0000 0023 每个字节去掉首位后的28位二进制数。

The ID3v2 tag size is the sum of the byte length of the extended
header, the padding and the frames after unsynchronisation. If a
footer is present this equals to ('total size' - 20) bytes, otherwise
('total size' - 10) bytes.

ID3标签的大小是35字节,加上10字节的头,所以总长度是45个字节。

子节位置 字节数 字段 数值 解析
10-13 4 帧ID 5453 5345 ASCII码,TSSE(Software/Hardware and settings used for encoding)
14-17 4 帧大小 0000 000f 15字节,不包含帧头的10字节。
18-19 2 标志 0000 描述帧的特征,不影响解析,可以不深究。
20-34 15 帧数据 034c ... 3030 00 Lavf58.29.100

10个字节的头和15字节的数据,共25个字节。

在ID3v2标签在尾部填充了10个空字节(0x00),把差的10个字节凑齐。

数据第1帧

image.png

帧头32个字节,ff fb 40 c0,由11个部分组成,用如下方式AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM(00_0000)标记。

A_11 B_2 C_2 D_1 E_4 F_2 G_1 H_1 I_2 J_2 K_1 L_1 M_2
1111 1111 111 11 01 1 0100 00 0 0 11 00 0 0 00
标记 位数 位置 描述
A 11 31-21 1111 1111 1 同步信息,所有位均为1,表示帧开始。
B 2 20-19 11 版本,MPEG1。
C 2 18-17 01 层,Layer3。
D 1 16 1 CRC校验,等于1,不校验。
E 4 15-12 0100 传输率,等于0100,56kbps。需要查对照表(看参考资料)。
F 2 11-10 00 采样率,等于00,44.1kHz。
G 1 9 0 填充位,等于0,不需要。
H 1 8 0 保留字
I 2 7-6 11 声道模式,11-单声道
J 2 5-4 00 扩充模式,无
K 1 3 0 版权,0-不合法
L 1 2 0 原版标识,0-非原版
M 2 1-0 00 强调方式,等于00,未定义。

需要注意采样率(11-10位)是和版本和层有对应关系的,其取值对照含义如下表:

bits MPEG1 MPEG2 MPEG2.5
00 44100 22050 11025
01 48000 24000 12000
10 32000 16000 8000
11 reserv. reserv. reserv.

17字节,首帧中数据全是0,还没有找到详细的解释。真正的数据帧这部分信息都是有对应值的,解码器要根据这些信息才能把数据解出来。

位置 长度 说明
0 4 496e 666f ASCII码,Info。VBR头是Xing公司提出的,所以也有用Xing作为标识的情况。
4 4 0000 000f 标志显示当前字段的标志,与逻辑或逻辑相结合。字段是强制性的。0x00000001 - 包含帧数字段;0x00000002 - 包含字节数字段;0x00000004 - 包含TOC部分 ;0x00000008 - 包含质量指示(0-100)。说明后面的4个部分内容都有。
8 4 0000 0180 384 帧数量 Number of Frames as Big-Endian DWORD (optional)
8 or 12 4 0001 3a2e 80430 帧字节数(去掉ID3标签) Number of Bytes in file as Big-Endian DWORD (optional)
8, 12 or 16 100 - 100 TOC entries for seeking as integral BYTE
8, 12, 16, 108, 112 or 116 4 0000 0000 Quality indicator as Big-Endian DWORDfrom 0 - best quality to 100 - worst quality
帧头 边信息 VBR标记 填充
4字节 17字节 120 字节 41字节

VBR头后面存储编码器的信息,比如 lame 在其后就是存储其版本,把第1帧填满。

后续数据帧

MP3中每帧的采样数和版本和层的对照是固定的,如下表:

MPEG1 MPEG2 MPEG2.5
Layer1 384 384 384
Layer2 1152 1152 1152
Layer3 1152 576 576

根据该表可知每帧的采样数是1152个,采样率是44100,每帧字节数:56(比特率)/44.1(采样率)x1152(帧采样数)/8 = 182字节(截断小数部分)。首帧的地址为2d,向后182个字节e3,是第2帧的开始位置。通过这种方式就可以找到后面每一帧的起始地址。

第2帧的起始地址0x00e3,帧头为ff fb 50 c4

A_11 B_2 C_2 D_1 E_4 F_2 G_1 H_1 I_2 J_2 K_1 L_1 M_2
1111 1111 111 11 01 1 0101 00 0 0 11 00 0 1 00

传输率和首帧的值不同,为0101,对应64kbps,计算帧大小为208字节(截断小数部分)。

第3帧的起始地址0x01b3,帧头为ff fb 52 c4

A_11 B_2 C_2 D_1 E_4 F_2 G_1 H_1 I_2 J_2 K_1 L_1 M_2
1111 1111 111 11 01 1 0101 00 1 0 11 00 0 1 00

和第1帧填充位值不同,等于1,需要填充。帧大小为截断后加1,等于209。

第4帧的起始地址0x0284,和计算结果一致。

转换为PCM

我们研究的目标是通过asterisk进行媒体的播放,mp3是一种有损压缩,那么将mp3转码为pcm后失真是否严重?下面将ffmpeg生成的mp3格式的sine波形转换为pcm_s16le,看看波形的变化。

ffmpeg -i sine-10s.mp3 -f s16le -c:a pcm_s16le sine-mp32s16le-10s.s16le

MP3转s16le和原始数据对比

红色为原始pcm_s16le的数据,蓝色为mp3转换的结果。

MP3转换的结果 原始pcm_s16le

虽然MP3确实导致了数据的损失,但是总体上还是很好的保持了波形。(MP3确实很厉害,只是原始数据的1/10不到,但是损失很小!)

8k采样率的MP3

前面用ffmpeg默认生成的mp3文件的采样率是44.1k,而且按照mp3的规范我们知道不同采样率会对应不同的版本和层,那么ffmpeg是否能生成一个采样率是电话系统常用的8k采样率的mp3?

ffmpeg -lavfi sine -t 10 -ar 8000 -f mp3 -c:a mp3 sine-8k-10s.mp3

image.png

文件的大小为10413字节,小了很多。

8k MP3

首帧的头为ff e3 38 c0,解析如下:

A_11 B_2 C_2 D_1 E_4 F_2 G_1 H_1 I_2 J_2 K_1 L_1 M_2
1111 1111 111 00 01 1 0011 10 0 0 11 00 0 0 00

版本域(B)的值是00,对应的是MPEG version2.5;层域(C)的值是01,对应的是Layer3;比特率域(E)的值是0011,对应的是24kbps;采样率域(F)的值是10,对应的是8000

从这个结果看,我们可以将从电话系统收到的8k音频数据保存为8k的mp3文件。

RTP音频流

ffmpeg -lavfi sine -f rtp rtp://127.0.0.1:5005

Ffmpeg输出

注意这时输出的sdp信息,payload type的值是97,对应的参数是PCMU/44100/1,含义是编码格式PCMU,采样率44100,单声道。

下面改变一下输出的参数:

ffmpeg -lavfi sine -ar 8000 -c:a pcm_alaw -f rtp rtp://127.0.0.1:5005

Ffmpeg输出

前面提到过8是预定义的媒体类型,对应的是pcma/8000/1

下面我们通过wireshark抓包看一下通过RTP传送的数据什么样。执行如下命令 ,启动nc监听端口5005ffmpeg向端口发送RTP包(直接用过滤器生成波形无法控制播放速率,导致收到的结果不争取,所以改成播放生成好的文件,添加-re参数播放速率),启动wireshark抓包:

ffmpeg -lavfi sine -t 10 -ar 8000 -f alaw -c:a pcm_alaw sine-8k-10s.alaw

nc -lu 5005

ffmpeg -re -ar 8000 -f alaw -i sine-8k-10s.alaw -c:a pcm_alaw -f rtp rtp://127.0.0.1:5005

将抓包结果解码为RTP包,在协议列(Protocol)选中一个UDP的格子:

wireshark解码UDP Wireshark解码UDP UDP解码为RTP 查看媒体流分析结果

下面我们查看一帧RTP数据的内部:

RTP包数据 alaw文件中的数据

由上两图可以看到,RTP Payload中的数据和原始alaw文件中数据完全一致,说明Payload中的数据是Payload type指定的格式。Payload的大小是320字节,也就是携带了320个采样,也就是0.0.4秒的时长。 前面提到过,Payload的大小受MTU的限制,所以ffmpeg发送数据时会控制每个RTP包中包含的采样数。

参考资料

wav文件格式分析与详解

MP3 wiki

ID3v2.3 详细

ID3v2.4

解释音频帧头的资料

MP3 Inside

RTP: A Transport Protocol for Real-Time Applications

RTP Profile for Audio and Video Conferences with Minimal Control

什么是MTU?为什么MTU值普遍都是1500?

上一篇 下一篇

猜你喜欢

热点阅读