WebRTC源码分析:音频模块结构分析

一、概要介绍WebRTC的音频处理流程,见下图:

webRTC将音频会话抽象为一个通道Channel,譬如A与B进行音频通话,则A需要建立一个Channel与B进行音频数据传输。上图中有三个Channel,每个Channel包含编解码和RTP/RTCP发送功能。

以一个Channel而言,应用程序中将包含三个活动线程,录音线程,音频接收线程和播放线程。

1)录音线程:负责麦克风音频的采集,见图中红色路径,采集到音频后,缓存到一定长度,进行音频处理,主要包括EC,AGC和NS等。然后送到Channel,经过音频

Codec模块编码,封装成RTP包,通过Socket发送出去;

2)接收线程:见蓝色路径,负责接收远端发送过来的音频包,解封RTP包,解码音频数据,送入NetEQ模块缓存。

3)播放线程:负责耳机声音播放,见绿色路径。播放线程去OutMixer中获取要播放的音频数据,首先依次获取参与会话的Channel中NetEQ存储的音频帧,可以对其做AGC和NS处理;然后混合多个Channel的音频信号,得到混合音频,传递给AudioProcessing模块进行远端分析。最后播放出来。

如下为本地回环录音和播放代码:

VoiceEngine* ve = VoiceEngine::Create();
VoEBase* base = VoEBase::GetInterface(ve);
base->Init();
int chId = base->CreateChannel();
base->SetSendDestination(chId,3000,"127.0.0.1",4000);
base->SetLocalReceiver(chId,3000,3001,"127.0.0.1");
base->StartPlayout(chId);
base->StartReceive(chId);
base->StartSend(chId);

//....sleep...wait.....

base->StopSend(chId);

base->StopReveive(chId);

base->StopPlayout(chId);

base->Terminate();

本文介绍WebRTC音频模块组成和结构,详细介绍音频引擎的配置和启动,相信看完本文后,很多人可以利用WebRTC完成一个音频通话程序开发。

一、对外接口

音频部分的对外主要接口如下,各个接口之间的关系如图1所示。

1)VoiceEngine:负责引擎的所有接口查询,存储共享数据信息ShareData。

2)VoEBase:负责音频处理的基本操作。

3)VoEAudioProcessing:音频信号处理接口,设置各个音频处理项的参数。

4)VoECodec:音频编解码接口,提供支持的编解码器查询,音频编解码设置。

5)VoEHardware:音频硬件设备接口,负责音频硬件设备的设置。

其它的接口还有VoENetEqStats,VoENetwork,VoERTP_RTCP,VoEVideoSync,VoEVolumeControl,VoEFile,VoECallReport,VoEDtmf,VoEMeidaProcess和VoEEncryption。

WebRTC使用继承实现接口转换和查询,接口之间的数据共享是通过ShareData完成,首先VoiceEngineImpl继承各个对外接口的实现,所以可以从VoiceEngineImpl很容易获取其他对外接口。而VoiceEngineImpl本身也继承ShareData,当从VoiceEngineImpl获取其他对外接口的同时,隐式的传递了ShareData指针,因此各个接口可以很方便的获取到ShareData的数据信息。因此虽然类与类之间的关系看起来比较混乱,但是使用上比较方便。

利用VoiceEngine获取对外接口:VoEInterfaceXX* pInterf = VoEInterfaceXX:GetInterface(pVoiceEngine);

二、模块组成

主要由五大模块组成:AudioDeviceModule音频设备模块,AudioProcess音频处理模块,AudioCodingModule音频编码模块,AudioConferenceMixer混音模块和RtpRtcp传输模块。

ShareData用于粘合各个模块之间的关系,负责管理全局的对象,包括AudioDeviceModule,TransmitMixer,OutputMixer,ChannelManager和AudioProcess。

录音流程:AudioDeviceWinCore负责采集音频数据,传递到AudioDeviceBuffer中缓存,AudioDeviceBuffer则将数据送入TransmixMixer,首先交给AudioProcess进行近端音频处理,完成后分发到各个Channel中,Channel则通过AudioCodingModule进行编码,编码后再交付到RtpRtcp中经由RTPSender发送出去。

接收流程:RTPReceiver负责接收音频RTP包,接收到RTP包后交给Channel,Channel转交给AudioCodingModule中的ACMNetEQ模块,进行解码缓存。

播放流程:Channel从ACMNetEQ模块中取出缓存的解码音频数据,如果需要进行远端数据处理的话,传递给AudioProcess处理。最后所有Channel都汇入到OutputMixer中进行混音,混音后再传递到AudioProcess进行远端音频分析。最后送入AudioDeviceModule中的AudioDevceWinCore播放。

三、配置

1、音频引擎创建与删除

VoiceEngine*pVoeEngine = VoiceEngine::Create();

VoiceEngine::Delete(pVoeEngine);

2、音频收发

1)音频通话链路创建

WebRTC中的Channel,为一路音频。作为网络语音通信,至少要创建一路音频Channel。

Channel没有提供对外接口,是有VoEBase来管理的,通过索引号来选定对应的Channel。

VoEBase*base = VoEBase::GetInterface(pVoeEngine);

int ch0 =base->CreateChannel();

2)网络端口设置

音频通过RTP和RTCP发送出去,RTP和RTCP使用UDP实现,需要配置网络端口和地址。

//设置发送给.2机器的3000端口

base->SetSendDestination(ch0,3000,”192.168.8.2”);

//在本机的3000端口接收RTP包

base->SetLocalReceiver(ch0,3000);

3)音频编码选择

VoECodec负责编解码的配置。

VoECodec*codec = VoEBase::GetInterface(pVoeEngine);

设置Channel的编码类型之前,要查询支持的编码列表。

CodecInstinst;

Intnum = codec->NumOfCodecs();

for(int i=0; i<num; ++i)

{

Codec->GetCodec(I,inst);

//打印编码信息

}

//设置编码0

Codec->GetCodec(0,inst);

Codec->SetSendCodec(ch0,inst);

WebRTC自动识别编码类型,因此解码不需要设置。

4)启动

启动播放:base->StartPlayout(ch0);该操作含义是将通话ch0进行混音输出。

启动接收:base->StartReceive(ch0);开始接收后,每增加一路通话,引擎会将音频进行混音再输出。

启动发送:base->StartSend(ch0);启动发送的时候,会检查是否正在录音,如果已经开启录音,则不再开启;否则会执行音频设备录音操作。

3、音频处理的配置

VoEAudioProcessing负责音频处理的配置。

VoEAudioProcessing*pAudioProc = VoEAudioProcessing::GetInterface(pVoeEngine);

//启动AGC功能

pAudioProc->SetAgcStatus(true);

4、音频设备的配置

VoEHardware接口可以查看录音和播放设备,可以选择指定的设备进行音频通话。

VoEHardware*pHardware=VoEAudioProcessing::GetInterface(pVoeEngine);

Int numin =pHardware->GetNumOfRecordingDevices();

For(int i=0;i<numin; ++i)

{

pHardware->GetRecordingDeviceNames(…)

//打印录音设备

}

//选择设备0作为录音设备

pHardware->SetRecordingDevice(0);

播放设备配置类似。

时间: 2024-11-05 12:23:23

WebRTC源码分析:音频模块结构分析的相关文章

nginx源码分析--nginx模块解析

nginx的模块非常之多,可以认为所有代码都是以模块的形式组织,这包括核心模块和功能模块,针对不同的应用场合,并非所有的功能模块都要被用到,附录A给出的是默认configure(即简单的http服务器应用)下被连接的模块,这里虽说是模块连接,但nginx不会像apache或lighttpd那样在编译时生成so动态库而在程序执行时再进行动态加载,nginx模块源文件会在生成nginx时就直接被编译到其二进制执行文件中,所以如果要选用不同的功能模块,必须对nginx做重新配置和编译.对于功能模块的选

nginx源码分析之模块初始化

在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要通过结合源码来分析模块的初始化过程. 稍微了解nginx的人都知道nginx是高度模块化的,各个功能都封装在模块中,而各个模块的初始化则是根据配置文件来进行的,下面我们会看到nginx边解析配置文件中的指令,边初始化指令所属的模块,指令其实就是指示怎样初始化模块的. 模块初始化框架 模块的初始化主要

兄弟连区块链教程open-ethereum-pool矿池源码分析unlocker模块

兄弟连区块链教程open-ethereum-pool以太坊矿池源码分析unlocker模块open-ethereum-pool以太坊矿池-unlocker模块 unlocker模块配置 json"unlocker": {????"enabled": false,????"poolFee": 1.0,????"poolFeeAddress": "",????"donate": true,?

区块链教程btcpool矿池源码分析StratumServer模块解析

兄弟连区块链教程btcpool矿池源码分析StratumServer模块解析 核心机制总结 接收的job延迟超过60秒将丢弃 如果job中prevHash与本地job中prevHash不同,即为已产生新块,job中isClean状态将置为true????* true即要求矿机立即切换job 三种情况下将向矿机下发新job:???? 收到新高度的job???? 过去一个job为新高度且为空块job,且最新job为非空块job????* 达到预定的时间间隔30秒 最近一次下发job的时间将写入文件(

nginx源码分析——event模块

源码:nginx 1.12.0 一.简介 nginx是一款非常受欢迎的软件,具备高性能.模块化可定制的良好特性.之前写了一篇nginx的http模块分析的文章,主要对http处理模块进行了分析讲解,同时也涉及了nginx模块化的内容.至于nginx高性能的原因,希望能够在在这篇文章中就自己对于这方面的理解给大家分享一下. nginx的event处理模型包含两个方面:高效的IO处理函数,事件的异步处理(可选的线程池). 二.IO复用函数 nginx中包含epoll.poll.select.devp

nginx源码分析——http模块

     源码:nginx 1.12.0   一.nginx http模块简介 由于nginx的性能优势,现在已经有越来越多的单位.个人采用nginx或者openresty.tengine等衍生版来作为WEB服务器.负载均衡服务器.安全网关来使用.在这些场景下,依赖的就是nginx的http模块,nginx的设计者采用模块化的设计思路,允许用户在http请求处理的各个阶段添加自己设计的模块来实现自己的一些逻辑,扩充一些功能. 二.http模块功能介绍 http模块的丰富功能其实是由一个个http

Zepto源码分析-deferred模块

源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT license. // // Some code (c) 2005, 2013 jQuery Foundation, Inc. and other contributors ;(function($){ var slice = Array.prototype.slice function Deferr

Zepto源码分析-form模块

源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT license. ;(function($){ /** * 序列表单内容为JSON数组 * 返回类似[{a1:1},{a2:2}]的数组 * @returns {Array} */ $.fn.serializeArray = function() { var name, type, result =

jQuery源码分析--Event模块(2)

接下来就是触发事件了.事件触发后的处理函数的分发主要靠两个函数,一个jQuery.event.dispatch,一个是jQuery.event.handlers.这个dispatch会调用handlers,而handlers会返回一个数组,这个数组是符合本次事件条件的所有处理函数对象.dispatch只管执行.那这个handlers是如何运作的呢.绑定在一个元素上面的非代理事件是肯定要被触发的,所以会全数被返回.主要是代理事件的筛选,jQuery会从触发了事件(target所指的元素)的元素一级