parent
555a541680
commit
1245ccfefb
|
@ -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@
|
||||
|
|
|
@ -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,
|
||||
);
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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@
|
||||
|
|
|
@ -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,
|
||||
);
|
Loading…
Reference in New Issue