summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/app_mp3.c52
-rw-r--r--build_tools/menuselect-deps.in1
-rw-r--r--codecs/codec_codec2.c222
-rw-r--r--codecs/ex_codec2.h32
-rwxr-xr-xconfigure143
-rw-r--r--configure.ac3
-rw-r--r--include/asterisk/astobj2.h27
-rw-r--r--include/asterisk/autoconfig.h.in3
-rw-r--r--include/asterisk/format_cache.h5
-rw-r--r--main/astobj2.c75
-rw-r--r--main/codec_builtin.c25
-rw-r--r--main/format_cache.c10
-rw-r--r--makeopts.in3
13 files changed, 583 insertions, 18 deletions
diff --git a/apps/app_mp3.c b/apps/app_mp3.c
index 5712cfe33..05afe54b1 100644
--- a/apps/app_mp3.c
+++ b/apps/app_mp3.c
@@ -77,9 +77,10 @@ ASTERISK_REGISTER_FILE()
***/
static char *app = "MP3Player";
-static int mp3play(const char *filename, int fd)
+static int mp3play(const char *filename, unsigned int sampling_rate, int fd)
{
int res;
+ char sampling_rate_str[8];
res = ast_safe_fork(0);
if (res < 0)
@@ -93,30 +94,44 @@ static int mp3play(const char *filename, int fd)
dup2(fd, STDOUT_FILENO);
ast_close_fds_above_n(STDERR_FILENO);
+ snprintf(sampling_rate_str, 8, "%u", sampling_rate);
+
/* Execute mpg123, but buffer if it's a net connection */
- if (!strncasecmp(filename, "http://", 7)) {
+ if (!strncasecmp(filename, "http://", 7) && strstr(filename, ".m3u")) {
+ char buffer_size_str[8];
+ snprintf(buffer_size_str, 8, "%u", (int) 0.5*2*sampling_rate/1000); // 0.5 seconds for a live stream
+ /* Most commonly installed in /usr/local/bin */
+ execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
+ /* But many places has it in /usr/bin */
+ execl(MPG_123, "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
+ /* As a last-ditch effort, try to use PATH */
+ execlp("mpg123", "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
+ }
+ else if (!strncasecmp(filename, "http://", 7)) {
+ char buffer_size_str[8];
+ snprintf(buffer_size_str, 8, "%u", 6*2*sampling_rate/1000); // 6 seconds for a remote MP3 file
/* Most commonly installed in /usr/local/bin */
- execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
/* But many places has it in /usr/bin */
- execl(MPG_123, "mpg123", "-q", "-s", "-b", "1024","-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ execl(MPG_123, "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
/* As a last-ditch effort, try to use PATH */
- execlp("mpg123", "mpg123", "-q", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ execlp("mpg123", "mpg123", "-q", "-s", "-b", buffer_size_str, "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
}
else if (strstr(filename, ".m3u")) {
/* Most commonly installed in /usr/local/bin */
- execl(LOCAL_MPG_123, "mpg123", "-q", "-z", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", "-@", filename, (char *)NULL);
+ execl(LOCAL_MPG_123, "mpg123", "-q", "-z", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
/* But many places has it in /usr/bin */
- execl(MPG_123, "mpg123", "-q", "-z", "-s", "-b", "1024","-f", "8192", "--mono", "-r", "8000", "-@", filename, (char *)NULL);
+ execl(MPG_123, "mpg123", "-q", "-z", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
/* As a last-ditch effort, try to use PATH */
- execlp("mpg123", "mpg123", "-q", "-z", "-s", "-b", "1024", "-f", "8192", "--mono", "-r", "8000", "-@", filename, (char *)NULL);
+ execlp("mpg123", "mpg123", "-q", "-z", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, "-@", filename, (char *)NULL);
}
else {
/* Most commonly installed in /usr/local/bin */
- execl(MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ execl(MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
/* But many places has it in /usr/bin */
- execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ execl(LOCAL_MPG_123, "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
/* As a last-ditch effort, try to use PATH */
- execlp("mpg123", "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", "8000", filename, (char *)NULL);
+ execlp("mpg123", "mpg123", "-q", "-s", "-f", "8192", "--mono", "-r", sampling_rate_str, filename, (char *)NULL);
}
/* Can't use ast_log since FD's are closed */
fprintf(stderr, "Execute of mpg123 failed\n");
@@ -155,6 +170,9 @@ static int mp3_exec(struct ast_channel *chan, const char *data)
} myf = {
.f = { 0, },
};
+ struct ast_format * native_format;
+ unsigned int sampling_rate;
+ struct ast_format * write_format;
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "MP3 Playback requires an argument (filename)\n");
@@ -168,15 +186,19 @@ static int mp3_exec(struct ast_channel *chan, const char *data)
ast_stopstream(chan);
+ native_format = ast_format_cap_get_format(ast_channel_nativeformats(chan), 0);
+ sampling_rate = ast_format_get_sample_rate(native_format);
+ write_format = ast_format_cache_get_slin_by_rate(sampling_rate);
+
owriteformat = ao2_bump(ast_channel_writeformat(chan));
- res = ast_set_write_format(chan, ast_format_slin);
+ res = ast_set_write_format(chan, write_format);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to set write format to signed linear\n");
return -1;
}
myf.f.frametype = AST_FRAME_VOICE;
- myf.f.subclass.format = ast_format_slin;
+ myf.f.subclass.format = write_format;
myf.f.mallocd = 0;
myf.f.offset = AST_FRIENDLY_OFFSET;
myf.f.src = __PRETTY_FUNCTION__;
@@ -184,7 +206,7 @@ static int mp3_exec(struct ast_channel *chan, const char *data)
myf.f.delivery.tv_usec = 0;
myf.f.data.ptr = myf.frdata;
- res = mp3play(data, fds[1]);
+ res = mp3play(data, sampling_rate, fds[1]);
if (!strncasecmp(data, "http://", 7)) {
timeout = 10000;
}
@@ -211,7 +233,7 @@ static int mp3_exec(struct ast_channel *chan, const char *data)
res = 0;
break;
}
- next = ast_tvadd(next, ast_samp2tv(myf.f.samples, 8000));
+ next = ast_tvadd(next, ast_samp2tv(myf.f.samples, sampling_rate));
} else {
ms = ast_waitfor(chan, ms);
if (ms < 0) {
diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in
index f194482e9..a04440955 100644
--- a/build_tools/menuselect-deps.in
+++ b/build_tools/menuselect-deps.in
@@ -4,6 +4,7 @@ COROSYNC=@PBX_COROSYNC@
CRYPTO=@PBX_CRYPTO@
BFD=@PBX_BFD@
BISON=@PBX_BISON@
+CODEC2=@PBX_CODEC2@
CURL=@PBX_CURL@
DAHDI=@PBX_DAHDI@
DLADDR=@PBX_DLADDR@
diff --git a/codecs/codec_codec2.c b/codecs/codec_codec2.c
new file mode 100644
index 000000000..e446854c3
--- /dev/null
+++ b/codecs/codec_codec2.c
@@ -0,0 +1,222 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2016, Alexander Traud
+ *
+ * Alexander Traud <pabstraud@compuserve.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Translate between signed linear and Codec 2
+ *
+ * \author Alexander Traud <pabstraud@compuserve.com>
+ *
+ * \note http://www.rowetel.com/codec2.html
+ *
+ * \ingroup codecs
+ */
+
+/*** MODULEINFO
+ <depend>codec2</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include "asterisk/codec.h" /* for AST_MEDIA_TYPE_AUDIO */
+#include "asterisk/frame.h" /* for ast_frame */
+#include "asterisk/linkedlists.h" /* for AST_LIST_NEXT, etc */
+#include "asterisk/logger.h" /* for ast_log, etc */
+#include "asterisk/module.h"
+#include "asterisk/rtp_engine.h" /* ast_rtp_engine_(un)load_format */
+#include "asterisk/translate.h" /* for ast_trans_pvt, etc */
+
+#include <codec2/codec2.h>
+
+#define BUFFER_SAMPLES 8000
+#define CODEC2_SAMPLES 160 /* consider codec2_samples_per_frame(.) */
+#define CODEC2_FRAME_LEN 6 /* consider codec2_bits_per_frame(.) */
+
+/* Sample frame data */
+#include "asterisk/slin.h"
+#include "ex_codec2.h"
+
+struct codec2_translator_pvt {
+ struct CODEC2 *state; /* May be encoder or decoder */
+ int16_t buf[BUFFER_SAMPLES];
+};
+
+static int codec2_new(struct ast_trans_pvt *pvt)
+{
+ struct codec2_translator_pvt *tmp = pvt->pvt;
+
+ tmp->state = codec2_create(CODEC2_MODE_2400);
+
+ if (!tmp->state) {
+ ast_log(LOG_ERROR, "Error creating Codec 2 conversion\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*! \brief decode and store in outbuf. */
+static int codec2tolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct codec2_translator_pvt *tmp = pvt->pvt;
+ int x;
+
+ for (x = 0; x < f->datalen; x += CODEC2_FRAME_LEN) {
+ unsigned char *src = f->data.ptr + x;
+ int16_t *dst = pvt->outbuf.i16 + pvt->samples;
+
+ codec2_decode(tmp->state, dst, src);
+
+ pvt->samples += CODEC2_SAMPLES;
+ pvt->datalen += CODEC2_SAMPLES * 2;
+ }
+
+ return 0;
+}
+
+/*! \brief store samples into working buffer for later decode */
+static int lintocodec2_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct codec2_translator_pvt *tmp = pvt->pvt;
+
+ memcpy(tmp->buf + pvt->samples, f->data.ptr, f->datalen);
+ pvt->samples += f->samples;
+
+ return 0;
+}
+
+/*! \brief encode and produce a frame */
+static struct ast_frame *lintocodec2_frameout(struct ast_trans_pvt *pvt)
+{
+ struct codec2_translator_pvt *tmp = pvt->pvt;
+ struct ast_frame *result = NULL;
+ struct ast_frame *last = NULL;
+ int samples = 0; /* output samples */
+
+ while (pvt->samples >= CODEC2_SAMPLES) {
+ struct ast_frame *current;
+
+ /* Encode a frame of data */
+ codec2_encode(tmp->state, pvt->outbuf.uc, tmp->buf + samples);
+
+ samples += CODEC2_SAMPLES;
+ pvt->samples -= CODEC2_SAMPLES;
+
+ current = ast_trans_frameout(pvt, CODEC2_FRAME_LEN, CODEC2_SAMPLES);
+
+ if (!current) {
+ continue;
+ } else if (last) {
+ AST_LIST_NEXT(last, frame_list) = current;
+ } else {
+ result = current;
+ }
+ last = current;
+ }
+
+ /* Move the data at the end of the buffer to the front */
+ if (samples) {
+ memmove(tmp->buf, tmp->buf + samples, pvt->samples * 2);
+ }
+
+ return result;
+}
+
+static void codec2_destroy_stuff(struct ast_trans_pvt *pvt)
+{
+ struct codec2_translator_pvt *tmp = pvt->pvt;
+
+ if (tmp->state) {
+ codec2_destroy(tmp->state);
+ }
+}
+
+static struct ast_translator codec2tolin = {
+ .name = "codec2tolin",
+ .src_codec = {
+ .name = "codec2",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 8000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 8000,
+ },
+ .format = "slin",
+ .newpvt = codec2_new,
+ .framein = codec2tolin_framein,
+ .destroy = codec2_destroy_stuff,
+ .sample = codec2_sample,
+ .desc_size = sizeof(struct codec2_translator_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator lintocodec2 = {
+ .name = "lintocodec2",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 8000,
+ },
+ .dst_codec = {
+ .name = "codec2",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 8000,
+ },
+ .format = "codec2",
+ .newpvt = codec2_new,
+ .framein = lintocodec2_framein,
+ .frameout = lintocodec2_frameout,
+ .destroy = codec2_destroy_stuff,
+ .sample = slin8_sample,
+ .desc_size = sizeof(struct codec2_translator_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = (BUFFER_SAMPLES * CODEC2_FRAME_LEN + CODEC2_SAMPLES - 1) / CODEC2_SAMPLES,
+};
+
+static int unload_module(void)
+{
+ int res = 0;
+
+ res |= ast_rtp_engine_unload_format(ast_format_codec2);
+ res |= ast_unregister_translator(&lintocodec2);
+ res |= ast_unregister_translator(&codec2tolin);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res = 0;
+
+ res |= ast_register_translator(&codec2tolin);
+ res |= ast_register_translator(&lintocodec2);
+ res |= ast_rtp_engine_load_format(ast_format_codec2);
+
+ if (res) {
+ unload_module();
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Codec 2 Coder/Decoder");
diff --git a/codecs/ex_codec2.h b/codecs/ex_codec2.h
new file mode 100644
index 000000000..f2f4c9723
--- /dev/null
+++ b/codecs/ex_codec2.h
@@ -0,0 +1,32 @@
+/*! \file
+ * \brief 8-bit raw data
+ *
+ * Copyright (C) 2016, Alexander Traud
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+#include "asterisk/format_cache.h" /* for ast_format_codec2 */
+#include "asterisk/frame.h" /* for ast_frame, etc */
+
+static uint8_t ex_codec2[] = {
+ 0xea, 0xca, 0x14, 0x85, 0x91, 0x78,
+};
+
+static struct ast_frame *codec2_sample(void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .datalen = sizeof(ex_codec2),
+ .samples = CODEC2_SAMPLES,
+ .mallocd = 0,
+ .offset = 0,
+ .src = __PRETTY_FUNCTION__,
+ .data.ptr = ex_codec2,
+ };
+
+ f.subclass.format = ast_format_codec2;
+
+ return &f;
+}
diff --git a/configure b/configure
index 43114a359..3e5140ddb 100755
--- a/configure
+++ b/configure
@@ -1164,6 +1164,10 @@ PBX_COROSYNC
COROSYNC_DIR
COROSYNC_INCLUDE
COROSYNC_LIB
+PBX_CODEC2
+CODEC2_DIR
+CODEC2_INCLUDE
+CODEC2_LIB
PBX_CAP
CAP_DIR
CAP_INCLUDE
@@ -1338,6 +1342,7 @@ with_bfd
with_execinfo
with_bluetooth
with_cap
+with_codec2
with_cpg
with_curses
with_crypt
@@ -2077,6 +2082,7 @@ Optional Packages:
--with-execinfo=PATH use Stack Backtrace files in PATH
--with-bluetooth=PATH use Bluetooth files in PATH
--with-cap=PATH use POSIX 1.e capabilities files in PATH
+ --with-codec2=PATH use Codec 2 Audio Decoder/Encoder files in PATH
--with-cpg=PATH use Corosync files in PATH
--with-curses=PATH use curses files in PATH
--with-crypt=PATH use password and data encryption files in PATH
@@ -9000,6 +9006,38 @@ fi
+ CODEC2_DESCRIP="Codec 2 Audio Decoder/Encoder"
+ CODEC2_OPTION="codec2"
+ PBX_CODEC2=0
+
+# Check whether --with-codec2 was given.
+if test "${with_codec2+set}" = set; then :
+ withval=$with_codec2;
+ case ${withval} in
+ n|no)
+ USE_CODEC2=no
+ # -1 is a magic value used by menuselect to know that the package
+ # was disabled, other than 'not found'
+ PBX_CODEC2=-1
+ ;;
+ y|ye|yes)
+ ac_mandatory_list="${ac_mandatory_list} CODEC2"
+ ;;
+ *)
+ CODEC2_DIR="${withval}"
+ ac_mandatory_list="${ac_mandatory_list} CODEC2"
+ ;;
+ esac
+
+fi
+
+
+
+
+
+
+
+
COROSYNC_DESCRIP="Corosync"
COROSYNC_OPTION="cpg"
PBX_COROSYNC=0
@@ -30531,6 +30569,111 @@ fi
fi
+if test "x${PBX_CODEC2}" != "x1" -a "${USE_CODEC2}" != "no"; then
+ pbxlibdir=""
+ # if --with-CODEC2=DIR has been specified, use it.
+ if test "x${CODEC2_DIR}" != "x"; then
+ if test -d ${CODEC2_DIR}/lib; then
+ pbxlibdir="-L${CODEC2_DIR}/lib"
+ else
+ pbxlibdir="-L${CODEC2_DIR}"
+ fi
+ fi
+ pbxfuncname="codec2_create"
+ if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
+ AST_CODEC2_FOUND=yes
+ else
+ ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
+ CFLAGS="${CFLAGS} "
+ as_ac_Lib=`$as_echo "ac_cv_lib_codec2_${pbxfuncname}" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lcodec2" >&5
+$as_echo_n "checking for ${pbxfuncname} in -lcodec2... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcodec2 ${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. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ${pbxfuncname} ();
+int
+main ()
+{
+return ${pbxfuncname} ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$as_ac_Lib=yes"
+else
+ eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+ AST_CODEC2_FOUND=yes
+else
+ AST_CODEC2_FOUND=no
+fi
+
+ CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
+ fi
+
+ # now check for the header.
+ if test "${AST_CODEC2_FOUND}" = "yes"; then
+ CODEC2_LIB="${pbxlibdir} -lcodec2 "
+ # if --with-CODEC2=DIR has been specified, use it.
+ if test "x${CODEC2_DIR}" != "x"; then
+ CODEC2_INCLUDE="-I${CODEC2_DIR}/include"
+ fi
+ CODEC2_INCLUDE="${CODEC2_INCLUDE} "
+ if test "xcodec2/codec2.h" = "x" ; then # no header, assume found
+ CODEC2_HEADER_FOUND="1"
+ else # check for the header
+ ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
+ CPPFLAGS="${CPPFLAGS} ${CODEC2_INCLUDE}"
+ ac_fn_c_check_header_mongrel "$LINENO" "codec2/codec2.h" "ac_cv_header_codec2_codec2_h" "$ac_includes_default"
+if test "x$ac_cv_header_codec2_codec2_h" = xyes; then :
+ CODEC2_HEADER_FOUND=1
+else
+ CODEC2_HEADER_FOUND=0
+fi
+
+
+ CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
+ fi
+ if test "x${CODEC2_HEADER_FOUND}" = "x0" ; then
+ CODEC2_LIB=""
+ CODEC2_INCLUDE=""
+ else
+ if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
+ CODEC2_LIB=""
+ fi
+ PBX_CODEC2=1
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_CODEC2 1
+_ACEOF
+
+ fi
+ fi
+fi
+
+
+
+
if test "x${PBX_COROSYNC}" != "x1" -a "${USE_COROSYNC}" != "no"; then
pbxlibdir=""
# if --with-COROSYNC=DIR has been specified, use it.
diff --git a/configure.ac b/configure.ac
index ba13bf46d..950cfc5a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -407,6 +407,7 @@ AST_EXT_LIB_SETUP([BFD], [Debug symbol decoding], [bfd])
AST_EXT_LIB_SETUP([BKTR], [Stack Backtrace], [execinfo])
AST_EXT_LIB_SETUP([BLUETOOTH], [Bluetooth], [bluetooth])
AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap])
+AST_EXT_LIB_SETUP([CODEC2], [Codec 2 Audio Decoder/Encoder], [codec2])
AST_EXT_LIB_SETUP([COROSYNC], [Corosync], [cpg])
AST_EXT_LIB_SETUP_OPTIONAL([COROSYNC_CFG_STATE_TRACK], [A callback only in corosync 1.x], [COROSYNC], [cfg])
AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
@@ -2341,6 +2342,8 @@ else
AST_EXT_LIB_CHECK([RADIUS], [radiusclient-ng], [rc_read_config], [radiusclient-ng.h])
fi
+AST_EXT_LIB_CHECK([CODEC2], [codec2], [codec2_create], [codec2/codec2.h])
+
AST_EXT_LIB_CHECK([COROSYNC], [cpg], [cpg_join], [corosync/cpg.h], [-lcfg])
AST_EXT_LIB_CHECK([COROSYNC_CFG_STATE_TRACK], [cfg], [corosync_cfg_state_track], [corosync/cfg.h], [-lcfg])
diff --git a/include/asterisk/astobj2.h b/include/asterisk/astobj2.h
index 0472c1b37..28ae73e87 100644
--- a/include/asterisk/astobj2.h
+++ b/include/asterisk/astobj2.h
@@ -368,6 +368,13 @@ enum ao2_alloc_opts {
AO2_ALLOC_OPT_LOCK_NOLOCK = (2 << 0),
/*! The ao2 object locking option field mask. */
AO2_ALLOC_OPT_LOCK_MASK = (3 << 0),
+ /*!
+ * \internal The ao2 object uses a separate object for locking.
+ *
+ * \note This option is used internally by ao2_alloc_with_lockobj and
+ * should never be passed directly to ao2_alloc.
+ */
+ AO2_ALLOC_OPT_LOCK_OBJ = AO2_ALLOC_OPT_LOCK_MASK,
};
/*!
@@ -408,6 +415,26 @@ void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned in
/*! @} */
+/*!
+ * \since 14.1.0
+ * \brief Allocate and initialize an object with separate locking.
+ *
+ * \param data_size The sizeof() of the user-defined structure.
+ * \param destructor_fn The destructor function (can be NULL)
+ * \param lockobj A separate ao2 object that will provide locking.
+ * \param debug_msg An ao2 object debug tracing message.
+ * \return A pointer to user-data.
+ *
+ * \see \ref ao2_alloc for additional details.
+ *
+ * \note lockobj must be a valid AO2 object.
+ */
+#define ao2_alloc_with_lockobj(data_size, destructor_fn, lockobj, tag) \
+ __ao2_alloc_with_lockobj((data_size), (destructor_fn), (lockobj), (tag), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+
+void *__ao2_alloc_with_lockobj(size_t data_size, ao2_destructor_fn destructor_fn, void *lockobj,
+ const char *tag, const char *file, int line, const char *func) attribute_warn_unused_result;
+
/*! \brief
* Reference/unreference an object and return the old refcount.
*
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index bdd93bb64..f9a6b46e4 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -145,6 +145,9 @@
/* Define to 1 if you have the `closefrom' function. */
#undef HAVE_CLOSEFROM
+/* Define to 1 if you have the Codec 2 Audio Decoder/Encoder library. */
+#undef HAVE_CODEC2
+
/* Define to 1 if you have the Corosync library. */
#undef HAVE_COROSYNC
diff --git a/include/asterisk/format_cache.h b/include/asterisk/format_cache.h
index 3894ad21b..6099c59ea 100644
--- a/include/asterisk/format_cache.h
+++ b/include/asterisk/format_cache.h
@@ -209,6 +209,11 @@ extern struct ast_format *ast_format_siren7;
extern struct ast_format *ast_format_opus;
/*!
+ * \brief Built-in cached Codec 2 format.
+ */
+extern struct ast_format *ast_format_codec2;
+
+/*!
* \brief Built-in cached t140 format.
*/
extern struct ast_format *ast_format_t140;
diff --git a/main/astobj2.c b/main/astobj2.c
index 5c92f263f..604e6a1ae 100644
--- a/main/astobj2.c
+++ b/main/astobj2.c
@@ -108,6 +108,17 @@ struct astobj2_rwlock {
void *user_data[0];
};
+struct ao2_lockobj_priv {
+ void *lock;
+};
+
+/* AstObj2 with locking provided by a separate object. */
+struct astobj2_lockobj {
+ struct ao2_lockobj_priv lockobj;
+ struct __priv_data priv_data;
+ void *user_data[0];
+};
+
#ifdef AO2_DEBUG
struct ao2_stats ao2;
#endif
@@ -118,6 +129,9 @@ struct ao2_stats ao2;
#define INTERNAL_OBJ_RWLOCK(user_data) \
((struct astobj2_rwlock *) (((char *) (user_data)) - sizeof(struct astobj2_rwlock)))
+#define INTERNAL_OBJ_LOCKOBJ(user_data) \
+ ((struct astobj2_lockobj *) (((char *) (user_data)) - sizeof(struct astobj2_lockobj)))
+
#define INTERNAL_OBJ(user_data) \
(struct astobj2 *) ((char *) user_data - sizeof(struct astobj2))
@@ -187,6 +201,7 @@ int __ao2_lock(void *user_data, enum ao2_lock_req lock_how, const char *file, co
struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
struct astobj2_lock *obj_mutex;
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
int res = 0;
if (obj == NULL) {
@@ -231,6 +246,10 @@ int __ao2_lock(void *user_data, enum ao2_lock_req lock_how, const char *file, co
case AO2_ALLOC_OPT_LOCK_NOLOCK:
/* The ao2 object has no lock. */
break;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
+ res = __ao2_lock(obj_lockobj->lockobj.lock, lock_how, file, func, line, var);
+ break;
default:
ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
user_data);
@@ -245,6 +264,7 @@ int __ao2_unlock(void *user_data, const char *file, const char *func, int line,
struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
struct astobj2_lock *obj_mutex;
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
int res = 0;
int current_value;
@@ -281,6 +301,10 @@ int __ao2_unlock(void *user_data, const char *file, const char *func, int line,
case AO2_ALLOC_OPT_LOCK_NOLOCK:
/* The ao2 object has no lock. */
break;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
+ res = __ao2_unlock(obj_lockobj->lockobj.lock, file, func, line, var);
+ break;
default:
ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
user_data);
@@ -295,6 +319,7 @@ int __ao2_trylock(void *user_data, enum ao2_lock_req lock_how, const char *file,
struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
struct astobj2_lock *obj_mutex;
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
int res = 0;
if (obj == NULL) {
@@ -339,6 +364,10 @@ int __ao2_trylock(void *user_data, enum ao2_lock_req lock_how, const char *file,
case AO2_ALLOC_OPT_LOCK_NOLOCK:
/* The ao2 object has no lock. */
return 0;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
+ res = __ao2_trylock(obj_lockobj->lockobj.lock, lock_how, file, func, line, var);
+ break;
default:
ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n",
user_data);
@@ -370,6 +399,7 @@ enum ao2_lock_req __adjust_lock(void *user_data, enum ao2_lock_req lock_how, int
{
struct astobj2 *obj = INTERNAL_OBJ(user_data);
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
enum ao2_lock_req orig_lock;
switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) {
@@ -400,6 +430,10 @@ enum ao2_lock_req __adjust_lock(void *user_data, enum ao2_lock_req lock_how, int
break;
}
break;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
+ orig_lock = __adjust_lock(obj_lockobj->lockobj.lock, lock_how, keep_stronger);
+ break;
default:
ast_log(LOG_ERROR, "Invalid lock option on ao2 object %p\n", user_data);
/* Fall through */
@@ -441,6 +475,7 @@ int __ao2_ref(void *user_data, int delta,
struct astobj2 *obj = __INTERNAL_OBJ_CHECK(user_data, file, line, func);
struct astobj2_lock *obj_mutex;
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
int current_value;
int ret;
void *weakproxy = NULL;
@@ -552,6 +587,12 @@ int __ao2_ref(void *user_data, int delta,
case AO2_ALLOC_OPT_LOCK_NOLOCK:
ast_free(obj);
break;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ obj_lockobj = INTERNAL_OBJ_LOCKOBJ(user_data);
+ ao2_t_ref(obj_lockobj->lockobj.lock, -1, "release lockobj");
+
+ ast_free(obj_lockobj);
+ break;
default:
ast_log(__LOG_ERROR, file, line, func,
"Invalid lock option on ao2 object %p\n", user_data);
@@ -581,13 +622,14 @@ void __ao2_cleanup(void *obj)
}
}
-void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options,
- const char *tag, const char *file, int line, const char *func)
+static void *internal_ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options,
+ void *lockobj, const char *tag, const char *file, int line, const char *func)
{
/* allocation */
struct astobj2 *obj;
struct astobj2_lock *obj_mutex;
struct astobj2_rwlock *obj_rwlock;
+ struct astobj2_lockobj *obj_lockobj;
switch (options & AO2_ALLOC_OPT_LOCK_MASK) {
case AO2_ALLOC_OPT_LOCK_MUTEX:
@@ -614,6 +656,22 @@ void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned in
return NULL;
}
break;
+ case AO2_ALLOC_OPT_LOCK_OBJ:
+ lockobj = ao2_t_bump(lockobj, "set lockobj");
+ if (!lockobj) {
+ ast_log(__LOG_ERROR, file, line, func, "AO2_ALLOC_OPT_LOCK_OBJ requires a non-NULL lockobj.\n");
+ return NULL;
+ }
+
+ obj_lockobj = __ast_calloc(1, sizeof(*obj_lockobj) + data_size, file, line, func);
+ if (obj_lockobj == NULL) {
+ ao2_t_ref(lockobj, -1, "release lockobj for failed alloc");
+ return NULL;
+ }
+
+ obj_lockobj->lockobj.lock = lockobj;
+ obj = (struct astobj2 *) &obj_lockobj->priv_data;
+ break;
default:
/* Invalid option value. */
ast_log(__LOG_DEBUG, file, line, func, "Invalid lock option requested\n");
@@ -643,6 +701,19 @@ void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned in
return EXTERNAL_OBJ(obj);
}
+void *__ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn, unsigned int options,
+ const char *tag, const char *file, int line, const char *func)
+{
+ return internal_ao2_alloc(data_size, destructor_fn, options, NULL, tag, file, line, func);
+}
+
+void *__ao2_alloc_with_lockobj(size_t data_size, ao2_destructor_fn destructor_fn, void *lockobj,
+ const char *tag, const char *file, int line, const char *func)
+{
+ return internal_ao2_alloc(data_size, destructor_fn, AO2_ALLOC_OPT_LOCK_OBJ, lockobj,
+ tag, file, line, func);
+}
+
unsigned int ao2_options_get(void *obj)
{
struct astobj2 *orig_obj;
diff --git a/main/codec_builtin.c b/main/codec_builtin.c
index 15147980d..facb5a84c 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -108,6 +108,30 @@ static struct ast_codec g723 = {
.get_length = g723_length,
};
+static int codec2_samples(struct ast_frame *frame)
+{
+ return 160 * (frame->datalen / 6);
+}
+
+static int codec2_length(unsigned int samples)
+{
+ return (samples / 160) * 6;
+}
+
+static struct ast_codec codec2 = {
+ .name = "codec2",
+ .description = "Codec 2",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 8000,
+ .minimum_ms = 20,
+ .maximum_ms = 300,
+ .default_ms = 20,
+ .minimum_bytes = 6,
+ .samples_count = codec2_samples,
+ .get_length = codec2_length,
+ .smooth = 1,
+};
+
static int none_samples(struct ast_frame *frame)
{
return frame->datalen;
@@ -869,6 +893,7 @@ int ast_codec_builtin_init(void)
{
int res = 0;
+ res |= CODEC_REGISTER_AND_CACHE(codec2);
res |= CODEC_REGISTER_AND_CACHE(g723);
res |= CODEC_REGISTER_AND_CACHE(ulaw);
res |= CODEC_REGISTER_AND_CACHE(alaw);
diff --git a/main/format_cache.c b/main/format_cache.c
index b4d426092..c704f1c37 100644
--- a/main/format_cache.c
+++ b/main/format_cache.c
@@ -218,6 +218,11 @@ struct ast_format *ast_format_siren7;
struct ast_format *ast_format_opus;
/*!
+ * \brief Built-in cached codec2 format.
+ */
+struct ast_format *ast_format_codec2;
+
+/*!
* \brief Built-in cached t140 format.
*/
struct ast_format *ast_format_t140;
@@ -328,6 +333,7 @@ static void format_cache_shutdown(void)
ao2_replace(ast_format_testlaw, NULL);
ao2_replace(ast_format_g719, NULL);
ao2_replace(ast_format_opus, NULL);
+ ao2_replace(ast_format_codec2, NULL);
ao2_replace(ast_format_jpeg, NULL);
ao2_replace(ast_format_png, NULL);
ao2_replace(ast_format_h261, NULL);
@@ -360,7 +366,9 @@ int ast_format_cache_init(void)
static void set_cached_format(const char *name, struct ast_format *format)
{
- if (!strcmp(name, "g723")) {
+ if (!strcmp(name, "codec2")) {
+ ao2_replace(ast_format_codec2, format);
+ } else if (!strcmp(name, "g723")) {
ao2_replace(ast_format_g723, format);
} else if (!strcmp(name, "ulaw")) {
ao2_replace(ast_format_ulaw, format);
diff --git a/makeopts.in b/makeopts.in
index d4347daf1..f0b0d0ef5 100644
--- a/makeopts.in
+++ b/makeopts.in
@@ -126,6 +126,9 @@ BFD_LIB=@BFD_LIB@
BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@
BLUETOOTH_LIB=@BLUETOOTH_LIB@
+CODEC2_INCLUDE=@CODEC2_INCLUDE@
+CODEC2_LIB=@CODEC2_LIB@
+
CURL_INCLUDE=@CURL_INCLUDE@
CURL_LIB=@CURL_LIB@