WebRTC 音频QoS研究(2):如何从WebRTC中提取出音频QoS代码(Windows下并编译成dll库文件)

转载请注明出处:https://blog.csdn.net/qq_29621351/article/details/80991615

WebRTC版本:M66 

      WebRTC的语音QoS机制几乎可以说是行业的标杆,其实现的方式主要融入了三种技术,包括丢包重传(NACK),前向纠错(FEC)以及原GIPS公司的网络均衡器(NetEqualizer,简称NetEQ)。前两种都是在牺牲一定成本的前提下,尽最大努力降低丢包率,而NetEQ是在前两者的基础上降低延迟、弥补丢包。

      WebRTC语音质量效果好绝不是因为使用了其中某一项技术,而是三种技术相结合并且有策略的使用,例如对于实时音视频来讲,丢包重传技术由于需要等待重传包,往往会增加延时,而它在与前向纠错同时使用的过程中,只有在前向纠错无法恢复丢失的数据包时,才会使用丢包重传,并且即使开启了丢包重传,对于它重传过来的数据包,是否使用还要权衡网络的RTT状态值。WebRTC源码中对于语音QoS的封装层次很深,NetEQ封装在最底层,其中包含着抖动消除、解码和丢包隐藏(也就是丢包补偿)。在NetEQ的外层,封装着NACK和FEC。某些编解码器也支持带内的FEC(例如opus)。

      其实我是做Android版的WebRTC的,但是Android版本WebRTC调试实在是太不方便了,所以我一般做什么都预先在Windows上做完,这样可以用VS单步调试,然后在Linux上模仿着做。这次主要是在WebRTC中将音频QoS接收端处理提取出来,用C语言格式封装成库,以后可以随拿随用。首先需要有VS开发环境,WebRTC源码,至少在VS上面编译了一遍WebRTC(以往的文章中有方法),这样有基础环境会好很多。

    从WebRTC中提取音频QoS的代码分为很多种层面,最底层从neteq_impl层面,再高一层可以从,我选择了从audio_coding_module层面来实现,因为从这一层不仅可以使用NetEQ的抖动缓冲和丢包隐藏功能,还能享受到某些编码器(例如opus)集成的FEC和DTX功能,如果更高的话就是channel层,而这已经脱离了简单的QoS处理的层面,接口调用的层面体现在WebRTC源码的文件中。

audio_coding_module.cc
acm_receiver.cc
neteq_impl.cc

 如果想编译成.so文件使用方便,最好用C语言封装。

提取代码

      首先在自己的电脑上完整的编译一遍Windows版本WebRTC,这在我以前的文章中有方法,然后自己新建一个工程,取个名字。我的叫acm,工程最终将编译生成dll文件。

然后需要定义你想要封装的接口,写一个文件acm.def内容如下,接口的名字和数量随便改,这就是我定义的5种接口,分别是创建、初始化、释放、插入RTP包和去除PCM数据。可以根据自己的需要定义形式。

LIBRARY acm
EXPORTS
WebRtcQos_Create @ 1
WebRtcQos_Init @ 2
WebRtcQos_Free @ 3
WebRtcQos_InsertPacket @ 4
WebRtcQos_getAudioData @ 5

将这个文件放在与acm.cpp源文件相同目录下,然后在工程中将其添加进去,右键acm项目—>添加—>现有项,不用在意我这个文件夹下有多少文件,这都是我在完成工程之后添加的文件。

接下来配置acm调用WebRTC源码头文件的导入目录以及预处理宏定义

右键点击acm解决方案,然后选择属性 —> C/C++ —> 常规

属性 —> C/C++ —> 预处理器(NOMINMAX预处理变量为可能出现的一种BUG的解决方式)

接下来就可以基于WebRTC里面的audio_coding_module.h里面定义的接口进行自己的QoS处理封装了,我的处理比较简单直接,只定义了5个接口。封装完之后生成解决方案。

生成解决方案成功之后理论上写上那么一小段程序调用这个dll库是可以运行的,但是如我们所知,事实上编译的过程只是编译了一个调用语句,没有要求具体的实现,而链接的过程才需要实际的代码,所以我们要将自己编译的WebRTC中的peerconnection_client工程所编译好的xxx.obj、xxx.lib、xxx.dll文件链接到现有的acm工程中,这样可以节省很多繁琐的寻找依赖头文件的过程。至于该链接哪些xxx.obj、xxx.lib、xxx.dll,其实我也不是很清楚,但你可以一点点的去尝试,我的做法是,我找到了webrtc_checkout\src\out\Debug\obj\examples\peerconnection_client.ninja文件,这个文件主要包含peerconnection_client编译过程中的预处理变量、导入头文件的路径、编译器标志和链接文件等等,如下。

在link之后的基本上就是需要链接的文件,于是我把他原封不动的链接到acm工程中,连接方式如下,但是这样做可能导致最后链接生成的acm.dll文件过于庞大,所以大家也可以一点点试着选择性的去除链接文件,如果对功能有影响就保留,没影响就删除链接。

以上路径均可以写成绝对路径,然后由于我们要生成的是dll动态链接库文件,所以要设置模块定义文件。

此外,还需要将acm中的代码调试设置成多线程调试,如下。

这是因为acm中的两个函数是分别需要在两个线程中调用。然后编译就会在acm工程源码目录的debug下生成acm.dll文件,当某个程序调用它时,就将它拷贝到这个程序.exe文件的源码目录下。

最终,你可以再创建一个工程test_acm。

并将acm.dll拷贝到工程源码目录下,然后需要设置一些与acm工程类似的相关变量。

然后就编译运行test_acm,测试逆接口的封装,注意每更改一次acm源码,就需要重新编译acm源码,生成新的acm.dll文件,然后重新拷贝到test_acm工程中去。

至于如何封装接口,以后接口成熟了,我可能会上传到github中去。这里就不介绍了。

如果有在实施过程中出现问题的,可以下方评论,我会尽力解决。

猜你喜欢

转载自blog.csdn.net/qq_29621351/article/details/80991615