diff --git a/configure.ac b/configure.ac index 705043ef5f..47afda4666 100644 --- a/configure.ac +++ b/configure.ac @@ -831,6 +831,10 @@ PKG_CHECK_MODULES([AMR], [opencore-amrnb >= 0.1.0],[ AM_CONDITIONAL([HAVE_AMR],[true])],[ AC_MSG_RESULT([no]); AM_CONDITIONAL([HAVE_AMR],[false])]) +PKG_CHECK_MODULES([AMRWB], [opencore-amrwb >= 0.1.0 vo-amrwbenc >= 0.1.0],[ + AM_CONDITIONAL([HAVE_AMRWB],[true])],[ + AC_MSG_RESULT([no]); AM_CONDITIONAL([HAVE_AMRWB],[false])]) + AC_CHECK_LIB(apr-1, apr_pool_mutex_set, use_system_apr=yes, use_system_apr=no) AM_CONDITIONAL([SYSTEM_APR],[test "${use_system_apr}" = "yes"]) AC_CHECK_LIB(aprutil-1, apr_queue_pop_timeout, use_system_aprutil=yes, use_system_aprutil=no) @@ -1934,7 +1938,6 @@ AM_CONDITIONAL(ISMAC, [test `uname -s` = Darwin]) AM_CONDITIONAL(ISFREEBSD, [test `uname -s` = FreeBSD]) AM_CONDITIONAL(IS64BITLINUX, [test `uname -m` = x86_64]) -AM_CONDITIONAL(HAVE_AMRWB, [ test -d ${switch_srcdir}/libs/amrwb ]) AM_CONDITIONAL(HAVE_G723_1, [ test -d ${switch_srcdir}/libs/libg723_1 ]) AM_CONDITIONAL(HAVE_G729, [ test -d ${switch_srcdir}/libs/libg729 ]) diff --git a/src/mod/codecs/mod_amrwb/LEGAL b/src/mod/codecs/mod_amrwb/LEGAL new file mode 100644 index 0000000000..f05c6c45aa --- /dev/null +++ b/src/mod/codecs/mod_amrwb/LEGAL @@ -0,0 +1,14 @@ +LEGAL. + +Please bear in mind that the software, as modified by Athonet, implements +functions for the AMR standard, which are covered by third parties' essential patents. +To the best of Athonet's knowledge, such essential patent rights holders are: +Nokia, Orange, Acacia (now VoiceAge) and Ericsson. + +ATHONET HEREBY DISCLAIMS ANY AND ALL LIABILITY IN CASE YOUR USE, +MODIFICATION, COPY OR FURTHER DISTRIBUTION OF THE SOFTWARE, AS +MODIFIED BY ATHONET, INFRINGES IN ANY WAY THIRD PARTY'S INTELLECTUAL PROPERTY RIGHTS. + +THIS DISCLAIMER SHALL NOT PREJUDICE ANY OTHER LIMITATION OF LIABILITY +INCLUDED IN THE APPLICABLE LICENSE. + diff --git a/src/mod/codecs/mod_amrwb/Makefile.am b/src/mod/codecs/mod_amrwb/Makefile.am index ddb1a56999..d3a7d67761 100644 --- a/src/mod/codecs/mod_amrwb/Makefile.am +++ b/src/mod/codecs/mod_amrwb/Makefile.am @@ -1,20 +1,21 @@ include $(top_srcdir)/build/modmake.rulesam MODNAME=mod_amrwb -AMRWB_DIR=$(switch_srcdir)/libs/amrwb -AMRWB_BUILDDIR=$(switch_builddir)/libs/amrwb -AMRWB_A=$(AMRWB_BUILDDIR)/libamrwb.a - mod_LTLIBRARIES = mod_amrwb.la -mod_amrwb_la_SOURCES = mod_amrwb.c +if HAVE_AMRWB +mod_amrwb_la_SOURCES = mod_amrwb.c bitshift.c amrwb_be.c +else +mod_amrwb_la_SOURCES = mod_amrwb.c +endif + mod_amrwb_la_CFLAGS = $(AM_CFLAGS) mod_amrwb_la_LIBADD = $(switch_builddir)/libfreeswitch.la mod_amrwb_la_LDFLAGS = -avoid-version -module -no-undefined -shared if HAVE_AMRWB -BUILT_SOURCES= $(AMRWB_A) -mod_amrwb_la_CFLAGS += -I$(AMRWB_DIR) -mod_amrwb_la_LIBADD += $(AMRWB_A) +# install AMRWB libraries: opencore-amrwb (decoder) and vo-amrwbenc (encoder) - check README +mod_amrwb_la_CFLAGS += $(AMRWB_CFLAGS) +mod_amrwb_la_LIBADD += $(AMRWB_LIBS) else mod_amrwb_la_CFLAGS += -DAMRWB_PASSTHROUGH endif diff --git a/src/mod/codecs/mod_amrwb/README b/src/mod/codecs/mod_amrwb/README new file mode 100644 index 0000000000..d7f0cf385d --- /dev/null +++ b/src/mod/codecs/mod_amrwb/README @@ -0,0 +1,29 @@ +Tested with mobile devices (VOLTE) : +Samsung S6 Edge, iPhone, Samsung Note4, Samsung S6 + +Tested with SIP clients : +Linphone, pjsip + +INSTALL + +1. install AMRWB libraries + +libopencore-amrwb-dev - Adaptive Multi-Rate - Wideband speech codec - development files +libopencore-amrwb0 - Adaptive Multi-Rate - Wideband speech codec - shared library +libopencore-amrwb0-dbg - Adaptive Multi-Rate - Wideband speech codec - debugging symbols +libvo-amrwbenc-dev - VisualOn AMR-WB encoder library (development files) +libvo-amrwbenc0 - VisualOn AMR-WB encoder library +vo-amrwbenc-dbg - VisualOn AMR-WB encoder library (debugging symbols) + +apt-get install libopencore-amrwb-dev libopencore-amrwb0 libopencore-amrwb0-dbg libvo-amrwbenc-dev libvo-amrwbenc0 vo-amrwbenc-dbg + +This was tested on Debian 8. + +2. copy files dec_if.h and enc_if.h in the directory with the sourcecode (mod_amrwb). + +cp /usr/include/opencore-amrwb/dec_if.h . +cp /usr/include/vo-amrwbenc/enc_if.h . + +3. make, make install + + diff --git a/src/mod/codecs/mod_amrwb/amrwb_be.c b/src/mod/codecs/mod_amrwb/amrwb_be.c new file mode 100644 index 0000000000..12489d8256 --- /dev/null +++ b/src/mod/codecs/mod_amrwb/amrwb_be.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016, Athonet (www.athonet.com) + * Dragos Oancea + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __AMRWB_BE_H__ +#include "bitshift.h" +#include "amrwb_be.h" + +extern const int switch_amrwb_frame_sizes[]; + +/* Bandwidth Efficient AMR-WB */ +/* https://tools.ietf.org/html/rfc4867#page-17 */ + +/* this works the same as in AMR NB*/ +extern switch_bool_t switch_amrwb_pack_be(unsigned char *shift_buf, int n) +{ + uint8_t save_toc, ft; + + save_toc = shift_buf[1]; + + /* we must convert OA TOC -> BE TOC */ + /* OA TOC + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |F| FT |Q|P1|P2| + +-+-+-+-+-+-+-+-+ + F (1 bit): see definition in Section 4.3.2. + + FT (4 bits, unsigned integer): see definition in Section 4.3.2. + + Q (1 bit): see definition in Section 4.3.2. + + P bits: padding bits, MUST be set to zero, and MUST be ignored on reception. + */ + + /* BE TOC: + 0 1 2 3 4 5 + +-+-+-+-+-+-+ + |F| FT |Q| + +-+-+-+-+-+-+ + F = 0 , FT = XXXX , Q = 1 + eg: Frame Types (FT): ftp://www.3gpp.org/tsg_sa/TSG_SA/TSGS_04/Docs/PDF/SP-99253.pdf - table 1a + */ + + ft = save_toc >> 3 ; /* drop Q, P1, P2 */ + ft &= ~(1 << 5); /* clear - will mark just 1 frame - bit F */ + + /* we only encode one frame, so bit 0 of TOC will be 0 */ + shift_buf[0] |= (ft >> 1); /* first 3 bits of FT */ + + switch_amr_array_lshift(6, shift_buf+1, n); + /*make sure we clear the bit - it will be used as padding of the trailing byte */ + shift_buf[1] |= 1 << 6; /* set bit Q instead of P1 */ + if (( ft >> 0 ) & 1) { + /* set last bit of TOC instead of P2 */ + shift_buf[1] |= 1 << 7; + } else { + /* reset last bit of TOC instead of P2 */ + shift_buf[1] &= ~(1 << 7); + } + + return SWITCH_TRUE; +} + +extern switch_bool_t switch_amrwb_unpack_be(unsigned char *encoded_buf, uint8_t *tmp, int encoded_len) +{ + int framesz, index, ft; + uint8_t shift_tocs[2] = {0x00, 0x00}; + uint8_t *shift_buf; + + memcpy(shift_tocs, encoded_buf, 2); + /* shift for BE */ + switch_amr_array_lshift(4, shift_tocs, 2); + ft = shift_tocs[0] >> 3; + ft &= ~(1 << 5); /* Frame Type*/ + shift_buf = encoded_buf + 1; /* skip CMR */ + /* shift for BE */ + switch_amr_array_lshift(2, shift_buf, encoded_len - 1); + /* get frame size */ + index = ((shift_tocs[0] >> 3) & 0x0f); + if (index > 10) { + return SWITCH_FALSE; + } + framesz = switch_amrwb_frame_sizes[index]; + tmp[0] = shift_tocs[0]; /* save TOC */ + memcpy(&tmp[1], shift_buf, framesz); + + return SWITCH_TRUE; +} +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/src/mod/codecs/mod_amrwb/amrwb_be.h b/src/mod/codecs/mod_amrwb/amrwb_be.h new file mode 100644 index 0000000000..6a871b4f0c --- /dev/null +++ b/src/mod/codecs/mod_amrwb/amrwb_be.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016, Athonet (www.athonet.com) + * Dragos Oancea + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __AMRWB_BE_H__ +#define __AMRWB_BE_H__ + +/* Bandwidth Efficient AMR-WB */ +extern switch_bool_t switch_amrwb_pack_be(unsigned char *shift_buf, int n); +extern switch_bool_t switch_amrwb_unpack_be(unsigned char *encoded_buf, uint8_t *tmp, int encoded_len); + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ + diff --git a/src/mod/codecs/mod_amrwb/bitshift.c b/src/mod/codecs/mod_amrwb/bitshift.c new file mode 100644 index 0000000000..825433cd9f --- /dev/null +++ b/src/mod/codecs/mod_amrwb/bitshift.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016, Athonet (www.athonet.com) + * Paolo Missiaggia + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __BITSHIFT_H__ +#include "bitshift.h" + +/* + * LEFT shift of an entire array of N bits, with N included between 0 and 8 + */ +extern int switch_amr_array_lshift(uint8_t lshift, uint8_t *buf, int a_len) +{ + int i = 0; + uint8_t first_byte; + uint8_t second_byte; + + if (!buf || !a_len) + return (-1); + + if ((lshift < 0) || (lshift > 8)) + return (-1); + + first_byte = 0xFF >> lshift; + second_byte = ~first_byte; + + for (i = 1; i < a_len; i++) { + buf[i - 1] = ((buf[i - 1] & first_byte) << lshift) | + ((buf[i] & second_byte) >> (8 - lshift)); + } + i--; + buf[i] = ((buf[i] & first_byte) << lshift); + return (0); +} +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ + diff --git a/src/mod/codecs/mod_amrwb/bitshift.h b/src/mod/codecs/mod_amrwb/bitshift.h new file mode 100644 index 0000000000..f448ecf652 --- /dev/null +++ b/src/mod/codecs/mod_amrwb/bitshift.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016, Athonet (www.athonet.com) + * Paolo Missiaggia + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of the original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef __BITSHIFT_H__ +#define __BITSHIFT_H__ + +#include "switch.h" +/* + * LEFT shift of an entire array of N bits, with N included between 0 and 8 + */ +extern int switch_amr_array_lshift(uint8_t lshift, uint8_t *buf, int a_len); + +#endif + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ + diff --git a/src/mod/codecs/mod_amrwb/mod_amrwb.c b/src/mod/codecs/mod_amrwb/mod_amrwb.c index 681e3d9536..60cf954428 100644 --- a/src/mod/codecs/mod_amrwb/mod_amrwb.c +++ b/src/mod/codecs/mod_amrwb/mod_amrwb.c @@ -25,6 +25,9 @@ * * Anthony Minessale II * Brian K. West + * Dragos Oancea + * Federico Favaro + * Marco Sinibaldi * * The amrwb codec itself is not distributed with this module. * @@ -38,8 +41,11 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_amrwb_load); SWITCH_MODULE_DEFINITION(mod_amrwb, mod_amrwb_load, NULL, NULL); #ifndef AMRWB_PASSTHROUGH -#include "dec_if.h" -#include "enc_if.h" +#include "opencore-amrwb/dec_if.h" /*AMR-WB decoder API*/ +#include "vo-amrwbenc/enc_if.h" /*AMR-WB encoder API*/ + +#include "bitshift.h" +#include "amrwb_be.h" typedef enum { AMRWB_OPT_OCTET_ALIGN = (1 << 0), @@ -71,14 +77,105 @@ struct amrwb_context { switch_byte_t ptime; switch_byte_t channels; switch_byte_t flags; + int max_red; + int debug; }; -#define AMRWB_DEFAULT_BITRATE AMRWB_BITRATE_24K +#define SWITCH_AMRWB_DEFAULT_BITRATE AMRWB_BITRATE_24K static struct { switch_byte_t default_bitrate; + switch_byte_t volte; + switch_byte_t adjust_bitrate; + int debug; } globals; +const int switch_amrwb_frame_sizes[] = {17, 23, 32, 36, 40, 46, 50, 58, 60, 5}; + +#define SWITCH_AMRWB_OUT_MAX_SIZE 61 +#define SWITCH_AMRWB_MODES 10 /* Silence Indicator (SID) included */ + +static switch_bool_t switch_amrwb_unpack_oa(unsigned char *buf, uint8_t *tmp, int encoded_data_len) +{ + uint8_t *tocs; + int index; + int framesz; + + buf++;/* CMR skip */ + tocs = buf; + index = ((tocs[0]>>3) & 0xf); + buf++; /* point to voice payload */ + + if (index > 10) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMRWB decoder (OA): Invalid TOC: 0x%x", index); + return SWITCH_FALSE; + } + framesz = switch_amrwb_frame_sizes[index]; + if (framesz > encoded_data_len - 1) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMRWB decoder (OA): Invalid frame size: %d\n", framesz); + return SWITCH_FALSE; + } + tmp[0] = tocs[0]; + memcpy(&tmp[1], buf, framesz); + + return SWITCH_TRUE; +} + +static switch_bool_t switch_amrwb_pack_oa(unsigned char *shift_buf, int n) +{ +/* Interleaving code here */ + return SWITCH_TRUE; +} + +static switch_bool_t switch_amrwb_info(unsigned char *encoded_buf, int encoded_data_len, int payload_format, char *print_text) +{ + uint8_t *tocs; + int framesz, index, not_last_frame, q, ft; + uint8_t shift_tocs[2] = {0x00, 0x00}; + + if (!encoded_buf) { + return SWITCH_FALSE; + } + + /* payload format can be OA (octed-aligned) or BE (bandwidth efficient)*/ + if (payload_format) { + /* OA */ + encoded_buf++; /* CMR skip */ + tocs = encoded_buf; + index = (tocs[0] >> 3) & 0x0f; + if (index > SWITCH_AMRWB_MODES) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMRWB decoder (OA): Invalid TOC 0x%x\n", index); + return SWITCH_FALSE; + } + framesz = switch_amrwb_frame_sizes[index]; + not_last_frame = (tocs[0] >> 7) & 1; + q = (tocs[0] >> 2) & 1; + ft = tocs[0] >> 3; + ft &= ~(1 << 5); /* Frame Type */ + } else { + /* BE */ + memcpy(shift_tocs, encoded_buf, 2); + /* shift for BE */ + switch_amr_array_lshift(4, shift_tocs, 2); + not_last_frame = (shift_tocs[0] >> 7) & 1; + q = (shift_tocs[0] >> 2) & 1; + ft = shift_tocs[0] >> 3; + ft &= ~(1 << 5); /* Frame Type */ + index = (shift_tocs[0] >> 3) & 0x0f; + if (index > SWITCH_AMRWB_MODES) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMRWB decoder (BE): Invalid TOC 0x%x\n", index); + return SWITCH_FALSE; + } + framesz = switch_amrwb_frame_sizes[index]; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s): FT: [0x%x] Q: [0x%x] Frame flag: [%d]\n", + print_text, payload_format ? "OA":"BE", ft, q, not_last_frame); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s): AMRWB encoded voice payload sz: [%d] : | encoded_data_len: [%d]\n", + print_text, payload_format ? "OA":"BE", framesz, encoded_data_len); + + return SWITCH_TRUE; +} #endif static switch_status_t switch_amrwb_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings) @@ -103,6 +200,17 @@ static switch_status_t switch_amrwb_init(switch_codec_t *codec, switch_codec_fla return SWITCH_STATUS_FALSE; } else { + /* "mode" may mean two different things: + * "Octed Aligned" or "Bandwidth Efficient" encoding mode , + * or the actual bitrate which is set with FMTP param "mode-set". */ + /* https://tools.ietf.org/html/rfc4867 */ + + /* set the default mode just in case there's no "mode-set" FMTP param */ + context->enc_mode = globals.default_bitrate; + + /* octet-align = 0 - per RFC - if there's no `octet-align` FMTP value then BE is employed */ + switch_clear_flag(context, AMRWB_OPT_OCTET_ALIGN); + if (codec->fmtp_in) { argc = switch_separate_string(codec->fmtp_in, ';', argv, (sizeof(argv) / sizeof(argv[0]))); for (x = 0; x < argc; x++) { @@ -129,7 +237,7 @@ static switch_status_t switch_amrwb_init(switch_codec_t *codec, switch_codec_fla if (atoi(arg)) { switch_set_flag(context, AMRWB_OPT_ROBUST_SORTING); } - } else if (!strcasecmp(data, "interveaving")) { + } else if (!strcasecmp(data, "interleaving")) { if (atoi(arg)) { switch_set_flag(context, AMRWB_OPT_INTERLEAVING); } @@ -141,23 +249,23 @@ static switch_status_t switch_amrwb_init(switch_codec_t *codec, switch_codec_fla context->channels = (switch_byte_t) atoi(arg); } else if (!strcasecmp(data, "maxptime")) { context->max_ptime = (switch_byte_t) atoi(arg); + } else if (!strcasecmp(data, "max-red")) { + context->max_red = atoi(arg); } else if (!strcasecmp(data, "mode-set")) { int y, m_argc; - char *m_argv[9]; /* AMR-WB has 9 modes, AMR has 8 */ + char *m_argv[SWITCH_AMRWB_MODES-1]; /* AMRWB has 9 modes */ m_argc = switch_separate_string(arg, ',', m_argv, (sizeof(m_argv) / sizeof(m_argv[0]))); for (y = 0; y < m_argc; y++) { context->enc_modes |= (1 << atoi(m_argv[y])); + context->enc_mode = atoi(m_argv[y]); } } } } } - /* init to default if there's no "mode-set" param */ - context->enc_mode = globals.default_bitrate; - /* choose the highest mode (bitrate) for high audio quality from fmtp "mode-set" param */ - /* Note: mode-set = 0 is a valid mode */ if (context->enc_modes) { + /* choose the highest mode (bitrate) for high audio quality. */ for (i = 8; i > -1; i--) { if (context->enc_modes & (1 << i)) { context->enc_mode = (switch_byte_t) i; @@ -166,9 +274,17 @@ static switch_status_t switch_amrwb_init(switch_codec_t *codec, switch_codec_fla } } + if (globals.adjust_bitrate) { + switch_set_flag(codec, SWITCH_CODEC_FLAG_HAS_ADJ_BITRATE); + } - switch_snprintf(fmtptmp, sizeof(fmtptmp), "octet-align=%d; mode-set=%d", switch_test_flag(context, AMRWB_OPT_OCTET_ALIGN) ? 1 : 0, - context->enc_mode); + if (!globals.volte) { + switch_snprintf(fmtptmp, sizeof(fmtptmp), "octet-align=%d; mode-set=%d", + switch_test_flag(context, AMRWB_OPT_OCTET_ALIGN) ? 1 : 0, context->enc_mode); + } else { + switch_snprintf(fmtptmp, sizeof(fmtptmp), "octet-align=%d; mode-set=%d; max-red=0; mode-change-capability=2", + switch_test_flag(context, AMRWB_OPT_OCTET_ALIGN) ? 1 : 0, context->enc_mode); + } codec->fmtp_out = switch_core_strdup(codec->memory_pool, fmtptmp); context->encoder_state = NULL; @@ -196,7 +312,7 @@ static switch_status_t switch_amrwb_destroy(switch_codec_t *codec) if (context->encoder_state) { E_IF_exit(context->encoder_state); - } +} if (context->decoder_state) { D_IF_exit(context->decoder_state); } @@ -217,14 +333,37 @@ static switch_status_t switch_amrwb_encode(switch_codec_t *codec, return SWITCH_STATUS_FALSE; #else struct amrwb_context *context = codec->private_info; + int n; + unsigned char *shift_buf = encoded_data; if (!context) { return SWITCH_STATUS_FALSE; } - *encoded_data_len = E_IF_encode(context->encoder_state, context->enc_mode, (int16_t *) decoded_data, (switch_byte_t *) encoded_data, 0); + n = E_IF_encode(context->encoder_state, context->enc_mode, (int16_t *) decoded_data, (switch_byte_t *) encoded_data + 1, 0); + if (n < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMRWB encoder: E_IF_encode() ERROR!\n"); + return SWITCH_STATUS_FALSE; + } + + /* set CMR + TOC (F + 3 bits of FT), 1111 = CMR: No mode request */ + *(switch_byte_t *) encoded_data = 0xf0; + *encoded_data_len = n; + + if (switch_test_flag(context, AMRWB_OPT_OCTET_ALIGN)) { + switch_amrwb_pack_oa(shift_buf, n); /* the payload is OA as it + comes out of the encoding function */ + *encoded_data_len = n + 1; + } else { + switch_amrwb_pack_be(shift_buf, n); + } + + if (globals.debug) { + switch_amrwb_info(shift_buf, *encoded_data_len, switch_test_flag(context, AMRWB_OPT_OCTET_ALIGN) ? 1 : 0, "AMRWB encoder"); + } return SWITCH_STATUS_SUCCESS; + #endif } @@ -240,28 +379,159 @@ static switch_status_t switch_amrwb_decode(switch_codec_t *codec, return SWITCH_STATUS_FALSE; #else struct amrwb_context *context = codec->private_info; + unsigned char *buf = encoded_data; + uint8_t tmp[SWITCH_AMRWB_OUT_MAX_SIZE]; if (!context) { return SWITCH_STATUS_FALSE; } - D_IF_decode(context->decoder_state, (unsigned char *) encoded_data, (int16_t *) decoded_data, 0); + if (globals.debug) { + switch_amrwb_info(buf, encoded_data_len, switch_test_flag(context, AMRWB_OPT_OCTET_ALIGN) ? 1 : 0, "AMRWB decoder"); + } + + if (switch_test_flag(context, AMRWB_OPT_OCTET_ALIGN)) { + /* Octed Aligned */ + if (!switch_amrwb_unpack_oa(buf, tmp, encoded_data_len)) { + return SWITCH_STATUS_FALSE; + } + } else { + /* Bandwidth Efficient */ + if (!switch_amrwb_unpack_be(buf, tmp, encoded_data_len)) { + return SWITCH_STATUS_FALSE; + } + } + + D_IF_decode(context->decoder_state, tmp, (int16_t *) decoded_data, 0); + *decoded_data_len = codec->implementation->decoded_bytes_per_packet; return SWITCH_STATUS_SUCCESS; #endif } +#ifndef AMRWB_PASSTHROUGH +static switch_status_t switch_amrwb_control(switch_codec_t *codec, + switch_codec_control_command_t cmd, + switch_codec_control_type_t ctype, + void *cmd_data, + switch_codec_control_type_t atype, + void *cmd_arg, + switch_codec_control_type_t *rtype, + void **ret_data) +{ + struct amrwb_context *context = codec->private_info; + + switch(cmd) { + case SCC_DEBUG: + { + int32_t level = *((uint32_t *) cmd_data); + context->debug = level; + } + break; + case SCC_AUDIO_ADJUST_BITRATE: + { + const char *cmd = (const char *)cmd_data; + + if (!strcasecmp(cmd, "increase")) { + if (context->enc_mode < SWITCH_AMRWB_MODES - 1) { + int mode_step = 2; /*this is the mode, not the actual bitrate*/ + context->enc_mode = context->enc_mode + mode_step; + if (globals.debug || context->debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "AMRWB encoder: Adjusting mode to %d (increase)\n", context->enc_mode); + } + } + } else if (!strcasecmp(cmd, "decrease")) { + if (context->enc_mode > 0) { + int mode_step = 2; /*this is the mode, not the actual bitrate*/ + context->enc_mode = context->enc_mode - mode_step; + if (globals.debug || context->debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "AMRWB encoder: Adjusting mode to %d (decrease)\n", context->enc_mode); + } + } + } else if (!strcasecmp(cmd, "default")) { + context->enc_mode = globals.default_bitrate; + if (globals.debug || context->debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "AMRWB encoder: Adjusting mode to %d (default)\n", context->enc_mode); + } + } else { + /*minimum bitrate (AMRWB mode)*/ + context->enc_mode = 0; + if (globals.debug || context->debug) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "AMRWB encoder: Adjusting mode to %d (minimum)\n", context->enc_mode); + } + } + } + break; + default: + break; + } + + return SWITCH_STATUS_SUCCESS; +} +#endif + +static char *generate_fmtp(switch_memory_pool_t *pool , int octet_align) +{ + char buf[256] = { 0 }; + + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "octet-align=%d; ", octet_align); + +#ifndef AMRWB_PASSTHROUGH + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "mode-set=%d; ", globals.default_bitrate); + + if (globals.volte) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "max-red=0; mode-change-capability=2; "); + } +#endif + + if (end_of(buf) == ' ') { + *(end_of_p(buf) - 1) = '\0'; + } + + return switch_core_strdup(pool, buf); +} + +#ifndef AMRWB_PASSTHROUGH + +#define AMRWB_DEBUG_SYNTAX "" +SWITCH_STANDARD_API(mod_amrwb_debug) +{ + if (zstr(cmd)) { + stream->write_function(stream, "-USAGE: %s\n", AMRWB_DEBUG_SYNTAX); + } else { + if (!strcasecmp(cmd, "on")) { + globals.debug = 1; + stream->write_function(stream, "AMRWB Debug: on\n"); + } else if (!strcasecmp(cmd, "off")) { + globals.debug = 0; + stream->write_function(stream, "AMRWB Debug: off\n"); + } else { + stream->write_function(stream, "-USAGE: %s\n", AMRWB_DEBUG_SYNTAX); + } + } + return SWITCH_STATUS_SUCCESS; +} +#endif + /* Registration */ SWITCH_MODULE_LOAD_FUNCTION(mod_amrwb_load) { switch_codec_interface_t *codec_interface; + char *default_fmtp_oa = NULL; + char *default_fmtp_be = NULL; + #ifndef AMRWB_PASSTHROUGH + switch_api_interface_t *commands_api_interface; char *cf = "amrwb.conf"; switch_xml_t cfg, xml, settings, param; memset(&globals, 0, sizeof(globals)); - globals.default_bitrate = AMRWB_DEFAULT_BITRATE; + globals.default_bitrate = SWITCH_AMRWB_DEFAULT_BITRATE; if ((xml = switch_xml_open_cfg(cf, &cfg, NULL))) { if ((settings = switch_xml_child(cfg, "settings"))) { @@ -271,6 +541,13 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_amrwb_load) if (!strcasecmp(var, "default-bitrate")) { globals.default_bitrate = (switch_byte_t) atoi(val); } + if (!strcasecmp(var, "volte")) { + /* ETSI TS 126 236 compatibility: http://www.etsi.org/deliver/etsi_ts/126200_126299/126236/10.00.00_60/ts_126236v100000p.pdf */ + globals.volte = (switch_byte_t) atoi(val); + } + if (!strcasecmp(var, "adjust-bitrate")) { + globals.adjust_bitrate = (switch_byte_t) atoi(val); + } } } } @@ -279,10 +556,38 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_amrwb_load) /* connect my internal structure to the blank pointer passed to me */ *module_interface = switch_loadable_module_create_module_interface(pool, modname); - SWITCH_ADD_CODEC(codec_interface, "AMR-WB"); +#ifndef AMRWB_PASSTHROUGH + SWITCH_ADD_API(commands_api_interface, "amrwb_debug", "Set AMR-WB Debug", mod_amrwb_debug, AMRWB_DEBUG_SYNTAX); + + switch_console_set_complete("add amrwb_debug on"); + switch_console_set_complete("add amrwb_debug off"); +#else +#define SWITCH_AMRWB_OUT_MAX_SIZE 0 +#endif + + SWITCH_ADD_CODEC(codec_interface, "AMR-WB / Octet Aligned"); + + default_fmtp_oa = generate_fmtp(pool, 1); + switch_core_codec_add_implementation(pool, codec_interface, - SWITCH_CODEC_TYPE_AUDIO, 100, "AMR-WB", "octet-align=0", 16000, 16000, 23850, - 20000, 320, 640, 0, 1, 1, switch_amrwb_init, switch_amrwb_encode, switch_amrwb_decode, switch_amrwb_destroy); + SWITCH_CODEC_TYPE_AUDIO, 100, "AMR-WB", default_fmtp_oa, + 16000, 16000, 23850, 20000, 320, 640, SWITCH_AMRWB_OUT_MAX_SIZE, 1, 1, + switch_amrwb_init, switch_amrwb_encode, switch_amrwb_decode, switch_amrwb_destroy); +#ifndef AMRWB_PASSTHROUGH + codec_interface->implementations->codec_control = switch_amrwb_control; +#endif + + SWITCH_ADD_CODEC(codec_interface, "AMR-WB / Bandwidth Efficient"); + + default_fmtp_be = generate_fmtp(pool, 0); + + switch_core_codec_add_implementation(pool, codec_interface, + SWITCH_CODEC_TYPE_AUDIO, 110, "AMR-WB", default_fmtp_be, + 16000, 16000, 23850, 20000, 320, 640, SWITCH_AMRWB_OUT_MAX_SIZE, 1, 1, + switch_amrwb_init, switch_amrwb_encode, switch_amrwb_decode, switch_amrwb_destroy); +#ifndef AMRWB_PASSTHROUGH + codec_interface->implementations->codec_control = switch_amrwb_control; +#endif /* indicate that the module should continue to be loaded */ return SWITCH_STATUS_SUCCESS; }