diff options
author | Liong Sauw Ming <ming@teluu.com> | 2015-10-06 05:57:51 +0000 |
---|---|---|
committer | Liong Sauw Ming <ming@teluu.com> | 2015-10-06 05:57:51 +0000 |
commit | e5906b82cd39fc0e4f6ab7e0138e6e81da7ab8ef (patch) | |
tree | 859a56ff1f82672f1c7912155fbfa20f70d8f9c4 | |
parent | 17276f318cc81d64c26aa766ae763b2773659dbf (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-x | aconfigure | 123 | ||||
-rw-r--r-- | aconfigure.ac | 76 | ||||
-rw-r--r-- | pjmedia/build/Makefile | 2 | ||||
-rw-r--r-- | pjmedia/build/os-auto.mak.in | 8 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/config.h | 18 | ||||
-rw-r--r-- | pjmedia/include/pjmedia/echo.h | 53 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/echo_common.c | 21 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/echo_internal.h | 15 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/echo_webrtc.c | 371 | ||||
-rw-r--r-- | pjsip-apps/src/pjsua/pjsua_app_config.c | 20 |
10 files changed, 695 insertions, 12 deletions
@@ -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."); |