FS-9825: Added AMR-WB transcoding capabilities (in both Bandwidth Efficient and Octet Aligned modes)

This commit is contained in:
Dragos Oancea 2016-12-07 06:07:23 -05:00
parent 7884bee06a
commit 3ccf1704c1
9 changed files with 699 additions and 27 deletions

View 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 ])

View File

@ -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.

View 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

View File

@ -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

View File

@ -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:
*/

View File

@ -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:
*/

View File

@ -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:
*/

View File

@ -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:
*/

View 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;
}