webrtc에 대한 에코 취소 지연 시간 추정

에코 제거라는 지연 시간을 추정하는 것은 합리적이지 않습니다. 여기서 핵심은 webrtc를 호출하는 조건 경계를 추정하는 것입니다. webrtc 에코 제거가 적용되기 위한 전제 조건은 맨 끝 소리의 정보를 얻는 것입니다. , 그런 다음 근단 소리와 원단 소리를 얻습니다.근단 소리에서 원단 소리와 결합하여 근단 소리에서 원단 소리의 에코 정보를 시뮬레이션하고 그런 다음 에코 정보를 죽입니다.따라서 정렬에 저장된 맨 끝 사운드에 해당하는 사운드가 없으면 webrtc 에코 제거 효과가 좋지 않거나 효과가 없으므로 이 시간 차이가 매우 중요하며 종합적으로 필요합니다. 하드웨어 상황과 함께 delay_time을 측정합니다.

webrtc의 반향 제거(aec, aecm) 알고리즘은 주로 다음과 같은 중요한 모듈을 포함합니다.

  • 에코 지연 추정
  • NLMS(정규화된 최소 평균 제곱 적응 알고리즘)
  • NLP(비선형 필터링)
  • CNG(컴포트 노이즈 생성)

에코 지연 추정

 

이 그림에는 무시할 수 있는 것들이 많이 있습니다. T0, T1, T2에 집중합시다.

  • T0는 소리가 스피커에서 마이크까지 전달되는 시간을 의미하며, 일반적으로 마이크와 스피커 사이의 거리가 그리 멀지 않기 때문에 이 시간은 무시할 수 있습니다. 1밀리초를 초과하지 않습니다.
  • T1은 멀리서 들리는 소리를 나타내며 이 소리가 반향 제거 원격 인터페이스(WebRtcAec_BufferFarend)로 전달되는 시간부터 재생될 때까지의 시간입니다. 일반적으로 수신된 오디오 데이터가 이 인터페이스로 전송되면 상위 계층에서 스피커로 전송되는 순간이므로 사운드가 재생 대기열에서 타이밍을 시작하여 재생되는 시간으로 이해될 수 있습니다.
  • T2는 화자에서 한 조각의 소리를 모아 근단 처리 기능(WebRtcAec_Process)으로 보내는 순간을 의미하는데, 소리가 모아졌기 때문에 바로 반향 제거 과정이 진행되기 때문에 이 시간을 이해할 수 있다. 마이크에 의해 사운드가 수집되는 타이밍의 시작으로 코드가 오디오 PCM 데이터를 가져오는 데 걸리는 시간입니다.
  • 지연=T0+T1+T2, 실제로는 T1+T2입니다.

일반적으로 장치가 적절한 지연을 찾을 수 있는 경우 이 장치의 반향 제거 처리는 노이즈 감소 이득만큼 어렵습니다. 예를 들어 iPhone의 고정 지연은 60ms입니다. 그러나 이것은 코드의 위치에 따라 다르며, 칩 내부에 있는 경우 상대적으로 시간이 작고 수정이 용이하며, 시스템 응용 계층 소프트웨어에 있는 경우 전체 시간이 불확실하다. 상대적으로 큽니다. apm_->set_stream_delay_ms(delay_ms_);

예를 들어, android 시스템에서 webrtc의 패키지 호출은 다음과 같습니다.

#include "webrtc_apm.h"
//#include "webrtc/common_types.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/include/module_common_types.h"
#include "webrtc/api/audio/audio_frame.h"
#include "YuvConvert.h"

using namespace webrtc;
//using namespace cbase;

WebrtcAPM::WebrtcAPM(int process_smp, int reverse_smp)
: apm_(nullptr)
, far_frame_(new AudioFrame)
, near_frame_(new AudioFrame)
, ref_cnt_(0)
, sample_rate_(8000)
, samples_per_channel_(8000/100)
, channels_(1)
, frame_size_10ms_(8000/100*sizeof(int16_t))
, delay_ms_(60)
, process_sample_rate_(process_smp)//44100
, reverse_sample_rate_(reverse_smp)//48000
{
    audio_send_ = new unsigned char[kMaxDataSizeSamples_];
    audio_reverse_ = new unsigned char[kMaxDataSizeSamples_];

#if defined(__APPLE__)
    delay_ms_ = 60;
#endif

#if defined(VAD_TEST)
        //create webrtc vad
        ty_vad_create(8000, 1);
        ty_set_vad_level(vad_level::LOW);
        ty_vad_set_recordfile("/sdcard/vadfile.pcm");
#endif
}

WebrtcAPM::~WebrtcAPM()
{
    if(far_frame_)
    {
        delete far_frame_;
        far_frame_ = NULL ;
    }

    if (near_frame_) {
        delete near_frame_ ;
        near_frame_ = NULL ;
    }

    if (audio_send_) {
        delete[] audio_send_;
    }

    if (audio_reverse_) {
        delete[] audio_reverse_;
    }

#if defined(VAD_TEST)
        //destory webrtc vad
        ty_vad_destory();
#endif
}

void WebrtcAPM::set_sample_rate(int sample_rate)
{
    sample_rate_ = sample_rate;
    samples_per_channel_ = sample_rate_ / 100;
}

int WebrtcAPM::frame_size()
{
    return frame_size_10ms_;
}

void WebrtcAPM::set_ace_delay(int delay)
{
    LOGI("set aec delay to %d ms \n", delay);
    delay_ms_ = delay;
}

void WebrtcAPM::set_reverse_stream(int reverse_sample_rate)
{
    std::lock_guard<std::mutex> guard(mutex_);

    reverse_sample_rate_ = reverse_sample_rate;
    if (resampleReverse) {
        delete resampleReverse;
        resampleReverse = NULL ;
    }

    resampleReverse = new webrtc::Resampler(reverse_sample_rate_,sample_rate_,channels_);
    int result = resampleReverse->Reset(reverse_sample_rate_,sample_rate_,channels_);
    if (result != 0) {
        LOGE("reset resampleReverse fail,%d!\n", result);
    }
}

int WebrtcAPM::init()
{
    std::lock_guard<std::mutex> guard(mutex_);

    resampleReverse = new webrtc::Resampler(reverse_sample_rate_,sample_rate_,channels_);
    int result = resampleReverse->Reset(reverse_sample_rate_,sample_rate_,channels_);
    if (result != 0) {
        LOGE("reset resampleReverse fail,%d!\n", result);
    }

    resampleIn = new webrtc::Resampler(sample_rate_,process_sample_rate_,channels_);
    result = resampleIn->Reset(sample_rate_,process_sample_rate_,channels_);
    if (result != 0) {
        LOGE("reset resampleIn fail,%d!\n", result);
    }

    initAPM();

    LOGI("initAPM success!");

    return 0;//crash without this
}

void WebrtcAPM::uninit()
{
    std::lock_guard<std::mutex> guard(mutex_);

    deInitAPM();
    safe_delete(resampleIn);
    safe_delete(resampleReverse);
}

void WebrtcAPM::reset_apm(){
    std::lock_guard<std::mutex> guard(mutex_);
    deInitAPM();
    initAPM();
}

int WebrtcAPM::initAPM(){
    apm_ = AudioProcessingBuilder().Create();
    if (apm_ == nullptr) {
        LOGE("AudioProcessing create failed");
        return -1;
    }

    AudioProcessing::Config config;
    config.echo_canceller.enabled = true;
    config.echo_canceller.mobile_mode = true;

    config.gain_controller1.enabled = true;
    config.gain_controller1.mode = AudioProcessing::Config::GainController1::kAdaptiveDigital;
    config.gain_controller1.analog_level_minimum = 0;
    config.gain_controller1.analog_level_maximum = 255;

    config.noise_suppression.enabled = true;
    config.noise_suppression.level = AudioProcessing::Config::NoiseSuppression::Level::kModerate;

    config.gain_controller2.enabled = true;
    config.high_pass_filter.enabled = true;
    config.voice_detection.enabled = true;

    apm_->ApplyConfig(config);
    LOGI("AudioProcessing initialize success \n");

    far_frame_->sample_rate_hz_ = sample_rate_;
    far_frame_->samples_per_channel_ = samples_per_channel_;
    far_frame_->num_channels_ = channels_;

    near_frame_->sample_rate_hz_ = sample_rate_;
    near_frame_->samples_per_channel_ = samples_per_channel_;
    near_frame_->num_channels_ = channels_;

    frame_size_10ms_ = samples_per_channel_ * channels_ * sizeof(int16_t);

    //LOGI("AudioProcessing initialize success end\n");
    return 0;

}

void WebrtcAPM::deInitAPM(){
    //std::lock_guard<std::mutex> guard(mutex_);
    //if (--ref_cnt_ == 0) {
        LOGI("destroy WebrtcAPM \n");
        safe_delete(apm_);
    //}
}

//8000->44100
void WebrtcAPM::process_stream(uint8_t *buffer,int bufferLength, uint8_t *bufferOut, int* pOutLen, bool bUseAEC)
{
    if (bUseAEC) {
        std::lock_guard<std::mutex> guard(mutex_);
        if (apm_) {
            int frame_count = bufferLength / frame_size_10ms_;
        
            for (int i = 0; i < frame_count; i++) {
                 apm_->set_stream_delay_ms(delay_ms_);
                 // webrtc apm process 10ms datas every time
                 memcpy((void*)near_frame_->data(), buffer + i*frame_size_10ms_, frame_size_10ms_);
                 int res = apm_->ProcessStream(near_frame_->data(),
                                        StreamConfig(near_frame_->sample_rate_hz_, near_frame_->num_channels_),
                                        StreamConfig(near_frame_->sample_rate_hz_, near_frame_->num_channels_),
                                        (int16_t * const)near_frame_->data());
                 if (res != 0) {
                     LOGE("ProcessStream failed, ret %d \n",res);
                 }

#if  defined(VAD_TEST)                        
            bool ret = ty_vad_process(buffer + i*frame_size_10ms_, frame_size_10ms_);
#endif

                 memcpy(buffer + i*frame_size_10ms_, near_frame_->data(), frame_size_10ms_);
            }
        }
    }

    if (resampleIn && apm_) {
        //resample
        size_t outlen = 0 ;
        int result = resampleIn->Push((int16_t*)buffer, bufferLength/sizeof(int16_t), (int16_t*)bufferOut, kMaxDataSizeSamples_/sizeof(int16_t), outlen);
        if (result != 0) {
            LOGE("resampleIn error, result = %d, outlen = %d\n", result, outlen);
        }
        *pOutLen = outlen;
    }
}

//48000->8000
void WebrtcAPM::process_reverse_10ms_stream(uint8_t *bufferIn, int bufferLength, uint8_t *bufferOut, int* pOutLen, bool bUseAEC)
{
    size_t outlen = 0 ;
    if (resampleReverse && apm_) {
        //resample
        int result = resampleReverse->Push((int16_t*)bufferIn, bufferLength/sizeof(int16_t), (int16_t*)audio_reverse_, kMaxDataSizeSamples_/sizeof(int16_t), outlen);
        if (result != 0) {
            LOGE("resampleReverse error, result = %d, outlen = %d\n", result, outlen);
        }
    }
    else {
		memcpy(audio_reverse_, bufferIn, bufferLength);
		outlen = bufferLength;
	}

    *pOutLen = outlen;

    if (!bUseAEC){
        //copy data and return
        memcpy(bufferOut, audio_reverse_, frame_size_10ms_);
        return;
    }

    std::lock_guard<std::mutex> guard(mutex_);
    if (apm_) {
        memcpy((void*)far_frame_->data(), audio_reverse_, frame_size_10ms_);
        int res = apm_->ProcessReverseStream(far_frame_->data(),
                                        StreamConfig(far_frame_->sample_rate_hz_, far_frame_->num_channels_),
                                        StreamConfig(far_frame_->sample_rate_hz_, far_frame_->num_channels_),
                                        (int16_t * const)far_frame_->data());
        if (res != 0) {
            LOGE("ProcessReverseStream failed, ret %d \n",res);
        }
        memcpy(bufferOut, audio_reverse_, frame_size_10ms_);//far_frame_->data()
    }
}

추천

출처blog.csdn.net/huapeng_guo/article/details/132054951