聲學回聲消除是透過消除或者移除本地話筒中拾取到的遠端的音訊訊號來阻止遠端的聲音返回去的一種處理方
法。linphone上使用speex 庫實現了回聲消除外掛,speex庫是目前開源的聲學回聲消除做的比較好的庫。下
面總結一下,linphone上的回聲消除部分。
第一部分--配置
linphone的聲音部分,是可以配置的,初始化linphone的時候,會根據配置檔案的內容來配置聲音部分,包括
回聲消除部分。
linphone_core_new()
|
linphone_core_init()
|
sound_config_read(lc)
{
......
#ifndef __ios
tmp=TRUE;
#else
tmp=FALSE; /* on iOS we have builtin echo cancellation.*/
#endif
/從配置檔案讀取ec的配置,讀取失敗,則為tmp的值,否則,tmp被賦值
tmp=lp_config_get_int(lc->config,"sound","echocancellation",tmp);
linphone_core_enable_echo_cancellation(lc,tmp);//儲存ec
//讀取ea的值,預設值為0
linphone_core_enable_echo_limiter(lc, lp_config_get_int(lc->config,"sound","echolimiter",0));
linphone_core_enable_agc(lc, lp_config_get_int(lc->config,"sound","agc",0));//讀取agc的值,預設為0
......
}
這裡,會從配置檔案讀取 echocancellation、echolimiter和agc的鍵值,如果配置檔案裡沒有配置,那麼用tmp
作為預設值。看看配置檔案,竟然沒有這幾項的配置。。。那就是用tmp的值了,我是基於android平臺,那麼
配置的結果是:ec =1,ea=0,agc = 0;
其中一個地方要展開一下,
void linphone_call_enable_echo_limiter(LinphoneCall *call, bool_t val){
if (call!=NULL && call->audiostream!=NULL ) {
if (val) {
const char *type=lp_config_get_string(call->core->config,"sound","el_type","mic");
if (strcasecmp(type,"mic")==0)
audio_stream_enable_echo_limiter(call->audiostream,ELControlMic);
else if (strcasecmp(type,"full")==0)
audio_stream_enable_echo_limiter(call->audiostream,ELControlFull);
} else {
audio_stream_enable_echo_limiter(call->audiostream,ELInactive);
}
}
}
配置ea的時候,還會讀取el_type 的配置,這是個字串,值有三種:mic、full 和其他。
這個值用來設定什麼呢?
void audio_stream_enable_echo_limiter(AudioStream *stream, EchoLimiterType type){
stream->el_type=type;
if (stream->volsend){
bool_t enable_noise_gate = stream->el_type==ELControlFull;
ms_filter_call_method(stream->volrecv,MS_VOLUME_ENABLE_NOISE_GATE,&enable_noise_gate);
ms_filter_call_method(stream->volsend,MS_VOLUME_SET_PEER,type!=ELInactive?stream->volrecv:NULL);
} else {
ms_warning("cannot set echo limiter to mode [%i] because no volume send",type);
}
}
stream->volrecv和stream->volsend都是外掛MS_VOLUME_ID,可見,當鍵值是full的時候,會enable noise gate,
當鍵值是mic或full的時候,會把volsend 的peer外掛設定為volrecv。
簡單的說,是在VOLUME外掛(聲音增益外掛)裡處理回聲。後面再細說。
第二部分--初始化
當接聽電話時,會初始化音訊流,同時會初始化回聲消除外掛。
call_accepted()
|
linphone_core_update_streams()
|
linphone_call_init_media_streams()
{
......
//1
call->audiostream=audiostream=audio_stream_new(md->streams[0].port,linphone_core_ipv6_enabled(lc));
//2
if (linphone_core_echo_limiter_enabled(lc)){
const char *type=lp_config_get_string(lc->config,"sound","el_type","mic");
if (strcasecmp(type,"mic")==0)
audio_stream_enable_echo_limiter(audiostream,ELControlMic);
else if (strcasecmp(type,"full")==0)
audio_stream_enable_echo_limiter(audiostream,ELControlFull);
}
//3
audio_stream_enable_gain_control(audiostream,TRUE);
//4
if (linphone_core_echo_cancellation_enabled(lc)){
int len,delay,framesize;
const char *statestr=lp_config_get_string(lc->config,"sound","ec_state",NULL);
len=lp_config_get_int(lc->config,"sound","ec_tail_len",0);
delay=lp_config_get_int(lc->config,"sound","ec_delay",0);
framesize=lp_config_get_int(lc->config,"sound","ec_framesize",0);
audio_stream_set_echo_canceller_params(audiostream,len,delay,framesize);
if (statestr && audiostream->ec){
ms_filter_call_method(audiostream->ec,MS_ECHO_CANCELLER_SET_STATE_STRING,(void*)statestr);
}
}
//5
audio_stream_enable_automatic_gain_control(audiostream,linphone_core_agc_enabled(lc));
{
int enabled=lp_config_get_int(lc->config,"sound","noisegate",0);
audio_stream_enable_noise_gate(audiostream,enabled);
}
......
}
1.在audio_stream_new函式里,會新建回聲消除外掛:stream->ec=ms_filter_new(MS_SPEEX_EC_ID);新建插
件時,會呼叫此外掛的init回撥。
2.根據ea的值配置VOLUME外掛。
3.設定use_gc 為true。
4.根據配置檔案,配置speexec外掛。
5.根據agc配置use_agc,讀取noisegate的鍵值,並配置volsend。
**這一部分程式碼都是初始化外掛和配置外掛,配置完了就可以啟動音訊流了。
順便說下linphone 的音訊外掛組合:
傳送:
ms_filter_link: MSAndSoundRead:0-->MSSpeexEC:1
ms_filter_link: MSSpeexEC:1-->MSVolume:0
ms_filter_link: MSVolume:0-->MSDtmfGen:0
ms_filter_link: MSDtmfGen:0-->MSAlawEnc:0
ms_filter_link: MSAlawEnc:0-->MSRtpSend:0
接收:
ms_filter_link: MSRtpRecv:0-->MSAlawDec:0
ms_filter_link: MSAlawDec:0-->MSDtmfGen:0
ms_filter_link: MSDtmfGen:0-->MSVolume:0
ms_filter_link: MSVolume:0-->MSEqualizer:0
ms_filter_link: MSEqualizer:0-->MSSpeexEC:0
ms_filter_link: MSSpeexEC:0-->MSAndSoundWrite:0
第三部分--運轉
在呼叫init之後,馬上就會開始連結傳送和接收外掛,並啟動音訊流。
call_accepted()
|
linphone_core_update_streams()
|
linphone_call_init_media_streams();
linphone_call_start_media_streams();
|
linphone_call_start_audio_stream()
|
audio_stream_start_full()
{
new rtp-send dtmf-gen,volume filter;
呼叫外掛的方法,設定引數;
連結音訊外掛傳送的串;
連結音訊外掛接收的串;
啟動ticker,執行這兩個外掛串;
}
總結一下,對回聲消除起作用的兩個外掛是speexec 和volume,下面具體看這兩個外掛。
第四部分:MS_SPEEX_EC_ID外掛
前面我們看到,在音訊接收和傳送的外掛串裡都有speexec外掛,但是此外掛只在audio_stream_new裡新建了一次,
也就是說,傳送和接收的外掛串裡使用的speexec是同一個!沒錯,speexec外掛有兩個輸入和兩個輸出,
/* inputs[0]= reference signal from far end (sent to soundcard)
* inputs[1]= near speech & echo signal (read from soundcard)
* outputs[0]= is a copy of inputs[0] to be sent to soundcard
* outputs[1]= near end speech, echo removed - towards far end
*/
speexec如何工作?
1.speexec外掛先快取本地錄音的pcm流和遠端的pcm流;
2.把遠端的pcm流複製一份,交給音效卡。
2.參考遠端傳送過來的pcm流,透過演算法過濾掉本地錄音pcm流的回聲;
3.把得到的純淨的pcm流發給遠端;
4.釋放所有快取;
5.同步本地音訊流和遠端音訊流。
關於同步:我們知道,消除回聲演算法要做的就是從錄製的聲音資料中找到回聲,並去除,這就需要參考正確的
遠端聲音資料,如果參考的內容不對,那肯定找不到回聲了。所以遠端的聲音資料和本地聲音資料的同步,就是
關鍵。我們本地錄製的速度是均勻穩定的,而網路傳輸過來的遠端資料的速度是不穩定的,linphone每間隔固定
的時間段,檢查一次遠端資料的大小,如果大於framesize,則認為積累了過多的遠端資料,需要丟棄一部分。
第五部分:MS_VOLUME_ID外掛
噪聲抑制
————————————————
版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。
原文連結:https://blog.csdn.net/dxpqxb/article/details/7937106