From patchwork Thu May 15 08:29:13 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacob Erlbeck X-Patchwork-Id: 349093 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org X-Greylist: delayed 2471 seconds by postgrey-1.34 at bilbo; Thu, 15 May 2014 18:41:43 EST Received: from ganesha.gnumonks.org (ganesha.gnumonks.org [IPv6:2001:780:45:1d:225:90ff:fe52:c662]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id D750B1400DA for ; Thu, 15 May 2014 18:41:43 +1000 (EST) Received: from localhost ([127.0.0.1] helo=ganesha.gnumonks.org) by ganesha.gnumonks.org with esmtp (Exim 4.72) (envelope-from ) id 1WkrEF-0007xh-Iu; Thu, 15 May 2014 10:41:27 +0200 Received: from mail.sysmocom.de ([2a01:4f8:191:444c::2:4]) by ganesha.gnumonks.org with esmtp (Exim 4.72) (envelope-from ) id 1WkrDi-0007xD-50 for openbsc@lists.osmocom.org; Thu, 15 May 2014 10:40:58 +0200 Received: from sysmocom-tmp.home (24-134-58-61-dynip.superkabel.de [24.134.58.61]) by mail.sysmocom.de (Postfix) with ESMTPSA id CC92958F0C; Thu, 15 May 2014 08:40:53 +0000 (UTC) From: Jacob Erlbeck To: openbsc@lists.osmocom.org Subject: [PATCH 05/11] mgcp: Add RTP audio transcoding Date: Thu, 15 May 2014 10:29:13 +0200 Message-Id: <1400142559-26788-5-git-send-email-jerlbeck@sysmocom.de> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1400142559-26788-1-git-send-email-jerlbeck@sysmocom.de> References: <1399891147-31419-1-git-send-email-jerlbeck@sysmocom.de> <1400142559-26788-1-git-send-email-jerlbeck@sysmocom.de> X-Spam-Score: 2.1 (++) X-Spam-Report: SpamASsassin versoin 3.3.1 on ganesha.gnumonks.org summary: Content analysis details: (2.1 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 1.8 URIBL_BLACK Contains an URL listed in the URIBL blacklist [URIs: configure.ac] 0.1 TW_CF BODY: Odd Letter Triples with CF 0.1 TW_MG BODY: Odd Letter Triples with MG 0.1 TW_GC BODY: Odd Letter Triples with GC 0.1 TW_LR BODY: Odd Letter Triples with LR Cc: Jacob Erlbeck X-BeenThere: openbsc@lists.osmocom.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Development of the OpenBSC GSM base station controller List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: openbsc-bounces@lists.osmocom.org Errors-To: openbsc-bounces@lists.osmocom.org This patch implements audio transcoding between the formats GSM, PCMA, L16, and optionally G.729. The feature needs to be enabled by using the autoconf option '--enable-mgcp-transcoding'. In this case mgcp_transcode.c will be compiled and linked to osmo-bsc_mgcp, and the transcoding functions provided will be registered as processing callbacks. If G.729 support is required, libcg729 needs to be installed and '--with-g729' must be passed to ./configure. Ticket: OW#1111 Sponsored-by: On-Waves ehf --- openbsc/configure.ac | 15 + openbsc/src/osmo-bsc_mgcp/Makefile.am | 10 +- openbsc/src/osmo-bsc_mgcp/g711common.h | 187 ++++++++++++ openbsc/src/osmo-bsc_mgcp/mgcp_main.c | 10 + openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c | 452 ++++++++++++++++++++++++++++ openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h | 34 +++ 6 files changed, 706 insertions(+), 2 deletions(-) create mode 100644 openbsc/src/osmo-bsc_mgcp/g711common.h create mode 100644 openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c create mode 100644 openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h diff --git a/openbsc/configure.ac b/openbsc/configure.ac index 978f526..ead05af 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -56,6 +56,21 @@ fi AM_CONDITIONAL(BUILD_SMPP, test "x$osmo_ac_build_smpp" = "xyes") AC_SUBST(osmo_ac_build_smpp) +# Enable/disable transcoding within osmo-bsc_mgcp? +AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])], + [osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"]) +AC_ARG_WITH([g729], [AS_HELP_STRING([--with-g729], [Enable G.729 encoding/decoding.])], [osmo_ac_with_g729="$withval"],[osmo_ac_with_g729="no"]) + +if test "$osmo_ac_mgcp_transcoding" = "yes" ; then + AC_SEARCH_LIBS(gsm_create, gsm) + if test "$osmo_ac_with_g729" = "yes" ; then + PKG_CHECK_MODULES(LIBBCG729, libbcg729 >= 0.1, [AC_DEFINE([HAVE_BCG729], [1], [Use bgc729 decoder/encoder])]) + fi + AC_DEFINE(BUILD_MGCP_TRANSCODING, 1, [Define if we want to build the MGCP gateway with transcoding support]) +fi +AM_CONDITIONAL(BUILD_MGCP_TRANSCODING, test "x$osmo_ac_mgcp_transcoding" = "xyes") +AC_SUBST(osmo_ac_mgcp_transcoding) + found_libgtp=yes PKG_CHECK_MODULES(LIBGTP, libgtp, , found_libgtp=no) diff --git a/openbsc/src/osmo-bsc_mgcp/Makefile.am b/openbsc/src/osmo-bsc_mgcp/Makefile.am index 0456cf1..a620e7a 100644 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ b/openbsc/src/osmo-bsc_mgcp/Makefile.am @@ -1,10 +1,16 @@ AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) + $(LIBOSMOVTY_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) \ + $(LIBBCG729_CFLAGS) bin_PROGRAMS = osmo-bsc_mgcp osmo_bsc_mgcp_SOURCES = mgcp_main.c +if BUILD_MGCP_TRANSCODING + osmo_bsc_mgcp_SOURCES += mgcp_transcode.c +endif osmo_bsc_mgcp_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ $(top_builddir)/src/libmgcp/libmgcp.a -lrt \ - $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) + $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) $(LIBBCG729_LIBS) + +noinst_HEADERS = g711common.h mgcp_transcode.h diff --git a/openbsc/src/osmo-bsc_mgcp/g711common.h b/openbsc/src/osmo-bsc_mgcp/g711common.h new file mode 100644 index 0000000..cb35fc6 --- /dev/null +++ b/openbsc/src/osmo-bsc_mgcp/g711common.h @@ -0,0 +1,187 @@ +/* + * PCM - A-Law conversion + * Copyright (c) 2000 by Abramo Bagnara + * + * Wrapper for linphone Codec class by Simon Morlat + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +static inline int val_seg(int val) +{ + int r = 0; + val >>= 7; /*7 = 4 + 3*/ + if (val & 0xf0) { + val >>= 4; + r += 4; + } + if (val & 0x0c) { + val >>= 2; + r += 2; + } + if (val & 0x02) + r += 1; + return r; +} + +/* + * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + * G711 is designed for 13 bits input signal, this function add extra shifting to take this into account. + */ + +static inline unsigned char s16_to_alaw(int pcm_val) +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; + } else { + mask = 0x55; + pcm_val = -pcm_val; + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + } + + if (pcm_val < 256) /*256 = 32 << 3*/ + aval = pcm_val >> 4; /*4 = 1 + 3*/ + else { + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + } + return aval ^ mask; +} + +/* + * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM + * + */ +static inline int alaw_to_s16(unsigned char a_val) +{ + int t; + int seg; + + a_val ^= 0x55; + t = a_val & 0x7f; + if (t < 16) + t = (t << 4) + 8; + else { + seg = (t >> 4) & 0x07; + t = ((t & 0x0f) << 4) + 0x108; + t <<= seg -1; + } + return ((a_val & 0x80) ? t : -t); +} +/* + * s16_to_ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + if (pcm_val < 0) { + pcm_val = 0x84 - pcm_val; + mask = 0x7f; + } else { + pcm_val += 0x84; + mask = 0xff; + } + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + return uval ^ mask; +} + +/* + * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +static inline int ulaw_to_s16(unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & 0x0f) << 3) + 0x84; + t <<= (u_val & 0x70) >> 4; + + return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84)); +} diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c index 14ec221..6b72965 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c @@ -49,6 +49,10 @@ #include "../../bscconfig.h" +#ifdef BUILD_MGCP_TRANSCODING +#include "mgcp_transcode.h" +#endif + /* this is here for the vty... it will never be called */ void subscr_put() { abort(); } @@ -207,6 +211,12 @@ int main(int argc, char **argv) if (!cfg) return -1; +#ifdef BUILD_MGCP_TRANSCODING + cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup; + cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp; + cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format; +#endif + vty_info.copyright = openbsc_copyright; vty_init(&vty_info); logging_vty_add_cmds(&log_info); diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c new file mode 100644 index 0000000..7247c88 --- /dev/null +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c @@ -0,0 +1,452 @@ +/* + * (C) 2014 by Sysmocom s.f.m.c. GmbH + * (C) 2014 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include "bscconfig.h" + +#include "g711common.h" +#include +#ifdef HAVE_BCG729 +#include +#include +#endif + +#include +#include +#include + +#include + +enum audio_format { + AF_INVALID, + AF_S16, + AF_L16, + AF_GSM, + AF_G729, + AF_PCMA +}; + +struct mgcp_process_rtp_state { + /* decoding */ + enum audio_format src_fmt; + union { + gsm gsm_handle; +#ifdef HAVE_BCG729 + bcg729DecoderChannelContextStruct *g729_dec; +#endif + } src; + size_t src_frame_size; + size_t src_samples_per_frame; + + /* processing */ + + /* encoding */ + enum audio_format dst_fmt; + union { + gsm gsm_handle; +#ifdef HAVE_BCG729 + bcg729EncoderChannelContextStruct *g729_enc; +#endif + } dst; + size_t dst_frame_size; + size_t dst_samples_per_frame; +}; + +static enum audio_format get_audio_format(const struct mgcp_rtp_end *rtp_end) +{ + if (rtp_end->subtype_name) { + if (!strcmp("GSM", rtp_end->subtype_name)) + return AF_GSM; + if (!strcmp("PCMA", rtp_end->subtype_name)) + return AF_PCMA; +#ifdef HAVE_BCG729 + if (!strcmp("G729", rtp_end->subtype_name)) + return AF_G729; +#endif + if (!strcmp("L16", rtp_end->subtype_name)) + return AF_L16; + } + + switch (rtp_end->payload_type) { + case 3 /* GSM */: + return AF_GSM; + case 8 /* PCMA */: + return AF_PCMA; +#ifdef HAVE_BCG729 + case 18 /* G.729 */: + return AF_G729; +#endif + case 11 /* L16 */: + return AF_L16; + default: + return AF_INVALID; + } +} + +static void l16_encode(short *sample, unsigned char *buf, size_t n) +{ + for (; n > 0; --n, ++sample, buf += 2) { + buf[0] = sample[0] >> 8; + buf[1] = sample[0] & 0xff; + } +} + +static void l16_decode(unsigned char *buf, short *sample, size_t n) +{ + for (; n > 0; --n, ++sample, buf += 2) + sample[0] = ((short)buf[0] << 8) | buf[1]; +} + +static void alaw_encode(short *sample, unsigned char *buf, size_t n) +{ + for (; n > 0; --n) + *(buf++) = s16_to_alaw(*(sample++)); +} + +static void alaw_decode(unsigned char *buf, short *sample, size_t n) +{ + for (; n > 0; --n) + *(sample++) = alaw_to_s16(*(buf++)); +} + +static int processing_state_destructor(struct mgcp_process_rtp_state *state) +{ + switch (state->src_fmt) { + case AF_GSM: + if (state->dst.gsm_handle) + gsm_destroy(state->src.gsm_handle); + break; +#ifdef HAVE_BCG729 + case AF_G729: + if (state->src.g729_dec) + closeBcg729DecoderChannel(state->src.g729_dec); + break; +#endif + default: + break; + } + switch (state->dst_fmt) { + case AF_GSM: + if (state->dst.gsm_handle) + gsm_destroy(state->dst.gsm_handle); + break; +#ifdef HAVE_BCG729 + case AF_G729: + if (state->dst.g729_enc) + closeBcg729EncoderChannel(state->dst.g729_enc); + break; +#endif + default: + break; + } + return 0; +} + +int mgcp_transcoding_setup(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end) +{ + struct mgcp_process_rtp_state *state = dst_end->rtp_process_data; + enum audio_format src_fmt, dst_fmt; + + /* cleanup first */ + if (state) { + talloc_free(state); + dst_end->rtp_process_data = NULL; + } + + if (!src_end) + return 0; + + src_fmt = get_audio_format(src_end); + dst_fmt = get_audio_format(dst_end); + + LOGP(DMGCP, LOGL_ERROR, + "Checking transcoding: %s (%d) -> %s (%d)\n", + src_end->subtype_name, src_end->payload_type, + dst_end->subtype_name, dst_end->payload_type); + + if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) { + if (!src_end->subtype_name || !dst_end->subtype_name) + /* Not enough info, do nothing */ + return 0; + + if (strcmp(src_end->subtype_name, dst_end->subtype_name) == 0) + /* Nothing to do */ + return 0; + + LOGP(DMGCP, LOGL_ERROR, + "Cannot transcode: %s codec not supported (%s -> %s).\n", + src_fmt != AF_INVALID ? "destination" : "source", + src_end->audio_name, dst_end->audio_name); + return -EINVAL; + } + + if (src_end->rate && dst_end->rate && src_end->rate != dst_end->rate) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot transcode: rate conversion (%d -> %d) not supported.\n", + src_end->rate, dst_end->rate); + return -EINVAL; + } + + state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state); + talloc_set_destructor(state, processing_state_destructor); + dst_end->rtp_process_data = state; + + state->src_fmt = src_fmt; + + switch (state->src_fmt) { + case AF_L16: + case AF_S16: + state->src_frame_size = 80 * sizeof(short); + state->src_samples_per_frame = 80; + break; + case AF_GSM: + state->src_frame_size = sizeof(gsm_frame); + state->src_samples_per_frame = 160; + state->src.gsm_handle = gsm_create(); + if (!state->src.gsm_handle) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize GSM decoder.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + state->src_frame_size = 10; + state->src_samples_per_frame = 80; + state->src.g729_dec = initBcg729DecoderChannel(); + if (!state->src.g729_dec) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize G.729 decoder.\n"); + return -EINVAL; + } + break; +#endif + case AF_PCMA: + state->src_frame_size = 80; + state->src_samples_per_frame = 80; + break; + default: + break; + } + + state->dst_fmt = dst_fmt; + + switch (state->dst_fmt) { + case AF_L16: + case AF_S16: + state->dst_frame_size = 80*sizeof(short); + state->dst_samples_per_frame = 80; + break; + case AF_GSM: + state->dst_frame_size = sizeof(gsm_frame); + state->dst_samples_per_frame = 160; + state->dst.gsm_handle = gsm_create(); + if (!state->dst.gsm_handle) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize GSM encoder.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + state->dst_frame_size = 10; + state->dst_samples_per_frame = 80; + state->dst.g729_enc = initBcg729EncoderChannel(); + if (!state->dst.g729_enc) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to initialize G.729 decoder.\n"); + return -EINVAL; + } + break; +#endif + case AF_PCMA: + state->dst_frame_size = 80; + state->dst_samples_per_frame = 80; + break; + default: + break; + } + + LOGP(DMGCP, LOGL_INFO, + "Initialized RTP processing on: 0x%x " + "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n", + ENDPOINT_NUMBER(endp), + src_fmt, src_end->payload_type, src_end->rate, src_end->fmtp_extra, + dst_fmt, dst_end->payload_type, dst_end->rate, dst_end->fmtp_extra); + + return 0; +} + +void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, + int *payload_type, + const char**audio_name, + const char**fmtp_extra) +{ + struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data; + if (!state || endp->net_end.payload_type < 0) { + *payload_type = endp->bts_end.payload_type; + *audio_name = endp->bts_end.audio_name; + *fmtp_extra = endp->bts_end.fmtp_extra; + return; + } + + *payload_type = endp->net_end.payload_type; + *fmtp_extra = endp->net_end.fmtp_extra; + *audio_name = endp->net_end.audio_name; +} + + +int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + char *data, int *len, int buf_size) +{ + struct mgcp_process_rtp_state *state = dst_end->rtp_process_data; + size_t rtp_hdr_size = 12; + char *payload_data = data + rtp_hdr_size; + int payload_len = *len - rtp_hdr_size; + size_t sample_cnt = 0; + size_t sample_idx; + int16_t samples[10*160]; + uint8_t *src = (uint8_t *)payload_data; + uint8_t *dst = (uint8_t *)payload_data; + size_t nbytes = payload_len; + size_t frame_remainder; + + if (!state) + return 0; + + if (state->src_fmt == state->dst_fmt) + return 0; + + /* TODO: check payload type (-> G.711 comfort noise) */ + + /* Decode src into samples */ + while (nbytes >= state->src_frame_size) { + if (sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(samples)) { + LOGP(DMGCP, LOGL_ERROR, + "Sample buffer too small: %d > %d.\n", + sample_cnt + state->src_samples_per_frame, + ARRAY_SIZE(samples)); + return -ENOSPC; + } + switch (state->src_fmt) { + case AF_GSM: + if (gsm_decode(state->src.gsm_handle, + (gsm_byte *)src, samples + sample_cnt) < 0) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to decode GSM.\n"); + return -EINVAL; + } + break; +#ifdef HAVE_BCG729 + case AF_G729: + bcg729Decoder(state->src.g729_dec, src, 0, samples + sample_cnt); + break; +#endif + case AF_PCMA: + alaw_decode(src, samples + sample_cnt, + state->src_samples_per_frame); + break; + case AF_S16: + memmove(samples + sample_cnt, src, + state->src_frame_size); + break; + case AF_L16: + l16_decode(src, samples + sample_cnt, + state->src_samples_per_frame); + break; + default: + break; + } + src += state->src_frame_size; + nbytes -= state->src_frame_size; + sample_cnt += state->src_samples_per_frame; + } + + /* Add silence if necessary */ + frame_remainder = sample_cnt % state->dst_samples_per_frame; + if (frame_remainder) { + size_t silence = state->dst_samples_per_frame - frame_remainder; + if (sample_cnt + silence > ARRAY_SIZE(samples)) { + LOGP(DMGCP, LOGL_ERROR, + "Sample buffer too small for silence: %d > %d.\n", + sample_cnt + silence, + ARRAY_SIZE(samples)); + return -ENOSPC; + } + + while (silence > 0) { + samples[sample_cnt] = 0; + sample_cnt += 1; + silence -= 1; + } + } + + /* Encode samples into dst */ + sample_idx = 0; + nbytes = 0; + while (sample_idx + state->dst_samples_per_frame <= sample_cnt) { + if (nbytes + state->dst_frame_size > buf_size) { + LOGP(DMGCP, LOGL_ERROR, + "Encoding (RTP) buffer too small: %d > %d.\n", + nbytes + state->dst_frame_size, buf_size); + return -ENOSPC; + } + switch (state->dst_fmt) { + case AF_GSM: + gsm_encode(state->dst.gsm_handle, + samples + sample_idx, dst); + break; +#ifdef HAVE_BCG729 + case AF_G729: + bcg729Encoder(state->dst.g729_enc, + samples + sample_idx, dst); + break; +#endif + case AF_PCMA: + alaw_encode(samples + sample_idx, dst, + state->src_samples_per_frame); + break; + case AF_S16: + memmove(dst, samples + sample_idx, state->dst_frame_size); + break; + case AF_L16: + l16_encode(samples + sample_idx, dst, + state->src_samples_per_frame); + break; + default: + break; + } + dst += state->dst_frame_size; + nbytes += state->dst_frame_size; + sample_idx += state->dst_samples_per_frame; + } + + *len = rtp_hdr_size + nbytes; + /* Patch payload type */ + data[1] = (data[1] & 0x80) | (dst_end->payload_type & 0x7f); + + return 0; +} diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h new file mode 100644 index 0000000..2dfb06a --- /dev/null +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.h @@ -0,0 +1,34 @@ +/* + * (C) 2014 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +#ifndef OPENBSC_MGCP_TRANSCODE_H +#define OPENBSC_MGCP_TRANSCODE_H + +int mgcp_transcoding_setup(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + struct mgcp_rtp_end *src_end); + +void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, + int *payload_type, + const char**audio_name, + const char**fmtp_extra); + +int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + char *data, int *len, int buf_size); +#endif /* OPENBSC_MGCP_TRANSCODE_H */