[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