自学内容网 自学内容网

音频3A一——webrtc源码3A的启用方法和具体流程


前言

在上一篇文章中,音频3A——初步了解音频3A,大致介绍了3A的作用、使用场景以及带来了哪些问题,同时列举了一些各个平台常用的3A开源库,再接下来的文章中,博主打算以webrtc(实在过于经典)来介绍具体的3A算法,所以需要读者对于webrtc拥有一定的了解。

由于webrtc过于庞大,3A只是webrtc中的一个模块,并且博主在接下来的文章中会涉及到很多webrtc中具体的实现,所以在正式进入到webrtc 3A算法之前,先介绍webrtc中3A的具体使用流程,以便于有兴趣的读者可以对照具体代码进行查看。

|版本声明:山河君,未经博主允许,禁止转载


一、webrtc开启3A

1.3A对象的创建

webrtc中所有的对外3A接口都在webrtc\src\api\audio\audio_processing.h文件中,创建方法也在里面

class RTC_EXPORT AudioProcessingBuilder {

......

  // Creates an APM instance with the specified config or the default one if
  // unspecified. Injects the specified components transferring the ownership
  // to the newly created APM instance - i.e., except for the config, the
  // builder is reset to its initial state.
  rtc::scoped_refptr<AudioProcessing> Create();
.....
};

通过_audioProcessingPtr = webrtc::AudioProcessingBuilder().Create(); 先创建3A的实例对象

2. 3A的配置

创建实例后,可以选择对应的3A配置,这个配置选项是AudioProcessing的内部类,内部类存在大量的配置选项

class RTC_EXPORT AudioProcessing : public RefCountInterface {
 public:
  struct RTC_EXPORT Config {
  .....
  };
  .....
 }

下面是具体的设置项:

  1. HighPassFilter
struct HighPassFilter {
  bool enabled = false;
  bool apply_in_full_band = true;
} high_pass_filter;
  • enabled:开启高通滤波器
  • apply_in_full_band:全屏段使用
  • 备注:过滤低频声音,人耳对于低频声音不敏感,且回声大多是低频
  1. EchoCanceller
struct EchoCanceller {
  bool enabled = false;
  bool mobile_mode = false;
  bool export_linear_aec_output = false;
  // Enforce the highpass filter to be on (has no effect for the mobile
  // mode).
  bool enforce_high_pass_filtering = true;
} echo_canceller;
  • enabled:表示是否启用回声消除。设置为 true 时启用此功能。
  • mobile_mode:表示是否使用移动模式。当为 true 时,会调整算法以适应移动设备的音频特性。
  • export_linear_aec_output:如果设置为 true,将导出线性 AEC 输出。这通常用于调试和分析。
  • enforce_high_pass_filtering:强制开启高通滤波器。此选项在移动模式下没有效果,但在其他情况下有助于去除低频噪声。
  1. NoiseSuppression
 struct NoiseSuppression {
   bool enabled = false;
   enum Level { kLow, kModerate, kHigh, kVeryHigh };
   Level level = kModerate;
   bool analyze_linear_aec_output_when_available = false;
 } noise_suppression;
  • enabled:指示是否启用噪声抑制功能。如果设置为 true,则噪声抑制将被应用于音频流。
  • level: 设置噪声抑制的强度。可能的值包括:
    1)kLow: 低级别的噪声抑制,适合较轻的背景噪声。
    2)kModerate: 中等级别的噪声抑制,适用于一般背景噪声。
    3)kHigh: 高级别的噪声抑制,适合较强的背景噪声。
    4)kVeryHigh: 非常高的噪声抑制,适合极其嘈杂的环境。
  • analyze_linear_aec_output_when_available: 指示在可能的情况下是否分析线性回声消除(AEC)输出。当设置为 true 时,噪声抑制算法将尝试分析回声消除的输出,以便更好地去除背景噪声。这可以提高噪声抑制的效果,尤其是在有回声的环境中
  1. TransientSuppression
struct TransientSuppression {
   bool enabled = false;
 } transient_suppression;
  • enabled: 指示是否启用瞬态抑制功能。如果设置为 true,则启用瞬态抑制,音频处理模块会尝试检测并减少瞬态噪声。如果设置为 false,则不进行瞬态抑制处理。
  1. GainController1/GainController2
    调整AGC的配置有两个GainController1和GainController2,前者是针对麦克风输入,后者是针对扬声器输出,AGC为了针对不同场景,存在很多参数,比如需要限制增益范围,最大限制等等,这里只针对常用场景进行介绍,有需要的小伙伴可以再根据需要选择
  • enabled:开启增益
  • Mode: 枚举类型,定义增益控制的工作模式:
    1)kAdaptiveAnalog: 适用于有模拟音量控制的设备,结合模拟增益和数字压缩。
    2)kAdaptiveDigital: 适用于没有模拟音量控制的设备,主要在数字域中进行增益调整和压缩。
    3)kFixedDigital: 只启用数字压缩,通过固定增益来处理输入信号,适合信号级别可预测的嵌入式设备。
  • target_level_dbfs: 目标峰值水平,以 dBFs(相对于数字满刻度的分贝)表示。通常使用正值。例如,3 表示目标水平为 -3 dBFs。
  • compression_gain_db: 数字压缩阶段可以施加的最大增益,以 dB 表示。数值越大,压缩效果越强。0 表示不进行压缩,范围为 [0, 90]。
  • enable_limiter:指示是否启用限制器功能。如果启用,压缩阶段会将信号限制在目标水平之下。

配置完成后调用 _audioProcessingPtr->ApplyConfig(config);完成设置。

3.注册到Peerconnection

3A实例创建完成后,在一开始创建peerconnection时就可以注册进去,当然因为3A是独立的模块,也可以在创建媒体引擎时或者开启voiceengine时单独在别的地方进行创建

RTC_EXPORT rtc::scoped_refptr<PeerConnectionFactoryInterface>
CreatePeerConnectionFactory(
    rtc::Thread* network_thread,
    rtc::Thread* worker_thread,
    rtc::Thread* signaling_thread,
    rtc::scoped_refptr<AudioDeviceModule> default_adm,
    rtc::scoped_refptr<AudioEncoderFactory> audio_encoder_factory,
    rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory,
    std::unique_ptr<VideoEncoderFactory> video_encoder_factory,
    std::unique_ptr<VideoDecoderFactory> video_decoder_factory,
    rtc::scoped_refptr<AudioMixer> audio_mixer,
    rtc::scoped_refptr<AudioProcessing> audio_processing,
    std::unique_ptr<AudioFrameProcessor> audio_frame_processor = nullptr,
    std::unique_ptr<FieldTrialsView> field_trials = nullptr);

二、webrtc中3A的调用流程

  1. 在创建PeerConnection时把3A实例注册进去后,PeerConnection会记录所有的依赖项
rtc::scoped_refptr<PeerConnectionFactoryInterface> CreatePeerConnectionFactory(
    rtc::Thread* network_thread,
    rtc::Thread* worker_thread,
    rtc::Thread* signaling_thread,
    rtc::scoped_refptr<AudioDeviceModule> default_adm,
    rtc::scoped_refptr<AudioEncoderFactory> audio_encoder_factory,
    rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory,
    std::unique_ptr<VideoEncoderFactory> video_encoder_factory,
    std::unique_ptr<VideoDecoderFactory> video_decoder_factory,
    rtc::scoped_refptr<AudioMixer> audio_mixer,
    rtc::scoped_refptr<AudioProcessing> audio_processing,
    std::unique_ptr<AudioFrameProcessor> audio_frame_processor,
    std::unique_ptr<FieldTrialsView> field_trials) {
  PeerConnectionFactoryDependencies dependencies;
  .....
  
  if (audio_processing) {
    dependencies.audio_processing = std::move(audio_processing);
  } else {
    dependencies.audio_processing = AudioProcessingBuilder().Create();
  }
  .....
  
  EnableMedia(dependencies);
  return CreateModularPeerConnectionFactory(std::move(dependencies));
}
  1. 在创建Peerconnection时,会创建连接上下文

// Static
rtc::scoped_refptr<PeerConnectionFactory> PeerConnectionFactory::Create(
    PeerConnectionFactoryDependencies dependencies) {
    .......
  auto context = ConnectionContext::Create(
      CreateEnvironment(std::move(dependencies.trials),
                        std::move(dependencies.task_queue_factory)),
     .....
}
  1. 在创建上下文中,会创建媒体引擎MediaEngine,同时把audio_processing传入

ConnectionContext::ConnectionContext(
    const Environment& env,
    PeerConnectionFactoryDependencies* dependencies)
    .....
      media_engine_(
          dependencies->media_factory != nullptr
              ? dependencies->media_factory->CreateMediaEngine(env_,
                                                               *dependencies)
              : nullptr),
    .....
  1. 媒体引擎会MediaEngine ,分别创建音频引擎audio_engine 和视频引擎video_engine ,同时把audio_processing传入音频引擎
std::unique_ptr<MediaEngineInterface> CreateMediaEngine(
    const Environment& env,
    PeerConnectionFactoryDependencies& deps) override {
  auto audio_engine = std::make_unique<WebRtcVoiceEngine>(....
  auto video_engine = std::make_unique<WebRtcVideoEngine>(....
}
  1. 音频引擎持有3A实例
WebRtcVoiceEngine::WebRtcVoiceEngine(
....
    rtc::scoped_refptr<webrtc::AudioProcessing> audio_processing,
   .....)
    : 
    .....
      apm_(audio_processing),
    ....
{...}
  1. 音频引擎audio_engine 初始化时,创建AudioState

void WebRtcVoiceEngine::Init() {
...
    audio_state_ = webrtc::AudioState::Create(config);
...
}
  1. AudioState拥有AudioTransportImpl实例,同时会把一些资源包括3A实例注册进去
AudioState::AudioState(const AudioState::Config& config)
    : config_(config),
      audio_transport_(config_.audio_mixer.get(),
                       config_.audio_processing.get(),
                       config_.async_audio_processing_factory.get()) {
                       ....
}

  1. 此时AudioTransportImpl会在音频采集和渲染过程中,将近端信号和远端信号塞入到3A里
  • 近端信号的塞入
int32_t AudioTransportImpl::RecordedDataIsAvailable(
    const void* audio_data,
    size_t number_of_frames,
    size_t bytes_per_sample,
    size_t number_of_channels,
    uint32_t sample_rate,
    uint32_t audio_delay_milliseconds,
    int32_t /*clock_drift*/,
    uint32_t /*volume*/,
    bool key_pressed,
    uint32_t& /*new_mic_volume*/,
    absl::optional<int64_t>
        estimated_capture_time_ns) {  
        ....
  ProcessCaptureFrame(audio_delay_milliseconds, key_pressed,
                      swap_stereo_channels, audio_processing_,
                      audio_frame.get());
        ....
}
void ProcessCaptureFrame(uint32_t delay_ms,
                         bool key_pressed,
                         bool swap_stereo_channels,
                         AudioProcessing* audio_processing,
                         AudioFrame* audio_frame) {
    ....                     
  if (audio_processing) {
    audio_processing->set_stream_delay_ms(delay_ms);
    audio_processing->set_stream_key_pressed(key_pressed);
    int error = ProcessAudioFrame(audio_processing, audio_frame);
  }
  ....
}
int ProcessAudioFrame(AudioProcessing* ap, AudioFrame* frame) {
 ...
  int result = ap->ProcessStream(frame->data(), input_config, output_config,
                                 frame->mutable_data());

  AudioProcessingStats stats = ap->GetStatistics();
  ...
}
  • 远端信号的塞入
int32_t AudioTransportImpl::NeedMorePlayData(const size_t nSamples,
                                             const size_t nBytesPerSample,
                                             const size_t nChannels,
                                             const uint32_t samplesPerSec,
                                             void* audioSamples,
                                             size_t& nSamplesOut,
                                             int64_t* elapsed_time_ms,
                                             int64_t* ntp_time_ms) {
        ....
  if (audio_processing_) {
    const auto error =
        ProcessReverseAudioFrame(audio_processing_, &mixed_frame_);
    RTC_DCHECK_EQ(error, AudioProcessing::kNoError);
  }
  ....
}
int ProcessReverseAudioFrame(AudioProcessing* ap, AudioFrame* frame) {
....
  int result = ap->ProcessReverseStream(frame->data(), input_config,
                                        output_config, frame->mutable_data());
....
}

总结

本篇博客是为了在后面更深入的进入到3A算法之前,对于webrtc中3A调用的配置以及3A工作流程的介绍,但是这一系列的博客最终目的是为了介绍3A算法的具体实现,在下一篇博客里,就会进入真正的算法细节了,会有大量的数学公式,建议如果读者想要进一步了解的话,可以先看看博主之前对于音频进阶的文章。

如果对您有所帮助,请帮忙点个赞吧!


原文地址:https://blog.csdn.net/qq_42956179/article/details/143178816

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!