一、
要回答这个问题,从最基本的数据传输开始说。将数据从一个麦克风开始传输到另一个客户端上,并且进行播放来说,它的流程是怎样的呢?
对于客户端A来说,它首先会将数据投递到一个SendPacket当中,也就是直流传输,然后会把数据解析成每一个帧,然后将每一个帧的数据解析成IoArgs,最终通过服务器将数据传递到B客户端中,B客户端接收到数据的时候,会反向解析出帧,然后解析成SendPacket,最后实现对整个音频的播放。
为什么需要两个连接相互辅助进行命令识别并传输音频。首先从A客户端发送数据到B客户端是一个音频数据流,而音频数据流在服务器上传输的时候使用的是桥接模式。这也就说明了在服务器端,其实不是将一个packet数据传输到另一个packet。如果需要用packet进行传输,这意味着我们需要对客户端的数据进行反向解析,解析之后需要进行存储,存储之后再进行另一个连接的调度,而整个过程是比较漫长的,从而会导致整个传输流程的延迟增加,因此导致了整个音频的不够顺畅,所以,在服务器端实现的是从IoArgs传输到IoArgs。
因此也就明白了为什么需要另一个连接来实现命令传输了,因为直流传输的整个桥接流,一旦进行了桥接模式,也就是把数据从一个IoArgs传输到另一个IoArgs,此时数据没有传输到上层,也就是不可能得到packet。一旦无法得到packet,也就是无法传输到业务数据层。业务数据层的数据是可以进行解析的,并且可以进行识别的,而对于IoArgs而言,是一个原始数据层,它的数据无法进行一个准确的识别,导致了之前的音频传输流无法进行命令识别,所以需要另外连接来辅助进行命令识别。
二、交互流程
首先,A客户端会建立好两个连接.
对于B客户端来说,也是建立两个连接。
对于桥接连接:首先在服务端肯定有两个连接,分别连接着两个客户端。这两个客户端连接之后的数据,从A客户端发送过去的数据到达服务器后,首先会被接收成IoArgs,然后会把这个IoArgs直接传输给另一个客户端,整个过程是一个可逆向的,可以循环进行交互的。A客户端将数据传输到服务器,服务器将数据传输给B客户端。同时,B客户端将数据传输给服务器,也可以再反向转发给A客户端,这就是桥接模式的核心原理。
对于命令连接来说,我们的服务端也有一个对应的命令连接,从A客户端先把数据投递到服务器的时候,服务器会对整个数据进行反向解析,解析到packet,而对于B连接也是一样,此时,具有两个队列的packet,此时可以对两个队列的数据进行识别。识别是在服务端完成的,整个过程是由服务端进行控制,遵循的是我们的命令结构,对命令进行解析,并且进行识别,比如说生成房间,或者通知某个客户端进行聊天,这个过程中涉及到是否把A客户端的信息同步给B客户端,这个过程也是由服务器端决定的,具体的流程由服务器端的业务进行判断。
所以,整个红色框的部分,就是服务器端的部分,这就是两个连接连接之后的整个传输流程。
三、Opus数据规则
一个客户端通过麦克风不断地产生数据,而这个数据我们称之为PCM数据流,也就是最原始的数据,这部分数据一旦被缓冲区存满之后,会不断地进行覆盖,而我们需要的就是从整个缓冲区中读取一部分数据出来。
此时,我们已经从缓冲区读取了10个字节,那要如何将数据填充到Opus压缩数据中呢?
我们会对数据进行Opus编码,而编码之后的数据可能只有6个字节,此时把它写到另一个buffer当中,而这个buffer写的时候从第三个坐标开始写,依次往后推,我们写6个坐标长度,这就代表了压缩之后的数据。而压缩之后的数据,在传输之前会先对这部分数据进行提取,此时提取到的数据是6个字节,所以会在前面的两个字节当中写入我们的长度信息,也就是0、6。那么整个的长度就是2个字节再加上6个字节,最终是8个字节长度。网络传输的时候,会把8个字节长度进行发送。发送到B客户端的时候,B客户端开始进行读取,此时,他会先读取长度,而读取长度,它有一个长度限制,它会先读2个字节,读取到2个字节后,也就是06,在客户端会进行short读取,读取之后会拿到长度信息,也就是6,那么它直到后面的Opus编码部分的长度,也就是6个字节,然后它会在之后面的长度当中读取6个字节出来,读取到新的buffer当中,那这是在新连接当中。当整个数据读取出来后,会对整个数据进行Opus节码,节码之后会对整个数据进行还原。
然后再对整个10个字节长度的数据进行播放。
这就是整个传输数据的流程。
为什么要对正Opus编码之后的数据提取长度呢?
其实是因为Opus的基本规则。