WIP on codec integration

Related: SY#6825
This commit is contained in:
Andreas Eversberg 2024-04-02 13:17:50 +02:00
parent 555a541680
commit 1245ccfefb
12 changed files with 641 additions and 0 deletions

View File

@ -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@

285
codecs/codec_vevs.c Normal file
View File

@ -0,0 +1,285 @@
/*
* Asterisk -- An open source telephony toolkit.
*/
/*! \file
*
* \brief Translate between signed linear and Vocal EVS codec
*
* \ingroup codecs
*/
/*** MODULEINFO
<depend>vevs</depend>
***/
#include "asterisk.h"
#include "asterisk/translate.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/utils.h"
#include "asterisk/linkedlists.h"
#include <stdbool.h>
#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,
);

28
codecs/ex_vevs.h Normal file
View File

@ -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;
}

139
configure vendored
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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@

152
res/res_format_attr_vevs.c Normal file
View File

@ -0,0 +1,152 @@
/*** MODULEINFO
<support_level>core</support_level>
***/
#include "asterisk.h"
#include <ctype.h> /* 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,
);