github:https://github.com/bigonelby/webrtcUml/tree/master/latest
这张图介绍了音频发送端采集,编码,发送的流程。整个内容分为8部分,分别为:核心Core;音频采集;AudioUnit;AudioSession;Resample && Process;Send Stream;Encode和Network
首先看核心Core。万物皆由始,Audio的整个内容的源头是什么呢?可以认为是VoiceEngineInterface,正是引擎层最终构建出完整的音频世界。实现这个Interface的正是大名鼎鼎的WebrtcVoiceEngine。WebrtcVoiceEngine有几个关键的组件,比如AudioDeviceModule,这个组件就是负责音频采集的,我们从这个组件开始看起
音频采集。刚才已经提到,WebrtcVoiceEngine有一个关键的组件,即AudioDeviceModule。这是一个Module,是一个抽象层,不同的平台有不同的实现。对于iOS而言,实现类是AudioDeviceModuleIOS。当然,这个实现类也不直接接触底层,真正干活的是他的小弟AudioDeviceIOS,这是整个音频采集层的核心。后面我们可以看到这个类起到承上启下的关键作用!我们先看看这个类有若干身份:首先他是一个通用的音频设备,即AudioDeviceGeneric;其次,他还是VoiceProcessingAudioUnitObserver,因此可以接收系统AudioUnit传回来的数据;最后,他还是AudioSessionObserver,这就表示他还可以监听AudioSession的一举一动。在收到AudioUnit的采集或播放数据后,他还会将数据缓存到FineAudioBuffer中,并最终送到AudioDeviceBuffer,最终通过注册的audio_transrpot_cb_将采集数据分发给AudioTransport。至此有个疑问,为何有FineAudioBuffer和AudioDeviceBuffer两个Buffer呢?实际上FineAudioBuffer作为更精细化的buffer,从设备获取的数据可能不足10ms,但是整个webrtc的pipeline里处理的音频数据都是以10ms为frame的。那么如何处理这种不对等呢?自然是引入buffer。因此当FineAudioBuffer积累的数据超过10ms时,就会构建成AudioDeviceBuffer,这样就可以保证完整的10ms数据分发给底层了!
AudioUnit和AudioSession。这两个才是系统级别的api,是真正的打工仔,webrtc通过这两个类控制音频模块。通过VoiceProcessingAudioUnit操纵AudioUnit,顾名思义,关注的是voice process后的i/o。并且会注册回调函数到系统的AudioUnit中。因此当AudioUnit中有采集数据时,就会通过注册好的回调函数OnDeliverRecordedData回调上来,并最终通过observer_将这个信息告知VoiceProcessingAudioUnitObserver,即AudioDeviceIOS。AudioDeviceIOS再通过VoiceProcessingAudioUnit的Render方法,得到真正的采集数据,于是将数据缓存至FineAudioBuffer中,并最终分发出去。除了采集数据,我们还需要一些控制信息,比如音量变化了,Router变化了等等信息,这就需要AVAudioSession了。AudioDeviceIOS通过RTCAudioSession向AVAudioSession中注册感兴趣的事件,通过NSNotificationCenter这个消息大管家注册具体感兴趣的通知,比如AVAudioSessionRouteChangeNotification等。当有相应事件发生时,就会通知给RTCAudioSession,RTCAudioSession进而通过注册的delegate将消息分发,通过RTCNativeAudioSessionDelegateAdapter,最终告知AudioSessionObserver,即AudioDeviceIOS,至此这些关键信息也可以被AudioDeviceIOS捕获。
Resample && Process。上面已经清楚了AudioDeviceIOS确实神通广大,他可以获取采集音频,和音频相关的控制信息的变化,并最终将采集的音频数据打包成10ms的AudioDeviceBuffer,并分发给AudioTransport。接下来,顺理成章的,AudioTransport的实现类AudioTransportImpl收到了来自AudioDeviceIOS的这份大礼,首先会交由PushResampler,如需要,则进行重采样工作,接着有AudioProcessing模块完成音频的3a处理
SendStream。至此,采集数据已经重采样并进行了3a处理,接下来就是分发给注册到AudioTransportImpl的audio_senders_了。这个类是AudioSender,其实现者是AudioSendStream,是在什么时机注册的呢?其实是大管家AudioState会维护一个sending_streams_的map,其value就是AudioSendStream。因此每次新加一个成员,即调用AudioState的AddSendingStream的时候,首先会将新成员管理在sending_streams_这个map中,另外就会调用UpdateAudioTransportWithSendingStreams将所有的AudioSendStream注册到AudioTransport中。因此AudioSendStream可以顺利拿到采集数据,并交给ChannelSendInterface进行接下来的处理
Encode && Network。ChannelSendInterface的实现类即为ChannelSend,顾名思义,他要进行真正的send工作了,在此之前要先进行编码。因此ChannelSend首先将采集数据交给了AudioCodingModule,这个模块是专门负责音频编码的。完成编码工作的是其实现类AudioCodingModelImpl的成员encode_stack_,这是AudioEncoder,负责真正的编码工作。和视频不同的是,这个编码是一个同步的过程,AudioEncoder将生成EncodedInfo对象同步返回。于是AudioCodingModuleImpl在收到这个编码后的信息后,交由packetization_callback_处理,即AudioPacketizationCallback的SendData接口。实现这个接口的还是ChannelSend,你看编码后的数据再次回到了ChannelSend的手中。这样就可以进行下一步的网络发送了。主要有两个步骤,其一是通过rtp_rtcp_检验是否应该发送RTCP数据,如果需要,则通过RTCPSender将相应的RTCP包发送。最后就是通过rtp_sender_audio_即RTPSenderAudio,将数据发送出去即SendAudio。最终构建RtpPacketToSend,并通过RTPSender的EnqueuePackets方法,将包入队列。后面的pacing过程就和video是一样的了,这里就不再赘述了。