Estimación del tiempo de retraso de cancelación de eco para webrtc

No es razonable estimar el tiempo de retraso llamado cancelación de eco. El núcleo aquí es estimar el límite de condición de llamar a webrtc. Todos sabemos que el requisito previo para que la cancelación de eco de webrtc tenga efecto es obtener la información del sonido del extremo lejano. , y luego obtenga el sonido del extremo cercano y el sonido del extremo lejano. Alineación, desde el sonido del extremo cercano, combinado con el sonido del extremo lejano para simular la información de eco del sonido del extremo lejano en el sonido del extremo cercano y luego elimine la información de eco. Por lo tanto, si no hay un sonido correspondiente para el sonido del extremo lejano guardado en la alineación, webrtc El efecto de cancelación de eco será pobre o ineficaz, por lo que esta diferencia de tiempo es muy importante y es necesario comprender de manera integral sopesar delay_time en combinación con la situación del hardware.

El algoritmo de cancelación de eco (aec, aecm) de webrtc incluye principalmente los siguientes módulos importantes:

  • Estimación de retardo de eco
  • NLMS (algoritmo adaptativo normalizado de mínimos cuadrados medios)
  • NLP (Filtrado no lineal)
  • GNC (Generación de ruido de confort)

Estimación de retardo de eco

 

Hay muchas cosas en esta imagen que se pueden ignorar. Centrémonos en T0, T1 y T2.

  • T0 representa el tiempo que tarda el sonido en pasar del parlante al micrófono. Este tiempo puede ser ignorado, porque generalmente la distancia entre el micrófono y el parlante no es muy grande. Considerando la velocidad del sonido a 340 metros por segundo, este tiempo no excederá de 1 milisegundo.
  • T1 representa el sonido que le llega desde la distancia, el tiempo desde que este sonido pasa a la interfaz remota de cancelación de eco (WebRtcAec_BufferFarend) hasta que se reproduce. En términos generales, cuando los datos de audio recibidos se transmiten a esta interfaz, es el momento en que la capa superior transmite al altavoz, por lo que puede entenderse como el momento en que el sonido comienza a cronometrarse en la cola de reproducción y se reproduce.
  • T2 representa el momento en que el altavoz recopila una pieza de sonido y luego la envía a la función de procesamiento de extremo cercano (WebRtcAec_Process). Dado que el sonido se recopila, el proceso de cancelación de eco se realizará de inmediato, por lo que este tiempo puede entenderse como el inicio del tiempo cuando el micrófono recoge el sonido y, a continuación, el tiempo que tarda el código en obtener los datos PCM de audio.
  • delay=T0+T1+T2, que en realidad es T1+T2.

En términos generales, si un dispositivo puede encontrar un retraso adecuado, entonces el procesamiento de cancelación de eco de este dispositivo es casi tan difícil como la ganancia de reducción de ruido. Por ejemplo, el retraso fijo del iPhone es de 60 ms. Sin embargo, esto depende de la ubicación del código.Si está dentro del chip, el tiempo es relativamente pequeño y es fácil de arreglar.Si está en el software de la capa de aplicación del sistema, todo el tiempo es incierto. Relativamente grande. apm_->set_stream_delay_ms(delay_ms_);

Por ejemplo, la llamada de paquete de webrtc bajo el sistema android:

#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()
    }
}

Supongo que te gusta

Origin blog.csdn.net/huapeng_guo/article/details/132054951
Recomendado
Clasificación