WebRTC音频引擎实现分析

WebRTC的音频引擎作为两大基础多媒体引擎之一,实现了音频数据的采集、前处理、编码、发送、接收、解码、混音、后处理、播放等一系列处理流程。本文在深入分析WebRTC源代码的基础上,学习并总结其音频引擎的实现框架和细节。

1. WebRTC音频引擎整体架构

WebRTC音频引擎的实现代码主要分布在如下几个源码目录中:

webrtc/audio
webrtc/common_audio
webrtc/media/engine
webrtc/voice_engine
webrtc/module/audio_coding
webrtc/module/audio_conference_mixer
webrtc/module/audio_device
webrtc/module/audio_processing

WebRTC音频引擎的整体架构如图1所示。

图1 WebRTC音频引擎的整体架构.png

从整个WebRTC框架结构来看,音频引擎和和视频引擎都位于比较底层的位置,负责音视频数据的采集、编解码、渲染播放等工作。音视频引擎的上一层是多媒体引擎WebRtcMediaEngine2,是对底层音视频引擎VideoEngine的进一步高层抽象,由WebRtcVoiceEngine对VoiceEngine进行封装,WebRtcVideoEngine2对VideoEngine进行封装。

在内部实现上,音频引擎VoiceEngineImpl通过一系列对象来实现音频处理,包括VoEAudioProcessingImpl、VoECodecImpl、VoENetworkImpl等等,每个对象负责具体某方面功能,例如VoEAudioProcessingImpl负责调用底层AudioProcessing模块对音频数据进行预处理。在这些功能对象中,比较重要的有VoEBaseImpl、SharedData和Channel。其中VoEBaseImpl是连接音频设备AudioDevice和音频引擎VoiceEngineImpl的纽带,是音频数据流水线上的重要一站;SharedData是一个聚合类,持有一系列重要对象;Channel则代表一路音频数据,负责大部分对该路数据的重要操作,包括音频数据的前处理、编解码、发送和接收、后处理、混音等等。

从功能依赖上讲,VoiceEngineImpl依赖五个重要的底层功能模块:音频数据采集和播放AudioDeviceModule 、音频数据预处理AudioProcessing、音频数据编解码AudioCodingModule、接收端音频数据缓冲区NetEq、接收端混音AudioConferenceMixer。此外音频数据编解码还依赖一系列音频编解码器如G711、G722、Opus等等。在发送端,音频数据由AudioDevice采集得到,经过AudioProcessing预处理后,到达AudioCodingModule进行编码,然后由RTPRTCP模块发送到网络。在接收端,音频数据经过RTPRTCP模块接收后到达AudioCodingModule,存储在NetEq中进行抖动控制和错误消除,然后解码。解码后的数据经过AudioConferenceMixer进行混音,最终发送到AudioDeviceModule进行播放。

2. WebRTC音频引擎重要数据结构

本节在第一节的基础上,静态分析WebRTC音频引擎实现上的一些重要数据结构。为了便于理解,采用从高层到底层的顺序进行分析。

WebRtcMediaEngine2在MediaEngine层对底层的音视频引擎进行封装,分别是WebRtcVoiceEngine和WebRtcVideoEngine2。而WebRtcVoiceEngine则封装了音频引擎层的VoiceEngineImpl对象。VoiceEngineImpl以多继承方式聚集一系列接口,包括SharedData、VoEAudioProcessingImpl、VoECodecImpl、VoENetworkImpl、VoEBaseImpl等等。

SharedData是一个聚合类,内部包括ChannelManager、AudioDeviceModule、OutputMixer、TransmitMixer、AudioProcess等对象,大部分关于VoiceEngineImpl的操作最终都会经过SharedData委托给内部对象。在创建SharedData对象时,其构造函数会创建一个名为“VoiceProcessThread”的线程,该线程用以处理音频引擎的周期性事务。

VoEBaseImpl是连接底层音频采集播放模块AudioDeviceModule和音频引擎内部音频通道Channel的重要纽带。它实现三个接口:VoEBase负责创建Channel、启动/停止音频数据的发送/接收;AudioTransport负责AudioDeviceModule模块和Channel之间数据传送,包括采集后的音频数据发送到Channel进行编码、从Channel拉取解码后的音频数据进行播放;AudioDeviceObserver负责把AudioDeviceModule工作过程中出现的错误和警告向上层报告。

Channel是对一路音频数据及其处理过程的抽象,是VoiceEngineImpl中最重要的底层实现类,其继承并实现RtpData、RtpFeedback、FileCallback、Transport、PacketizationCallback、ACMVADCallback、MixerParticipant等多个接口,分别负责音频数据编码后回掉、发送到网络、接收后存储到NetEq缓冲区、播放前混音等一些列重要操作。在类内部, Channel包含的重要成员对象包括RtpReceiver、RtpRtcpModule、AudioCodingModule、CodecManager、OutputMixer、TransmitMixer、ProcessThread、AudioDeviceModule、VoiceEngineObserver、Transport、AudioProcessing、PacketRouter等等。

AudioDeviceModule模块负责音频数据的采集和播放,是音频数据的发源地和目的地。其内部主要包含三个对象:AudioDeviceModule、AudioDeviceGeneric和AudioDeviceBuffer。AudioDeviceModule是对外接口类,负责对AudioDevice和AudioDeviceBuffer进行管理、设置和对音频数据进行传递。AudioDevice是平台相关的音频设备,它管理音频采集设备和播放设备,包括初始化、设置音频采集设备和播放设备、开始/停止设备、控制设备音量、设置设备的音频数据缓冲区,等等。在初始化阶段,AudioDevice创建采集线程和播放线程,用来执行采集任务和播放任务。AudioDeviceBuffer是音频数据缓冲区,负责临时存储和传递音频数据。

AudioCodingModule模块负责音频数据的编解码,它由音频引擎层的Channel持有并调用。在内部,AudioCodingModul包含如下重要对象:AcmReceiver、AudioEncoder、AudioDecoder和NetEq,其中AcmReceiver负责接收音频数据并存储到NetEq中,NetEq负责音频数据的抖动消除和错误隐藏,AudioEncoder负责音频数据编码,AudioDecoder负责音频数据解码。WebRTC支持一系列音频编解码器,包括CNG、G711、G722、ilbc、isac、opus等等。数据编码完成后通过AudioPacketizationCallback接口回调到Channel进行下一步发送工作,数据解码完成后由Channel拉取进行下一步播放工作。

Audio Processing模块实现音频数据的预处理操作,包括声学回声消除AEC、自动增益控制AGC、噪声抑制NS、语音活动检测VAD,等等。AudioProcessing聚合一系列子模块实现各种音频处理算法,其重要的对外接口由两个:ProcessStream()和ProcessReverseStream(),前者负责采集后编码前的音频数据的前处理,后者播放前解码后的音频数据的后处理。

TransmitMixer用于发送端混音。OutputMixer用于接收端混音。OutputMixer在内部使用AudioConferenceMixer负责解码后音频数据的混音操作。

3. WebRTC音频引擎数据流分析

本节在前两节分析的基础上,动态分析WebRTC音频引擎的数据流,包括音频数据的采集、前处理、编码、发送、接收、缓存、解码、混音、后处理、播放。如图2所示。

图2 WebRTC音频引擎数据流.png
3.1 音频引擎创建及初始化

音频引擎的创建及初始化流程如图3所示:

图3 WebRTC音频引擎创建及初始化.png

WebRTC音频引擎的创建从PeerConnectionFactory对象的创建及初始化开始,这个过程由WebRTC的应用程序发起,并在signal线程在进行,最终调用CreateMediaEngine_w()转到worker线程。在worker线程中,先创建WebRtcMediaEngine2,进而WebRtcVoiceEngine,最终创建VoiceEngineImpl。而最重要的初始化操作则VoEBaseImpl的Init()函数中完成。

3.2 音频数据的采集和编码

音频数据的采集是平台相关的,在此以Windows平台为例,整个采集和编码过程如图4所示:

图4 音频数据的采集和编码.png

在Windows 7平台上,WebRTC默认使用Windows Core接口采集和播放音频数据。采集线程叫做webrtc_core_audio_capture_thread,线程入口是AudioDeviceWindowCore的CaptureAudio函数。该函数从麦克风中采集到音频数据后,存储到AudioDeviceBuffer中,并调用DeliverRecordedData()把音频数据向上推送到VoEBaseImpl对象中。VoEBaseImpl对象调用ProcessRecordedDataWithAPM()函数进行处理,首先创建AudioFrame对象并进行前处理,然后进行解复合和混音,最后数据到达Channel进行编码和发送。

在Channel对象中,编码任务委托给AudioCodingModule对象,首先从AudioFrame中获取10ms的音频数据,然后调用具体的编码器如Opus进行编码。编码后的数据通过AudioPacketizationCallback接口的SendData()回到Channel对象进行下一步的RTP打包和发送过程。

3.3 音频数据的发送

音频数据在AudioCodingModule编码完成后,通过回调接口回到Channel对象进行下一步的RTP打包和发送过程,如图5所示。

图5 音频数据的发送.png

Channel调用把数据发送给RtpRtcp模块,后者经过一系列的调用进行RTP打包后到达RtpSender对象。如果没有配置平滑发送线程PacedSender,则RtpSender直接调用SendPacketToNetwork()把数据发送到network线程。否则数据会先存储到PacedSender线程中,再由后者进行平滑发送,最终数据发送到network线程。

3.4 音频数据的接收

network线程从网络接收到音频数据后,交给worker线程进行下一步接收。Worker线程的工作流程如图6所示。

图6 音频数据的接收.png

worker线程在收到网络数据后,通过BaseChannel的OnPacketReceived()接口向下传递,到达MediaEngine层的WebRtcVoiceMediaChannel,然后继续向下经过Call和AudioReceiveStream到达Channel的ReceivedRTPPacket()接口。Channel把数据推送到RtpRtcp模块进行RTP解包操作,得到纯音频数据,然后再经过Channel的OnReceivedPaylaodData()接口把数据推送到AudioCodingModule模块,最终经过AcmReceiver把数据存储在NetEq中,进行抖动消除和错误隐藏等操作。

3.5 音频数据的解码和播放

worker线程把接收到的音频数据存储到NetEq后,为播放线程提供数据源。播放线程具体负责音频数据解码和播放操作。Windows Core接口的播放线程名称为webrtc_core_audio_render_thread,其工作流程如图7所示。

图7 音频数据的解码和播放.png

AudioDeviceWindowsCore设备向AudioDeviceBuffer请求音频数据,后者进一步向VoeBaseImpl请求数据,接下来主要操作都在GetPlayoutData()中进行:1)在AudioConferenceMixer中对所有活动Channel中的音频数据进行混音,每个Channel都作为混音的参与者。这包括获取解码后的音频数据(从AudioCodingModule模块中解码音频数据并返回)、对音频数据进行混音、得到最终音频数据并返回给OutputMixer。2)OutputMixer对混音后的音频数据执行AudioProcessing后处理操作。3)对后处理操作后的音频数据进行再混合和再采样。最终OutputMixer拿到最终的音频数据,交给VoEBaseImpl,并进一步向下交给AudioDeviceBuffer。AudioDeviceBuffer则把数据交给AudioDeviceWindowsCore进行播放操作。

至此,我们完整分析了音频数据从采集到播放的全部过程。

4. 总结

本文在深入分析WebRTC关于音频引擎实现代码的基础上,首先给出了WebRTC音频引擎的整体框架,然后静态分析了其实现上的若干重要对象,最后完整分析了音频数据从采集到播放的完整流动过程。通过本文,对WebRTC有了更深入的认识和体会,为未来进一步学习打下坚实基础。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342