From 9b2cabced47630e31cfc57c1384af805170b6d2f Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 14 Mar 2013 09:58:31 +0100 Subject: [PATCH 1/1] Add AMR codec, for supporting EFR transcoding The AMR codec is added, but at this point only EFR payload is supported. --- Makefile.am | 9 ++++ configure.ac | 9 ++++ gsm.cpp | 93 ++++++++++++++++++++++++++++---- gsm.h | 2 +- gsm_audio.c | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gsm_audio.h | 11 ++++ gsm_ms.cpp | 2 +- 7 files changed, 286 insertions(+), 11 deletions(-) diff --git a/Makefile.am b/Makefile.am index e35f6f0..11eeb2c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -69,6 +69,14 @@ SUBDIRS += libgsmfr #endif +if ENABLE_GSMAMR + +GSM_INCLUDE += -DWITH_GSMAMR + +GSM_LIB += -lopencore-amrnb + +endif + 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 + endif diff --git a/configure.ac b/configure.ac index 8a3cba3..0050c15 100644 --- a/configure.ac +++ b/configure.ac @@ -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") +# 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], @@ -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$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)]) diff --git a/gsm.cpp b/gsm.cpp index b6372f5..7502eed 100644 --- 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 +#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; @@ -225,6 +233,13 @@ Pgsm::~Pgsm() 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: + 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; @@ -252,6 +275,25 @@ void Pgsm::frame_receive(void *arg) } #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: @@ -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++]; - 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); - 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 + break; } } 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; - frame->msg_type = GSM_TCHF_FRAME; + frame->msg_type = msg_type; 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); @@ -391,7 +452,7 @@ void Pgsm::modify_lchan(int media_type) { struct gsm_mncc *mode; - /* already modified to that payload type */ + /* already modified to that media type */ 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; } + + /* modify to GSM FR (this is GSM user side only, so there is FR supported only) */ + modify_lchan(MEDIA_TYPE_GSM); } /* 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; } + + /* modify to GSM FR, if not already */ + if (!p_g_media_type) { + modify_lchan(MEDIA_TYPE_GSM); + } } /* 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; + } 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; diff --git a/gsm.h b/gsm.h index 57920db..b39b005 100644 --- 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]; - 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); diff --git a/gsm_audio.c b/gsm_audio.c index 835ac98..eabf2f5 100644 --- a/gsm_audio.c +++ b/gsm_audio.c @@ -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); } +#ifdef WITH_GSMAMR + +#include +#include + +#include +#include + + +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" */ diff --git a/gsm_audio.h b/gsm_audio.h index 74e0dbb..76138fc 100644 --- a/gsm_audio.h +++ b/gsm_audio.h @@ -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); +#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 diff --git a/gsm_ms.cpp b/gsm_ms.cpp index b3eabc4..8a314bf 100644 --- a/gsm_ms.cpp +++ b/gsm_ms.cpp @@ -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 */ - 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); -- 2.13.6