Add AMR codec, for supporting EFR transcoding
authorAndreas Eversberg <jolly@eversberg.eu>
Thu, 14 Mar 2013 08:58:31 +0000 (09:58 +0100)
committerAndreas Eversberg <jolly@eversberg.eu>
Thu, 14 Mar 2013 08:58:31 +0000 (09:58 +0100)
The AMR codec is added, but at this point only EFR payload is
supported.

Makefile.am
configure.ac
gsm.cpp
gsm.h
gsm_audio.c
gsm_audio.h
gsm_ms.cpp

index e35f6f0..11eeb2c 100644 (file)
@@ -69,6 +69,14 @@ SUBDIRS += libgsmfr
 
 #endif
 
 
 #endif
 
+if ENABLE_GSMAMR
+
+GSM_INCLUDE += -DWITH_GSMAMR
+
+GSM_LIB += -lopencore-amrnb
+
+endif
+
 GSM_SOURCE += gsm_audio.c gsm.cpp
 
 if ENABLE_GSM_BS
 GSM_SOURCE += gsm_audio.c gsm.cpp
 
 if ENABLE_GSM_BS
@@ -86,6 +94,7 @@ GSM_INCLUDE += -DWITH_GSM_MS
 GSM_SOURCE += gsm_ms.cpp
 
 endif
 GSM_SOURCE += gsm_ms.cpp
 
 endif
+
 endif
 
 
 endif
 
 
index 8a3cba3..0050c15 100644 (file)
@@ -183,6 +183,14 @@ AM_CONDITIONAL(ENABLE_GSM_MS, test "x$with_gsm_ms" == "xyes" )
 
 AM_CONDITIONAL(ENABLE_GSM, test "x$with_gsm_bs" == "xyes" -o "x$with_gsm_ms" == "xyes")
 
 
 AM_CONDITIONAL(ENABLE_GSM, test "x$with_gsm_bs" == "xyes" -o "x$with_gsm_ms" == "xyes")
 
+# check for opencore-amrnb for AMR and EFR decoding
+found_opencore_amrnb=yes
+PKG_CHECK_MODULES(OPENCORE_AMRNB, opencore-amrnb >= 0.1.0, , found_opencore_amrnb=no)
+AM_CONDITIONAL(ENABLE_GSMAMR, test "$found_opencore_amrnb" = "yes")
+if test "$found_opencore_amrnb" = yes; then
+       AC_DEFINE(HAVE_OPENCORE_AMRNB, 1, [Define to 1 if OpenCore AMR-NB library is available])
+fi
+
 # check for ss5
 AC_ARG_WITH([ss5],
        [AS_HELP_STRING([--with-ss5],
 # check for ss5
 AC_ARG_WITH([ss5],
        [AS_HELP_STRING([--with-ss5],
@@ -247,6 +255,7 @@ AC_OUTPUT
 AS_IF([test "x$with_misdn" == xyes],[AC_MSG_NOTICE( Compiled with mISDN support )],[AC_MSG_NOTICE( Not compiled with mISDN support)])
 AS_IF([test "x$with_gsm_bs" == xyes],[AC_MSG_NOTICE( Compiled with GSM network side support )],[AC_MSG_NOTICE( Not compiled with GSM network side support)])
 AS_IF([test "x$with_gsm_ms" == xyes],[AC_MSG_NOTICE( Compiled with GSM mobile side support )],[AC_MSG_NOTICE( Not compiled with GSM mobile side support)])
 AS_IF([test "x$with_misdn" == xyes],[AC_MSG_NOTICE( Compiled with mISDN support )],[AC_MSG_NOTICE( Not compiled with mISDN support)])
 AS_IF([test "x$with_gsm_bs" == xyes],[AC_MSG_NOTICE( Compiled with GSM network side support )],[AC_MSG_NOTICE( Not compiled with GSM network side support)])
 AS_IF([test "x$with_gsm_ms" == xyes],[AC_MSG_NOTICE( Compiled with GSM mobile side support )],[AC_MSG_NOTICE( Not compiled with GSM mobile side support)])
+AS_IF([test "x$found_opencore_amrnb" == xyes],[AC_MSG_NOTICE( Compiled with GSM AMR codec support )],[AC_MSG_NOTICE( Not compiled with GSM AMR codec support)])
 AS_IF([test "x$with_asterisk" == xyes],[AC_MSG_NOTICE( Compiled with Asterisk channel driver support )],[AC_MSG_NOTICE( Not compiled with Asterisk channel driver support)])
 AS_IF([test "x$with_ss5" == xyes],[AC_MSG_NOTICE( Compiled with CCITT No.5 support )],[AC_MSG_NOTICE( Not compiled with CCITT No.5 support)])
 AS_IF([test "x$with_sip" == xyes],[AC_MSG_NOTICE( Compiled with SIP support )],[AC_MSG_NOTICE( Not compiled with SIP support)])
 AS_IF([test "x$with_asterisk" == xyes],[AC_MSG_NOTICE( Compiled with Asterisk channel driver support )],[AC_MSG_NOTICE( Not compiled with Asterisk channel driver support)])
 AS_IF([test "x$with_ss5" == xyes],[AC_MSG_NOTICE( Compiled with CCITT No.5 support )],[AC_MSG_NOTICE( Not compiled with CCITT No.5 support)])
 AS_IF([test "x$with_sip" == xyes],[AC_MSG_NOTICE( Compiled with SIP support )],[AC_MSG_NOTICE( Not compiled with SIP support)])
diff --git a/gsm.cpp b/gsm.cpp
index b6372f5..7502eed 100644 (file)
--- a/gsm.cpp
+++ b/gsm.cpp
@@ -194,6 +194,14 @@ Pgsm::Pgsm(int type, char *portname, struct port_settings *settings, struct inte
                trigger_work(&p_g_delete);
        }
 #endif
                trigger_work(&p_g_delete);
        }
 #endif
+#ifdef WITH_GSMAMR
+       p_g_amr_decoder = gsm_amr_create();
+       p_g_amr_encoder = gsm_amr_create();
+       if (!p_g_amr_encoder || !p_g_amr_decoder) {
+               PERROR("Failed to create GSM AMR codec instance\n");
+               trigger_work(&p_g_delete);
+       }
+#endif
        p_g_rxpos = 0;
        p_g_tch_connected = 0;
        p_g_media_type = 0;
        p_g_rxpos = 0;
        p_g_tch_connected = 0;
        p_g_media_type = 0;
@@ -225,6 +233,13 @@ Pgsm::~Pgsm()
        if (p_g_fr_decoder)
                gsm_fr_destroy(p_g_fr_decoder);
 //#endif
        if (p_g_fr_decoder)
                gsm_fr_destroy(p_g_fr_decoder);
 //#endif
+#ifdef WITH_GSMAMR
+       /* close codec */
+       if (p_g_amr_encoder)
+               gsm_amr_destroy(p_g_amr_encoder);
+       if (p_g_amr_decoder)
+               gsm_amr_destroy(p_g_amr_decoder);
+#endif
 }
 
 
 }
 
 
@@ -240,6 +255,14 @@ void Pgsm::frame_receive(void *arg)
 
        switch (frame->msg_type) {
        case GSM_TCHF_FRAME:
 
        switch (frame->msg_type) {
        case GSM_TCHF_FRAME:
+               if (p_g_media_type != MEDIA_TYPE_GSM) {
+                       PERROR("FR frame, but current media type mismatches.\n");
+                       return;
+               }
+               if (!p_g_fr_decoder) {
+                       PERROR("FR frame, but decoder not created.\n");
+                       return;
+               }
                if ((frame->data[0]>>4) != 0xd) {
                        PDEBUG(DEBUG_GSM, "received GSM frame with wrong magig 0x%x\n", frame->data[0]>>4);
                        goto bfi;
                if ((frame->data[0]>>4) != 0xd) {
                        PDEBUG(DEBUG_GSM, "received GSM frame with wrong magig 0x%x\n", frame->data[0]>>4);
                        goto bfi;
@@ -252,6 +275,25 @@ void Pgsm::frame_receive(void *arg)
                }
 #endif
                break;
                }
 #endif
                break;
+       case GSM_TCHF_FRAME_EFR:
+               if (p_g_media_type != MEDIA_TYPE_GSM_EFR) {
+                       PERROR("EFR frame, but current media type mismatches.\n");
+                       return;
+               }
+               if (!p_g_amr_decoder) {
+                       PERROR("EFR frame, but decoder not created.\n");
+                       return;
+               }
+               if ((frame->data[0]>>4) != 0xc)
+                       goto bfi;
+#ifdef WITH_GSMAMR
+               /* decode */
+               gsm_efr_decode(p_g_amr_decoder, frame->data, p_g_samples);
+               for (i = 0; i < 160; i++) {
+                       data[i] = audio_s16_to_law[p_g_samples[i] & 0xffff];
+               }
+#endif
+               break;
        case GSM_BAD_FRAME:
        default:
 bfi:
        case GSM_BAD_FRAME:
        default:
 bfi:
@@ -317,28 +359,47 @@ int Pgsm::audio_send(unsigned char *data, int len)
        /* write to rx buffer */
        while(len--) {
                p_g_rxdata[p_g_rxpos++] = audio_law_to_s32[*data++];
        /* write to rx buffer */
        while(len--) {
                p_g_rxdata[p_g_rxpos++] = audio_law_to_s32[*data++];
-               if (p_g_rxpos == 160) {
-                       p_g_rxpos = 0;
-
+               if (p_g_rxpos != 160)
+                       continue;
+               p_g_rxpos = 0;
+
+               switch (p_g_media_type) {
+               case MEDIA_TYPE_GSM:
+                       if (!p_g_fr_encoder) {
+                               PERROR("FR frame, but encoder not created.\n");
+                               break;
+                       }
 #ifdef WITH_GSMFR
                        /* encode data */
                        gsm_fr_encode(p_g_fr_encoder, p_g_rxdata, frame);
 #ifdef WITH_GSMFR
                        /* encode data */
                        gsm_fr_encode(p_g_fr_encoder, p_g_rxdata, frame);
-                       frame_send(frame);
+                       frame_send(frame, 33, GSM_TCHF_FRAME);
+#endif
+                       break;
+               case MEDIA_TYPE_GSM_EFR:
+                       if (!p_g_amr_encoder) {
+                               PERROR("EFR frame, but encoder not created.\n");
+                               break;
+                       }
+#ifdef WITH_GSMAMR
+                       /* encode data */
+                       gsm_efr_encode(p_g_amr_encoder, p_g_rxdata, frame);
+                       frame_send(frame, 31, GSM_TCHF_FRAME_EFR);
 #endif
 #endif
+                       break;
                }
        }
 
        return 0;
 }
 
                }
        }
 
        return 0;
 }
 
-void Pgsm::frame_send(void *_frame)
+void Pgsm::frame_send(void *_frame, int len, int msg_type)
 {
 {
-       unsigned char buffer[sizeof(struct gsm_data_frame) + 33];
+       unsigned char buffer[sizeof(struct gsm_data_frame) + len];
        struct gsm_data_frame *frame = (struct gsm_data_frame *)buffer;
        
        struct gsm_data_frame *frame = (struct gsm_data_frame *)buffer;
        
-       frame->msg_type = GSM_TCHF_FRAME;
+       frame->msg_type = msg_type;
        frame->callref = p_g_callref;
        frame->callref = p_g_callref;
-       memcpy(frame->data, _frame, 33);
+       memcpy(frame->data, _frame, len);
 
        if (p_g_lcr_gsm) {
                mncc_send(p_g_lcr_gsm, frame->msg_type, frame);
 
        if (p_g_lcr_gsm) {
                mncc_send(p_g_lcr_gsm, frame->msg_type, frame);
@@ -391,7 +452,7 @@ void Pgsm::modify_lchan(int media_type)
 {
        struct gsm_mncc *mode;
 
 {
        struct gsm_mncc *mode;
 
-       /* already modified to that payload type */
+       /* already modified to that media type */
        if (p_g_media_type == media_type)
                return;
 
        if (p_g_media_type == media_type)
                return;
 
@@ -438,6 +499,9 @@ void Pgsm::call_proc_ind(unsigned int msg_type, unsigned int callref, struct gsm
                send_and_free_mncc(p_g_lcr_gsm, frame->msg_type, frame);
                p_g_tch_connected = 1;
        }
                send_and_free_mncc(p_g_lcr_gsm, frame->msg_type, frame);
                p_g_tch_connected = 1;
        }
+
+       /* modify to GSM FR (this is GSM user side only, so there is FR supported only) */
+       modify_lchan(MEDIA_TYPE_GSM);
 }
 
 /* ALERTING INDICATION */
 }
 
 /* ALERTING INDICATION */
@@ -461,6 +525,11 @@ void Pgsm::alert_ind(unsigned int msg_type, unsigned int callref, struct gsm_mnc
                send_and_free_mncc(p_g_lcr_gsm, frame->msg_type, frame);
                p_g_tch_connected = 1;
        }
                send_and_free_mncc(p_g_lcr_gsm, frame->msg_type, frame);
                p_g_tch_connected = 1;
        }
+
+       /* modify to GSM FR, if not already */
+       if (!p_g_media_type) {
+               modify_lchan(MEDIA_TYPE_GSM);
+       }
 }
 
 /* CONNECT INDICATION */
 }
 
 /* CONNECT INDICATION */
@@ -516,8 +585,14 @@ void Pgsm::setup_cnf(unsigned int msg_type, unsigned int callref, struct gsm_mnc
                message->param.connectinfo.rtpinfo.media_types[0] = p_g_media_type;
                message->param.connectinfo.rtpinfo.payload_types[0] = p_g_payload_type;
                message->param.connectinfo.rtpinfo.payloads = 1;
                message->param.connectinfo.rtpinfo.media_types[0] = p_g_media_type;
                message->param.connectinfo.rtpinfo.payload_types[0] = p_g_payload_type;
                message->param.connectinfo.rtpinfo.payloads = 1;
+       } else {
+               /* modify to GSM FR, if not already
+               * for network side, this should have been already happened */
+               if (!p_g_media_type)
+                       modify_lchan(MEDIA_TYPE_GSM);
        }
 
        }
 
+
        if (p_g_rtp_bridge) {
                struct gsm_mncc_rtp *rtp;
 
        if (p_g_rtp_bridge) {
                struct gsm_mncc_rtp *rtp;
 
diff --git a/gsm.h b/gsm.h
index 57920db..b39b005 100644 (file)
--- a/gsm.h
+++ b/gsm.h
@@ -64,7 +64,7 @@ class Pgsm : public Port
        int p_g_rtp_media_types[8];
        unsigned char p_g_rtp_payload_types[8];
 
        int p_g_rtp_media_types[8];
        unsigned char p_g_rtp_payload_types[8];
 
-       void frame_send(void *_frame);
+       void frame_send(void *_frame, int len, int msg_type);
        void frame_receive(void *_frame);
        int audio_send(unsigned char *data, int len);
        int bridge_rx(unsigned char *data, int len);
        void frame_receive(void *_frame);
        int audio_send(unsigned char *data, int len);
        int bridge_rx(unsigned char *data, int len);
index 835ac98..eabf2f5 100644 (file)
@@ -50,5 +50,176 @@ void gsm_fr_encode(void *arg, signed short *samples, unsigned char *frame)
        gsm_encode((gsm)arg, (gsm_signal *)samples, (gsm_byte *)frame);
 }
 
        gsm_encode((gsm)arg, (gsm_signal *)samples, (gsm_byte *)frame);
 }
 
+#ifdef WITH_GSMAMR
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <opencore-amrnb/interf_dec.h>
+#include <opencore-amrnb/interf_enc.h>
+
+
+struct codec_efr_state {
+       void *encoder;
+       void *decoder;
+};
+
+/* create gsm instance */
+void *gsm_amr_create(void)
+{
+       struct codec_efr_state *st;
+
+       st = (struct codec_efr_state *)calloc(1, sizeof(*st));
+       if (!st)
+               return NULL;
+
+       st->encoder = Encoder_Interface_init(0);
+       st->decoder = Decoder_Interface_init();
+
+       return (void *)st;
+}
+
+/* free gsm instance */
+void gsm_amr_destroy(void *arg)
+{
+       struct codec_efr_state *st = (struct codec_efr_state *)arg;
+
+       Decoder_Interface_exit(st->decoder);
+       Encoder_Interface_exit(st->encoder);
+
+       return;
+}
+
+enum Mode amr_mode[8] = {
+        MR475,    /* 4.75 kbps */
+        MR515,    /* 5.15 kbps */
+       MR59,     /* 5.90 kbps */
+       MR67,     /* 6.70 kbps */
+       MR74,     /* 7.40 kbps */
+       MR795,    /* 7.95 kbps */
+       MR102,    /* 10.2 kbps */
+       MR122,    /* 12.2 kbps */
+};
+
+/* decode frame into samples, return error */
+int gsm_amr_decode(void *arg, unsigned char *frame, signed short *samples)
+{
+       struct codec_efr_state *st = (struct codec_efr_state *)arg;
+
+       Decoder_Interface_Decode(
+               st->decoder,
+               (const unsigned char*) frame + 1,
+               (short *) samples,
+               0
+       );
+
+       return 0;
+}
+
+/* encode samples into frame */
+int gsm_amr_encode(void *arg, signed short *samples, unsigned char *frame, int mode)
+{
+       struct codec_efr_state *st = (struct codec_efr_state *)arg;
+       int rv;
+
+       rv = Encoder_Interface_Encode(
+               st->encoder,
+               amr_mode[mode],
+               (const short*) samples,
+               (unsigned char*) frame + 1,
+               1
+       );
+
+       frame[0] = 0xf0; /* no request */
+
+       return rv;
+}
+
+const unsigned short gsm690_12_2_bitorder[244] = {
+         0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
+        10,  11,  12,  13,  14,  23,  15,  16,  17,  18,
+        19,  20,  21,  22,  24,  25,  26,  27,  28,  38,
+       141,  39, 142,  40, 143,  41, 144,  42, 145,  43,
+       146,  44, 147,  45, 148,  46, 149,  47,  97, 150,
+       200,  48,  98, 151, 201,  49,  99, 152, 202,  86,
+       136, 189, 239,  87, 137, 190, 240,  88, 138, 191,
+       241,  91, 194,  92, 195,  93, 196,  94, 197,  95,
+       198,  29,  30,  31,  32,  33,  34,  35,  50, 100,
+       153, 203,  89, 139, 192, 242,  51, 101, 154, 204,
+        55, 105, 158, 208,  90, 140, 193, 243,  59, 109,
+       162, 212,  63, 113, 166, 216,  67, 117, 170, 220,
+        36,  37,  54,  53,  52,  58,  57,  56,  62,  61,
+        60,  66,  65,  64,  70,  69,  68, 104, 103, 102,
+       108, 107, 106, 112, 111, 110, 116, 115, 114, 120,
+       119, 118, 157, 156, 155, 161, 160, 159, 165, 164,
+       163, 169, 168, 167, 173, 172, 171, 207, 206, 205,
+       211, 210, 209, 215, 214, 213, 219, 218, 217, 223,
+       222, 221,  73,  72,  71,  76,  75,  74,  79,  78,
+        77,  82,  81,  80,  85,  84,  83, 123, 122, 121,
+       126, 125, 124, 129, 128, 127, 132, 131, 130, 135,
+       134, 133, 176, 175, 174, 179, 178, 177, 182, 181,
+       180, 185, 184, 183, 188, 187, 186, 226, 225, 224,
+       229, 228, 227, 232, 231, 230, 235, 234, 233, 238,
+       237, 236,  96, 199,
+};
+
+/* decode frame into samples, return error */
+int gsm_efr_decode(void *arg, unsigned char *frame, signed short *samples)
+{
+       struct codec_efr_state *st = (struct codec_efr_state *)arg;
+       unsigned char cod[32], bit;
+       int i, si;
+
+       cod[0] = 0x3c; /* good AMR 12,2 frame */
+       memset(cod + 1, 0, 31);
+
+       for (i = 0; i < 244; i++) {
+               si = gsm690_12_2_bitorder[i] + 4;
+               bit = (frame[si >> 3] >> (7 - (si & 7))) & 1;
+               cod[(i >> 3) + 1] |= (bit << (7 - (i & 7)));
+       }
+
+       Decoder_Interface_Decode(
+               st->decoder,
+               (const unsigned char*) cod,
+               (short *) samples,
+               0
+       );
+
+       return 0;
+}
+
+/* encode samples into frame */
+int gsm_efr_encode(void *arg, signed short *samples, unsigned char *frame)
+{
+       struct codec_efr_state *st = (struct codec_efr_state *)arg;
+       int rv;
+       unsigned char cod[32], bit;
+       int i, di;
+
+       rv = Encoder_Interface_Encode(
+               st->encoder,
+               MR122,
+               (const short*) samples,
+               (unsigned char*) cod,
+               1
+       );
+
+       if (cod[0] != 0x3c)
+               return -1;
+
+       frame[0] = 0xc0;
+       memset(frame + 1, 0, 30);
+
+       for (i = 0; i < 244; i++) {
+               di = gsm690_12_2_bitorder[i] + 4;
+               bit = (cod[(i >> 3) + 1] >> (7 - (i & 7))) & 1;
+               frame[di >> 3] |= (bit << (7 - (di & 7)));
+       }
+       return rv;
+}
+
+#endif
+
 } /* extern "C" */
 
 } /* extern "C" */
 
index 74e0dbb..76138fc 100644 (file)
@@ -1,6 +1,17 @@
 
 
+#ifdef WITH_GSMFR
 void *gsm_fr_create(void);
 void gsm_fr_destroy(void *arg);
 int gsm_fr_decode(void *arg, unsigned char *frame, signed short *samples);
 void gsm_fr_encode(void *arg, signed short *samples, unsigned char *frame);
 void *gsm_fr_create(void);
 void gsm_fr_destroy(void *arg);
 int gsm_fr_decode(void *arg, unsigned char *frame, signed short *samples);
 void gsm_fr_encode(void *arg, signed short *samples, unsigned char *frame);
+#endif
+
+#ifdef WITH_GSMAMR
+void *gsm_amr_create(void);
+void gsm_amr_destroy(void *arg);
+int gsm_amr_decode(void *arg, unsigned char *frame, signed short *samples);
+int gsm_amr_encode(void *arg, signed short *samples, unsigned char *frame, int mode);
+int gsm_efr_decode(void *arg, unsigned char *frame, signed short *samples);
+int gsm_efr_encode(void *arg, signed short *samples, unsigned char *frame);
+#endif
 
 
index b3eabc4..8a314bf 100644 (file)
@@ -277,7 +277,7 @@ void Pgsm_ms::setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_
        epointlist_new(epoint->ep_serial);
 
        /* modify lchan to GSM codec V1 */
        epointlist_new(epoint->ep_serial);
 
        /* modify lchan to GSM codec V1 */
-       modify_lchan(RTP_PT_GSM_FULL);
+       modify_lchan(MEDIA_TYPE_GSM);
 
        /* send call proceeding */
        gsm_trace_header(p_interface_name, this, MNCC_CALL_CONF_REQ, DIRECTION_OUT);
 
        /* send call proceeding */
        gsm_trace_header(p_interface_name, this, MNCC_CALL_CONF_REQ, DIRECTION_OUT);