本文基于 WebRTC 中的示例应用 peerconnection_client 分析 WebRTC Audio 接收和发送的关键过程。首先是发送的过程,然后是接收的过程。
创建 webrtc::AudioState
应用程序择机初始化 PeerConnectionFactory
:
由 Conductor::InitializePeerConnection()
的代码可知,PeerConnectionFactory
竟然是随同 peer connection一起创建的。
在 webrtc/src/pc/channel_manager.cc
文件里定义的 ChannelManager::Init()
函数中会在另一个线程中起一个 task,调用 media_engine_->Init()
,完成媒体引擎的初始化:
媒体引擎初始化过程中,将会创建 AudioState:
AudioState 伴随着 PeerConnectionFactory
、ChannelManager
和 MediaEngine
的创建及初始化一起创建。
创建 WebRTC Call
应用程序根据需要创建 peer connection:
PeerConnectionFactory::CreatePeerConnection()
在创建 connection 时,会在另一个线程中起一个task 来创建 Call:
创建 WebRTC Call 的过程如下:
不难看出,在 WebRTC 中,Call 是 per peer connection 的。
为 WebRTC Call 注入的 AudioState 来自于全局的 MediaEngine 的 VoiceEngine。AudioState 是全局的,而 Call 则是 connection 局部的。
创建 WebRtcAudioReceiveStream
WebRTC 应用需要起一个专门的专门的连接,用于接收媒体协商信息。在收到媒体协商信息之后,则将媒体协商信息进行层层传递及处理:
在 webrtc/src/pc/channel.cc 文件里定义的 BaseChannel::SetRemoteContent()
函数中将收到的媒体协商信息,抛给另一个线程进行处理:
WebRtcAudioReceiveStream
最终由 webrtc/src/media/engine/webrtc_voice_engine.cc 文件中定义的 WebRtcVoiceMediaChannel::AddRecvStream()
创建:
创建 WebRtcAudioReceiveStream 时,也会一并创建 Call 的 AudioReceiveStream:
WebRtcAudioReceiveStream
创建完成后,随即将其加进 mixer,作为 mixer 的 audio source 之一:
WebRTC 中音频接收处理的关键流程
1. 从网络收到 UDP 包
|
|
在 webrtc/src/rtc_base/physical_socket_server.cc 文件里 PhysicalSocketServer
类的 Wait()
函数中,通过 epoll 机制等待网络数据包的到来。当数据包到来时,经过层层处理及传递,一直被传到 webrtc/src/pc/channel.cc 文件里 BaseChannel
类的 OnPacketReceived()
函数中,该函数又将数据包抛进另一个线程进行处理:
2. 媒体引擎对收到的音频包的处理
|
|
webrtc/src/pc/channel.cc 文件里 BaseChannel
类的 ProcessPacket()
函数将收到的音频包送进媒体引擎进行处理,这一过程包括,根据 RTP 包的 ssrc 派发进不同的 channel,ACM receiver 的处理,一直到最终插入 NetEq 的缓冲区。在 NetEq 中将会完成数据包的重排序,网络对抗,音频的解码等处理操作。
音频数据的解码及播放
AudioDevice 组件被初始化时,即会启动一个播放线程,如 (webrtc/src/modules/audio_device/linux/audio_device_pulse_linux.cc):
这个线程不断地从媒体引擎中拿数据进行播放,这个过程如下:
WebRTC 中对于音频,是即解码即播放的,播放和解码在同一个线程中完成,此外从解码到播放,还将完成回声消除,混音等处理。
上面创建 WebRtcAudioReceiveStream
,并接收到音频数据包,是这里能够从 mixer 拿到数据并播放的基础。
webrtc::AudioSendStream 的创建
webrtc::AudioSendStream 的创建由应用程序发起:
webrtc/src/pc/channel.cc
文件里的 BaseChannel::SetLocalContent()
将 MediaContentDescription 抛进 worker_thread 中进一步处理:
webrtc::AudioSendStream 最终在 Call 中创建:
音频数据的发送
如前面看到的,AudioDevice 组件被初始化时,在启动播放线程的同时,还会启动一个录制线程。录制线程捕获录制的音频数据,一路传递进行处理:
AudioDevice 将录制的音频数据一直传递到 webrtc/src/audio/channel_send.cc 文件里的 ChannelSend::ProcessAndEncodeAudio()
:
在该函数中,录制获得的 AudioFrame 被抛进编码的线程中的 task 进行编码,封装为 RTP 包,并送进 PacedSender
,PacedSender
将 RTP 包放进 queue 中:
PacedSender
中的另一个线程从 queue 中拿到 RTP 包并发送:
RTP 包被 PacedSender
一直递到 webrtc/src/pc/channel.cc 文件里定义的 BaseChannel::SendPacket()
。BaseChannel::SendPacket()
将包抛进网络发送线程中发送:
网络发送线程将数据包通过系统 socket 发送到网络:
webrtc/src/rtc_base/physical_socket_server.cc 文件里定义的 PhysicalSocket::DoSendTo()
函数将数据包通过系统的 socket 发送到网络上: