FS-9664: mod_amr: add AMR NB transcoding capability in bandwidth-efficient mode. Tested with VOLTE devices.

This commit is contained in:
Dragos Oancea 2016-03-11 12:01:49 -05:00
parent 7d6ed4a41d
commit 63900a6d86
8 changed files with 534 additions and 46 deletions

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,24 @@
include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_amr
AMR_DIR=$(switch_srcdir)/libs/amr
AMR_BUILDDIR=$(switch_builddir)/libs/amr
AMR_A=$(AMR_BUILDDIR)/libamr.a
mod_LTLIBRARIES = mod_amr.la
mod_amr_la_SOURCES = mod_amr.c
if HAVE_AMR
mod_amr_la_SOURCES = mod_amr.c bitshift.c amr_be.c
else
mod_amr_la_SOURCES = mod_amr.c
endif
mod_amr_la_CFLAGS = $(AM_CFLAGS)
mod_amr_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_amr_la_LDFLAGS = -avoid-version -module -no-undefined -shared
if HAVE_AMR
BUILT_SOURCES= $(AMR_A)
mod_amr_la_CFLAGS += -I$(AMR_DIR)
mod_amr_la_LIBADD += $(AMR_A)
# install AMR library opencore-amrnb (check README)
mod_amr_la_CFLAGS += -I/usr/include/opencore-amrnb
mod_amr_la_LIBADD += -lopencore-amrnb
mod_amr_la_CFLAGS +=
else
mod_amr_la_CFLAGS += -DAMR_PASSTHROUGH
endif

View File

@ -0,0 +1,22 @@
Tested with mobile devices (VOLTE) :
Samsung S6 Edge, iPhone, Samsung Note4, Samsung S6
INSTALL
1. install the AMR library debs
libopencore-amrnb-dev - Adaptive Multi Rate speech codec - development files
libopencore-amrnb0 - Adaptive Multi Rate speech codec - shared library
libopencore-amrnb0-dbg - Adaptive Multi Rate speech codec - debugging symbols
apt-get install libopencore-amrnb-dev libopencore-amrnb0 libopencore-amrnb0-dbg
This was tested on Debian 8
2. copy these two files in the current directory (mod_amr)
cp ./usr/include/opencore-amrnb/interf_enc.h .
cp ./usr/include/opencore-amrnb/interf_dec.h .
3. run make, make install

View File

@ -0,0 +1,131 @@
/*
* 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 __AMR_BE_H__
#include "bitshift.h"
#include "amr_be.h"
extern const int switch_amr_frame_sizes[];
/* Bandwidth Efficient AMR-NB */
/* https://tools.ietf.org/html/rfc4867#page-17 */
extern switch_bool_t switch_amr_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_amr_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 > 9) {
return SWITCH_FALSE;
}
framesz = switch_amr_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 __AMR_BE_H__
#define __AMR_BE_H__
/* Bandwidth Efficient AMR */
extern switch_bool_t switch_amr_pack_be(unsigned char *shift_buf, int n);
extern switch_bool_t switch_amr_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

@ -43,6 +43,8 @@ SWITCH_MODULE_DEFINITION(mod_amr, mod_amr_load, NULL, NULL);
#include "interf_enc.h"
#include "interf_dec.h"
#include "bitshift.h"
#include "amr_be.h"
/*
* Check section 8.1 of rfc3267 for possible sdp options.
*
@ -110,6 +112,7 @@ struct amr_codec_settings {
switch_byte_t flags;
switch_byte_t enc_modes;
switch_byte_t enc_mode;
int max_red;
};
typedef struct amr_codec_settings amr_codec_settings_t;
@ -122,18 +125,21 @@ struct amr_context {
amr_codec_settings_t codec_settings;
switch_byte_t flags;
int dtx_mode;
int max_red;
};
#define AMR_DEFAULT_BITRATE AMR_BITRATE_1220
#define SWITCH_AMR_DEFAULT_BITRATE AMR_BITRATE_1220
static struct {
switch_byte_t default_bitrate;
switch_byte_t volte; /* enable special fmtp for VoLTE compliance */
int debug;
} globals;
static const int switch_amr_frame_sizes[] = {12,13,15,17,19,20,26,31,5,0};
const int switch_amr_frame_sizes[] = {12,13,15,17,19,20,26,31,5,0};
#define AMR_OUT_MAX_SIZE 32
#define SWITCH_AMR_OUT_MAX_SIZE 32
#define SWITCH_AMR_MODES 9 /* plus SID */
static switch_bool_t switch_amr_unpack_oa(unsigned char *buf, uint8_t *tmp, int encoded_data_len)
{
@ -141,17 +147,17 @@ static switch_bool_t switch_amr_unpack_oa(unsigned char *buf, uint8_t *tmp, int
int index;
int framesz;
buf++; /*CMR skip*/
buf++; /* CMR skip */
tocs = buf;
index = ((tocs[0]>>3) & 0xf);
buf++; /*point to voice payload*/
if (index > 9) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMR decoder (OA): Bad AMRWB TOC, index = %i", index);
buf++; /* point to voice payload */
if (index > SWITCH_AMR_MODES) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMR decoder (OA): Invalid Table Of Contents (TOC): 0x%x\n", index);
return SWITCH_FALSE;
}
framesz = switch_amr_frame_sizes[index];
if (framesz > encoded_data_len - 1) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMR decoder (OA): Truncated AMR frame\n");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMR decoder (OA): Invalid frame size: %d\n", framesz);
return SWITCH_FALSE;
}
tmp[0] = tocs[0];
@ -159,31 +165,55 @@ static switch_bool_t switch_amr_unpack_oa(unsigned char *buf, uint8_t *tmp, int
return SWITCH_TRUE;
}
static switch_bool_t switch_amr_pack_oa(unsigned char *shift_buf, int n)
{
/* nothing here for now. This is where interleaving code would go */
return SWITCH_TRUE;
}
static switch_bool_t switch_amr_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*/
encoded_buf++; /* CMR skip */
tocs = encoded_buf;
index = (tocs[0] >> 3) & 0x0f;
if (index > SWITCH_AMR_MODES) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMR decoder (OA): Invalid Table Of Contents (TOC): 0x%x\n", index);
return SWITCH_FALSE;
}
framesz = switch_amr_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 mode not supported yet */
return SWITCH_FALSE;
/* 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_AMR_MODES) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMR decoder (BE): Invalid Table Of Contents (TOC): 0x%x\n", index);
return SWITCH_FALSE;
}
framesz = switch_amr_frame_sizes[index];
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s (%s): FT: [0x%x] Q: [0x%x] Frame flag: [%d]\n",
@ -223,7 +253,24 @@ static switch_status_t switch_amr_init(switch_codec_t *codec, switch_codec_flag_
memset(&codec_fmtp, '\0', sizeof(struct switch_codec_fmtp));
codec_fmtp.private_info = &amr_codec_settings;
context->codec_settings = amr_codec_settings;
/* "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 */
/* we set the default mode (bitrate) just in case "mode-set" is not present in FMTP. */
/* bitrate = value read from config file or default mode 7 */
context->enc_mode = globals.default_bitrate;
/* set octet-align = 0 - it must be 0 by default when there is no "octet-align" param in FMTP, per RFC :
*
* "octet-align: Permissible values are 0 and 1. If 1, octet-aligned
* operation SHALL be used. If 0 or if not present,
* bandwidth-efficient operation is employed."
*
*/
switch_clear_flag(context, AMR_OPT_OCTET_ALIGN);
if (codec->fmtp_in) {
argc = switch_separate_string(codec->fmtp_in, ';', argv, (sizeof(argv) / sizeof(argv[0])));
@ -238,7 +285,7 @@ static switch_status_t switch_amr_init(switch_codec_t *codec, switch_codec_flag_
if (!strcasecmp(data, "octet-align")) {
if (atoi(arg)) {
switch_set_flag(context, AMR_OPT_OCTET_ALIGN);
}
}
} else if (!strcasecmp(data, "mode-change-neighbor")) {
if (atoi(arg)) {
switch_set_flag(context, AMR_OPT_MODE_CHANGE_NEIGHBOR);
@ -263,9 +310,11 @@ static switch_status_t switch_amr_init(switch_codec_t *codec, switch_codec_flag_
context->codec_settings.channels = (switch_byte_t) atoi(arg);
} else if (!strcasecmp(data, "maxptime")) {
context->codec_settings.max_ptime = (switch_byte_t) atoi(arg);
} else if (!strcasecmp(data, "max-red")) {
context->codec_settings.max_red = atoi(arg);
} else if (!strcasecmp(data, "mode-set")) {
int y, m_argc;
char *m_argv[8];
char *m_argv[SWITCH_AMR_MODES-1];
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]));
@ -275,11 +324,8 @@ static switch_status_t switch_amr_init(switch_codec_t *codec, switch_codec_flag_
}
}
/*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 = 7; i > -1; i--) {
if (context->enc_modes & (1 << i)) {
context->enc_mode = (switch_byte_t) i;
@ -288,8 +334,14 @@ static switch_status_t switch_amr_init(switch_codec_t *codec, switch_codec_flag_
}
}
switch_snprintf(fmtptmp, sizeof(fmtptmp), "octet-align=%d; mode-set=%d", switch_test_flag(context, AMR_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, AMR_OPT_OCTET_ALIGN) ? 1 : 0,
context->enc_mode);
} else {
/* some UEs reject the call with 488 if mode-change-capability is not 2 */
switch_snprintf(fmtptmp, sizeof(fmtptmp), "octet-align=%d; mode-set=%d; max-red=0; mode-change-capability=2",
switch_test_flag(context, AMR_OPT_OCTET_ALIGN) ? 1 : 0, context->enc_mode);
}
codec->fmtp_out = switch_core_strdup(codec->memory_pool, fmtptmp);
context->encoder_state = NULL;
@ -303,6 +355,8 @@ static switch_status_t switch_amr_init(switch_codec_t *codec, switch_codec_flag_
context->decoder_state = Decoder_Interface_init();
}
context->codec_settings = amr_codec_settings;
codec->private_info = context;
return SWITCH_STATUS_SUCCESS;
@ -351,19 +405,32 @@ static switch_status_t switch_amr_encode(switch_codec_t *codec,
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, AMR_OPT_OCTET_ALIGN)) {
*(switch_byte_t *) encoded_data = 0xf0; /*CMR*/
/* this function does nothing for now - the payload is already OA
as it comes out of the encoding function */
switch_amr_pack_oa(shift_buf, n);
*encoded_data_len = n + 1;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMR encoder: BE mode not supported!\n");
return SWITCH_STATUS_FALSE;
switch_amr_pack_be(shift_buf, n);
/* mode 0 = 95 speech bits -> 96 bytes - 1 padding bit */
/* mode 1 = 103 speech bits -> 104 bytes - 1 padding bit */
/* mode 5 = 159 speech bits -> 160 bytes - 1 padding bit */
if ((context->enc_mode == 5 ) || (context->enc_mode == 1) || (context->enc_mode == 0)) {
/* modes 0,1 and 5 have only 1 padding bit and due to shifting
* we may have an extra 0 byte at the end of the voice payload (bit stuffing) */
if (shift_buf[n-1] != 0x0) {
*encoded_data_len = n + 1;
}
}
}
#ifndef AMR_PASSTHROUGH
if (globals.debug) {
switch_amr_info(shift_buf, *encoded_data_len, switch_test_flag(context, AMR_OPT_OCTET_ALIGN) ? 1 : 0, "AMR encoder");
switch_amr_info(shift_buf, *encoded_data_len, switch_test_flag(context, AMR_OPT_OCTET_ALIGN) ? 1 : 0, "AMR encoder");
}
#endif
return SWITCH_STATUS_SUCCESS;
#endif
@ -382,26 +449,26 @@ static switch_status_t switch_amr_decode(switch_codec_t *codec,
#else
struct amr_context *context = codec->private_info;
unsigned char *buf = encoded_data;
uint8_t tmp[AMR_OUT_MAX_SIZE];
uint8_t tmp[SWITCH_AMR_OUT_MAX_SIZE];
if (!context) {
return SWITCH_STATUS_FALSE;
}
#ifndef AMR_PASSTHROUGH
if (globals.debug) {
switch_amr_info(buf, encoded_data_len, switch_test_flag(context, AMR_OPT_OCTET_ALIGN) ? 1 : 0, "AMR decoder");
switch_amr_info(buf, encoded_data_len, switch_test_flag(context, AMR_OPT_OCTET_ALIGN) ? 1 : 0, "AMR decoder");
}
#endif
if (switch_test_flag(context, AMR_OPT_OCTET_ALIGN)) {
/*Octed Aligned*/
/* Octed Aligned */
if (!switch_amr_unpack_oa(buf, tmp, encoded_data_len)) {
return SWITCH_STATUS_FALSE;
}
} else {
/*"Bandwidth Efficient"*/
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AMR decoder: BE mode not supported!\n");
return SWITCH_STATUS_FALSE;
/* Bandwidth Efficient */
if (!switch_amr_unpack_be(buf, tmp, encoded_data_len)) {
return SWITCH_STATUS_FALSE;
}
}
Decoder_Interface_Decode(context->decoder_state, tmp, (int16_t *) decoded_data, 0);
@ -411,7 +478,30 @@ static switch_status_t switch_amr_decode(switch_codec_t *codec,
#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 AMR_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 AMR_PASSTHROUGH
#define AMRWB_DEBUG_SYNTAX "<on|off>"
SWITCH_STANDARD_API(mod_amr_debug)
{
@ -430,12 +520,14 @@ SWITCH_STANDARD_API(mod_amr_debug)
}
return SWITCH_STATUS_SUCCESS;
}
#endif
#endif
/* Registration */
SWITCH_MODULE_LOAD_FUNCTION(mod_amr_load)
{
switch_codec_interface_t *codec_interface;
char *default_fmtp_oa = NULL;
char *default_fmtp_be = NULL;
#ifndef AMR_PASSTHROUGH
switch_api_interface_t *commands_api_interface;
@ -443,7 +535,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_amr_load)
switch_xml_t cfg, xml, settings, param;
memset(&globals, 0, sizeof(globals));
globals.default_bitrate = AMR_DEFAULT_BITRATE;
globals.default_bitrate = SWITCH_AMR_DEFAULT_BITRATE;
if ((xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
if ((settings = switch_xml_child(cfg, "settings"))) {
@ -453,6 +545,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_amr_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);
}
}
}
}
@ -461,24 +557,31 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_amr_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");
#ifndef AMR_PASSTHROUGH
SWITCH_ADD_API(commands_api_interface, "amr_debug", "Set AMR Debug", mod_amr_debug, AMRWB_DEBUG_SYNTAX);
switch_console_set_complete("add amr_debug on");
switch_console_set_complete("add amr_debug off");
#endif
#endif
SWITCH_ADD_CODEC(codec_interface, "AMR / Octet Aligned");
default_fmtp_oa = generate_fmtp(pool, 1);
switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */
96, /* the IANA code number */
"AMR", /* the IANA code name */
"octet-align=1", /* default fmtp to send (can be overridden by the init function) */
default_fmtp_oa, /* default fmtp to send (can be overridden by the init function) */
8000, /* samples transferred per second */
8000, /* actual samples transferred per second */
12200, /* bits transferred per second */
20000, /* number of microseconds per frame */
160, /* number of samples per frame */
320, /* number of bytes per frame decompressed */
#ifndef AMR_PASSTHROUGH
SWITCH_AMR_OUT_MAX_SIZE, /* number of bytes per frame compressed */
#else
0, /* number of bytes per frame compressed */
#endif
1, /* number of channels represented */
1, /* number of frames per network packet */
switch_amr_init, /* function to initialize a codec handle using this implementation */
@ -486,6 +589,32 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_amr_load)
switch_amr_decode, /* function to decode encoded data into raw data */
switch_amr_destroy); /* deinitalize a codec handle using this implementation */
SWITCH_ADD_CODEC(codec_interface, "AMR / Bandwidth Efficient");
default_fmtp_be = generate_fmtp(pool, 0);
switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */
97, /* the IANA code number */
"AMR", /* the IANA code name */
default_fmtp_be, /* default fmtp to send (can be overridden by the init function) */
8000, /* samples transferred per second */
8000, /* actual samples transferred per second */
12200, /* bits transferred per second */
20000, /* number of microseconds per frame */
160, /* number of samples per frame */
320, /* number of bytes per frame decompressed */
#ifndef AMR_PASSTHROUGH
SWITCH_AMR_OUT_MAX_SIZE, /* number of bytes per frame compressed */
#else
0, /* number of bytes per frame compressed */
#endif
1, /* number of channels represented */
1, /* number of frames per network packet */
switch_amr_init, /* function to initialize a codec handle using this implementation */
switch_amr_encode, /* function to encode raw data into encoded data */
switch_amr_decode, /* function to decode encoded data into raw data */
switch_amr_destroy); /* deinitalize a codec handle using this implementation */
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}