# 基于Wi-Fi Direct的音频传输系统
## 摘要
我们实现了一个软件系统:它可以通过Wi-Fi Direct建立一个网络,自主发现网络中的某一设备并向该设备发送音频信号。该设备收到音频信号后,如果有多个来源,将对这些音频信号混频。最终这个设备讲接收的音频信号播放。
## 应用场景
本项目实现之初旨在共享局域网中的音频设备(如蓝牙耳机或大型扬声器)。除此之外,本项目实现的协议能够用于搭建基于 WiFi Direct 网络的高效的实时音频传输网络,能够用于实现音频会议等场景。
## 背景技术
### 1.Wi-Fi Direct技术
相较于蓝牙技术,Wi-Fi技术有带宽更大、传输距离更远、抗干扰能力更强等优点,而且还能够使用传统的TCP/IP网络协议栈。但是传统Wi-Fi网络的建立需要有一台设备充当接入点(AP),不能方便的构建网络。而Wi-Fi Direct技术则解决了这个问题,它不需要指定的设备充当AP,多台设备可以以Ad-hoc自组网的形式组织网络。为了方便快捷的传输音频信号,因此我们选用Wi-Fi Direct技术组成网络。
### 2.服务发现协议、心跳协议和音频控制协议
在该部分,我们称音频信号的发送者为Client端,音频信号的接受者为Server端。
在我们设想的情景中,Server端可能是网络中的任意一台设备,因此Server端的地址是不确定的。如果让用户自行寻找Server端的地址,这显然是一件不合理的事情,因此我们需要设计一个服务发现协议,让Client端可以自动的发现网络中的Server端。同时,为了确保通信的正确进行,我们需要Server端和Client端每隔一段时间都去确认双方是否都还在当前网路中。为此我们设计了心跳协议。
为了方便Server端接收并播放音频流,Client端在开始发送音频前和停止发送音频后,都应该向Server端告知自身当前的播放状态。因此我们设计了音频控制协议。
服务发现协议和心跳协议如图1所示。其过程大致如下:
1. Client端:向网络广播消息“client”,表示有新的Client加入网络中。
2. Server端:在接受到广播消息后,向广播源(即对应的Client)发送消息“server”,表明自己的身份。
心跳协议由Client端定时执行。其过程大致如下:
1. Client端:向Server发送心跳消息“server?”并等待回复。如果不能在较短时间内接收到Server的回复,则认为该Server已经离开网络。
2. Server端:接收到Client的心跳消息后回复消息“serverYes”。如果在规定时间内没有接收到Client的心跳消息,则认为该Client已经离开网络。
图1:**服务发现协议和心跳协议**
![](https://www.writebug.com/myres/static/uploads/2022/7/9/6e350d9c4eb2569dc193958bbde6773c.writebug)
音频控制协议如图2所示。音频控制协议包含两种子协议:请求播放和请求暂停。其过程大致如下:
请求播放:
- Client端:向Server发送消息“play”。
- Server端:接收到消息后,准备好接收音频流,并向Client发送消息“playOk”。
- Client端:接收到消息后,通过RTP协议向Server发送音频信号。
请求暂停:
- Client端:向Server发送消息“stop”并停止发送音频信号。
图2:**音频控制协议**
![](https://www.writebug.com/myres/static/uploads/2022/7/9/19acc0b41bda8e725c25e8d21ebd1bf7.writebug)
### 3.RTP协议和RTCP协议
#### 3.1 RTP概览
我们项目中对音频信号的传输是通过RTP实现的。RTP协议详细说明了在互联网上传输音频和视频的标准数据包格式,常用于流媒体系统中,是一个多播协议但也可以用于单播应用中,非常符合我们项目的需求。
RTP协议是一种基于UDP的无连接的传输协议,和RTCP控制协议一起使用。RTP协议为Internet上端到端的实时传输提供时间信息和流同步,但它并不保证服务质量。服务质量控制功能由RTCP协议来提供。
图3展示了一个典型流媒体应用的体系结构。RTP协议被划分在传输层,建立在UDP上。因此,同UDP协议一样,为了实现实时传输功能,RTP也有固定的封装形式,接下来将具体介绍RTP数据包的封装结构。
图3:**典型流媒体应用的体系结构**
![](https://www.writebug.com/myres/static/uploads/2022/7/9/06b27920c569b002de5a0f3c6fe8ea42.writebug)
#### 3.2 RTP的封装格式
图4展示了RTP报文的头部格式。其各部分含义如下:
- 版本号(V):2比特,用来标志使用的RTP版本。
- 填充位(P):1比特,如果该位置位,则该RTP包的尾部就包含附加的填充字节。
- 扩展位(X):1比特,如果该位置位的话,RTP固定头部后面就跟有一个扩展头部。
- CSRC计数器(CC):4比特,含有固定头部后面跟着的CSRC的数目。
- 标记位(M):1比特,该位的解释由配置文档(Profile)来承担.
- 载荷类型(PT):7比特,标识了RTP载荷的类型。
- 序列号(SN):16比特,发送方在每发送完一个RTP包后就将该域的值增加1,接收方可以由该域检测包的丢失及恢复包序列。序列号的初始值是随机的。
- 时间戳(timestamp):32比特,记录了该包中数据的第一个字节的采样时刻。在一次会话开始时,时间戳初始化成一个初始值。即使在没有信号发送时,时间戳的数值也要随时间而不断地增加。时间戳是去除抖动和实现同步不可缺少的。
- 同步源标识符(SSRC):32比特,同步源就是指RTP包流的来源。在同一个RTP会话中不能有两个相同的SSRC值。该标识符是随机选取的,用于唯一标识RTP会话中的参与者。
- 贡献源列表(CSRC List):0~15项,每项32比特,用来标志对一个RTP混合器产生的新包有贡献的所有RTP包的源。由混合器将这些有贡献的SSRC标识符插入表中。SSRC标识符都被列出来,以便接收端能正确指出交谈双方的身份。(混合器用于需要将多个源的音频包进行合并以及接收者能接收的音频编码格式不一致的情况中)
图4:RTP**报文的头部格式**
![](https://www.writebug.com/myres/static/uploads/2022/7/9/67d1373783f45695380de0b3a4446b68.writebug)
#### 3.3 RTCP的封装结构
RTCP也是基于UDP进行传送的,封装的主要是一些控制信息,用于检测与反馈服务质量、同步媒体、标识多播组中的成员等等。在RTP会话期间,各参与者周期性地传送RTCP包。RTCP包中含有已发送的数据包的数量、丢失的数据包的数量等统计资料,各参与者可以利用这些信息动态地改变传输速率。RTP和RTCP配合使用,它们能以有效的反馈和最小的开销使传输效率最佳化,因而特别适合传送网上的实时数据。
由于RTCP仅包含一些控制信息,因此分组长度很短,可以将很多RTCP分组封装在一个UDP包中。RTCP有如下五种分组类型:
| 类型 |缩写表示 |用途 |
|----|----|----|
| 200 |SR(Sender Report) |发送端报告 |
| 201 |RR(Receiver Report) |接收端报告 |
| 202 |SDES(Source Description Items) |源点描述 |
| 203 |BYE |结束传输 |
| 204 |APP |特定应用 |
上述五种封装类型的内部结构基本相同,下面以SR为例进行介绍:
发送端报告分组SR用于使发送端以多播的方式向所有接收端报告发送情况,主要内容包括:相应RTP流的SSRC值、RTP流中最新产生的RTP分组的时间戳、RTP流包含的分组数、RTP流包含的字节数等。图5展示了RTCP SR头的结构,其中各部分的含义如下:
- 版本(V):同RTP包头域。
- 填充(P):同RTP包头域。
- 接收报告计数器(RC):5比特,该SR包中的接收报告块的数目,可以为�