Skip to content
Snippets Groups Projects
Commit dac50fad authored by Alexandre Savard's avatar Alexandre Savard
Browse files

#5855: Implement pjsip echo canceller

parent 764a6576
Branches
Tags
No related merge requests found
...@@ -144,8 +144,8 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool, ...@@ -144,8 +144,8 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
return status; return status;
/* Finally, create mutex */ /* Finally, create mutex */
status = pj_lock_create_recursive_mutex(pool, b->obj_name, // status = pj_lock_create_recursive_mutex(pool, b->obj_name,
&b->lock); // &b->lock);
if (status != PJ_SUCCESS) if (status != PJ_SUCCESS)
return status; return status;
...@@ -162,15 +162,15 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_destroy(pjmedia_delay_buf *b) ...@@ -162,15 +162,15 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_destroy(pjmedia_delay_buf *b)
PJ_ASSERT_RETURN(b, PJ_EINVAL); PJ_ASSERT_RETURN(b, PJ_EINVAL);
pj_lock_acquire(b->lock); // pj_lock_acquire(b->lock);
status = pjmedia_wsola_destroy(b->wsola); status = pjmedia_wsola_destroy(b->wsola);
if (status == PJ_SUCCESS) if (status == PJ_SUCCESS)
b->wsola = NULL; b->wsola = NULL;
pj_lock_release(b->lock); // pj_lock_release(b->lock);
pj_lock_destroy(b->lock); // pj_lock_destroy(b->lock);
b->lock = NULL; b->lock = NULL;
return status; return status;
...@@ -273,13 +273,13 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b, ...@@ -273,13 +273,13 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b,
PJ_ASSERT_RETURN(b && frame, PJ_EINVAL); PJ_ASSERT_RETURN(b && frame, PJ_EINVAL);
pj_lock_acquire(b->lock); // pj_lock_acquire(b->lock);
update(b, OP_PUT); update(b, OP_PUT);
status = pjmedia_wsola_save(b->wsola, frame, PJ_FALSE); status = pjmedia_wsola_save(b->wsola, frame, PJ_FALSE);
if (status != PJ_SUCCESS) { if (status != PJ_SUCCESS) {
pj_lock_release(b->lock); // pj_lock_release(b->lock);
return status; return status;
} }
...@@ -316,7 +316,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b, ...@@ -316,7 +316,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b,
pjmedia_circ_buf_write(b->circ_buf, frame, b->samples_per_frame); pjmedia_circ_buf_write(b->circ_buf, frame, b->samples_per_frame);
pj_lock_release(b->lock); // pj_lock_release(b->lock);
return PJ_SUCCESS; return PJ_SUCCESS;
} }
...@@ -327,7 +327,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b, ...@@ -327,7 +327,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b,
PJ_ASSERT_RETURN(b && frame, PJ_EINVAL); PJ_ASSERT_RETURN(b && frame, PJ_EINVAL);
pj_lock_acquire(b->lock); // pj_lock_acquire(b->lock);
update(b, OP_GET); update(b, OP_GET);
...@@ -342,7 +342,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b, ...@@ -342,7 +342,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b,
if (status == PJ_SUCCESS) { if (status == PJ_SUCCESS) {
TRACE__((b->obj_name,"Successfully generate 1 frame")); TRACE__((b->obj_name,"Successfully generate 1 frame"));
if (pjmedia_circ_buf_get_len(b->circ_buf) == 0) { if (pjmedia_circ_buf_get_len(b->circ_buf) == 0) {
pj_lock_release(b->lock); // pj_lock_release(b->lock);
return PJ_SUCCESS; return PJ_SUCCESS;
} }
...@@ -363,7 +363,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b, ...@@ -363,7 +363,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b,
/* The buffer is empty now, reset it */ /* The buffer is empty now, reset it */
pjmedia_circ_buf_reset(b->circ_buf); pjmedia_circ_buf_reset(b->circ_buf);
pj_lock_release(b->lock); // pj_lock_release(b->lock);
return PJ_SUCCESS; return PJ_SUCCESS;
} }
...@@ -371,7 +371,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b, ...@@ -371,7 +371,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b,
pjmedia_circ_buf_read(b->circ_buf, frame, b->samples_per_frame); pjmedia_circ_buf_read(b->circ_buf, frame, b->samples_per_frame);
pj_lock_release(b->lock); //pj_lock_release(b->lock);
return PJ_SUCCESS; return PJ_SUCCESS;
} }
...@@ -381,7 +381,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b) ...@@ -381,7 +381,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b)
{ {
PJ_ASSERT_RETURN(b, PJ_EINVAL); PJ_ASSERT_RETURN(b, PJ_EINVAL);
pj_lock_acquire(b->lock); // pj_lock_acquire(b->lock);
b->recalc_timer = RECALC_TIME; b->recalc_timer = RECALC_TIME;
...@@ -391,7 +391,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b) ...@@ -391,7 +391,7 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b)
/* Reset WSOLA */ /* Reset WSOLA */
pjmedia_wsola_reset(b->wsola, 0); pjmedia_wsola_reset(b->wsola, 0);
pj_lock_release(b->lock); // pj_lock_release(b->lock);
PJ_LOG(5,(b->obj_name,"Delay buffer is reset")); PJ_LOG(5,(b->obj_name,"Delay buffer is reset"));
......
...@@ -20,6 +20,7 @@ libaudio_la_SOURCES = \ ...@@ -20,6 +20,7 @@ libaudio_la_SOURCES = \
samplerateconverter.cpp \ samplerateconverter.cpp \
delaydetection.cpp \ delaydetection.cpp \
echocancel.cpp \ echocancel.cpp \
echosuppress.cpp \
speexechocancel.cpp \ speexechocancel.cpp \
noisesuppress.cpp \ noisesuppress.cpp \
audioprocessing.cpp \ audioprocessing.cpp \
...@@ -39,6 +40,7 @@ noinst_HEADERS = \ ...@@ -39,6 +40,7 @@ noinst_HEADERS = \
algorithm.h \ algorithm.h \
delaydetection.h \ delaydetection.h \
echocancel.h \ echocancel.h \
echosuppress.h \
speexechocancel.h \ speexechocancel.h \
noisesuppress.h \ noisesuppress.h \
audioprocessing.h \ audioprocessing.h \
......
...@@ -123,7 +123,7 @@ AudioRtpRecord::~AudioRtpRecord() ...@@ -123,7 +123,7 @@ AudioRtpRecord::~AudioRtpRecord()
} }
AudioRtpRecordHandler::AudioRtpRecordHandler (SIPCall *ca) : _audioRtpRecord (), _ca (ca) {} AudioRtpRecordHandler::AudioRtpRecordHandler (SIPCall *ca) : _audioRtpRecord (), _ca (ca), echoCanceller(ca->getMemoryPool()) {}
AudioRtpRecordHandler::~AudioRtpRecordHandler() {} AudioRtpRecordHandler::~AudioRtpRecordHandler() {}
...@@ -299,14 +299,16 @@ int AudioRtpRecordHandler::processDataEncode (void) ...@@ -299,14 +299,16 @@ int AudioRtpRecordHandler::processDataEncode (void)
_audioRtpRecord._audioProcess->processAudio (micDataConverted, nbSample * sizeof (SFLDataFormat)); _audioRtpRecord._audioProcess->processAudio (micDataConverted, nbSample * sizeof (SFLDataFormat));
} }
echoCanceller.process(micDataConverted, micDataEchoCancelled, nbSample * sizeof(SFLDataFormat)); // echoCanceller.process(micDataConverted, micDataEchoCancelled, nbSample * sizeof(SFLDataFormat));
if(Manager::instance().getEchoCancelState() != "enabled") {
echoCanceller.getData(micData);
}
_audioRtpRecord.audioProcessMutex.leave(); _audioRtpRecord.audioProcessMutex.leave();
_audioRtpRecord.audioCodecMutex.enter(); _audioRtpRecord.audioCodecMutex.enter();
compSize = _audioRtpRecord._audioCodec->encode (micDataEncoded, micDataEchoCancelled, nbSample * sizeof (SFLDataFormat)); compSize = _audioRtpRecord._audioCodec->encode (micDataEncoded, micData, nbSample * sizeof (SFLDataFormat));
// compSize = _audioRtpRecord._audioCodec->encode (micDataEncoded, micDataConverted, nbSample * sizeof (SFLDataFormat)); // compSize = _audioRtpRecord._audioCodec->encode (micDataEncoded, micDataConverted, nbSample * sizeof (SFLDataFormat));
_audioRtpRecord.audioCodecMutex.leave(); _audioRtpRecord.audioCodecMutex.leave();
...@@ -320,9 +322,14 @@ int AudioRtpRecordHandler::processDataEncode (void) ...@@ -320,9 +322,14 @@ int AudioRtpRecordHandler::processDataEncode (void)
// _audioRtpRecord._audioProcess->processAudio (micDataEchoCancelled, nbSample * sizeof (SFLDataFormat)); // _audioRtpRecord._audioProcess->processAudio (micDataEchoCancelled, nbSample * sizeof (SFLDataFormat));
} }
echoCanceller.process(micData, micDataEchoCancelled, nbSample * sizeof(SFLDataFormat)); // echoCanceller.process(micData, micDataEchoCancelled, nbSample * sizeof(SFLDataFormat));
// echoCanceller.process(micData, micDataEchoCancelled, nbSample * sizeof(SFLDataFormat));
if(Manager::instance().getEchoCancelState() != "enabled") {
_debug("EchoCancel: -------------------------- getData");
echoCanceller.getData(micData);
}
teststream.write(reinterpret_cast<char *>(micDataEchoCancelled), nbSample * sizeof(SFLDataFormat)); teststream.write(reinterpret_cast<char *>(micData), nbSample * sizeof(SFLDataFormat));
_audioRtpRecord.audioProcessMutex.leave(); _audioRtpRecord.audioProcessMutex.leave();
...@@ -331,7 +338,7 @@ int AudioRtpRecordHandler::processDataEncode (void) ...@@ -331,7 +338,7 @@ int AudioRtpRecordHandler::processDataEncode (void)
// no resampling required // no resampling required
// compSize = _audioRtpRecord._audioCodec->encode (micDataEncoded, micData, nbSample * sizeof (SFLDataFormat)); // compSize = _audioRtpRecord._audioCodec->encode (micDataEncoded, micData, nbSample * sizeof (SFLDataFormat));
compSize = _audioRtpRecord._audioCodec->encode (micDataEncoded, micDataEchoCancelled, nbSample * sizeof (SFLDataFormat)); compSize = _audioRtpRecord._audioCodec->encode (micDataEncoded, micData, nbSample * sizeof (SFLDataFormat));
_audioRtpRecord.audioCodecMutex.leave(); _audioRtpRecord.audioCodecMutex.leave();
} }
...@@ -370,15 +377,19 @@ void AudioRtpRecordHandler::processDataDecode (unsigned char *spkrData, unsigned ...@@ -370,15 +377,19 @@ void AudioRtpRecordHandler::processDataDecode (unsigned char *spkrData, unsigned
nbSample = _audioRtpRecord._converter->upsampleData (spkrDataDecoded, spkrDataConverted, codecSampleRate, mainBufferSampleRate, nbSampleDown); nbSample = _audioRtpRecord._converter->upsampleData (spkrDataDecoded, spkrDataConverted, codecSampleRate, mainBufferSampleRate, nbSampleDown);
if(Manager::instance().getEchoCancelState() != "enabled") {
echoCanceller.putData(spkrDataConverted, nbSample * sizeof(SFLDataFormat)); echoCanceller.putData(spkrDataConverted, nbSample * sizeof(SFLDataFormat));
}
// put data in audio layer, size in byte // put data in audio layer, size in byte
Manager::instance().getMainBuffer()->putData (spkrDataConverted, nbSample * sizeof (SFLDataFormat), 100, _ca->getCallId()); Manager::instance().getMainBuffer()->putData (spkrDataConverted, nbSample * sizeof (SFLDataFormat), 100, _ca->getCallId());
} else { } else {
if(Manager::instance().getEchoCancelState() != "enabled") {
_debug("EchoCancel: ------------------------ Put Data");
echoCanceller.putData(spkrDataDecoded, expandedSize); echoCanceller.putData(spkrDataDecoded, expandedSize);
}
// put data in audio layer, size in byte // put data in audio layer, size in byte
Manager::instance().getMainBuffer()->putData (spkrDataDecoded, expandedSize, 100, _ca->getCallId()); Manager::instance().getMainBuffer()->putData (spkrDataDecoded, expandedSize, 100, _ca->getCallId());
} }
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "audio/audioprocessing.h" #include "audio/audioprocessing.h"
#include "audio/noisesuppress.h" #include "audio/noisesuppress.h"
#include "audio/speexechocancel.h" #include "audio/speexechocancel.h"
#include "audio/echosuppress.h"
#include "managerimpl.h" #include "managerimpl.h"
#include <ccrtp/rtp.h> #include <ccrtp/rtp.h>
...@@ -227,7 +228,7 @@ class AudioRtpRecordHandler ...@@ -227,7 +228,7 @@ class AudioRtpRecordHandler
SIPCall *_ca; SIPCall *_ca;
SpeexEchoCancel echoCanceller; EchoSuppress echoCanceller;
}; };
} }
......
/*
* EchoSuppress.cpp
*
* Created on: 2011-05-18
* Author: asavard
*/
#include "echosuppress.h"
#include "pj/os.h"
#define ECHO_CANCEL_MEM_SIZE 1000
EchoSuppress::EchoSuppress(pj_pool_t *pool)
{
pj_status_t status;
_debug("********************************************************************** Init echo suppress");
// pj_thread_register
pj_thread_desc aPJThreadDesc;
pj_thread_t *pjThread;
_debug("EchoCancel: Thread not yet registered to pjsip, registering");
status = pj_thread_register("EchoCanceller", aPJThreadDesc, &pjThread);
if (status != PJ_SUCCESS) {
_error("EchoCancel: Error: Could not register new thread");
}
// if (!pj_thread_is_registered()) {
// }
if (!pj_thread_is_registered()) {
_warn("EchoCancel: Thread not registered...");
}
// pj_thread_register();
// status = pj_init();
// if(status != PJ_SUCCESS) {
// _error("EchoCancel: Error: could not init pj");
// }
//
// pj_caching_pool_init(&echoCachingPool, &pj_pool_factory_default_policy, ECHO_CANCEL_MEM_SIZE);
// echoCancelPool = pj_pool_create_int(&echoCachingPool.factory, "EchoCancelPool", 1000, 1000, NULL);
echoCancelPool = pool;
status = pjmedia_echo_create(echoCancelPool, 8000, 160, 250, 0, PJMEDIA_ECHO_SIMPLE, &echoState);
if(status != PJ_SUCCESS) {
_error("EchoCancel: Error: Could not create echo canceller");
}
}
EchoSuppress::~EchoSuppress()
{
// pjmedia_echo_destroy(echoState);
// pj_pool_destroy(echoCancelPool);
//pj_caching_pool_destroy(&echoCachingPool);
}
void EchoSuppress::reset()
{
}
void EchoSuppress::putData (SFLDataFormat *inputData, int nbBytes)
{
pj_status_t status;
status = pjmedia_echo_playback(echoState, reinterpret_cast<pj_int16_t *>(inputData));
if(status != PJ_SUCCESS) {
_warn("EchoCancel: Warning: Problem while putting input data");
}
}
int EchoSuppress::getData(SFLDataFormat *outputData)
{
pj_status_t status;
status = pjmedia_echo_capture(echoState, reinterpret_cast<pj_int16_t *>(outputData), 0);
if(status != PJ_SUCCESS) {
_warn("EchoCancel: Warning: Problem while getting output data");
}
return 0;
}
void EchoSuppress::process (SFLDataFormat *data UNUSED, int nbBytes UNUSED) {}
int EchoSuppress::process (SFLDataFormat *inputData, SFLDataFormat *outputData, int nbBytes) { return 0; }
void EchoSuppress::process (SFLDataFormat *micData UNUSED, SFLDataFormat *spkrData UNUSED, SFLDataFormat *outputData UNUSED, int nbBytes UNUSED) {}
/*
* EchoSuppress.h
*
* Created on: 2011-05-18
* Author: asavard
*/
#ifndef ECHOSUPPRESS_H_
#define ECHOSUPPRESS_H_
#include "pjmedia/echo.h"
#include "pj/pool.h"
#include "audioprocessing.h"
class EchoSuppress: public Algorithm {
public:
EchoSuppress(pj_pool_t *pool);
virtual ~EchoSuppress();
virtual void reset (void);
/**
* Add speaker data into internal buffer
* \param inputData containing far-end voice data to be sent to speakers
*/
virtual void putData (SFLDataFormat *, int);
virtual int getData(SFLDataFormat *);
/**
* Unused
*/
virtual void process (SFLDataFormat *, int);
/**
* Perform echo cancellation using internal buffers
* \param inputData containing mixed echo and voice data
* \param outputData containing
*/
virtual int process (SFLDataFormat *, SFLDataFormat *, int);
/**
* Perform echo cancellation, application must provide its own buffer
* \param micData containing mixed echo and voice data
* \param spkrData containing far-end voice data to be sent to speakers
* \param outputData containing the processed data
* \param size in bytes
*/
virtual void process (SFLDataFormat *, SFLDataFormat *, SFLDataFormat *, int);
private:
/**
* Memory pool for echo cancellation
*/
pj_pool_t *echoCancelPool;
/**
* The internal state of the echo canceller
*/
pjmedia_echo_state *echoState;
};
#endif /* ECHOSUPPRESS_H_ */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment