WebRTC音频01 - 设备管理
WebRTC音频 02 - Windows平台设备管理
WebRTC音频 03 - 实时通信框架
WebRTC音频 04 - 关键类
WebRTC音频 05 - 音频采集编码(本文)
一、前言:
前面章节介绍了媒体协商和音频采集\播放的设备管理。接下来串起来分析下。
二、音频数据流转:
可以看出,我们音频数据流转会经过:采集设备、编码器、解码器、播放设备。我们前面设备管理章节已经分析了这几个设备的创建时机,大概回忆下:
- 采集设备和播放设备创建:在媒体引擎里面调用Adm模块的Init函数时候完成的。
- 编解码器创建:编解码器创建是需要呼叫端和被呼叫端商量的(协商),因此必须在呼叫端发送Offer给被呼叫端,被呼叫端回复Remote之后,两端选择出来了一个共同的编解码器参数,这个时候才可以创建。
采集/播放设备初始化前面讲很多了,在这儿看下编解码器的创建,看得出与媒体协商有很多关系。
三、编码器创建:
由于编解码器都属于ACM模块(AudioCodingModule),
1、类图:
可以看出ACM对外接口类AudioCodingModule里面定义了音频编码必须的方法,AudioCodingModuleImpl又做了补充,并且有个成员变量:AudioEncoder的对象指针。而AudioEncoder就是具体解码器类的接口类。下面具体看看这几个重要的方法。
- AudioCodingModule:接口类
- Create负责创建具体acm实例,也就是AudioCodingModuleImpl;
- SetEncoder:设置编码器;
- RegisterTransportCallback:上层用来注册回调函数的,编解码模块生成的数据通过这个给上层;
- Add10msData:负责给编码器送10ms的PCM数据;
- PlayoutData10Ms:负责从解码器获取10ms的PCM数据,送给扬声器播放;
- IncomingPacket:负责从网络模块接收已经去掉Rtp头但是编码过的纯音频数据;拿到之后交给解码器解码,然后调用 PlayoutData10Ms交给扬声器播放;
- AudioCodingModuleImpl:具体实现类
- encoder_stack:就是ACM模块选择的具体编码器的抽象指针;
- PreprocessToAddData:预处理要编码器的数据;
- Encode:进行具体编码工作;
2、流程图:
创建好Offer之后:
- 具体使用哪个编码器是在媒体协商的时候做的;先创建本地offer,然后调用 SetLocalDescription 设置offer到本地,然后调用VoiceChannel::SetLocalContent_w,最后构造出AudioSendStream(也就是Call模块的发送流);
- 前面说了Call模块里面的Stream里面有具体的Channel来连接编解码器,于是我们调用CreateChannelSend创建Channel;
- 在ChannelSend的构造函数中就创建了 AudioCodingModuleImpl;同时将自己(ChannelSend)作为回调对象,通过RegisterTransportCallback注册给AudioCodingModuleImpl;
收到Answer之后:
- 当对端的answer过来之后,通过SetRemoteContent_w->…->构造出具体的Opus编码器(AudioEncoderOpusImpl);
- 再通过SetEncoder将构造好的编码器设置给 AudioCodingModuleImpl;
- 至此,AudioCodingModuleImpl就可以使用 AudioEncoderOpusImpl这个具体的编码器了;
3、源代码走读:
前面协商的流程跳过,具体调用堆栈如下:
我们从ChannelSend的构造函数开始分析:
// 文件路径:audio\channel_send.cc
ChannelSend::ChannelSend(
Clock* clock,
TaskQueueFactory* task_queue_factory,
ProcessThread* module_process_thread,
Transport* rtp_transport,
RtcpRttStats* rtcp_rtt_stats,
RtcEventLog* rtc_event_log,
FrameEncryptorInterface* frame_encryptor,
const webrtc::CryptoOptions& crypto_options,
bool extmap_allow_mixed,
int rtcp_report_interval_ms,
uint32_t ssrc,
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
TransportFeedbackObserver* feedback_observer)
: event_log_(rtc_event_log),
_timeStamp(0), // This is just an offset, RTP module will add it's own
// random offset
_moduleProcessThreadPtr(module_process_thread),
input_mute_(false),
previous_frame_muted_(false),
_includeAudioLevelIndication(false),
rtcp_observer_(new VoERtcpObserver(this)),
feedback_observer_(feedback_observer),
rtp_packet_pacer_proxy_(new RtpPacketSenderProxy()),
retransmission_rate_limiter_(
new RateLimiter(clock, kMaxRetransmissionWindowMs)),
frame_encryptor_(frame_encryptor),
crypto_options_(crypto_options),
encoder_queue_(task_queue_factory->CreateTaskQueue(
"AudioEncoder",
TaskQueueFactory::Priority::NORMAL)),
fixing_timestamp_stall_(
!field_trial::IsDisabled("WebRTC-Audio-FixTimestampStall")) {
module_process_thread_checker_.Detach();
// 创建Acm,并保存到成员变量中
audio_coding_.reset(AudioCodingModule::Create(AudioCodingModule::Config()));
// ...
// 将自己注册到ACM模块当中
int error = audio_coding_->RegisterTransportCallback(this);
}
看得出,只做了两件值得注意的事情:创建acm,将自己注册进ACM模块,便于接收编码好的数据。
然后,就是等待Answer过来,Answer过来之后调用栈如下:
我们就从WebRtcVoiceMediaChannel::SetSendCodecs开始分析:
// Utility function called from SetSendParameters() to extract current send
// codec settings from the given list of codecs (originally from SDP). Both send
// and receive streams may be reconfigured based on the new settings.
bool WebRtcVoiceMediaChannel::SetSendCodecs(
const std::vector<AudioCodec>& codecs) {
// ...
// 遍历编码器列表
for (const AudioCodec& voice_codec : codecs) {
// 要排除舒适噪音编码器、拨号音编码器、Red编码器
if (!(IsCodec(voice_codec, kCnCodecName) ||
IsCodec(voice_codec, kDtmfCodecName) ||
IsCodec(voice_codec, kRedCodecName))) {
webrtc::SdpAudioFormat format(voice_codec.name, voice_codec.clockrate,
voice_codec.channels, voice_codec.params);
// 去音频引擎获取编码器信息
voice_codec_info = engine()->encoder_factory_->QueryAudioEncoder(format);
if (!voice_codec_info) {
RTC_LOG(LS_WARNING) << "Unknown codec " << ToString(voice_codec);
continue;
}
// 获取当前编码器的规格spec
send_codec_spec = webrtc::AudioSendStream::Config::SendCodecSpec(
voice_codec.id, format);
if (voice_codec.bitrate > 0) {
send_codec