音频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 {
.....
};
.....
}
下面是具体的设置项:
- HighPassFilter
struct HighPassFilter {
bool enabled = false;
bool apply_in_full_band = true;
} high_pass_filter;
- enabled:开启高通滤波器
- apply_in_full_band:全屏段使用
- 备注:过滤低频声音,人耳对于低频声音不敏感,且回声大多是低频
- 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:强制开启高通滤波器。此选项在移动模式下没有效果,但在其他情况下有助于去除低频噪声。
- 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 时,噪声抑制算法将尝试分析回声消除的输出,以便更好地去除背景噪声。这可以提高噪声抑制的效果,尤其是在有回声的环境中
- TransientSuppression
struct TransientSuppression {
bool enabled = false;
} transient_suppression;
- enabled: 指示是否启用瞬态抑制功能。如果设置为 true,则启用瞬态抑制,音频处理模块会尝试检测并减少瞬态噪声。如果设置为 false,则不进行瞬态抑制处理。
- 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的调用流程
- 在创建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));
}
- 在创建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)),
.....
}
- 在创建上下文中,会创建媒体引擎
MediaEngine
,同时把audio_processing
传入
ConnectionContext::ConnectionContext(
const Environment& env,
PeerConnectionFactoryDependencies* dependencies)
.....
media_engine_(
dependencies->media_factory != nullptr
? dependencies->media_factory->CreateMediaEngine(env_,
*dependencies)
: nullptr),
.....
- 媒体引擎会
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>(....
}
- 音频引擎持有3A实例
WebRtcVoiceEngine::WebRtcVoiceEngine(
....
rtc::scoped_refptr<webrtc::AudioProcessing> audio_processing,
.....)
:
.....
apm_(audio_processing),
....
{...}
- 音频引擎
audio_engine
初始化时,创建AudioState
void WebRtcVoiceEngine::Init() {
...
audio_state_ = webrtc::AudioState::Create(config);
...
}
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()) {
....
}
- 此时
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)!