summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiong Sauw Ming <ming@teluu.com>2015-10-06 05:57:51 +0000
committerLiong Sauw Ming <ming@teluu.com>2015-10-06 05:57:51 +0000
commite5906b82cd39fc0e4f6ab7e0138e6e81da7ab8ef (patch)
tree859a56ff1f82672f1c7912155fbfa20f70d8f9c4
parent17276f318cc81d64c26aa766ae763b2773659dbf (diff)
Fixed #1888: Support for WebRtc AEC
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@5186 74dad513-b988-da41-8d7b-12977e46ad98
-rwxr-xr-xaconfigure123
-rw-r--r--aconfigure.ac76
-rw-r--r--pjmedia/build/Makefile2
-rw-r--r--pjmedia/build/os-auto.mak.in8
-rw-r--r--pjmedia/include/pjmedia/config.h18
-rw-r--r--pjmedia/include/pjmedia/echo.h53
-rw-r--r--pjmedia/src/pjmedia/echo_common.c21
-rw-r--r--pjmedia/src/pjmedia/echo_internal.h15
-rw-r--r--pjmedia/src/pjmedia/echo_webrtc.c371
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app_config.c20
10 files changed, 695 insertions, 12 deletions
diff --git a/aconfigure b/aconfigure
index 83fc5d71..e1bf3591 100755
--- a/aconfigure
+++ b/aconfigure
@@ -638,6 +638,8 @@ libcrypto_present
libssl_present
openssl_h_present
ac_no_ssl
+ac_webrtc_ldflags
+ac_webrtc_cflags
ac_libyuv_ldflags
ac_libyuv_cflags
ac_openh264_ldflags
@@ -800,6 +802,8 @@ with_openh264
enable_openh264
with_libyuv
enable_libyuv
+with_webrtc
+enable_webrtc
enable_ipp
with_ipp
with_ipp_samples
@@ -1465,6 +1469,7 @@ Optional Features:
--disable-v4l2 Disable Video4Linux2 (default: not disabled)
--disable-openh264 Disable OpenH264 (default: not disabled)
--disable-libyuv Exclude libyuv in the build
+ --disable-webrtc Exclude webrtc in the build
--enable-ipp Enable Intel IPP support. Specify the Intel IPP
package and samples location using IPPROOT and
IPPSAMPLES env var or with --with-ipp and
@@ -1505,6 +1510,7 @@ Optional Packages:
--with-ffmpeg=DIR Specify alternate FFMPEG prefix
--with-openh264=DIR Specify alternate OpenH264 prefix
--with-libyuv=DIR Specify alternate libyuv prefix
+ --with-webrtc=DIR Specify alternate WebRtc prefix
--with-ipp=DIR Specify the Intel IPP location
--with-ipp-samples=DIR Specify the Intel IPP samples location
--with-ipp-arch=ARCH Specify the Intel IPP ARCH suffix, e.g. "64" or
@@ -7322,6 +7328,123 @@ fi
+
+# Check whether --with-webrtc was given.
+if test "${with_webrtc+set}" = set; then :
+ withval=$with_webrtc;
+else
+ with_webrtc=no
+
+fi
+
+
+if test "x$ac_cross_compile" != "x" -a "x$with_webrtc" = "xno"; then
+ enable_webrtc=no
+fi
+
+
+
+# Check whether --enable-webrtc was given.
+if test "${enable_webrtc+set}" = set; then :
+ enableval=$enable_webrtc; if test "$enable_webrtc" = "no"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Checking if webrtc is disabled...yes" >&5
+$as_echo "Checking if webrtc is disabled...yes" >&6; }
+ fi
+else
+
+ if test "x$with_webrtc" != "xno" -a "x$with_webrtc" != "x"; then
+ WEBRTC_PREFIX=$with_webrtc
+ WEBRTC_CFLAGS="-I$WEBRTC_PREFIX/src"
+
+ case $target in
+ *-apple-darwin_ios*)
+ case $ARCH in
+ *arm*)
+ WEBRTC_CFLAGS="-DPJMEDIA_WEBRTC_AEC_USE_MOBILE=1 $WEBRTC_CFLAGS"
+ WEBRTC_LDFLAGS="-L$WEBRTC_PREFIX/src/out_ios/Release-iphoneos"
+ WEBRTC_LIBS="-laudio_processing_neon -lcommon_audio_neon"
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ *mingw* | *cygw* | *win32* | *w32* | *darwin* | *linux* | *android*)
+ WEBRTC_LDFLAGS="-L$WEBRTC_PREFIX/src/out/Release"
+ WEBRTC_LIBS="-laudio_processing_sse2"
+ ;;
+ *)
+ ;;
+ esac
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using webrtc prefix... $with_webrtc" >&5
+$as_echo "Using webrtc prefix... $with_webrtc" >&6; }
+ else
+ WEBRTC_CFLAGS=""
+ WEBRTC_LDFLAGS=""
+ fi
+
+ WEBRTC_LIBS="$WEBRTC_LIBS -laudio_processing -lcommon_audio -lsystem_wrappers"
+
+ SAVED_LIBS="$LIBS"
+ SAVED_LDFLAGS="$LDFLAGS"
+ SAVED_CFLAGS="$CFLAGS"
+
+ LIBS="$WEBRTC_LIBS $LIBS"
+ LDFLAGS="$WEBRTC_LDFLAGS $LDFLAGS"
+ CFLAGS="$WEBRTC_CFLAGS $CFLAGS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for WebRtcAec_Process in -laudio_processing" >&5
+$as_echo_n "checking for WebRtcAec_Process in -laudio_processing... " >&6; }
+if ${ac_cv_lib_audio_processing_WebRtcAec_Process+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-laudio_processing
+ $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char WebRtcAec_Process ();
+int
+main ()
+{
+return WebRtcAec_Process ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_audio_processing_WebRtcAec_Process=yes
+else
+ ac_cv_lib_audio_processing_WebRtcAec_Process=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_audio_processing_WebRtcAec_Process" >&5
+$as_echo "$ac_cv_lib_audio_processing_WebRtcAec_Process" >&6; }
+if test "x$ac_cv_lib_audio_processing_WebRtcAec_Process" = xyes; then :
+ ac_webrtc_cflags="-DPJMEDIA_HAS_WEBRTC_AEC=1 $WEBRTC_CFLAGS"
+ ac_webrtc_ldflags="$WEBRTC_LDFLAGS $WEBRTC_LIBS"
+
+else
+ LIBS="$SAVED_LIBS"
+ LDFLAGS="$SAVED_LDFLAGS"
+ CFLAGS="$SAVED_CFLAGS"
+
+fi
+
+
+fi
+
+
# Check whether --enable-ipp was given.
if test "${enable_ipp+set}" = set; then :
enableval=$enable_ipp;
diff --git a/aconfigure.ac b/aconfigure.ac
index 179dfac2..0a4dcd7f 100644
--- a/aconfigure.ac
+++ b/aconfigure.ac
@@ -1256,6 +1256,82 @@ AC_ARG_ENABLE(libyuv,
])
+dnl # WebRtc alt prefix
+AC_ARG_WITH(webrtc,
+ AC_HELP_STRING([--with-webrtc=DIR],
+ [Specify alternate WebRtc prefix]),
+ [],
+ [with_webrtc=no]
+ )
+
+dnl # Do not use default webrtc installation if we are cross-compiling
+if test "x$ac_cross_compile" != "x" -a "x$with_webrtc" = "xno"; then
+ enable_webrtc=no
+fi
+
+dnl # WebRtc
+AC_SUBST(ac_webrtc_cflags)
+AC_SUBST(ac_webrtc_ldflags)
+AC_ARG_ENABLE(webrtc,
+ AC_HELP_STRING([--disable-webrtc],
+ [Exclude webrtc in the build]),
+ [if test "$enable_webrtc" = "no"; then
+ AC_MSG_RESULT([Checking if webrtc is disabled...yes])
+ fi],
+ [
+ if test "x$with_webrtc" != "xno" -a "x$with_webrtc" != "x"; then
+ WEBRTC_PREFIX=$with_webrtc
+ WEBRTC_CFLAGS="-I$WEBRTC_PREFIX/src"
+
+ case $target in
+ *-apple-darwin_ios*)
+ case $ARCH in
+ *arm*)
+ WEBRTC_CFLAGS="-DPJMEDIA_WEBRTC_AEC_USE_MOBILE=1 $WEBRTC_CFLAGS"
+ WEBRTC_LDFLAGS="-L$WEBRTC_PREFIX/src/out_ios/Release-iphoneos"
+ WEBRTC_LIBS="-laudio_processing_neon -lcommon_audio_neon"
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ *mingw* | *cygw* | *win32* | *w32* | *darwin* | *linux* | *android*)
+ WEBRTC_LDFLAGS="-L$WEBRTC_PREFIX/src/out/Release"
+ WEBRTC_LIBS="-laudio_processing_sse2"
+ ;;
+ *)
+ ;;
+ esac
+
+ AC_MSG_RESULT([Using webrtc prefix... $with_webrtc])
+ else
+ WEBRTC_CFLAGS=""
+ WEBRTC_LDFLAGS=""
+ fi
+
+ WEBRTC_LIBS="$WEBRTC_LIBS -laudio_processing -lcommon_audio -lsystem_wrappers"
+
+ SAVED_LIBS="$LIBS"
+ SAVED_LDFLAGS="$LDFLAGS"
+ SAVED_CFLAGS="$CFLAGS"
+
+ LIBS="$WEBRTC_LIBS $LIBS"
+ LDFLAGS="$WEBRTC_LDFLAGS $LDFLAGS"
+ CFLAGS="$WEBRTC_CFLAGS $CFLAGS"
+
+ AC_CHECK_LIB(audio_processing,
+ WebRtcAec_Process,
+ [ ac_webrtc_cflags="-DPJMEDIA_HAS_WEBRTC_AEC=1 $WEBRTC_CFLAGS"
+ ac_webrtc_ldflags="$WEBRTC_LDFLAGS $WEBRTC_LIBS"
+ ],
+ [ LIBS="$SAVED_LIBS"
+ LDFLAGS="$SAVED_LDFLAGS"
+ CFLAGS="$SAVED_CFLAGS"
+ ],
+ []
+ )
+ ])
+
dnl ########################################################
dnl # Intel IPP support
dnl #
diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile
index 702efdcb..26f09538 100644
--- a/pjmedia/build/Makefile
+++ b/pjmedia/build/Makefile
@@ -62,7 +62,7 @@ export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
bidirectional.o clock_thread.o codec.o conference.o \
conf_switch.o converter.o converter_libswscale.o converter_libyuv.o \
delaybuf.o echo_common.o \
- echo_port.o echo_suppress.o endpoint.o errno.o \
+ echo_port.o echo_suppress.o echo_webrtc.o endpoint.o errno.o \
event.o format.o ffmpeg_util.o \
g711.o jbuf.o master_port.o mem_capture.o mem_player.o \
null_port.o plc_common.o port.o splitcomb.o \
diff --git a/pjmedia/build/os-auto.mak.in b/pjmedia/build/os-auto.mak.in
index 7a072745..a6280a28 100644
--- a/pjmedia/build/os-auto.mak.in
+++ b/pjmedia/build/os-auto.mak.in
@@ -37,14 +37,18 @@ LIBYUV_LDFLAGS = @ac_libyuv_ldflags@
OPENH264_CFLAGS = @ac_openh264_cflags@
OPENH264_LDFLAGS = @ac_openh264_ldflags@
+# WebRtc
+WEBRTC_CFLAGS = @ac_webrtc_cflags@
+WEBRTC_LDFLAGS = @ac_webrtc_ldflags@
+
# PJMEDIA features exclusion
export CFLAGS += @ac_no_small_filter@ @ac_no_large_filter@ @ac_no_speex_aec@ \
$(SDL_CFLAGS) $(FFMPEG_CFLAGS) $(V4L2_CFLAGS) $(QT_CFLAGS) \
$(IOS_CFLAGS) $(ANDROID_CFLAGS) $(LIBYUV_CFLAGS) \
- $(OPENH264_CFLAGS)
+ $(OPENH264_CFLAGS) $(WEBRTC_CFLAGS)
export LDFLAGS += $(SDL_LDFLAGS) $(FFMPEG_LDFLAGS) $(V4L2_LDFLAGS) \
- $(LIBYUV_LDFLAGS) $(OPENH264_LDFLAGS)
+ $(LIBYUV_LDFLAGS) $(OPENH264_LDFLAGS) $(WEBRTC_LDFLAGS)
# Define the desired sound device backend
# Valid values are:
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index 8bd622ba..f2f8e829 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -657,6 +657,24 @@
/**
+ * WebRtc Accoustic Echo Cancellation (AEC).
+ * By default is disabled.
+ */
+#ifndef PJMEDIA_HAS_WEBRTC_AEC
+# define PJMEDIA_HAS_WEBRTC_AEC 0
+#endif
+
+/**
+ * Specify whether WebRtc EC should use its mobile version AEC.
+ *
+ * Default: 0 (no)
+ */
+#ifndef PJMEDIA_WEBRTC_AEC_USE_MOBILE
+# define PJMEDIA_WEBRTC_AEC_USE_MOBILE 0
+#endif
+
+
+/**
* Maximum number of parameters in SDP fmtp attribute.
*
* Default: 16
diff --git a/pjmedia/include/pjmedia/echo.h b/pjmedia/include/pjmedia/echo.h
index 4ed62ee9..82c6809c 100644
--- a/pjmedia/include/pjmedia/echo.h
+++ b/pjmedia/include/pjmedia/echo.h
@@ -66,7 +66,8 @@ typedef enum pjmedia_echo_flag
/**
* Force to use Speex AEC as the backend echo canceller algorithm.
- * This setting is mutually exclusive with PJMEDIA_ECHO_SIMPLE.
+ * This setting is mutually exclusive with PJMEDIA_ECHO_SIMPLE and
+ * PJMEDIA_ECHO_WEBRTC.
*/
PJMEDIA_ECHO_SPEEX = 1,
@@ -74,11 +75,18 @@ typedef enum pjmedia_echo_flag
* If PJMEDIA_ECHO_SIMPLE flag is specified during echo canceller
* creation, then a simple echo suppressor will be used instead of
* an accoustic echo cancellation. This setting is mutually exclusive
- * with PJMEDIA_ECHO_SPEEX.
+ * with PJMEDIA_ECHO_SPEEX and PJMEDIA_ECHO_WEBRTC.
*/
PJMEDIA_ECHO_SIMPLE = 2,
/**
+ * Force to use WebRTC AEC as the backend echo canceller algorithm.
+ * This setting is mutually exclusive with PJMEDIA_ECHO_SIMPLE and
+ * PJMEDIA_ECHO_SPEEX.
+ */
+ PJMEDIA_ECHO_WEBRTC = 3,
+
+ /**
* For internal use.
*/
PJMEDIA_ECHO_ALGO_MASK = 15,
@@ -101,7 +109,46 @@ typedef enum pjmedia_echo_flag
* If PJMEDIA_ECHO_USE_SW_ECHO flag is specified, software echo canceller
* will be used instead of device EC.
*/
- PJMEDIA_ECHO_USE_SW_ECHO = 64
+ PJMEDIA_ECHO_USE_SW_ECHO = 64,
+
+ /**
+ * If PJMEDIA_ECHO_USE_NOISE_SUPPRESSOR flag is specified, the echo
+ * canceller will also apply noise suppressor method to reduce noise.
+ */
+ PJMEDIA_ECHO_USE_NOISE_SUPPRESSOR = 128,
+
+ /**
+ * Use default aggressiveness setting for the echo canceller algorithm.
+ * This setting is mutually exclusive with the other aggressiveness
+ * settings.
+ */
+ PJMEDIA_ECHO_AGGRESSIVENESS_DEFAULT = 0,
+
+ /**
+ * Use conservative aggressiveness setting for the echo canceller
+ * algorithm. This setting is mutually exclusive with the other
+ * aggressiveness settings.
+ */
+ PJMEDIA_ECHO_AGGRESSIVENESS_CONSERVATIVE = 0x100,
+
+ /**
+ * Use moderate aggressiveness setting for the echo canceller algorithm.
+ * This setting is mutually exclusive with the other aggressiveness
+ * settings.
+ */
+ PJMEDIA_ECHO_AGGRESSIVENESS_MODERATE = 0x200,
+
+ /**
+ * Use aggressive aggressiveness setting for the echo canceller
+ * algorithm. This setting is mutually exclusive with the other
+ * aggressiveness settings.
+ */
+ PJMEDIA_ECHO_AGGRESSIVENESS_AGGRESSIVE = 0x300,
+
+ /**
+ * For internal use.
+ */
+ PJMEDIA_ECHO_AGGRESSIVENESS_MASK = 0xF00
} pjmedia_echo_flag;
diff --git a/pjmedia/src/pjmedia/echo_common.c b/pjmedia/src/pjmedia/echo_common.c
index b6bacbec..66cfdf72 100644
--- a/pjmedia/src/pjmedia/echo_common.c
+++ b/pjmedia/src/pjmedia/echo_common.c
@@ -125,6 +125,20 @@ static struct ec_operations ipp_aec_op =
#endif
/*
+ * WebRTC AEC prototypes
+ */
+#if defined(PJMEDIA_HAS_WEBRTC_AEC) && PJMEDIA_HAS_WEBRTC_AEC!=0
+static struct ec_operations webrtc_aec_op =
+{
+ "WebRTC AEC",
+ &webrtc_aec_create,
+ &webrtc_aec_destroy,
+ &webrtc_aec_reset,
+ &webrtc_aec_cancel_echo
+};
+#endif
+
+/*
* Create the echo canceller.
*/
PJ_DEF(pj_status_t) pjmedia_echo_create( pj_pool_t *pool,
@@ -185,6 +199,13 @@ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool,
#endif
+#if defined(PJMEDIA_HAS_WEBRTC_AEC) && PJMEDIA_HAS_WEBRTC_AEC!=0
+ } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_WEBRTC ||
+ (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT)
+ {
+ ec->op = &webrtc_aec_op;
+#endif
+
} else {
ec->op = &echo_supp_op;
}
diff --git a/pjmedia/src/pjmedia/echo_internal.h b/pjmedia/src/pjmedia/echo_internal.h
index 1b0b9d57..bc75e1aa 100644
--- a/pjmedia/src/pjmedia/echo_internal.h
+++ b/pjmedia/src/pjmedia/echo_internal.h
@@ -77,6 +77,21 @@ PJ_DECL(pj_status_t) ipp_aec_cancel_echo(void *state,
unsigned options,
void *reserved );
+PJ_DECL(pj_status_t) webrtc_aec_create(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned tail_ms,
+ unsigned options,
+ void **p_echo );
+PJ_DECL(pj_status_t) webrtc_aec_destroy(void *state );
+PJ_DECL(void) webrtc_aec_reset(void *state );
+PJ_DECL(pj_status_t) webrtc_aec_cancel_echo(void *state,
+ pj_int16_t *rec_frm,
+ const pj_int16_t *play_frm,
+ unsigned options,
+ void *reserved );
+
PJ_END_DECL
diff --git a/pjmedia/src/pjmedia/echo_webrtc.c b/pjmedia/src/pjmedia/echo_webrtc.c
new file mode 100644
index 00000000..b1355d04
--- /dev/null
+++ b/pjmedia/src/pjmedia/echo_webrtc.c
@@ -0,0 +1,371 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2011-2015 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjmedia/echo.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+
+#if defined(PJMEDIA_HAS_WEBRTC_AEC) && PJMEDIA_HAS_WEBRTC_AEC != 0
+
+#include <webrtc/modules/audio_processing/aec/include/echo_cancellation.h>
+#include <webrtc/modules/audio_processing/aecm/include/echo_control_mobile.h>
+#include <webrtc/modules/audio_processing/aec/aec_core.h>
+
+#include "echo_internal.h"
+
+#define THIS_FILE "echo_webrtc.c"
+
+#if PJMEDIA_WEBRTC_AEC_USE_MOBILE == 1
+ #include <webrtc/modules/audio_processing/ns/include/noise_suppression_x.h>
+
+ #define NsHandle NsxHandle
+ #define WebRtcNs_Create WebRtcNsx_Create
+ #define WebRtcNs_Init WebRtcNsx_Init
+ #define WebRtcNs_Free WebRtcNsx_Free
+
+ #define WebRtcAec_Create WebRtcAecm_Create
+ #define WebRtcAec_Init(handle, clock, sclock) WebRtcAecm_Init(handle, clock)
+ #define WebRtcAec_Free WebRtcAecm_Free
+ #define WebRtcAec_get_error_code WebRtcAecm_get_error_code
+ #define WebRtcAec_enable_delay_agnostic(core, enable)
+ #define WebRtcAec_set_config WebRtcAecm_set_config
+ #define WebRtcAec_BufferFarend WebRtcAecm_BufferFarend
+ #define AecConfig AecmConfig
+ #define SHOW_DELAY_METRICS 0
+ typedef short sample;
+#else
+ #include <webrtc/modules/audio_processing/ns/include/noise_suppression.h>
+
+ typedef float sample;
+
+ /* If SHOW_DELAY_METRICS is set to non-zero, delay metrics stats will
+ * be printed every SHOW_DELAY_METRICS-th call to webrtc_aec_cancel_echo().
+ * For example, if ptime is 20ms, set this to 250 to print the metrics
+ * every 250*20/1000=5 seconds.
+ */
+ #define SHOW_DELAY_METRICS 0
+
+#endif
+
+#define BUF_LEN 160
+
+typedef struct webrtc_ec
+{
+ void *AEC_inst;
+ NsHandle *NS_inst;
+ unsigned options;
+ unsigned samples_per_frame;
+ unsigned tail;
+ unsigned clock_rate;
+ unsigned channel_count;
+ unsigned subframe_len;
+ sample tmp_buf[BUF_LEN];
+ sample tmp_buf2[BUF_LEN];
+#if SHOW_DELAY_METRICS
+ unsigned counter;
+#endif
+} webrtc_ec;
+
+
+static void print_webrtc_aec_error(const char *tag, void *AEC_inst)
+{
+ unsigned status = WebRtcAec_get_error_code(AEC_inst);
+ PJ_LOG(3, (THIS_FILE, "WebRTC AEC error (%s) %d ", tag, status));
+}
+
+static void set_config(void *AEC_inst, unsigned options)
+{
+ unsigned aggr_opt = options & PJMEDIA_ECHO_AGGRESSIVENESS_MASK;
+ int status;
+ AecConfig aec_config;
+
+#if PJMEDIA_WEBRTC_AEC_USE_MOBILE
+ aec_config.echoMode = 3;
+ if (aggr_opt == PJMEDIA_ECHO_AGGRESSIVENESS_CONSERVATIVE)
+ aec_config.echoMode = 0;
+ else if (aggr_opt == PJMEDIA_ECHO_AGGRESSIVENESS_AGGRESSIVE)
+ aec_config.echoMode = 4;
+ aec_config.cngMode = AecmTrue;
+#else
+
+ aec_config.nlpMode = kAecNlpModerate;
+ if (aggr_opt == PJMEDIA_ECHO_AGGRESSIVENESS_CONSERVATIVE)
+ aec_config.nlpMode = kAecNlpConservative;
+ else if (aggr_opt == PJMEDIA_ECHO_AGGRESSIVENESS_AGGRESSIVE)
+ aec_config.nlpMode = kAecNlpAggressive;
+ else
+ aec_config.nlpMode = kAecNlpModerate;
+
+ aec_config.skewMode = kAecFalse;
+#if SHOW_DELAY_METRICS
+ aec_config.metricsMode = kAecTrue;
+ aec_config.delay_logging = kAecTrue;
+#else
+ aec_config.metricsMode = kAecFalse;
+ aec_config.delay_logging = kAecFalse;
+#endif
+
+#endif
+
+ status = WebRtcAec_set_config(AEC_inst, aec_config);
+ if (status != 0) {
+ print_webrtc_aec_error("Init config", AEC_inst);
+ }
+}
+
+/*
+ * Create the AEC.
+ */
+PJ_DEF(pj_status_t) webrtc_aec_create(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned tail_ms,
+ unsigned options,
+ void **p_echo )
+{
+ webrtc_ec *echo;
+ int status;
+
+ *p_echo = NULL;
+
+ echo = PJ_POOL_ZALLOC_T(pool, webrtc_ec);
+ PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM);
+
+ /* Currently we only support mono. */
+ if (channel_count != 1)
+ return PJ_ENOTSUP;
+
+ echo->channel_count = channel_count;
+ echo->samples_per_frame = samples_per_frame;
+ echo->tail = tail_ms;
+ echo->clock_rate = clock_rate;
+ /* SWB is processed as 160 frame size */
+ if (clock_rate > 8000)
+ echo->subframe_len = 160;
+ else
+ echo->subframe_len = 80;
+ echo->options = options;
+
+ /* Create WebRTC AEC */
+ echo->AEC_inst = WebRtcAec_Create();
+ if (!echo->AEC_inst) {
+ return PJ_ENOMEM;
+ }
+
+ /* Init WebRTC AEC */
+ status = WebRtcAec_Init(echo->AEC_inst, clock_rate, clock_rate);
+ if (status != 0) {
+ print_webrtc_aec_error("Init", echo->AEC_inst);
+ WebRtcAec_Free(echo->AEC_inst);
+ return PJ_ENOTSUP;
+ }
+
+ /* WebRtc is very dependent on delay calculation, which will be passed
+ * to WebRtcAec_Process() below. A poor estimate, even by as little as
+ * 40ms, may affect the echo cancellation results greatly.
+ * Hence, we need to enable delay-agnostic echo cancellation. This
+ * low-level feature relies on internally estimated delays between
+ * the process and reverse streams, thus not relying on reported
+ * system delays.
+ * Still, with the delay agnostic feature, it may take some time (5-10s
+ * or more) for the Aec module to learn the optimal delay, thus
+ * a good initial estimate is necessary for good EC quality in
+ * the beginning of a call.
+ */
+ WebRtcAec_enable_delay_agnostic(WebRtcAec_aec_core(echo->AEC_inst), 1);
+
+ set_config(echo->AEC_inst, options);
+
+ if (options & PJMEDIA_ECHO_USE_NOISE_SUPPRESSOR) {
+ echo->NS_inst = WebRtcNs_Create();
+ if (echo->NS_inst) {
+ status = WebRtcNs_Init(echo->NS_inst, clock_rate);
+ if (status != 0) {
+ WebRtcNs_Free(echo->NS_inst);
+ echo->NS_inst = NULL;
+ }
+ }
+ if (!echo->NS_inst) {
+ PJ_LOG(3, (THIS_FILE, "Unable to create WebRTC noise suppressor"));
+ }
+ }
+
+ PJ_LOG(3, (THIS_FILE, "WebRTC AEC%s successfully created with options %d",
+#if PJMEDIA_WEBRTC_AEC_USE_MOBILE
+ " mobile", options));
+#else
+ "", options));
+#endif
+
+ /* Done */
+ *p_echo = echo;
+ return PJ_SUCCESS;
+
+}
+
+
+/*
+ * Destroy AEC
+ */
+PJ_DEF(pj_status_t) webrtc_aec_destroy(void *state )
+{
+ webrtc_ec *echo = (webrtc_ec*) state;
+ PJ_ASSERT_RETURN(echo, PJ_EINVAL);
+
+ if (echo->AEC_inst) {
+ WebRtcAec_Free(echo->AEC_inst);
+ echo->AEC_inst = NULL;
+ }
+ if (echo->NS_inst) {
+ WebRtcNs_Free(echo->NS_inst);
+ echo->NS_inst = NULL;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Reset AEC
+ */
+PJ_DEF(void) webrtc_aec_reset(void *state )
+{
+ webrtc_ec *echo = (webrtc_ec*) state;
+ int status;
+
+ pj_assert(echo != NULL);
+
+ /* Re-initialize the EC */
+ status = WebRtcAec_Init(echo->AEC_inst, echo->clock_rate, echo->clock_rate);
+ if (status != 0) {
+ print_webrtc_aec_error("reset", echo->AEC_inst);
+ return;
+ }
+
+ set_config(echo->AEC_inst, echo->options);
+
+ PJ_LOG(4, (THIS_FILE, "WebRTC AEC reset succeeded"));
+}
+
+
+/*
+ * Perform echo cancellation.
+ */
+PJ_DEF(pj_status_t) webrtc_aec_cancel_echo( void *state,
+ pj_int16_t *rec_frm,
+ const pj_int16_t *play_frm,
+ unsigned options,
+ void *reserved )
+{
+ webrtc_ec *echo = (webrtc_ec*) state;
+ int status;
+ unsigned i, j, frm_idx = 0;
+ const sample * buf_ptr;
+ sample * out_buf_ptr;
+
+ /* Sanity checks */
+ PJ_ASSERT_RETURN(echo && rec_frm && play_frm, PJ_EINVAL);
+
+ for(i = echo->samples_per_frame / echo->subframe_len; i > 0; i--) {
+#if PJMEDIA_WEBRTC_AEC_USE_MOBILE
+ buf_ptr = &play_frm[frm_idx];
+#else
+ for (j = 0; j < echo->subframe_len; j++) {
+ echo->tmp_buf[j] = rec_frm[frm_idx+j];
+ echo->tmp_buf2[j] = play_frm[frm_idx+j];
+ }
+ buf_ptr = echo->tmp_buf2;
+#endif
+
+ /* Feed farend buffer */
+ status = WebRtcAec_BufferFarend(echo->AEC_inst, buf_ptr,
+ echo->subframe_len);
+ if (status != 0) {
+ print_webrtc_aec_error("Buffer farend", echo->AEC_inst);
+ return PJ_EUNKNOWN;
+ }
+
+ buf_ptr = echo->tmp_buf;
+ out_buf_ptr = echo->tmp_buf2;
+ if (echo->NS_inst) {
+#if PJMEDIA_WEBRTC_AEC_USE_MOBILE
+ buf_ptr = &rec_frm[frm_idx];
+ WebRtcNsx_Process(echo->NS_inst, &buf_ptr, echo->channel_count,
+ &out_buf_ptr);
+ buf_ptr = out_buf_ptr;
+ out_buf_ptr = echo->tmp_buf;
+#else
+ WebRtcNs_Analyze(echo->NS_inst, buf_ptr);
+#endif
+ }
+
+ /* Process echo cancellation */
+#if PJMEDIA_WEBRTC_AEC_USE_MOBILE
+ status = WebRtcAecm_Process(echo->AEC_inst, &rec_frm[frm_idx],
+ (echo->NS_inst? buf_ptr: NULL),
+ out_buf_ptr, echo->subframe_len,
+ echo->tail);
+#else
+ status = WebRtcAec_Process(echo->AEC_inst, &buf_ptr,
+ echo->channel_count, &out_buf_ptr,
+ echo->subframe_len, echo->tail, 0);
+#endif
+ if (status != 0) {
+ print_webrtc_aec_error("Process echo", echo->AEC_inst);
+ return PJ_EUNKNOWN;
+ }
+
+#if !PJMEDIA_WEBRTC_AEC_USE_MOBILE
+ if (echo->NS_inst) {
+ /* Noise suppression */
+ buf_ptr = echo->tmp_buf2;
+ out_buf_ptr = echo->tmp_buf;
+ WebRtcNs_Process(echo->NS_inst, &buf_ptr,
+ echo->channel_count, &out_buf_ptr);
+ }
+#endif
+
+ for (j = 0; j < echo->subframe_len; j++) {
+ rec_frm[frm_idx++] = (pj_int16_t)out_buf_ptr[j];
+ }
+ }
+
+#if SHOW_DELAY_METRICS
+ if (++echo->counter >= SHOW_DELAY_METRICS) {
+ int median, std;
+ float frac_delay;
+
+ if (WebRtcAec_GetDelayMetrics(echo->AEC_inst, &median, &std,
+ &frac_delay) == 0)
+ {
+ PJ_LOG(3, (THIS_FILE, "WebRTC delay metrics: median=%d, std=%d, "
+ "fraction of poor delays=%f",
+ median, std, frac_delay));
+ }
+ echo->counter = 0;
+ }
+#endif
+
+ return PJ_SUCCESS;
+}
+
+#endif
diff --git a/pjsip-apps/src/pjsua/pjsua_app_config.c b/pjsip-apps/src/pjsua/pjsua_app_config.c
index 633b45e9..00ba2784 100644
--- a/pjsip-apps/src/pjsua/pjsua_app_config.c
+++ b/pjsip-apps/src/pjsua/pjsua_app_config.c
@@ -23,6 +23,9 @@
#define MAX_APP_OPTIONS 128
+#define str(s) #s
+#define xstr(s) str(s)
+
char *stdout_refresh_text = "STDOUT_REFRESH";
/* Show usage */
@@ -141,17 +144,22 @@ static void usage(void)
puts (" --auto-conf Automatically put calls in conference with others");
puts (" --rec-file=file Open file recorder (extension can be .wav or .mp3");
puts (" --auto-rec Automatically record conversation");
- puts (" --quality=N Specify media quality (0-10, default=6)");
+ puts (" --quality=N Specify media quality (0-10, default="
+ xstr(PJSUA_DEFAULT_CODEC_QUALITY) ")");
puts (" --ptime=MSEC Override codec ptime to MSEC (default=specific)");
puts (" --no-vad Disable VAD/silence detector (default=vad enabled)");
- puts (" --ec-tail=MSEC Set echo canceller tail length (default=256)");
+ puts (" --ec-tail=MSEC Set echo canceller tail length (default="
+ xstr(PJSUA_DEFAULT_EC_TAIL_LEN) ")");
puts (" --ec-opt=OPT Select echo canceller algorithm (0=default, ");
- puts (" 1=speex, 2=suppressor)");
- puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is 30)");
+ puts (" 1=speex, 2=suppressor, 3=WebRtc)");
+ puts (" --ilbc-mode=MODE Set iLBC codec mode (20 or 30, default is "
+ xstr(PJSUA_DEFAULT_ILBC_MODE) ")");
puts (" --capture-dev=id Audio capture device ID (default=-1)");
puts (" --playback-dev=id Audio playback device ID (default=-1)");
- puts (" --capture-lat=N Audio capture latency, in ms (default=100)");
- puts (" --playback-lat=N Audio playback latency, in ms (default=100)");
+ puts (" --capture-lat=N Audio capture latency, in ms (default="
+ xstr(PJMEDIA_SND_DEFAULT_REC_LATENCY) ")");
+ puts (" --playback-lat=N Audio playback latency, in ms (default="
+ xstr(PJMEDIA_SND_DEFAULT_PLAY_LATENCY) ")");
puts (" --snd-auto-close=N Auto close audio device when idle for N secs (default=1)");
puts (" Specify N=-1 to disable this feature.");
puts (" Specify N=0 for instant close when unused.");