WebRTC 的音頻數(shù)據(jù)編碼及發(fā)送控制管線

WebRTC 的音頻數(shù)據(jù)處理發(fā)送的概念抽象層面的完整流程如下:

-----------------------------     --------------------------     ---------------------------
|                           |     |                        | ==> | webrtc::AudioProcessing |
| webrtc::AudioDeviceModule | ==> | webrtc::AudioTransport |     ---------------------------
|                           |     |                        |          ||
-----------------------------     --------------------------          ||
                                                                      ||
                                          +=+=========================+=+
                                          | |
                                          \ /
                                           |
                    -----------------------------------------------     ---------------------
                    | webrtc::AudioSender/webrtc::AudioSendStream | ==> | webrtc::Transport |
                    -----------------------------------------------     ---------------------
                                                                                ||
                                                                                \ /
                                                 -------------------------------------------
                                                 | cricket::MediaChannel::NetworkInterface |
                                                 -------------------------------------------

AudioDeviceModule 用于控制各個(gè)操作系統(tǒng)平臺(tái)的音頻設(shè)備,主要用來做音頻的采集和播放。webrtc::AudioTransport 是一個(gè)適配和膠水模塊,它把 AudioDeviceModule 的音頻數(shù)據(jù)采集和 webrtc::AudioProcessing 的音頻數(shù)據(jù)處理及 webrtc::AudioSender/webrtc::AudioSendStream 的音頻數(shù)據(jù)編碼和發(fā)送控制粘起來,webrtc::AudioTransport 把采集的音頻數(shù)據(jù)送給 webrtc::AudioProcessing 處理,之后再把處理后的數(shù)據(jù)給到 webrtc::AudioSender/webrtc::AudioSendStream 編碼發(fā)送出去。webrtc::AudioProcessing 用于做音頻數(shù)據(jù)處理,如降噪、自動(dòng)增益控制和回聲消除等。webrtc::AudioSender/webrtc::AudioSendStream 用于對(duì)音頻數(shù)據(jù)做編碼,比如 OPUS、AAC 等,RTP 打包和發(fā)送控制。webrtc::Transport 也是一個(gè)適配和膠水模塊,它把 webrtc::AudioSender/webrtc::AudioSendStream 得到的 RTP 和 RTCP 包發(fā)送給后面的網(wǎng)絡(luò)接口模塊。cricket::MediaChannel::NetworkInterface 用于實(shí)現(xiàn)真正地把 RTP 和 RTCP 包通過底層的網(wǎng)絡(luò)接口和協(xié)議發(fā)送,如 UDP 等,ICE 的作用即為創(chuàng)建一個(gè)工作良好的網(wǎng)絡(luò)接口模塊實(shí)例。

webrtc::AudioTransport 的實(shí)現(xiàn)位于 webrtc/audio/audio_transport_impl.h/webrtc/audio/audio_transport_impl.ccwebrtc::Transport 的實(shí)現(xiàn)為 cricket::WebRtcVoiceMediaChannel(位于 webrtc/media/engine/webrtc_voice_engine.h)。如果將音頻數(shù)據(jù)處理發(fā)送流水線上的適配和膠水模塊省掉,音頻數(shù)據(jù)處理發(fā)送流水線將可簡(jiǎn)化為類似下面這樣:

-----------------------------     ---------------------------     ---------------------------     -------------------------------------------
|                           |     |                         |     | webrtc::AudioSender/    |     |                                         |
| webrtc::AudioDeviceModule | ==> | webrtc::AudioProcessing | ==> | webrtc::AudioSendStream | ==> | cricket::MediaChannel::NetworkInterface |
|                           |     |                         |     |                         |     |                                         |
-----------------------------     ---------------------------     ---------------------------     -------------------------------------------
                                                 

這里來看下 webrtc::AudioSender/webrtc::AudioSendStream 中音頻數(shù)據(jù)編碼,RTP 包打包及發(fā)送控制的設(shè)計(jì)和實(shí)現(xiàn)。webrtc::AudioSender/webrtc::AudioSendStream 的實(shí)現(xiàn)位于 webrtc/audio/audio_send_stream.h / webrtc/audio/audio_send_stream.cc,相關(guān)的類層次結(jié)構(gòu)如下圖:

webrtc::AudioSender/webrtc::AudioSendStream

在 RTC 中,音頻數(shù)據(jù)的編碼發(fā)送不同于 RTMP 之類的推流方案的地方在于,在 RTC 中,音頻編碼碼率需要根據(jù)探測(cè)到的網(wǎng)絡(luò)條件,和接收端發(fā)回來的 RTCP 包,動(dòng)態(tài)地調(diào)整變換;同時(shí),由于沒有 TCP 這樣的傳輸質(zhì)量保障機(jī)制,需要根據(jù)探測(cè)的網(wǎng)絡(luò)狀態(tài),和 RTCP 反饋包,基于 RTP 對(duì)傳輸過程做控制,如 NACK,F(xiàn)EC 等。因而在 RTC 中,音頻數(shù)據(jù)編碼發(fā)送是一個(gè)相當(dāng)復(fù)雜的過程。

webrtc::AudioSendStream 的設(shè)計(jì)與實(shí)現(xiàn)主要可以從兩個(gè)角度來看,一是配置和控制,二是數(shù)據(jù)流。

對(duì)于配置和控制,可以對(duì) webrtc::AudioSendStream 執(zhí)行的配置和控制主要有如下這些:

  • 配置音頻編碼的編碼器及編碼參數(shù),如最大碼率、最小碼率、payload type、FEC 等;
  • 配置用于把 RTP 發(fā)送到網(wǎng)絡(luò)的 webrtc::Transport 、加密參數(shù)及靜音控制等;
  • webrtc::AudioSendStream 的生命周期控制,如啟動(dòng)停止;
  • 設(shè)置網(wǎng)絡(luò)傳輸上的額外開銷大小,及動(dòng)態(tài)更新的分配碼率。

對(duì)于數(shù)據(jù)流,一是采集處理的音頻數(shù)據(jù)被送進(jìn) webrtc::AudioSendStream 以做編碼和發(fā)送處理;二是網(wǎng)絡(luò)傳回的 RTCP 包,以對(duì)編碼發(fā)送過程產(chǎn)生影響。

傳回的 RTCP 包和 webrtc::AudioSendStream 的控制接口,共同構(gòu)成音頻數(shù)據(jù)編碼及發(fā)送控制過程的信息來源。

webrtc::AudioSendStream 的實(shí)現(xiàn)中,最主要的數(shù)據(jù)處理流程 —— 音頻數(shù)據(jù)編碼、發(fā)送過程,及相關(guān)模塊如下圖:

webrtc::AudioSendStream Send Audio

這個(gè)圖中的箭頭表示數(shù)據(jù)流動(dòng)的方向,數(shù)據(jù)在各個(gè)模塊中處理的先后順序?yàn)樽宰笙蛴摇?/p>

webrtc::AudioSendStream 的實(shí)現(xiàn)的數(shù)據(jù)處理流程中,輸入數(shù)據(jù)為 PCM,來自于 webrtc::AudioTransport,輸出則為 RTP 包,被送給 webrtc::Transport 發(fā)出去。

webrtc::AudioSendStream 的實(shí)現(xiàn)內(nèi)部,數(shù)據(jù)首先會(huì)經(jīng)過 ACM 模塊 來做編碼,隨后經(jīng)過編碼的音頻幀送進(jìn) rtp_rtcp 模塊 打成 RTP 包,然后 RTP 包被送進(jìn) pacing 模塊 做平滑發(fā)送控制,最后再在 rtp_rtcp 模塊 中被給到webrtc::Transport 發(fā)出去。

站在 webrtc::AudioSendStream 的視角,基于抽象的模塊及接口,搭建數(shù)據(jù)處理流水線是這個(gè)組件的機(jī)制設(shè)計(jì),各個(gè)模塊及接口的具體實(shí)現(xiàn)是機(jī)制下的策略。這里先來看一下,關(guān)于 webrtc::AudioSendStream 的實(shí)現(xiàn)的這個(gè)數(shù)據(jù)處理流水線的搭建過程。

webrtc::AudioSendStream 的實(shí)現(xiàn)加進(jìn) webrtc::AudioTransport

webrtc::AudioSendStream 的音頻 PCM 數(shù)據(jù)來源于 webrtc::AudioTransportwebrtc::AudioSendStream 的實(shí)現(xiàn)被作為 webrtc::AudioTransport 的一個(gè) webrtc::AudioSender 加進(jìn) webrtc::AudioTransport,在 webrtc::AudioSendStream 的生命周期函數(shù) Start() 被調(diào)用時(shí)執(zhí)行,這個(gè)添加的過程大體如下:

#0  webrtc::AudioTransportImpl::UpdateAudioSenders(std::vector<webrtc::AudioSender*, std::allocator<webrtc::AudioSender*> >, int, unsigned long) ()
    at webrtc/audio/audio_transport_impl.cc:262
#1  webrtc::internal::AudioState::UpdateAudioTransportWithSendingStreams() () at webrtc/audio/audio_state.cc:172
#2  webrtc::internal::AudioState::AddSendingStream(webrtc::AudioSendStream*, int, unsigned long) ()
    at webrtc/audio/audio_state.cc:100
#3  webrtc::internal::AudioSendStream::Start() () at webrtc/audio/audio_send_stream.cc:370

webrtc::AudioSendStream 把它自己加進(jìn) webrtc::AudioStatewebrtc::AudioState 把新加的 webrtc::AudioSendStream 和之前已經(jīng)添加的 webrtc::AudioSendStream 一起更新進(jìn) webrtc::AudioTransport。這個(gè)過程有幾個(gè)值得關(guān)注的地方:

  • webrtc::AudioTransport 支持把錄制獲得的同一份數(shù)據(jù)同時(shí)發(fā)送給多個(gè) webrtc::AudioSender/webrtc::AudioSendStream,webrtc::AudioSendStream 用于管理音頻數(shù)據(jù)的編碼和編碼數(shù)據(jù)的發(fā)送控制,這也就意味著,WebRTC 的音頻數(shù)據(jù)處理管線,支持同時(shí)把錄制獲得的音頻數(shù)據(jù),以不同的編碼方式和編碼數(shù)據(jù)發(fā)送控制機(jī)制及策略發(fā)送到不同的網(wǎng)絡(luò),比如一路發(fā)送到基于 UDP 傳輸?shù)?RTC 網(wǎng)絡(luò),另一路發(fā)送到基于 TCP 傳輸?shù)?RTMP 網(wǎng)絡(luò)。

  • 如果添加的 webrtc::AudioSendStream 是第一個(gè) webrtc::AudioSendStream,webrtc::AudioState 還會(huì)自動(dòng)地初始化并啟動(dòng)錄音。

webrtc::AudioSendStream 實(shí)現(xiàn)內(nèi)部的數(shù)據(jù)處理管線搭建

webrtc::AudioSendStream 實(shí)現(xiàn)內(nèi)部的數(shù)據(jù)處理管線是分步驟搭建的完成的。我們圍繞著上面的 webrtc::AudioSendStream Send Audio 圖 來看這個(gè)過程。

webrtc::AudioSendStream 對(duì)象創(chuàng)建,也就是 webrtc::voe::(anonymous namespace)::ChannelSend 對(duì)象創(chuàng)建時(shí),會(huì)創(chuàng)建一些關(guān)鍵對(duì)象,并建立部分各個(gè)對(duì)象之間的聯(lián)系,這個(gè)調(diào)用過程如下:

#0  webrtc::voe::(anonymous namespace)::ChannelSend::ChannelSend(webrtc::Clock*, webrtc::TaskQueueFactory*, webrtc::Transport*, webrtc::RtcpRttStats*, webrtc::RtcEventLog*, webrtc::FrameEncryptorInterface*, webrtc::CryptoOptions const&, bool, int, unsigned int, rtc::scoped_refptr<webrtc::FrameTransformerInterface>, webrtc::TransportFeedbackObserver*) () at webrtc/audio/channel_send.cc:450
#2  webrtc::voe::CreateChannelSend(webrtc::Clock*, webrtc::TaskQueueFactory*, webrtc::Transport*, webrtc::RtcpRttStats*, webrtc::RtcEventLog*, webrtc::FrameEncryptorInterface*, webrtc::CryptoOptions const&, bool, int, unsigned int, rtc::scoped_refptr<webrtc::FrameTransformerInterface>, webrtc::TransportFeedbackObserver*) () at webrtc/audio/channel_send.cc:953
#3  webrtc::internal::AudioSendStream::AudioSendStream(webrtc::Clock*, webrtc::AudioSendStream::Config const&, rtc::scoped_refptr<webrtc::AudioState> const&, webrtc::TaskQueueFactory*, webrtc::RtpTransportControllerSendInterface*, webrtc::BitrateAllocatorInterface*, webrtc::RtcEventLog*, webrtc::RtcpRttStats*, absl::optional<webrtc::RtpState> const&) () at webrtc/audio/audio_send_stream.cc:118
#4  webrtc::internal::Call::CreateAudioSendStream(webrtc::AudioSendStream::Config const&) () at webrtc/call/call.cc:897

webrtc::AudioSendStream 通過 webrtc::Call 創(chuàng)建,傳入了 webrtc::AudioSendStream::Config 包含與編解碼、RTP、加密相關(guān),及 webrtc::Transport 等各種配置。

webrtc::voe::(anonymous namespace)::ChannelSend 對(duì)象的構(gòu)造函數(shù)如下:

ChannelSend::ChannelSend(
    Clock* clock,
    TaskQueueFactory* task_queue_factory,
    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)
    : ssrc_(ssrc),
      event_log_(rtc_event_log),
      _timeStamp(0),  // This is just an offset, RTP module will add it's own
                      // random offset
      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")) {
  audio_coding_.reset(AudioCodingModule::Create(AudioCodingModule::Config()));

  RtpRtcpInterface::Configuration configuration;
  configuration.bandwidth_callback = rtcp_observer_.get();
  configuration.transport_feedback_callback = feedback_observer_;
  configuration.clock = (clock ? clock : Clock::GetRealTimeClock());
  configuration.audio = true;
  configuration.outgoing_transport = rtp_transport;

  configuration.paced_sender = rtp_packet_pacer_proxy_.get();

  configuration.event_log = event_log_;
  configuration.rtt_stats = rtcp_rtt_stats;
  configuration.retransmission_rate_limiter =
      retransmission_rate_limiter_.get();
  configuration.extmap_allow_mixed = extmap_allow_mixed;
  configuration.rtcp_report_interval_ms = rtcp_report_interval_ms;
  configuration.rtcp_packet_type_counter_observer = this;

  configuration.local_media_ssrc = ssrc;

  rtp_rtcp_ = ModuleRtpRtcpImpl2::Create(configuration);
  rtp_rtcp_->SetSendingMediaStatus(false);

  rtp_sender_audio_ = std::make_unique<RTPSenderAudio>(configuration.clock,
                                                       rtp_rtcp_->RtpSender());

  // Ensure that RTCP is enabled by default for the created channel.
  rtp_rtcp_->SetRTCPStatus(RtcpMode::kCompound);

  int error = audio_coding_->RegisterTransportCallback(this);
  RTC_DCHECK_EQ(0, error);
  if (frame_transformer)
    InitFrameTransformerDelegate(std::move(frame_transformer));
}

webrtc::voe::(anonymous namespace)::ChannelSend 對(duì)象的構(gòu)造函數(shù)的執(zhí)行過程如下:

  • 創(chuàng)建了一個(gè) RtpPacketSenderProxy 對(duì)象;
  • 創(chuàng)建了一個(gè) webrtc::AudioCodingModule 對(duì)象,建立起了下圖中標(biāo)號(hào)為 1 的這條連接;
  • 創(chuàng)建了一個(gè) webrtc::ModuleRtpRtcpImpl2 對(duì)象,在創(chuàng)建這個(gè)對(duì)象時(shí)傳入的 configuration 參數(shù)的 outgoing_transport 配置項(xiàng)指向了傳入的 webrtc::Transport,建立起了下圖中標(biāo)號(hào)為 10 的這條連接,configuration 參數(shù)的 paced_sender 配置項(xiàng)指向了前面創(chuàng)建的 RtpPacketSenderProxy 對(duì)象,建立起了下圖中標(biāo)號(hào)為 7 的這條連接;
  • 創(chuàng)建了一個(gè) webrtc::RTPSenderAudio 對(duì)象,通過 rtp_sender_audio_configrtp_sender 配置項(xiàng)傳入了從 webrtc::ModuleRtpRtcpImpl2 對(duì)象獲得的 webrtc::RTPSender 對(duì)象,建立起了下圖中標(biāo)號(hào)為 56 的這條連接;
  • this 作為 webrtc::AudioPacketizationCallback 注冊(cè)給了 webrtc::AudioCodingModule 對(duì)象,這樣就建立起了下圖中標(biāo)號(hào)為 2 的這條連接。
ChannelSend Pipeline

圖中標(biāo)為綠色的模塊為這個(gè)階段已經(jīng)接入 webrtc::voe::(anonymous namespace)::ChannelSend 的模塊,標(biāo)為黃色的則為那些還沒有接進(jìn)來的模塊;實(shí)現(xiàn)箭頭表示這個(gè)階段已經(jīng)建立的連接,虛線箭頭則表示還沒有建立的連接。

ChannelSendRegisterSenderCongestionControlObjects() 函數(shù)中,PacedSender 發(fā)送控制相關(guān)的模塊被接進(jìn)來:

#0  webrtc::voe::(anonymous namespace)::ChannelSend::RegisterSenderCongestionControlObjects(webrtc::RtpTransportControllerSendInterface*, webrtc::RtcpBandwidthObserver*) () at webrtc/audio/channel_send.cc:713
#1  webrtc::internal::AudioSendStream::ConfigureStream(webrtc::AudioSendStream::Config const&, bool) ()
    at webrtc/audio/audio_send_stream.cc:303
#2  webrtc::internal::AudioSendStream::AudioSendStream(webrtc::Clock*, webrtc::AudioSendStream::Config const&, rtc::scoped_refptr<webrtc::AudioState> const&, webrtc::TaskQueueFactory*, webrtc::RtpTransportControllerSendInterface*, webrtc::BitrateAllocatorInterface*, webrtc::RtcEventLog*, absl::optional<webrtc::RtpState> const&, std::unique_ptr<webrtc::voe::ChannelSendInterface, std::default_delete<webrtc::voe::ChannelSendInterface> >) ()
    at webrtc/audio/audio_send_stream.cc:170
#3  webrtc::internal::AudioSendStream::AudioSendStream(webrtc::Clock*, webrtc::AudioSendStream::Config const&, rtc::scoped_refptr<webrtc::AudioState> const&, webrtc::TaskQueueFactory*, webrtc::RtpTransportControllerSendInterface*, webrtc::BitrateAllocatorInterface*, webrtc::RtcEventLog*, webrtc::RtcpRttStats*, absl::optional<webrtc::RtpState> const&) () at webrtc/audio/audio_send_stream.cc:110

這個(gè)操作也發(fā)生在 webrtc::AudioSendStream 對(duì)象的創(chuàng)建期間。ChannelSendRegisterSenderCongestionControlObjects() 函數(shù)的實(shí)現(xiàn)如下:

void ChannelSend::RegisterSenderCongestionControlObjects(
    RtpTransportControllerSendInterface* transport,
    RtcpBandwidthObserver* bandwidth_observer) {
  RTC_DCHECK_RUN_ON(&worker_thread_checker_);
  RtpPacketSender* rtp_packet_pacer = transport->packet_sender();
  PacketRouter* packet_router = transport->packet_router();

  RTC_DCHECK(rtp_packet_pacer);
  RTC_DCHECK(packet_router);
  RTC_DCHECK(!packet_router_);
  rtcp_observer_->SetBandwidthObserver(bandwidth_observer);
  rtp_packet_pacer_proxy_->SetPacketPacer(rtp_packet_pacer);
  rtp_rtcp_->SetStorePacketsStatus(true, 600);
  packet_router_ = packet_router;
}

ChannelSend 從傳入的 webrtc::RtpTransportControllerSendInterface 對(duì)象中獲得 webrtc::RtpPacketSenderwebrtc::PacketRouter,webrtc::RtpPacketSender 被設(shè)置給前面創(chuàng)建的 RtpPacketSenderProxy 對(duì)象,這樣就建立起了前面的圖中標(biāo)號(hào)為 8 的這條連接,獲得的 webrtc::PacketRouter 保存起來備用。

這樣 webrtc::AudioSendStream 內(nèi)部的數(shù)據(jù)處理管線的狀態(tài)變?yōu)槿缦聢D所示:

ChannelSend Pipeline 2

音頻編碼器在 webrtc::AudioSendStream 的配置接口 Reconfigure() 被調(diào)用時(shí)創(chuàng)建,并被注入進(jìn) webrtc::AudioCodingModule。這個(gè)調(diào)用過程如下:

#0  webrtc::(anonymous namespace)::AudioCodingModuleImpl::ModifyEncoder(rtc::FunctionView<void (std::unique_ptr<webrtc::AudioEncoder, std::default_delete<webrtc::AudioEncoder> >*)>) () at webrtc/modules/audio_coding/acm2/audio_coding_module.cc:320
#1  webrtc::AudioCodingModule::SetEncoder(std::unique_ptr<webrtc::AudioEncoder, std::default_delete<webrtc::AudioEncoder> >) ()
    at webrtc/modules/audio_coding/include/audio_coding_module.h:96
#2  webrtc::voe::(anonymous namespace)::ChannelSend::SetEncoder(int, std::unique_ptr<webrtc::AudioEncoder, std::default_delete<webrtc::AudioEncoder> >) () at webrtc/audio/channel_send.cc:588
#3  webrtc::internal::AudioSendStream::SetupSendCodec(webrtc::AudioSendStream::Config const&) ()
    at webrtc/audio/audio_send_stream.cc:657
#4  webrtc::internal::AudioSendStream::ReconfigureSendCodec(webrtc::AudioSendStream::Config const&) ()
    at webrtc/audio/audio_send_stream.cc:688
#5  webrtc::internal::AudioSendStream::ConfigureStream(webrtc::AudioSendStream::Config const&, bool) ()
    at webrtc/audio/audio_send_stream.cc:323
#6  webrtc::internal::AudioSendStream::Reconfigure(webrtc::AudioSendStream::Config const&) ()
    at webrtc/audio/audio_send_stream.cc:195
#7  cricket::WebRtcVoiceMediaChannel::WebRtcAudioSendStream::ReconfigureAudioSendStream() ()
    at webrtc/media/engine/webrtc_voice_engine.cc:1181

來看一下 webrtc::AudioSendStream 中創(chuàng)建音頻編碼器的過程:

// Apply current codec settings to a single voe::Channel used for sending.
bool AudioSendStream::SetupSendCodec(const Config& new_config) {
  RTC_DCHECK(new_config.send_codec_spec);
  const auto& spec = *new_config.send_codec_spec;

  RTC_DCHECK(new_config.encoder_factory);
  std::unique_ptr<AudioEncoder> encoder =
      new_config.encoder_factory->MakeAudioEncoder(
          spec.payload_type, spec.format, new_config.codec_pair_id);

  if (!encoder) {
    RTC_DLOG(LS_ERROR) << "Unable to create encoder for "
                       << rtc::ToString(spec.format);
    return false;
  }

  // If a bitrate has been specified for the codec, use it over the
  // codec's default.
  if (spec.target_bitrate_bps) {
    encoder->OnReceivedTargetAudioBitrate(*spec.target_bitrate_bps);
  }

  // Enable ANA if configured (currently only used by Opus).
  if (new_config.audio_network_adaptor_config) {
    if (encoder->EnableAudioNetworkAdaptor(
            *new_config.audio_network_adaptor_config, event_log_)) {
      RTC_LOG(LS_INFO) << "Audio network adaptor enabled on SSRC "
                       << new_config.rtp.ssrc;
    } else {
      RTC_LOG(LS_INFO) << "Failed to enable Audio network adaptor on SSRC "
                       << new_config.rtp.ssrc;
    }
  }

  // Wrap the encoder in an AudioEncoderCNG, if VAD is enabled.
  if (spec.cng_payload_type) {
    AudioEncoderCngConfig cng_config;
    cng_config.num_channels = encoder->NumChannels();
    cng_config.payload_type = *spec.cng_payload_type;
    cng_config.speech_encoder = std::move(encoder);
    cng_config.vad_mode = Vad::kVadNormal;
    encoder = CreateComfortNoiseEncoder(std::move(cng_config));

    RegisterCngPayloadType(*spec.cng_payload_type,
                           new_config.send_codec_spec->format.clockrate_hz);
  }

  // Wrap the encoder in a RED encoder, if RED is enabled.
  if (spec.red_payload_type) {
    AudioEncoderCopyRed::Config red_config;
    red_config.payload_type = *spec.red_payload_type;
    red_config.speech_encoder = std::move(encoder);
    encoder = std::make_unique<AudioEncoderCopyRed>(std::move(red_config));
  }

  // Set currently known overhead (used in ANA, opus only).
  // If overhead changes later, it will be updated in UpdateOverheadForEncoder.
  {
    MutexLock lock(&overhead_per_packet_lock_);
    size_t overhead = GetPerPacketOverheadBytes();
    if (overhead > 0) {
      encoder->OnReceivedOverhead(overhead);
    }
  }

  StoreEncoderProperties(encoder->SampleRateHz(), encoder->NumChannels());
  channel_send_->SetEncoder(new_config.send_codec_spec->payload_type,
                            std::move(encoder));

  return true;
}

RTC 的音頻編碼比較特別的地方,一是編碼的數(shù)據(jù)主要是語音,二是編碼策略總是與弱網(wǎng)對(duì)抗緊密相關(guān)。從 AudioSendStream::SetupSendCodec() 函數(shù)創(chuàng)建音頻編碼器的過程也能看出來。在實(shí)際的音頻數(shù)據(jù)編碼器如 OPUS,AAC 等外面,還可以套上舒適噪音編碼器,或可以生成冗余幀的 RED 編碼器。

音頻編碼器創(chuàng)建完成之后,前面的圖中標(biāo)號(hào)為 34 的兩條連接就建立起來的。

這樣 webrtc::AudioSendStream 內(nèi)部的數(shù)據(jù)處理管線的狀態(tài)變?yōu)槿缦聢D所示:

ChannelSend Pipeline 3

webrtc::AudioSendStream 的生命周期函數(shù) Start() 被調(diào)用時(shí),webrtc::PacketRouterwebrtc::ModuleRtpRtcpImpl2 被連接了起來,這個(gè)調(diào)用過程如下:

#0  webrtc::voe::(anonymous namespace)::ChannelSend::StartSend() () at webrtc/audio/channel_send.cc:531
#1  webrtc::internal::AudioSendStream::Start() () at webrtc/audio/audio_send_stream.cc:368

ChannelSend::StartSend() 函數(shù)實(shí)現(xiàn)如下:

void ChannelSend::StartSend() {
  RTC_DCHECK_RUN_ON(&worker_thread_checker_);
  RTC_DCHECK(!sending_);
  sending_ = true;

  RTC_DCHECK(packet_router_);
  packet_router_->AddSendRtpModule(rtp_rtcp_.get(), /*remb_candidate=*/false);
  rtp_rtcp_->SetSendingMediaStatus(true);
  int ret = rtp_rtcp_->SetSendingStatus(true);
  RTC_DCHECK_EQ(0, ret);

  // It is now OK to start processing on the encoder task queue.
  encoder_queue_.PostTask([this] {
    RTC_DCHECK_RUN_ON(&encoder_queue_);
    encoder_queue_is_active_ = true;
  });
}

這樣前面的圖中標(biāo)號(hào)為 9 的這條連接也建立起了。 webrtc::AudioSendStream 內(nèi)部的數(shù)據(jù)處理管線最終建立完成。

將上面圖中各箭頭旁邊的標(biāo)號(hào)按照建立的次序來標(biāo),則如下圖所示:

ChannelSend Pipeline 4

總結(jié)下來,webrtc::AudioSendStream 內(nèi)部的數(shù)據(jù)處理管線分 4 步完成了建立。

本文的分析中,含有一些函數(shù)調(diào)用棧的信息,函數(shù)調(diào)用棧的信息中甚至包含了代碼所在的源文件及行號(hào)。這里的分析基于 OpenRTCClient 中的 WebRTC M98 的源碼進(jìn)行。

后面再分模塊深入研究音頻數(shù)據(jù)編碼及發(fā)送控制的過程。

Done.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容