[Pkg-voip-commits] [asterisk] 02/06: Add AMR support: Add patch to add AMR and AMR-WB codec modules supporting transcoding. Build-depend on libopencore-amrnb-dev libopencore-amrwb-dev.

Jonas Smedegaard dr at jones.dk
Mon Mar 21 09:36:38 UTC 2016


This is an automated email from the git hooks/post-receive script.

js pushed a commit to branch master
in repository asterisk.

commit d94ec0c4ba588f8ddf46e2979879cd759d3e77e2
Author: Jonas Smedegaard <dr at jones.dk>
Date:   Mon Mar 21 00:25:45 2016 +0100

    Add AMR support: Add patch to add AMR and AMR-WB codec modules supporting transcoding. Build-depend on libopencore-amrnb-dev libopencore-amrwb-dev.
---
 debian/control           |    2 +
 debian/patches/amr.patch | 1270 ++++++++++++++++++++++++++++++++++++++++++++++
 debian/patches/series    |    1 +
 3 files changed, 1273 insertions(+)

diff --git a/debian/control b/debian/control
index 73b0d90..4355656 100644
--- a/debian/control
+++ b/debian/control
@@ -38,6 +38,8 @@ Build-Depends:
  libneon27-gnutls-dev | libneon27-dev,
  libnewt-dev,
  libogg-dev,
+ libopencore-amrnb-dev,
+ libopencore-amrwb-dev,
  libopenr2-dev [linux-any],
  libopus-dev,
  libpjproject-dev,
diff --git a/debian/patches/amr.patch b/debian/patches/amr.patch
new file mode 100644
index 0000000..a5fd787
--- /dev/null
+++ b/debian/patches/amr.patch
@@ -0,0 +1,1270 @@
+Description: Add AMR and AMR-WB codec modules supporting transcoding
+ To add a codec for SIP/SDP (m=, rtmap, and ftmp), you create a format
+ module in Asterisk: `codec_amr.patch` (for m= and rtmap) and
+ `res/res_format_attr_amr.c` (for fmtp). However, this requires both
+ call legs to support AMR (pass-through only). If one leg does not
+ support AMR, the call has no audio. Or, if you use the pre-recorded
+ voice and music files of Asterisk, these files cannot be heard, because
+ they are not in AMR but in slin. Therefore, this repository adds not
+ just a format module for the audio-codecs AMR and AMR-WB but a
+ transcoding module as well: `codecs/codec_amr.c`.
+ .
+ This is an implementation of IETF
+ [RFC 4867](http://tools.ietf.org/html/rfc4867). Sometimes, AMR is
+ called AMR Narrowband (AMR-NB). AMR Wideband (ITU-T Recommendation
+ G.722.2) is sometimes abbreviated W-AMR
+ ([GSA](http://www.gsacom.com/hdvoice/)). GSMA Mobile
+ [HD Voice](https://www.youtube.com/playlist?&list=PLj1MyDu3jckpSciPQ1Max0W6HDSaY8-n4)
+ is AMR-WB. Research papers comparing AMR and AMR-WB with other audio
+ codecs:
+ [InterSpeech 2010](http://research.nokia.com/files/public/%5B12%5D_Interspeech%202010_Voice%20Quality%20Evaluation%20of%20Recent%20Open%20Source%20Codecs.pdf),
+ [ICASSP 2010](http://research.nokia.com/files/public/%5B11%5D_ICASSP2010_Voice%20Quality%20Evaluation%20of%20Various%20Codecs.pdf),
+ [InterSpeech 2011](http://research.nokia.com/files/public/%5B16%5D_InterSpeech2011_Voice_Quality_Characterization_of_IETF_Opus_Codec.pdf).
+ Further
+ [examples…](http://www.voiceage.com/Audio-Samples-Listening-Room.html)
+
+Origin: https://github.com/traud/asterisk-amr
+Author: Alexander Traud <pabstraud at compuserve.com>
+Last-Update: 2016-03-21
+License: Unlicense
+ This is free and unencumbered software released into the public domain.
+ .
+ Anyone is free to copy, modify, publish, use, compile, sell, or
+ distribute this software, either in source code form or as a compiled
+ binary, for any purpose, commercial or non-commercial, and by any
+ means.
+ .
+ In jurisdictions that recognize copyright laws, the author or authors
+ of this software dedicate any and all copyright interest in the
+ software to the public domain. We make this dedication for the benefit
+ of the public at large and to the detriment of our heirs and
+ successors. We intend this dedication to be an overt act of
+ relinquishment in perpetuity of all present and future rights to this
+ software under copyright law.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ .
+ For more information, please refer to <http://unlicense.org/>
+
+--- a/build_tools/menuselect-deps.in
++++ b/build_tools/menuselect-deps.in
+@@ -1,4 +1,7 @@
+ ALSA=@PBX_ALSA@
++AMR_NB=@PBX_AMR_NB@
++AMR_WB_DECODER=@PBX_AMR_WB_DECODER@
++AMR_WB_ENCODER=@PBX_AMR_WB_ENCODER@
+ BLUETOOTH=@PBX_BLUETOOTH@
+ COROSYNC=@PBX_COROSYNC@
+ CRYPTO=@PBX_CRYPTO@
+--- /dev/null
++++ b/codecs/codec_amr.c
+@@ -0,0 +1,405 @@
++/*** MODULEINFO
++	 <depend>amr_nb</depend>
++	 <depend>amr_wb_decoder</depend>
++	 <depend>amr_wb_encoder</depend>
++***/
++
++#include "asterisk.h"
++
++/* version 1.0 */
++/* based on codecs/codec_opus.c */
++
++#include "asterisk/codec.h"             /* for AST_MEDIA_TYPE_AUDIO */
++#include "asterisk/format.h"            /* for ast_format_get_attribute_data */
++#include "asterisk/frame.h"             /* for ast_frame, etc */
++#include "asterisk/linkedlists.h"       /* for AST_LIST_NEXT, etc */
++#include "asterisk/logger.h"            /* for ast_log, ast_debug, etc */
++#include "asterisk/module.h"
++#include "asterisk/translate.h"         /* for ast_trans_pvt, etc */
++
++#include <opencore-amrnb/interf_dec.h>
++#include <opencore-amrnb/interf_enc.h>
++#include <opencore-amrwb/dec_if.h>
++#include <vo-amrwbenc/enc_if.h>
++
++#include "asterisk/amr.h"
++
++#define BUFFER_SAMPLES 16000 /* 1000 milliseconds */
++
++/* Sample frame data */
++#include "asterisk/slin.h"
++#include "ex_amr.h"
++
++struct amr_coder_pvt {
++	void *state; /* May be encoder or decoder */
++	unsigned int frames;
++	int16_t buf[BUFFER_SAMPLES];
++};
++
++static int lintoamr_new(struct ast_trans_pvt *pvt)
++{
++	struct amr_coder_pvt *apvt = pvt->pvt;
++	const unsigned int sample_rate = pvt->t->src_codec.sample_rate;
++
++	struct amr_attr *attr = pvt->explicit_dst ? ast_format_get_attribute_data(pvt->explicit_dst) : NULL;
++	const int dtx = attr ? attr->vad : 0;
++
++	if (8000 == sample_rate) {
++		apvt->state = Encoder_Interface_init(dtx);
++	} else if (16000 == sample_rate) {
++		apvt->state = E_IF_init();
++	}
++
++	if (NULL == apvt->state) {
++		ast_log(LOG_ERROR, "Error creating the AMR encoder for %d\n", sample_rate);
++		return -1;
++	}
++
++	apvt->frames = 0;
++	ast_debug(3, "Created encoder (%d -> AMR) %p (Format %p)\n", sample_rate, apvt, pvt->explicit_dst);
++
++	return 0;
++}
++
++static int amrtolin_new(struct ast_trans_pvt *pvt)
++{
++	struct amr_coder_pvt *apvt = pvt->pvt;
++	const unsigned int sample_rate = pvt->t->dst_codec.sample_rate;
++
++	if (8000 == sample_rate) {
++		apvt->state = Decoder_Interface_init();
++	} else if (16000 == sample_rate) {
++		apvt->state = D_IF_init();
++	}
++
++	if (NULL == apvt->state) {
++		ast_log(LOG_ERROR, "Error creating the AMR decoder for %d\n", sample_rate);
++		return -1;
++	}
++
++	apvt->frames = 0;
++	ast_debug(3, "Created decoder (AMR -> %d) %p\n", sample_rate, apvt);
++
++	return 0;
++}
++
++static int lintoamr_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
++{
++	struct amr_coder_pvt *apvt = pvt->pvt;
++
++	/* XXX We should look at how old the rest of our stream is, and if it
++	   is too old, then we should overwrite it entirely, otherwise we can
++	   get artifacts of earlier talk that do not belong */
++	memcpy(apvt->buf + pvt->samples, f->data.ptr, f->datalen);
++	pvt->samples += f->samples;
++
++	return 0;
++}
++
++static struct ast_frame *lintoamr_frameout(struct ast_trans_pvt *pvt)
++{
++	struct amr_coder_pvt *apvt = pvt->pvt;
++	const unsigned int sample_rate = pvt->t->src_codec.sample_rate;
++	const unsigned int frame_size = sample_rate / 50;
++	struct ast_frame *result = NULL;
++	struct ast_frame *last = NULL;
++	int samples = 0; /* output samples */
++
++	struct amr_attr *attr = ast_format_get_attribute_data(pvt->f.subclass.format);
++	const int dtx = attr ? attr->vad : 0;
++	const int mode = attr ? attr->mode_current : 0;
++	const int aligned = attr ? attr->octet_align : 0;
++
++	while (pvt->samples >= frame_size) {
++		struct ast_frame *current;
++		const int forceSpeech = 0; /* ignored by underlying API anyway */
++		const short *speech = apvt->buf + samples;
++		unsigned char *out = pvt->outbuf.uc + 1;
++		int status = -1; /* result value; either error or output bytes */
++
++		if (8000 == sample_rate) {
++			status = Encoder_Interface_Encode(apvt->state, mode, speech, out, forceSpeech);
++		} else if (16000 == sample_rate) {
++			status = E_IF_encode(apvt->state, mode, speech, out, dtx);
++		}
++
++		samples += frame_size;
++		pvt->samples -= frame_size;
++
++		if (status < 0) {
++			ast_log(LOG_ERROR, "Error encoding the AMR frame\n");
++			current = NULL;
++		} else if (aligned) {
++			pvt->outbuf.uc[0] = (15 << 4); /* Change-Mode Request (CMR): no */
++			/* add one byte, because we added the CMR byte */
++			current = ast_trans_frameout(pvt, status + 1, frame_size);
++		} else {
++			const int another = ((out[0] >> 7) & 0x01);
++			const int type    = ((out[0] >> 3) & 0x0f);
++			const int quality = ((out[0] >> 2) & 0x01);
++			unsigned int i;
++
++			/* to shift in place, clear bits beyond end and at start */
++			out[0] = 0;
++			out[status] = 0;
++			/* shift in place, 6 bits */
++			for (i = 0; i < status; i++) {
++				out[i] = ((out[i] << 6) | (out[i + 1] >> 2));
++			}
++			/* restore first two bytes: [ CMR |F| FT |Q] */
++			out[0] |= ((type << 7) | (quality << 6));
++			pvt->outbuf.uc[0] = ((15 << 4) | (another << 3) | (type >> 1)); /* CMR: no */
++
++			if (8000 == sample_rate) {
++				/* https://tools.ietf.org/html/rfc4867#section-3.6 */
++				const int octets[16] = { 14, 15, 16, 18, 20, 22, 27, 32, 7 };
++
++				status = octets[type];
++			} else if (16000 == sample_rate) {
++				/* 3GPP TS 26.201, Table A.1b, plus CMR (4 bits) and F (1 bit) / 8 */
++				const int octets[16] = { 18, 24, 33, 37, 41, 47, 51, 59, 61, 7 };
++
++				status = octets[type];
++			}
++
++			current = ast_trans_frameout(pvt, status, frame_size);
++		}
++
++		if (!current) {
++			continue;
++		} else if (last) {
++			AST_LIST_NEXT(last, frame_list) = current;
++		} else {
++			result = current;
++		}
++		last = current;
++	}
++
++	/* Move the data at the end of the buffer to the front */
++	if (samples) {
++		memmove(apvt->buf, apvt->buf + samples, pvt->samples * 2);
++	}
++
++	return result;
++}
++
++static int amrtolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
++{
++	struct amr_coder_pvt *apvt = pvt->pvt;
++	const unsigned int sample_rate = pvt->t->dst_codec.sample_rate;
++	const unsigned int frame_size = sample_rate / 50;
++
++	struct amr_attr *attr = ast_format_get_attribute_data(f->subclass.format);
++	const int aligned = attr ? attr->octet_align : 0;
++	const unsigned char mode_next = *(unsigned char *) f->data.ptr >> 4;
++	const int bfi = 0; /* ignored by underlying API anyway */
++	unsigned char temp[f->datalen];
++	unsigned char *in;
++
++	if (attr && mode_next < 15) {
++		attr->mode_current = mode_next;
++	}
++
++	/* 
++	 * Decoders expect the "MIME storage format" (RFC 4867 chapter 5) which is
++	 * octet aligned. On the other hand, the "RTP payload format" (chapter 4)
++	 * is prefixed with a change-mode request (CMR; 1 byte in octet-aligned
++	 * mode). Therefore, we do +1 to jump over the first byte.
++	 */
++
++	if (aligned) {
++		in = f->data.ptr + 1;
++	} else {
++		in = f->data.ptr;
++		const int another = ((in[0] >> 3) & 0x01);
++		const int type    = ((in[0] << 1 | in[1] >> 7) & 0x0f);
++		const int quality = ((in[1] >> 6) & 0x01);
++		unsigned int i;
++
++		/* shift in place, 2 bits */
++		for (i = 1; i < (f->datalen - 1); i++) {
++			temp[i] = ((in[i] << 2) | (in[i + 1] >> 6));
++		}
++		temp[f->datalen - 1] = in[f->datalen - 1] << 2;
++		/* restore first byte: [F| FT |Q] */
++		temp[0] = ((another << 7) | (type << 3) | (quality << 2));
++		in = temp;
++	}
++
++	if ((apvt->frames == 0) && (in[0] & 0x80)) {
++		apvt->frames = 1;
++		ast_log(LOG_WARNING, "multiple frames per packet were not tested\n");
++	}
++
++	if (8000 == sample_rate) {
++		Decoder_Interface_Decode(apvt->state, in, pvt->outbuf.i16 + pvt->datalen, bfi);
++	} else if (16000 == sample_rate) {
++		D_IF_decode(apvt->state, in, pvt->outbuf.i16 + pvt->datalen, bfi);
++	}
++
++	pvt->samples += frame_size;
++	pvt->datalen += frame_size * 2;
++
++	return 0;
++}
++
++static void lintoamr_destroy(struct ast_trans_pvt *pvt)
++{
++	struct amr_coder_pvt *apvt = pvt->pvt;
++	const unsigned int sample_rate = pvt->t->src_codec.sample_rate;
++
++	if (!apvt || !apvt->state) {
++		return;
++	}
++
++	if (8000 == sample_rate) {
++		Encoder_Interface_exit(apvt->state);
++	} else if (16000 == sample_rate) {
++		E_IF_exit(apvt->state);
++	}
++	apvt->state = NULL;
++
++	ast_debug(3, "Destroyed encoder (%d -> AMR) %p\n", sample_rate, apvt);
++}
++
++static void amrtolin_destroy(struct ast_trans_pvt *pvt)
++{
++	struct amr_coder_pvt *apvt = pvt->pvt;
++	const unsigned int sample_rate = pvt->t->dst_codec.sample_rate;
++
++	if (!apvt || !apvt->state) {
++		return;
++	}
++
++	if (8000 == sample_rate) {
++		Decoder_Interface_exit(apvt->state);
++	} else if (16000 == sample_rate) {
++		D_IF_exit(apvt->state);
++	}
++	apvt->state = NULL;
++
++	ast_debug(3, "Destroyed decoder (AMR -> %d) %p\n", sample_rate, apvt);
++}
++
++static struct ast_translator amrtolin = {
++	.name = "amrtolin",
++	.src_codec = {
++		.name = "amr",
++		.type = AST_MEDIA_TYPE_AUDIO,
++		.sample_rate = 8000,
++	},
++	.dst_codec = {
++		.name = "slin",
++		.type = AST_MEDIA_TYPE_AUDIO,
++		.sample_rate = 8000,
++	},
++	.format = "slin",
++	.newpvt = amrtolin_new,
++	.framein = amrtolin_framein,
++	.destroy = amrtolin_destroy,
++	.sample = amr_sample,
++	.desc_size = sizeof(struct amr_coder_pvt),
++	.buffer_samples = BUFFER_SAMPLES / 2,
++	/* actually: 50 * channels[6] * redundancy[5] * (mode7[31] + CRC[1] + FT[1] + CMR[1]) */
++	.buf_size = BUFFER_SAMPLES,
++};
++
++static struct ast_translator lintoamr = {
++	.name = "lintoamr",
++	.src_codec = {
++		.name = "slin",
++		.type = AST_MEDIA_TYPE_AUDIO,
++		.sample_rate = 8000,
++	},
++	.dst_codec = {
++		.name = "amr",
++		.type = AST_MEDIA_TYPE_AUDIO,
++		.sample_rate = 8000,
++	},
++	.format = "amr",
++	.newpvt = lintoamr_new,
++	.framein = lintoamr_framein,
++	.frameout = lintoamr_frameout,
++	.destroy = lintoamr_destroy,
++	.sample = slin8_sample,
++	.desc_size = sizeof(struct amr_coder_pvt),
++	.buffer_samples = BUFFER_SAMPLES / 2,
++	.buf_size = BUFFER_SAMPLES,
++};
++
++static struct ast_translator amrtolin16 = {
++	.name = "amrtolin16",
++	.src_codec = {
++		.name = "amrwb",
++		.type = AST_MEDIA_TYPE_AUDIO,
++		.sample_rate = 16000,
++	},
++	.dst_codec = {
++		.name = "slin",
++		.type = AST_MEDIA_TYPE_AUDIO,
++		.sample_rate = 16000,
++	},
++	.format = "slin16",
++	.newpvt = amrtolin_new,
++	.framein = amrtolin_framein,
++	.destroy = amrtolin_destroy,
++	.sample = amrwb_sample,
++	.desc_size = sizeof(struct amr_coder_pvt),
++	.buffer_samples = BUFFER_SAMPLES,
++	/* actually: 50 * channels[6] * redundancy[5] * (mode8[60] + CRC[1] + FT[1] + CMR[1]) */
++	.buf_size = BUFFER_SAMPLES * 2,
++};
++
++static struct ast_translator lin16toamr = {
++	.name = "lin16toamr",
++	.src_codec = {
++		.name = "slin",
++		.type = AST_MEDIA_TYPE_AUDIO,
++		.sample_rate = 16000,
++	},
++	.dst_codec = {
++		.name = "amrwb",
++		.type = AST_MEDIA_TYPE_AUDIO,
++		.sample_rate = 16000,
++	},
++	.format = "amrwb",
++	.newpvt = lintoamr_new,
++	.framein = lintoamr_framein,
++	.frameout = lintoamr_frameout,
++	.destroy = lintoamr_destroy,
++	.sample = slin16_sample,
++	.desc_size = sizeof(struct amr_coder_pvt),
++	.buffer_samples = BUFFER_SAMPLES,
++	.buf_size = BUFFER_SAMPLES * 2,
++};
++
++static int unload_module(void)
++{
++	int res;
++
++	res = ast_unregister_translator(&amrtolin);
++	res |= ast_unregister_translator(&lintoamr);
++	res |= ast_unregister_translator(&amrtolin16);
++	res |= ast_unregister_translator(&lin16toamr);
++
++	return res;
++}
++
++static int load_module(void)
++{
++	int res;
++
++	res = ast_register_translator(&amrtolin);
++	res |= ast_register_translator(&lintoamr);
++	res |= ast_register_translator(&amrtolin16);
++	res |= ast_register_translator(&lin16toamr);
++
++	if (res) {
++		unload_module();
++		return AST_MODULE_LOAD_FAILURE;
++	}
++
++	return AST_MODULE_LOAD_SUCCESS;
++}
++
++AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "AMR Coder/Decoder");
+--- /dev/null
++++ b/codecs/ex_amr.h
+@@ -0,0 +1,49 @@
++#include "asterisk/format_cache.h"      /* for ast_format_amr(wb) */
++#include "asterisk/frame.h"             /* for ast_frame, etc */
++
++static uint8_t ex_amr[] = {
++	0xf0, 0x6d, 0x47, 0x8c, 0xc3, 0x0d, 0x03, 0xec,
++	0xe2, 0x18, 0x3e, 0x28, 0x20, 0x80
++};
++
++static struct ast_frame *amr_sample(void)
++{
++	static struct ast_frame f = {
++		.frametype = AST_FRAME_VOICE,
++		.datalen = sizeof(ex_amr),
++		.samples = 160,
++		.mallocd = 0,
++		.offset = 0,
++		.src = __PRETTY_FUNCTION__,
++		.data.ptr = ex_amr,
++	};
++
++	f.subclass.format = ast_format_amr;
++
++	return &f;
++}
++
++static uint8_t ex_amrwb[] = {
++	0xf1, 0x5e, 0x51, 0x98, 0xc5, 0x64, 0xc7, 0xc5,
++	0x0c, 0x6c, 0x82, 0x19, 0x16, 0x03, 0xf0, 0x0a,
++	0x0b, 0x57, 0x53, 0x51, 0x7f, 0x97, 0x97, 0x79,
++	0x31, 0xdd, 0x73, 0x1b, 0x92, 0x54, 0xf5, 0x79,
++	0x9a,
++};
++
++static struct ast_frame *amrwb_sample(void)
++{
++	static struct ast_frame f = {
++		.frametype = AST_FRAME_VOICE,
++		.datalen = sizeof(ex_amrwb),
++		.samples = 320,
++		.mallocd = 0,
++		.offset = 0,
++		.src = __PRETTY_FUNCTION__,
++		.data.ptr = ex_amrwb,
++	};
++
++	f.subclass.format = ast_format_amrwb;
++
++	return &f;
++}
+--- a/configure.ac
++++ b/configure.ac
+@@ -400,6 +400,9 @@
+ # to make things easier for the users.
+ 
+ AST_EXT_LIB_SETUP([ALSA], [Advanced Linux Sound Architecture], [asound])
++AST_EXT_LIB_SETUP([AMR_NB], [AMR Audio Codec (Narrowband) Decoder/Encoder], [opencore-amrnb])
++AST_EXT_LIB_SETUP([AMR_WB_DECODER], [AMR-WB Audio Codec (Wideband) Decoder], [opencore-amrwb])
++AST_EXT_LIB_SETUP([AMR_WB_ENCODER], [AMR-WB Audio Codec (Wideband) Encoder], [vo-amrwbenc])
+ AST_EXT_LIB_SETUP([BFD], [Debug symbol decoding], [bfd])
+ 
+ # BKTR is used for backtrace support on platforms that do not
+@@ -1284,6 +1287,10 @@
+ 
+ AST_EXT_LIB_CHECK([ALSA], [asound], [snd_spcm_init], [alsa/asoundlib.h], [-lm -ldl])
+ 
++AST_EXT_LIB_CHECK([AMR_NB], [opencore-amrnb], [Encoder_Interface_init], [opencore-amrnb/interf_enc.h])
++AST_EXT_LIB_CHECK([AMR_WB_DECODER], [opencore-amrwb], [D_IF_init], [opencore-amrwb/dec_if.h])
++AST_EXT_LIB_CHECK([AMR_WB_ENCODER], [vo-amrwbenc], [E_IF_init], [vo-amrwbenc/enc_if.h])
++
+ AST_EXT_LIB_CHECK([BFD], [bfd], [bfd_openr], [bfd.h])
+ 
+ if test "${PBX_BFD}" = "0"; then
+--- /dev/null
++++ b/include/asterisk/amr.h
+@@ -0,0 +1,19 @@
++#ifndef _AST_FORMAT_AMR_H_
++#define _AST_FORMAT_AMR_H_
++
++struct amr_attr {
++	unsigned int octet_align;
++	unsigned int mode_set:9;
++	unsigned int mode_change_period;
++	unsigned int mode_change_capability;
++	unsigned int mode_change_neighbor;
++	unsigned int crc;
++	unsigned int robust_sorting;
++	unsigned int interleaving;
++	int max_red;
++	/* internal variables for transcoding module */
++	unsigned char mode_current; /* see amr_clone for default */
++	int vad;                    /* see amr_clone for default */
++};
++
++#endif /* _AST_FORMAT_AMR_H */
+--- a/include/asterisk/format_cache.h
++++ b/include/asterisk/format_cache.h
+@@ -209,6 +209,16 @@
+ extern struct ast_format *ast_format_opus;
+ 
+ /*!
++ * \brief Built-in cached amr format.
++ */
++extern struct ast_format *ast_format_amr;
++
++/*!
++ * \brief Built-in cached amrwb format.
++ */
++extern struct ast_format *ast_format_amrwb;
++
++/*!
+  * \brief Built-in cached t140 format.
+  */
+ extern struct ast_format *ast_format_t140;
+--- a/include/asterisk/format_compatibility.h
++++ b/include/asterisk/format_compatibility.h
+@@ -71,6 +71,10 @@
+ #define AST_FORMAT_SPEEX16 (1ULL << 33)
+ /*! Opus audio (8kHz, 16kHz, 24kHz, 48Khz) */
+ #define AST_FORMAT_OPUS (1ULL << 34)
++/*! AMR audio (8kHz) */
++#define AST_FORMAT_AMR (1ULL << 35)
++/*! AMR-WB audio (16kHz) */
++#define AST_FORMAT_AMR_WB (1ULL << 36)
+ /*! Raw testing-law data (G.711) */
+ #define AST_FORMAT_TESTLAW (1ULL << 47)
+ /*! H.261 Video */
+--- a/main/codec_builtin.c
++++ b/main/codec_builtin.c
+@@ -717,6 +717,54 @@
+ 	.samples_count = opus_samples,
+ };
+ 
++static int amr_samples(struct ast_frame *frame)
++{
++	return 160;
++}
++
++static int amr_length(unsigned int samples)
++{
++	ast_log(LOG_NOTICE, "untested; please report failure or success: %u\n", samples); return samples / 8;
++}
++
++static struct ast_codec amr = {
++	.name = "amr",
++	.description = "AMR",
++	.type = AST_MEDIA_TYPE_AUDIO,
++	.sample_rate = 8000,
++	.minimum_ms = 20,
++	.maximum_ms = 20,
++	.default_ms = 20,
++	.minimum_bytes = 0, /* no smooth */
++	.samples_count = amr_samples,
++	.get_length = amr_length,
++	.smooth = 0,
++};
++
++static int amrwb_samples(struct ast_frame *frame)
++{
++	return 320;
++}
++
++static int amrwb_length(unsigned int samples)
++{
++	ast_log(LOG_NOTICE, "untested; please report failure or success: %u\n", samples); return samples / 16;
++}
++
++static struct ast_codec amrwb = {
++	.name = "amrwb",
++	.description = "AMR-WB",
++	.type = AST_MEDIA_TYPE_AUDIO,
++	.sample_rate = 16000,
++	.minimum_ms = 20,
++	.maximum_ms = 20,
++	.default_ms = 20,
++	.minimum_bytes = 0, /* no smooth */
++	.samples_count = amrwb_samples,
++	.get_length = amrwb_length,
++	.smooth = 0,
++};
++
+ static struct ast_codec jpeg = {
+ 	.name = "jpeg",
+ 	.description = "JPEG image",
+@@ -837,6 +885,8 @@
+ 	res |= CODEC_REGISTER_AND_CACHE(testlaw);
+ 	res |= CODEC_REGISTER_AND_CACHE(g719);
+ 	res |= CODEC_REGISTER_AND_CACHE(opus);
++	res |= CODEC_REGISTER_AND_CACHE(amr);
++	res |= CODEC_REGISTER_AND_CACHE_NAMED("amrwb", amrwb);
+ 	res |= CODEC_REGISTER_AND_CACHE(jpeg);
+ 	res |= CODEC_REGISTER_AND_CACHE(png);
+ 	res |= CODEC_REGISTER_AND_CACHE(h261);
+--- a/main/format_cache.c
++++ b/main/format_cache.c
+@@ -218,6 +218,16 @@
+ struct ast_format *ast_format_opus;
+ 
+ /*!
++ * \brief Built-in cached amr format.
++ */
++struct ast_format *ast_format_amr;
++
++/*!
++ * \brief Built-in cached amrwb format.
++ */
++struct ast_format *ast_format_amrwb;
++
++/*!
+  * \brief Built-in cached t140 format.
+  */
+ struct ast_format *ast_format_t140;
+@@ -320,6 +330,8 @@
+ 	ao2_replace(ast_format_testlaw, NULL);
+ 	ao2_replace(ast_format_g719, NULL);
+ 	ao2_replace(ast_format_opus, NULL);
++	ao2_replace(ast_format_amr, NULL);
++	ao2_replace(ast_format_amrwb, NULL);
+ 	ao2_replace(ast_format_jpeg, NULL);
+ 	ao2_replace(ast_format_png, NULL);
+ 	ao2_replace(ast_format_h261, NULL);
+@@ -404,6 +416,10 @@
+ 		ao2_replace(ast_format_g719, format);
+ 	} else if (!strcmp(name, "opus")) {
+ 		ao2_replace(ast_format_opus, format);
++	} else if (!strcmp(name, "amr")) {
++		ao2_replace(ast_format_amr, format);
++	} else if (!strcmp(name, "amrwb")) {
++		ao2_replace(ast_format_amrwb, format);
+ 	} else if (!strcmp(name, "jpeg")) {
+ 		ao2_replace(ast_format_jpeg, format);
+ 	} else if (!strcmp(name, "png")) {
+--- a/main/format_compatibility.c
++++ b/main/format_compatibility.c
+@@ -78,6 +78,10 @@
+ 		return AST_FORMAT_SPEEX16;
+ 	} else if (ast_format_cmp(format, ast_format_opus) == AST_FORMAT_CMP_EQUAL) {
+ 		return AST_FORMAT_OPUS;
++	} else if (ast_format_cmp(format, ast_format_amr) == AST_FORMAT_CMP_EQUAL) {
++		return AST_FORMAT_AMR;
++	} else if (ast_format_cmp(format, ast_format_amrwb) == AST_FORMAT_CMP_EQUAL) {
++		return AST_FORMAT_AMR_WB;
+ 	} else if (ast_format_cmp(format, ast_format_testlaw) == AST_FORMAT_CMP_EQUAL) {
+ 		return AST_FORMAT_TESTLAW;
+ 	} else if (ast_format_cmp(format, ast_format_h261) == AST_FORMAT_CMP_EQUAL) {
+@@ -145,6 +149,10 @@
+ 		return AST_FORMAT_SPEEX16;
+ 	} else if (codec->id == ast_format_get_codec_id(ast_format_opus)) {
+ 		return AST_FORMAT_OPUS;
++	} else if (codec->id == ast_format_get_codec_id(ast_format_amr)) {
++		return AST_FORMAT_AMR;
++	} else if (codec->id == ast_format_get_codec_id(ast_format_amrwb)) {
++		return AST_FORMAT_AMR_WB;
+ 	} else if (codec->id == ast_format_get_codec_id(ast_format_testlaw)) {
+ 		return AST_FORMAT_TESTLAW;
+ 	} else if (codec->id == ast_format_get_codec_id(ast_format_h261)) {
+@@ -232,6 +240,12 @@
+ 	/*! Opus audio (8kHz, 16kHz, 24kHz, 48Khz) */
+ 	case AST_FORMAT_OPUS:
+ 		return ast_format_opus;
++	/*! AMR audio (8kHz) */
++	case AST_FORMAT_AMR:
++		return ast_format_amr;
++	/*! AMR-WB audio (16kHz) */
++	case AST_FORMAT_AMR_WB:
++		return ast_format_amrwb;
+ 	/*! Raw mu-law data (G.711) */
+ 	case AST_FORMAT_TESTLAW:
+ 		return ast_format_testlaw;
+--- a/main/rtp_engine.c
++++ b/main/rtp_engine.c
+@@ -2183,6 +2183,8 @@
+ 	/* Opus and VP8 */
+ 	set_next_mime_type(ast_format_opus, 0,  "audio", "opus", 48000);
+ 	set_next_mime_type(ast_format_vp8, 0,  "video", "VP8", 90000);
++	set_next_mime_type(ast_format_amr, 0,  "audio", "AMR", 8000);
++	set_next_mime_type(ast_format_amrwb, 0,  "audio", "AMR-WB", 16000);
+ 
+ 	/* Define the static rtp payload mappings */
+ 	add_static_payload(0, ast_format_ulaw, 0);
+@@ -2227,6 +2229,8 @@
+ 	/* Opus and VP8 */
+ 	add_static_payload(100, ast_format_vp8, 0);
+ 	add_static_payload(107, ast_format_opus, 0);
++	add_static_payload(108, ast_format_amr, 0);
++	add_static_payload(109, ast_format_amrwb, 0);
+ 
+ 	return 0;
+ }
+--- a/makeopts.in
++++ b/makeopts.in
+@@ -119,6 +119,13 @@
+ ALSA_INCLUDE=@ALSA_INCLUDE@
+ ALSA_LIB=@ALSA_LIB@
+ 
++AMR_NB_INCLUDE=@AMR_NB_INCLUDE@
++AMR_NB_LIB=@AMR_NB_LIB@
++AMR_WB_DECODER_INCLUDE=@AMR_WB_DECODER_INCLUDE@
++AMR_WB_DECODER_LIB=@AMR_WB_DECODER_LIB@
++AMR_WB_ENCODER_INCLUDE=@AMR_WB_ENCODER_INCLUDE@
++AMR_WB_ENCODER_LIB=@AMR_WB_ENCODER_LIB@
++
+ BFD_INCLUDE=@BFD_INCLUDE@
+ BFD_LIB=@BFD_LIB@
+ 
+--- /dev/null
++++ b/res/res_format_attr_amr.c
+@@ -0,0 +1,488 @@
++#include "asterisk.h"
++
++/* version 3.0, compatiblity overview as of October 2015 */
++/* based on res/res_format_attr_silk.c */
++
++#include <ctype.h>                      /* for tolower */
++#include <math.h>                       /* for log10, floor */
++
++#include "asterisk/module.h"
++#include "asterisk/format.h"
++#include "asterisk/astobj2.h"           /* for ao2_bump */
++#include "asterisk/format_cache.h"      /* for ast_format_amr(wb) */
++#include "asterisk/logger.h"            /* for ast_debug, ast_log, etc */
++#include "asterisk/strings.h"           /* for ast_str_append */
++#include "asterisk/utils.h"             /* for MAX, ast_calloc, ast_free, etc */
++
++#include "asterisk/amr.h"
++
++/* Asterisk internal defaults; can differ from RFC defaults */ 
++static struct amr_attr default_amr_attr = {
++	.octet_align            =  0, /* bandwidth efficient */
++	.mode_set               =  0, /* all modes           */
++	.mode_change_period     =  0, /* not specified       */
++	.mode_change_capability =  0, /* not supported       */
++	.mode_change_neighbor   =  0, /* change to any       */
++	.crc                    =  0, /* off                 */
++	.robust_sorting         =  0, /* off                 */
++	.interleaving           =  0, /* off                 */
++	.max_red                = -1, /* no redundancy limit */
++};
++
++static void amr_destroy(struct ast_format *format)
++{
++	struct amr_attr *attr = ast_format_get_attribute_data(format);
++	
++	ast_free(attr);
++}
++
++static int amr_clone(const struct ast_format *src, struct ast_format *dst)
++{
++	struct amr_attr *original = ast_format_get_attribute_data(src);
++	struct amr_attr *attr = ast_malloc(sizeof(*attr));
++
++	if (!attr) {
++		return -1;
++	}
++
++	if (original) {
++		*attr = *original;
++	} else {
++		*attr = default_amr_attr;
++		/* internal variables for transcoding module */
++		if (16000 == ast_format_get_sample_rate(src)) {
++			attr->mode_current = 8;
++			attr->vad = 0;
++		} else {
++			attr->mode_current = 7;
++			attr->vad = 1;
++		}
++	}
++
++	ast_format_set_attribute_data(dst, attr);
++
++	return 0;
++}
++
++static struct ast_format *amr_parse_sdp_fmtp(const struct ast_format *format, const char *attrib)
++{
++	struct ast_format *cloned;
++	struct amr_attr *attr;
++	unsigned int val;
++	char *attributes;
++	char *tmp;
++	const unsigned int size = 9; /* same as bit-field definition of mode_set */
++	int v[size];
++	/* init each slot as 'not specified' */
++	for (val = 0; val < size; val = val + 1) {
++		v[val] = -1;
++	}
++
++	cloned = ast_format_clone(format);
++	if (!cloned) {
++		return NULL;
++	}
++	attr = ast_format_get_attribute_data(cloned);
++
++	/* lower-case everything, so we are case-insensitive */
++	/* no implementation is known which is affected by this */
++	attributes = ast_strdupa(attrib);
++	for (tmp = attributes; *tmp; ++tmp) {
++		*tmp = tolower(*tmp);
++	} /* based on channels/chan_sip.c:process_a_sdp_image() */
++
++	attr->octet_align = 0;
++	tmp = strstr(attributes, "octet-align=");
++	if (tmp) {
++		if (sscanf(tmp, "octet-align=%30u", &val) == 1) {
++			attr->octet_align = val;
++		}
++	}
++
++	attr->mode_set = 0;
++	tmp = strstr(attributes, "mode-set=");
++	if (tmp) {
++		if (sscanf(tmp, "mode-set=%30u,%30u,%30u,%30u,%30u,%30u,%30u,%30u,%30u",
++				   &v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7], &v[8]) > 0) {
++			for (val = 0; val < size; val = val + 1) {
++				if (0 <= v[val] && v[val] < size) {
++					attr->mode_set = (attr->mode_set | (1 << v[val]));
++					attr->mode_current = v[val];
++				}
++			}
++		}
++	}
++
++	attr->mode_change_capability = 0;
++	tmp = strstr(attributes, "mode-change-capability=");
++	if (tmp) {
++		if (sscanf(tmp, "mode-change-capability=%30u", &val) == 1) {
++			attr->mode_change_capability = val;
++		}
++	}
++
++	attr->mode_change_period = 0;
++	tmp = strstr(attributes, "mode-change-period=");
++	if (tmp) {
++		if (sscanf(tmp, "mode-change-period=%30u", &val) == 1) {
++			attr->mode_change_period = val;
++		}
++	}
++
++	attr->mode_change_neighbor = 0;
++	tmp = strstr(attributes, "mode-change-neighbor=");
++	if (tmp) {
++		if (sscanf(tmp, "mode-change-neighbor=%30u", &val) == 1) {
++			attr->mode_change_neighbor = val;
++		}
++	}
++
++	attr->crc = 0;
++	tmp = strstr(attributes, "crc=");
++	if (tmp) {
++		if (sscanf(tmp, "crc=%30u", &val) == 1) {
++			attr->crc = val;
++			if (attr->crc) {
++				attr->octet_align = 1;
++			}
++		}
++	}
++
++	attr->robust_sorting = 0;
++	tmp = strstr(attributes, "robust-sorting=");
++	if (tmp) {
++		if (sscanf(tmp, "robust-sorting=%30u", &val) == 1) {
++			attr->robust_sorting = val;
++			if (attr->robust_sorting) {
++				attr->octet_align = 1;
++			}
++		}
++	}
++
++	attr->interleaving = 0;
++	tmp = strstr(attributes, "interleaving");
++	if (tmp) {
++		attr->interleaving = 1;
++		attr->octet_align = 1;
++	}
++
++	attr->max_red = -1;
++	tmp = strstr(attributes, "max-red=");
++	if (tmp) {
++		if (sscanf(tmp, "max-red=%30u", &val) == 1) {
++			attr->max_red = val;
++		}
++	}
++
++	return cloned;
++}
++
++static void amr_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
++{
++	int appended = 0;
++	int listed = 0;
++	struct amr_attr *attr = ast_format_get_attribute_data(format);
++
++	if (!attr) {
++		attr = &default_amr_attr;
++	}
++
++	if (0 != attr->octet_align) {
++		if (0 == appended) {
++			ast_str_append(str, 0, "a=fmtp:%d ", payload);
++		} else {
++			ast_str_append(str, 0, ";");
++		}
++		ast_str_append(str, 0, "octet-align=%d", attr->octet_align);
++		appended = appended + 1;
++	}
++	if (0 != attr->mode_set)
++	{
++		if (0 == appended) {
++			ast_str_append(str, 0, "a=fmtp:%d ", payload);
++		} else {
++			ast_str_append(str, 0, ";");
++		}
++		ast_str_append(str, 0, "mode-set=");
++		if (attr->mode_set & 0x01) {
++			if (0 == listed) {
++				ast_str_append(str, 0, "0");
++			} else {
++				ast_str_append(str, 0, ",0");
++			}
++			listed = listed + 1;
++		}
++		if (attr->mode_set & 0x02) {
++			if (0 == listed) {
++				ast_str_append(str, 0, "1");
++			} else {
++				ast_str_append(str, 0, ",1");
++			}
++			listed = listed + 1;
++		}
++		if (attr->mode_set & 0x04) {
++			if (0 == listed) {
++				ast_str_append(str, 0, "2");
++			} else {
++				ast_str_append(str, 0, ",2");
++			}
++			listed = listed + 1;
++		}
++		if (attr->mode_set & 0x08) {
++			if (0 == listed) {
++				ast_str_append(str, 0, "3");
++			} else {
++				ast_str_append(str, 0, ",3");
++			}
++			listed = listed + 1;
++		}
++		if (attr->mode_set & 0x10) {
++			if (0 == listed) {
++				ast_str_append(str, 0, "4");
++			} else {
++				ast_str_append(str, 0, ",4");
++			}
++			listed = listed + 1;
++		}
++		if (attr->mode_set & 0x20) {
++			if (0 == listed) {
++				ast_str_append(str, 0, "5");
++			} else {
++				ast_str_append(str, 0, ",5");
++			}
++			listed = listed + 1;
++		}
++		if (attr->mode_set & 0x40) {
++			if (0 == listed) {
++				ast_str_append(str, 0, "6");
++			} else {
++				ast_str_append(str, 0, ",6");
++			}
++			listed = listed + 1;
++		}
++		if (attr->mode_set & 0x80) {
++			if (0 == listed) {
++				ast_str_append(str, 0, "7");
++			} else {
++				ast_str_append(str, 0, ",7");
++			}
++			listed = listed + 1;
++		}
++		if (attr->mode_set & 0x100) {
++			if (0 == listed) {
++				ast_str_append(str, 0, "8");
++			} else {
++				ast_str_append(str, 0, ",8");
++			}
++			listed = listed + 1;
++		}
++		appended = appended + 1;
++	}
++	if (0 != attr->mode_change_capability) {
++		if (0 == appended) {
++			ast_str_append(str, 0, "a=fmtp:%d ", payload);
++		} else {
++			ast_str_append(str, 0, ";");
++		}
++		ast_str_append(str, 0, "mode-change-capability=%d", attr->mode_change_capability);
++		appended = appended + 1;
++	}
++	if (0 != attr->mode_change_period) {
++		if (0 == appended) {
++			ast_str_append(str, 0, "a=fmtp:%d ", payload);
++		} else {
++			ast_str_append(str, 0, ";");
++		}
++		ast_str_append(str, 0, "mode-change-period=%d", attr->mode_change_period);
++		appended = appended + 1;
++	}
++	if (0 != attr->mode_change_neighbor) {
++		if (0 == appended) {
++			ast_str_append(str, 0, "a=fmtp:%d ", payload);
++		} else {
++			ast_str_append(str, 0, ";");
++		}
++		ast_str_append(str, 0, "mode-change-neighbor=%d", attr->mode_change_neighbor);
++		appended = appended + 1;
++	}
++	if (0 != attr->crc) {
++		if (0 == appended) {
++			ast_str_append(str, 0, "a=fmtp:%d ", payload);
++		} else {
++			ast_str_append(str, 0, ";");
++		}
++		ast_str_append(str, 0, "crc=%d", attr->crc);
++		appended = appended + 1;
++	}
++	if (0 != attr->robust_sorting) {
++		if (0 == appended) {
++			ast_str_append(str, 0, "a=fmtp:%d ", payload);
++		} else {
++			ast_str_append(str, 0, ";");
++		}
++		ast_str_append(str, 0, "robust-sorting=%d", attr->robust_sorting);
++		appended = appended + 1;
++	}
++	if (0 != attr->interleaving) {
++		if (0 == appended) {
++			ast_str_append(str, 0, "a=fmtp:%d ", payload);
++		} else {
++			ast_str_append(str, 0, ";");
++		}
++		ast_str_append(str, 0, "interleaving");
++		appended = appended + 1;
++	}
++	if (0 <= attr->max_red) {
++		if (0 == appended) {
++			ast_str_append(str, 0, "a=fmtp:%d ", payload);
++		} else {
++			ast_str_append(str, 0, ";");
++		}
++		ast_str_append(str, 0, "max-red=%d", attr->max_red);
++		appended = appended + 1;
++	}
++	if (0 != appended) {
++		ast_str_append(str, 0, "\r\n");
++	}
++	/* 
++	 * AMR-WB, GSM gateway compatible setting:
++	 * ast_str_append(str, 0, "a=fmtp:%d mode-set=0,1,2; mode-change-period=2; mode-change-neighbor=1\r\n", payload);
++	 * less than 25kb/s in SIP RTP which equals iLBC:30 in data traffic, and AMR-WB is a wide-band codec!
++	 */
++}
++
++/*
++ * Octet Alignment
++ *
++ * AMR and AMR-WB support 'octet-align=1' which does not stuff the
++ * header bits over octet borders and which is readable in
++ * Wireshark. This parameter is negotiated via SDP. Belledonne and
++ * CounterPath do not offer AMR, only AMR-WB. Android offers only AMR.
++ * 
++ * Nokia Symbian/S60: configurable via user-interface; default: off
++ * Nokia Series 40: configurable via OMA Client Provisioning (OMA CP); default: off
++ * Nokia Asha Software Platform: configurable via OMA CP; default: on
++ * CounterPath Bria (iOS): no setting known; off
++ * CounterPath Bria (Android): see iOS
++ * CounterPath Bria (BlackBerry 10): AMR/AMR-WB not available, as of version 1.3.1
++ *    Bug: since version 3.4, fmtp is missing = negotiating octet-aligned mode fails
++ * BeeHD (iOS): no setting known; on
++ *    Bug: Nokia Symbian/S60 calls BeeHD, no audio in BeeHD; AMR-WB works
++ * Belledonne Linphone 1.0.2 (Windows Phone 8): no setting known; on
++ *    Bug: ignores octet-align=0 in fmtp = distorted audio
++ * CSipSimple 1.02r2330 (Android): no setting known; off
++ *    Bug: CSipSimple calls a Nokia Symbian/S60, no audio in Nokia; AMR works
++ *    Bug: AMR-WB ignores octet-align=1 in fmtp = distorted audio; AMR works
++ *         fixed upstream with <https://trac.pjsip.org/repos/changeset/5122>
++ * Google Android 5: no setting known; off
++ * Join (iOS): no setting known; on
++ *    Bug: AMR-WB ignores octet-align=0 in fmtp = distorted audio; AMR works
++ * PortGo (iOS): no setting known; off (is able to change alignment through negotiation)
++ */
++static struct ast_format *amr_getjoint(const struct ast_format *format1, const struct ast_format *format2)
++{
++	struct amr_attr *attr1 = ast_format_get_attribute_data(format1);
++	struct amr_attr *attr2 = ast_format_get_attribute_data(format2);
++	struct amr_attr *attr_res;
++	struct ast_format *jointformat = NULL;
++
++	if (!attr1) {
++		attr1 = &default_amr_attr;
++	}
++
++	if (!attr2) {
++		attr2 = &default_amr_attr;
++	}
++
++	if (format1 == ast_format_amrwb || format1 == ast_format_amr) {
++		jointformat = (struct ast_format *) format2;
++	}
++	if (format2 == ast_format_amrwb || format2 == ast_format_amr) {
++		jointformat = (struct ast_format *) format1;
++	}
++	if (format1 == format2) {
++		if (!jointformat) {
++			ast_debug(3, "Both formats were not cached but the same.\n");
++			jointformat = (struct ast_format *) format1;
++		} else {
++			ast_debug(3, "Both formats were cached.\n");
++			jointformat = NULL;
++		}
++	}
++	if (!jointformat) {
++		ast_debug(3, "Which pointer shall be returned? Let us create a new one!\n");
++		jointformat = ast_format_clone(format1);
++	} else {
++		ao2_bump(jointformat);
++	}
++	if (!jointformat) {
++		return NULL;
++	}
++	attr_res = ast_format_get_attribute_data(jointformat);
++	
++	if (0 == attr1->mode_set && 0 == attr2->mode_set) {
++		attr_res->mode_set = 0; /* both allowed all = 0 */
++	} else if (0 != attr1->mode_set && 0 == attr2->mode_set) {
++		attr_res->mode_set = attr1->mode_set; /* attr2 allowed all */
++	} else if (0 == attr1->mode_set && 0 != attr2->mode_set) {
++		attr_res->mode_set = attr2->mode_set; /* attr1 allowed all */
++	} else { /* both parties restrict, let us check if they match */
++		attr_res->mode_set = (attr1->mode_set & attr2->mode_set);
++		if (0 == attr_res->mode_set) {
++			/* not expected because everyone supports 0,1,2 */
++			ast_log(LOG_WARNING, "mode-set did not match\n");
++			return NULL;
++		}
++	}
++	attr_res->mode_change_period = MAX(attr1->mode_change_period, attr2->mode_change_period);
++	attr_res->mode_change_capability = MAX(attr1->mode_change_capability, attr2->mode_change_capability);
++	attr_res->mode_change_neighbor = MAX(attr1->mode_change_neighbor, attr2->mode_change_neighbor);
++	attr_res->crc = MAX(attr1->crc, attr2->crc);
++	attr_res->robust_sorting = MAX(attr1->robust_sorting, attr2->robust_sorting);
++	attr_res->interleaving = MAX(attr1->interleaving, attr2->interleaving);
++	attr_res->max_red = MAX(attr1->max_red, attr2->max_red);
++	
++	/* internal variables for transcoding module */
++	/* starting point; later, changes with a change-mode request (CMR) */
++	if (0 < attr_res->mode_set) {
++		attr_res->mode_current = floor(log10(attr_res->mode_set) / log10(2));
++	}
++	attr_res->vad = MAX(attr1->vad, attr2->vad);
++
++	return jointformat;
++}
++
++static struct ast_format_interface amr_interface = {
++	.format_destroy = amr_destroy,
++	.format_clone = amr_clone,
++	.format_cmp = NULL,
++	.format_get_joint = amr_getjoint,
++	.format_attribute_set = NULL,
++	.format_parse_sdp_fmtp = amr_parse_sdp_fmtp,
++	.format_generate_sdp_fmtp = amr_generate_sdp_fmtp,
++};
++
++static int load_module(void)
++{
++	if (ast_format_interface_register("amr", &amr_interface)) {
++		return AST_MODULE_LOAD_DECLINE;
++	}
++
++	if (ast_format_interface_register("amrwb", &amr_interface)) {
++		return AST_MODULE_LOAD_DECLINE;
++	}
++
++	return AST_MODULE_LOAD_SUCCESS;
++}
++
++static int unload_module(void)
++{
++	return 0;
++}
++
++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
++	"AMR Format Attribute Module",
++	.load = load_module,
++	.unload = unload_module,
++	.load_pri = AST_MODPRI_CHANNEL_DEPEND,
++);
diff --git a/debian/patches/series b/debian/patches/series
index e1bec45..605258d 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -28,3 +28,4 @@ configure-osarch
 
 opus.patch
 vp8.patch
+amr.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/asterisk.git



More information about the Pkg-voip-commits mailing list