]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-9825: Added AMR-WB transcoding capabilities (in both Bandwidth Efficient and Octet...
authorDragos Oancea <droancea@yahoo.com>
Wed, 7 Dec 2016 11:07:23 +0000 (06:07 -0500)
committerDragos Oancea <droancea@yahoo.com>
Wed, 7 Dec 2016 17:06:01 +0000 (12:06 -0500)
configure.ac
src/mod/codecs/mod_amrwb/LEGAL [new file with mode: 0644]
src/mod/codecs/mod_amrwb/Makefile.am
src/mod/codecs/mod_amrwb/README [new file with mode: 0644]
src/mod/codecs/mod_amrwb/amrwb_be.c [new file with mode: 0644]
src/mod/codecs/mod_amrwb/amrwb_be.h [new file with mode: 0644]
src/mod/codecs/mod_amrwb/bitshift.c [new file with mode: 0644]
src/mod/codecs/mod_amrwb/bitshift.h [new file with mode: 0644]
src/mod/codecs/mod_amrwb/mod_amrwb.c

index 705043ef5f72b671d14a62db8d932f8f03f08730..47afda4666beb586a20b672a1da2982cdf1b12d3 100644 (file)
@@ -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 (file)
index 0000000..f05c6c4
--- /dev/null
@@ -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.
+
index ddb1a56999233f6d47bfdf1b35b9272b83e87b17..d3a7d677610ea4362517c44fd3c9b6bf2568634b 100644 (file)
@@ -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 (file)
index 0000000..d7f0cf3
--- /dev/null
@@ -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 (file)
index 0000000..12489d8
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016, Athonet (www.athonet.com)
+ * Dragos Oancea  <dragos.oancea@athonet.com>
+ * 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 (file)
index 0000000..6a871b4
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016, Athonet (www.athonet.com)
+ * Dragos Oancea  <dragos.oancea@athonet.com>
+ * 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 (file)
index 0000000..825433c
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016, Athonet (www.athonet.com)
+ * Paolo Missiaggia  <paolo.missiaggia@athonet.com>
+ * 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 (file)
index 0000000..f448ecf
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016, Athonet (www.athonet.com)
+ * Paolo Missiaggia  <paolo.missiaggia@athonet.com>
+ * 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:
+ */
+
index 681e3d953649b5827bdb25c656b105d4d5e55c5b..60cf95442818d00a761dbc54afa06c64a02960e3 100644 (file)
@@ -25,6 +25,9 @@
  * 
  * Anthony Minessale II <anthm@freeswitch.org>
  * Brian K. West <brian@freeswitch.org>
+ * Dragos Oancea <dragos.oancea@athonet.com>
+ * Federico Favaro <federico.favaro@athonet.com>
+ * Marco Sinibaldi <marco.sinibaldi@athonet.com>
  *
  * 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 "<on|off>"
+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;
 }