WebRTC音频 05 - 音频采集编码

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值