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, +);