diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in
index 6594a922eb..1d9703419a 100644
--- a/build_tools/menuselect-deps.in
+++ b/build_tools/menuselect-deps.in
@@ -57,6 +57,7 @@ SPANDSP=@PBX_SPANDSP@
SPEEX=@PBX_SPEEX@
SPEEXDSP=@PBX_SPEEXDSP@
SPEEX_PREPROCESS=@PBX_SPEEX_PREPROCESS@
+VEVS=@PBX_VEVS@
SQLITE3=@PBX_SQLITE3@
SRTP=@PBX_SRTP@
SS7=@PBX_SS7@
diff --git a/codecs/codec_vevs.c b/codecs/codec_vevs.c
new file mode 100644
index 0000000000..14de95c4dd
--- /dev/null
+++ b/codecs/codec_vevs.c
@@ -0,0 +1,285 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ */
+
+/*! \file
+ *
+ * \brief Translate between signed linear and Vocal EVS codec
+ *
+ * \ingroup codecs
+ */
+
+/*** MODULEINFO
+ vevs
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/translate.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+
+#include
+#define uint8 uint8_t
+#define uint16 uint16_t
+#define uint32 uint32_t
+#define vbool bool
+#define sint15 int16_t
+#include "vocal-evs/evs.h"
+
+#define BUFFER_SAMPLES 8000 /* 0.5 seconds @ 16 kHz */
+#define BUFFER_FRAMES 8192 /* Enough for about 0.5 seconds @ 128 Kbps */
+
+/* Sample frame data */
+
+#include "asterisk/slin.h"
+#include "ex_vevs.h"
+
+/* Encoder and decoder instances */
+
+struct evs_enc_pvt {
+ evs_enc_ctx_t enc; /* Encoder states */
+ int chunk; /* Size of chunk to encode (in samples) */
+ int16_t buf[BUFFER_SAMPLES]; /* Buffer to store received uncompressed audio */
+};
+
+struct evs_dec_pvt {
+ evs_dec_ctx_t dec; /* Decoder states */
+ int chunk; /* Size of chunk to decode (in samples) */
+};
+
+#warning hacking
+int vocal_evs_init_coder (evs_enc_ctx_t *ctx, uint16 bandwidth, uint16 sampling_frequency, uint32 bitrate, vbool dtx_enable) { return 320; }
+int vocal_evs_process_coder (evs_enc_ctx_t *ctx, uint8 *enc_data, uint16 enc_data_size, const sint15 *enc_samples, uint16 sample_count) { return 320; }
+int vocal_evs_close_coder (evs_enc_ctx_t *ctx) { return 0; }
+int vocal_evs_init_decoder (evs_dec_ctx_t *ctx, uint16 sampling_frequency) { return 320; }
+int vocal_evs_process_decoder (evs_dec_ctx_t *ctx, sint15 *dec_samples, uint16 max_sample_count, uint8 *dec_data, uint16 dec_data_size) { return 320; }
+int vocal_evs_close_decoder (evs_dec_ctx_t *ctx) { return 0; }
+
+
+/* Create and destroy codec intances */
+
+static int evs_enc_new(struct ast_trans_pvt *pvt)
+{
+ struct evs_enc_pvt *enc = pvt->pvt;
+ const unsigned int sample_rate = pvt->t->src_codec.sample_rate;
+ int rc;
+
+ memset(enc, 0, sizeof(*enc));
+ rc = vocal_evs_init_coder(&enc->enc, EVS_BW_NB, EVS_SF_16K, EVS_BR_5K90, false);
+ if (rc <= 0) {
+ ast_log(LOG_ERROR, "Error creating the Vocal EVS encoder\n");
+ return -1;
+ }
+ enc->chunk = rc;
+
+ ast_debug(3, "Created encoder (Vocal EVS) with sample rate %d\n", sample_rate);
+
+ return 0;
+}
+
+static void evs_enc_destroy(struct ast_trans_pvt *pvt)
+{
+ struct evs_enc_pvt *enc = pvt->pvt;
+
+ vocal_evs_close_coder(&enc->enc);
+
+ ast_debug(3, "Destroyed encoder (Vocal EVS)\n");
+}
+
+static int evs_dec_new(struct ast_trans_pvt *pvt)
+{
+ struct evs_dec_pvt *dec = pvt->pvt;
+ const unsigned int sample_rate = pvt->t->src_codec.sample_rate;
+ int rc;
+
+ memset(dec, 0, sizeof(*dec));
+ rc = vocal_evs_init_decoder(&dec->dec, EVS_SF_16K);
+ if (rc <= 0) {
+ ast_log(LOG_ERROR, "Error creating the Vocal EVS decoder\n");
+ return -1;
+ }
+ dec->chunk = rc;
+
+ ast_debug(3, "Created decoder (Vocal EVS) with sample rate %d\n", sample_rate);
+
+ return 0;
+}
+
+static void evs_dec_destroy(struct ast_trans_pvt *pvt)
+{
+ struct evs_dec_pvt *dec = pvt->pvt;
+
+ vocal_evs_close_decoder(&dec->dec);
+
+ ast_debug(3, "Destroyed encoder (Vocal EVS)\n");
+}
+
+/* Encoder */
+
+static int lintoevs_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct evs_enc_pvt *enc = pvt->pvt;
+
+ /* XXX We should look at how old the rest of our stream is, and if it
+ is too old, then we should overwrite it entirely, otherwise we can
+ get artifacts of earlier talk that do not belong */
+ if (pvt->samples + f->samples > BUFFER_SAMPLES) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ memcpy(enc->buf + pvt->samples, f->data.ptr, f->datalen);
+ pvt->samples += f->samples;
+ return 0;
+}
+
+static struct ast_frame *lintoevs_frameout(struct ast_trans_pvt *pvt)
+{
+ struct evs_enc_pvt *enc = pvt->pvt;
+ struct ast_frame *result = NULL;
+ struct ast_frame *last = NULL;
+ int samples = 0; /* output samples */
+
+ while (pvt->samples >= enc->chunk) {
+ struct ast_frame *current;
+ int rc;
+
+ rc = vocal_evs_process_coder(&enc->enc, pvt->outbuf.uc, BUFFER_FRAMES, enc->buf, enc->chunk);
+ if (rc <= 0) {
+ ast_log(LOG_ERROR, "Error encoding with Vocal EVS encoder\n");
+ pvt->samples = 0;
+ return NULL;
+ }
+ samples += enc->chunk;
+ pvt->samples -= enc->chunk;
+ current = ast_trans_frameout(pvt, rc, enc->chunk);
+ if (!current) {
+ continue;
+ } else if (last) {
+ /* Append frame */
+ AST_LIST_NEXT(last, frame_list) = current;
+ } else {
+ /* Return first frame */
+ result = current;
+ }
+ last = current;
+ }
+
+ /* Move the data at the end of the buffer to the front */
+ if (samples) {
+ memmove(enc->buf, enc->buf + samples, pvt->samples * sizeof(int16_t));
+ }
+
+ return result;
+}
+
+/* Decoder */
+
+static int evstolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct evs_dec_pvt *dec = pvt->pvt;
+ int16_t *dst = pvt->outbuf.i16;
+ int rc;
+
+ if (BUFFER_SAMPLES - pvt->samples < dec->chunk) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+
+ if (f->datalen == 0) {
+ ast_log(LOG_DEBUG, "Conceal missing EVS frame.\n");
+ /* The documentation does not give a hint on how bad frames indicated and if they are concealed at all. This may not work!*/
+ rc = vocal_evs_process_decoder(&dec->dec, dst + pvt->samples, BUFFER_SAMPLES - pvt->samples, NULL, 0);
+ if (rc <= 0) {
+ ast_log(LOG_ERROR, "Error concealing missing EVS frame with Vocal EVS decoder\n");
+ return -1;
+ }
+ } else {
+ rc = vocal_evs_process_decoder(&dec->dec, dst + pvt->samples, BUFFER_SAMPLES - pvt->samples, f->data.ptr, f->datalen);
+ if (rc <= 0) {
+ ast_log(LOG_ERROR, "Error decoding with Vocal EVS decoder\n");
+ return -1;
+ }
+ }
+ pvt->samples += rc;
+ pvt->datalen += rc * sizeof(int16_t);
+
+ return 0;
+}
+
+static struct ast_translator lintoevs = {
+ .name = "lintovevs",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 16000,
+ },
+ .dst_codec = {
+ .name = "vevs",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 16000,
+ },
+ .format = "vevs",
+ .newpvt = evs_enc_new,
+ .framein = lintoevs_framein,
+ .frameout = lintoevs_frameout,
+ .destroy = evs_enc_destroy,
+ .sample = slin16_sample,
+ .buf_size = BUFFER_FRAMES,
+ .desc_size = sizeof (struct evs_enc_pvt ),
+};
+
+static struct ast_translator evstolin = {
+ .name = "vevstolin",
+ .src_codec = {
+ .name = "vevs",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 16000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 16000,
+ },
+ .format = "slin",
+ .newpvt = evs_dec_new,
+ .framein = evstolin_framein,
+ .destroy = evs_dec_destroy,
+ .sample = vevs_sample,
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+ .desc_size = sizeof (struct evs_dec_pvt ),
+};
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_translator(&lintoevs);
+ res |= ast_unregister_translator(&evstolin);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_translator(&evstolin);
+ res |= ast_register_translator(&lintoevs);
+
+ if (res) {
+ unload_module();
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Vocal EVS Coder/Decoder",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+);
diff --git a/codecs/ex_vevs.h b/codecs/ex_vevs.h
new file mode 100644
index 0000000000..84bbf5f57c
--- /dev/null
+++ b/codecs/ex_vevs.h
@@ -0,0 +1,28 @@
+#include "asterisk/format_cache.h" /* for ast_format_evs */
+#include "asterisk/frame.h" /* for ast_frame, etc */
+
+static uint8_t ex_vevs[] = { /* PRIMARY_16400, WB */
+ 0x05, 0x51, 0x63, 0x4e, 0xa7, 0x87, 0x0c, 0xc4,
+ 0x50, 0x3c, 0xcf, 0x60, 0x19, 0xbf, 0xd3, 0x93,
+ 0xf4, 0xd9, 0x49, 0xac, 0x89, 0xce, 0x4c, 0x4d,
+ 0x5e, 0x01, 0xff, 0x80, 0x00, 0x17, 0x17, 0xd5,
+ 0x73, 0x7b, 0xd5, 0x1d, 0xe1, 0xcf, 0x65, 0x0b,
+ 0xee, 0x95
+};
+
+static struct ast_frame *vevs_sample(void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .datalen = sizeof(ex_vevs),
+ .samples = 320,
+ .mallocd = 0,
+ .offset = 0,
+ .src = __PRETTY_FUNCTION__,
+ .data.ptr = ex_vevs,
+ };
+
+ f.subclass.format = ast_format_vevs;
+
+ return &f;
+}
diff --git a/configure b/configure
index 07764740dc..a371b91c16 100755
--- a/configure
+++ b/configure
@@ -1017,6 +1017,10 @@ PBX_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK
PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_DIR
PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_INCLUDE
PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_LIB
+PBX_VEVS
+VEVS_DIR
+VEVS_INCLUDE
+VEVS_LIB
PBX_BEANSTALK
BEANSTALK_DIR
BEANSTALK_INCLUDE
@@ -2216,6 +2220,7 @@ Optional Packages:
--with-opusfile=PATH use Opusfile files in PATH
--with-postgres=PATH use PostgreSQL files in PATH
--with-beanstalk=PATH use Beanstalk Job Queue files in PATH
+ --with-bluetooth=PATH use Vocal EVS Audio Decoder/Encoder files in PATH
--with-pjproject=PATH use PJPROJECT files in PATH
--with-popt=PATH use popt files in PATH
--with-portaudio=PATH use PortAudio files in PATH
@@ -13151,6 +13156,41 @@ fi
+#FIXME
+
+ VEVS_DESCRIP="Vocal EVS Audio Decoder/Encoder"
+ VEVS_OPTION="bluetooth"
+ PBX_VEVS=0
+
+# Check whether --with-bluetooth was given.
+if test ${with_bluetooth+y}
+then :
+ withval=$with_bluetooth;
+ case ${withval} in
+ n|no)
+ USE_VEVS=no
+ # -1 is a magic value used by menuselect to know that the package
+ # was disabled, other than 'not found'
+ PBX_VEVS=-1
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} VEVS"
+ ;;
+ *)
+ VEVS_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} VEVS"
+ ;;
+ esac
+
+fi
+
+
+
+
+
+
+
+#AST_EXT_LIB_SETUP([VEVS], [Vocal EVS Audio Decoder/Encoder], [vocal-evs])
if test "x${PBX_PJPROJECT}" != "x1" ; then
@@ -26471,6 +26511,105 @@ fi
+#FIXME
+
+if test "x${PBX_VEVS}" != "x1" -a "${USE_VEVS}" != "no"; then
+ pbxlibdir=""
+ # if --with-VEVS=DIR has been specified, use it.
+ if test "x${VEVS_DIR}" != "x"; then
+ if test -d ${VEVS_DIR}/lib; then
+ pbxlibdir="-L${VEVS_DIR}/lib"
+ else
+ pbxlibdir="-L${VEVS_DIR}"
+ fi
+ fi
+
+ ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
+ CFLAGS="${CFLAGS} "
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ba2str in -lbluetooth" >&5
+printf %s "checking for ba2str in -lbluetooth... " >&6; }
+if test ${ac_cv_lib_bluetooth_ba2str+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lbluetooth ${pbxlibdir} $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. */
+char ba2str ();
+int
+main (void)
+{
+return ba2str ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ ac_cv_lib_bluetooth_ba2str=yes
+else $as_nop
+ ac_cv_lib_bluetooth_ba2str=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bluetooth_ba2str" >&5
+printf "%s\n" "$ac_cv_lib_bluetooth_ba2str" >&6; }
+if test "x$ac_cv_lib_bluetooth_ba2str" = xyes
+then :
+ AST_VEVS_FOUND=yes
+else $as_nop
+ AST_VEVS_FOUND=no
+fi
+
+ CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+
+
+ # now check for the header.
+ if test "${AST_VEVS_FOUND}" = "yes"; then
+ VEVS_LIB="${pbxlibdir} -lbluetooth "
+ # if --with-VEVS=DIR has been specified, use it.
+ if test "x${VEVS_DIR}" != "x"; then
+ VEVS_INCLUDE="-I${VEVS_DIR}/include"
+ fi
+ VEVS_INCLUDE="${VEVS_INCLUDE} "
+
+ # check for the header
+ ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${VEVS_INCLUDE}"
+ ac_fn_c_check_header_compile "$LINENO" "bluetooth/bluetooth.h" "ac_cv_header_bluetooth_bluetooth_h" "$ac_includes_default"
+if test "x$ac_cv_header_bluetooth_bluetooth_h" = xyes
+then :
+ VEVS_HEADER_FOUND=1
+else $as_nop
+ VEVS_HEADER_FOUND=0
+fi
+
+ CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+
+ if test "x${VEVS_HEADER_FOUND}" = "x0" ; then
+ VEVS_LIB=""
+ VEVS_INCLUDE=""
+ else
+
+ PBX_VEVS=1
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_VEVS 1
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+#AST_EXT_LIB_CHECK([VEVS], [vocal-evs], [vevs_enc], [vocal-evs/evs.h])
+
PG_CONFIG=":"
if test "${USE_PGSQL}" != "no"; then
if test "x${PGSQL_DIR}" != "x"; then
diff --git a/configure.ac b/configure.ac
index 0e1a11eeda..ec5f5b1253 100644
--- a/configure.ac
+++ b/configure.ac
@@ -579,6 +579,9 @@ AST_EXT_LIB_SETUP([OPUS], [Opus], [opus])
AST_EXT_LIB_SETUP([OPUSFILE], [Opusfile], [opusfile])
AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres])
AST_EXT_LIB_SETUP([BEANSTALK], [Beanstalk Job Queue], [beanstalk])
+#FIXME
+AST_EXT_LIB_SETUP([VEVS], [Vocal EVS Audio Decoder/Encoder], [bluetooth])
+#AST_EXT_LIB_SETUP([VEVS], [Vocal EVS Audio Decoder/Encoder], [vocal-evs])
if test "x${PBX_PJPROJECT}" != "x1" ; then
AST_EXT_LIB_SETUP([PJPROJECT], [PJPROJECT], [pjproject])
@@ -2409,6 +2412,10 @@ AST_EXT_LIB_CHECK([BLUETOOTH], [bluetooth], [ba2str], [bluetooth/bluetooth.h])
AST_EXT_LIB_CHECK([BEANSTALK], [beanstalk], [bs_version], [beanstalk.h])
+#FIXME
+AST_EXT_LIB_CHECK([VEVS], [bluetooth], [ba2str], [bluetooth/bluetooth.h])
+#AST_EXT_LIB_CHECK([VEVS], [vocal-evs], [vevs_enc], [vocal-evs/evs.h])
+
PG_CONFIG=":"
if test "${USE_PGSQL}" != "no"; then
if test "x${PGSQL_DIR}" != "x"; then
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index debf540c3b..023915f7d3 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -1226,6 +1226,9 @@
/* Define to 1 if you have the `vasprintf' function. */
#undef HAVE_VASPRINTF
+/* Define to 1 if you have the Vocal EVS Audio Decoder/Encoder library. */
+#undef HAVE_VEVS
+
/* Define to 1 if you have the `vfork' function. */
#undef HAVE_VFORK
diff --git a/include/asterisk/format_cache.h b/include/asterisk/format_cache.h
index 20b9d3afbb..c1c3689d15 100644
--- a/include/asterisk/format_cache.h
+++ b/include/asterisk/format_cache.h
@@ -245,6 +245,10 @@ extern struct ast_format *ast_format_silk8;
extern struct ast_format *ast_format_silk12;
extern struct ast_format *ast_format_silk16;
extern struct ast_format *ast_format_silk24;
+/*!
+ * \brief Built-in cached Vocal EVS format.
+ */
+extern struct ast_format *ast_format_vevs;
/*!
* \brief Initialize format cache support within the core.
diff --git a/main/codec_builtin.c b/main/codec_builtin.c
index 02c9f2e79a..25591d3578 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -925,6 +925,16 @@ static struct ast_codec silk24 = {
.samples_count = silk_samples
};
+static struct ast_codec vevs = {
+ .name = "vevs",
+ .description = "Vocal EVS",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 16000,
+ .minimum_ms = 20,
+ .maximum_ms = 300,
+ .default_ms = 20,
+};
+
#define CODEC_REGISTER_AND_CACHE(codec) \
({ \
int __res_ ## __LINE__ = 0; \
@@ -1003,6 +1013,7 @@ int ast_codec_builtin_init(void)
res |= CODEC_REGISTER_AND_CACHE_NAMED("silk12", silk12);
res |= CODEC_REGISTER_AND_CACHE_NAMED("silk16", silk16);
res |= CODEC_REGISTER_AND_CACHE_NAMED("silk24", silk24);
+ res |= CODEC_REGISTER_AND_CACHE(vevs);
return res;
}
diff --git a/main/format_cache.c b/main/format_cache.c
index 74aa0fe093..0ad2cdeaef 100644
--- a/main/format_cache.c
+++ b/main/format_cache.c
@@ -252,6 +252,10 @@ struct ast_format *ast_format_silk8;
struct ast_format *ast_format_silk12;
struct ast_format *ast_format_silk16;
struct ast_format *ast_format_silk24;
+/*!
+ * \brief Built-in cached Vocal EVS format.
+ */
+struct ast_format *ast_format_vevs;
/*! \brief Number of buckets to use for the media format cache (should be prime for performance reasons) */
#define CACHE_BUCKETS 53
@@ -359,6 +363,7 @@ static void format_cache_shutdown(void)
ao2_replace(ast_format_silk12, NULL);
ao2_replace(ast_format_silk16, NULL);
ao2_replace(ast_format_silk24, NULL);
+ ao2_replace(ast_format_vevs, NULL);
}
int ast_format_cache_init(void)
@@ -468,6 +473,8 @@ static void set_cached_format(const char *name, struct ast_format *format)
ao2_replace(ast_format_silk16, format);
} else if (!strcmp(name, "silk24")) {
ao2_replace(ast_format_silk24, format);
+ } else if (!strcmp(name, "vevs")) {
+ ao2_replace(ast_format_vevs, format);
}
}
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 7009ad73fa..7ce5c70f43 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -3724,6 +3724,7 @@ int ast_rtp_engine_init(void)
set_next_mime_type(ast_format_opus, 0, "audio", "opus", 48000);
set_next_mime_type(ast_format_vp8, 0, "video", "VP8", 90000);
set_next_mime_type(ast_format_vp9, 0, "video", "VP9", 90000);
+ set_next_mime_type(ast_format_vevs, 0, "audio", "EVS", 16000);
/* Define the static rtp payload mappings */
add_static_payload(0, ast_format_ulaw, 0);
diff --git a/makeopts.in b/makeopts.in
index f7824f3476..f3eef0494e 100644
--- a/makeopts.in
+++ b/makeopts.in
@@ -351,4 +351,7 @@ SNDFILE_LIB=@SNDFILE_LIB@
BEANSTALK_INCLUDE=@BEANSTALK_INCLUDE@
BEANSTALK_LIB=@BEANSTALK_LIB@
+VEVS_INCLUDE=@VEVS_INCLUDE@
+VEVS_LIB=@VEVS_LIB@
+
HAVE_SBIN_LAUNCHD=@PBX_LAUNCHD@
diff --git a/res/res_format_attr_vevs.c b/res/res_format_attr_vevs.c
new file mode 100644
index 0000000000..89b9d8fe28
--- /dev/null
+++ b/res/res_format_attr_vevs.c
@@ -0,0 +1,152 @@
+/*** MODULEINFO
+ core
+ ***/
+
+#include "asterisk.h"
+
+#include /* for tolower */
+
+#include "asterisk/module.h"
+#include "asterisk/format.h"
+#include "asterisk/logger.h" /* for ast_log, LOG_WARNING */
+#include "asterisk/strings.h" /* for ast_str_append */
+#include "asterisk/utils.h" /* for MAX, MIN */
+
+struct evs_attr {
+ int dummy;
+};
+
+static void evs_destroy(struct ast_format *format)
+{
+ struct evs_attr *attr = ast_format_get_attribute_data(format);
+
+ ast_free(attr);
+}
+
+static void attr_init(struct evs_attr *attr)
+{
+ memset(attr, 0, sizeof(*attr));
+}
+
+static int evs_clone(const struct ast_format *src, struct ast_format *dst)
+{
+ struct evs_attr *original = ast_format_get_attribute_data(src);
+ struct evs_attr *attr = ast_malloc(sizeof(*attr));
+
+#warning hacking
+abort();
+ if (!attr) {
+ return -1;
+ }
+
+ if (original) {
+ *attr = *original;
+ } else {
+ attr_init(attr);
+ }
+
+ ast_format_set_attribute_data(dst, attr);
+
+ return 0;
+}
+
+static struct ast_format *evs_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
+{
+ char *attribs = ast_strdupa(attributes), *attrib;
+ struct ast_format *cloned;
+ struct evs_attr *attr;
+// unsigned int val;
+
+ cloned = ast_format_clone(format);
+ if (!cloned) {
+ return NULL;
+ }
+ attr = ast_format_get_attribute_data(cloned);
+
+ /* lower-case everything, so we are case-insensitive */
+ for (attrib = attribs; *attrib; ++attrib) {
+ *attrib = tolower(*attrib);
+ } /* based on channels/chan_sip.c:process_a_sdp_image() */
+
+ ast_log(LOG_WARNING, "Unhandled received attribute '%s', please fix!\n", attrib);
+
+ return cloned;
+}
+
+static void evs_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
+{
+ struct evs_attr *attr = ast_format_get_attribute_data(format);
+
+#warning hacking
+// if (!attr) {
+// return;
+// }
+
+ ast_str_append(str, 0, "a=fmtp:%u br=5.9; bw=nb-wb\r\n", payload);
+}
+
+static enum ast_format_cmp_res evs_cmp(const struct ast_format *format1, const struct ast_format *format2)
+{
+ if (ast_format_get_sample_rate(format1) == ast_format_get_sample_rate(format2)) {
+ return AST_FORMAT_CMP_EQUAL;
+ }
+
+ return AST_FORMAT_CMP_NOT_EQUAL;
+}
+
+static struct ast_format *evs_getjoint(const struct ast_format *format1, const struct ast_format *format2)
+{
+ struct evs_attr *attr1 = ast_format_get_attribute_data(format1);
+ struct evs_attr *attr2 = ast_format_get_attribute_data(format2);
+ struct ast_format *jointformat;
+ struct evs_attr *attr_res;
+
+ if (ast_format_get_sample_rate(format1) != ast_format_get_sample_rate(format2)) {
+ return NULL;
+ }
+
+ jointformat = ast_format_clone(format1);
+ if (!jointformat) {
+ return NULL;
+ }
+ attr_res = ast_format_get_attribute_data(jointformat);
+
+ if (!attr1 || !attr2) {
+ attr_init(attr_res);
+ } else {
+ /* Take the lowest max bitrate */
+// attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
+ }
+
+ return jointformat;
+}
+
+static struct ast_format_interface evs_interface = {
+ .format_destroy = evs_destroy,
+ .format_clone = evs_clone,
+ .format_cmp = evs_cmp,
+ .format_get_joint = evs_getjoint,
+ .format_parse_sdp_fmtp = evs_parse_sdp_fmtp,
+ .format_generate_sdp_fmtp = evs_generate_sdp_fmtp,
+};
+
+static int load_module(void)
+{
+ if (ast_format_interface_register("vevs", &evs_interface)) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Vocal EVS Format Attribute Module",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,
+);