[Asterisk-code-review] codecs: Add Codec 2 mode 2400. (asterisk[master])

Joshua Colp asteriskteam at digium.com
Sun Sep 4 14:11:34 CDT 2016


Joshua Colp has submitted this change and it was merged.

Change subject: codecs: Add Codec 2 mode 2400.
......................................................................


codecs: Add Codec 2 mode 2400.

ASTERISK-26217 #close

Change-Id: I1e45d8084683fab5f2b272bf35f4a149cea8b8d6
---
M build_tools/menuselect-deps.in
A codecs/codec_codec2.c
A codecs/ex_codec2.h
M configure
M configure.ac
M include/asterisk/autoconfig.h.in
M include/asterisk/format_cache.h
M main/codec_builtin.c
M main/format_cache.c
M makeopts.in
10 files changed, 446 insertions(+), 1 deletion(-)

Approvals:
  Mark Michelson: Looks good to me, but someone else must approve
  Matt Jordan: Looks good to me, approved
  Joshua Colp: Looks good to me, but someone else must approve; Verified



diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in
index f194482..a044409 100644
--- a/build_tools/menuselect-deps.in
+++ b/build_tools/menuselect-deps.in
@@ -4,6 +4,7 @@
 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 0000000..e446854
--- /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 at 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 at 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 0000000..f2f4c97
--- /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 6d3faf2..64c7683 100755
--- a/configure
+++ b/configure
@@ -1152,6 +1152,10 @@
 COROSYNC_DIR
 COROSYNC_INCLUDE
 COROSYNC_LIB
+PBX_CODEC2
+CODEC2_DIR
+CODEC2_INCLUDE
+CODEC2_LIB
 PBX_CAP
 CAP_DIR
 CAP_INCLUDE
@@ -1326,6 +1330,7 @@
 with_execinfo
 with_bluetooth
 with_cap
+with_codec2
 with_cpg
 with_curses
 with_crypt
@@ -2065,6 +2070,7 @@
   --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
@@ -8976,6 +8982,38 @@
 	*)
 	CAP_DIR="${withval}"
 	ac_mandatory_list="${ac_mandatory_list} CAP"
+	;;
+	esac
+
+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
 
@@ -30372,6 +30410,111 @@
 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 dedfd8a..96a20d0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -407,6 +407,7 @@
 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])
@@ -2336,6 +2337,8 @@
 	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/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index 51f0f14..7d2a08c 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -142,6 +142,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 3894ad2..6099c59 100644
--- a/include/asterisk/format_cache.h
+++ b/include/asterisk/format_cache.h
@@ -209,6 +209,11 @@
 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/codec_builtin.c b/main/codec_builtin.c
index 50fbf55..6fc0fd8 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -107,6 +107,30 @@
 	.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;
@@ -863,6 +887,7 @@
 {
 	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 b4d4260..c704f1c 100644
--- a/main/format_cache.c
+++ b/main/format_cache.c
@@ -218,6 +218,11 @@
 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 @@
 	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 @@
 
 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 d4347da..f0b0d0e 100644
--- a/makeopts.in
+++ b/makeopts.in
@@ -126,6 +126,9 @@
 BLUETOOTH_INCLUDE=@BLUETOOTH_INCLUDE@
 BLUETOOTH_LIB=@BLUETOOTH_LIB@
 
+CODEC2_INCLUDE=@CODEC2_INCLUDE@
+CODEC2_LIB=@CODEC2_LIB@
+
 CURL_INCLUDE=@CURL_INCLUDE@
 CURL_LIB=@CURL_LIB@
 

-- 
To view, visit https://gerrit.asterisk.org/3244
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I1e45d8084683fab5f2b272bf35f4a149cea8b8d6
Gerrit-PatchSet: 6
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Alexander Traud <pabstraud at compuserve.com>
Gerrit-Reviewer: Alexander Traud <pabstraud at compuserve.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Mark Michelson <mmichelson at digium.com>
Gerrit-Reviewer: Matt Jordan <mjordan at digium.com>
Gerrit-Reviewer: Matthew Fredrickson <creslin at digium.com>
Gerrit-Reviewer: Richard Mudgett <rmudgett at digium.com>



More information about the asterisk-code-review mailing list