diff --git a/openbsc/configure.ac b/openbsc/configure.ac index 26a7b6209..72f6e6ade 100644 --- a/openbsc/configure.ac +++ b/openbsc/configure.ac @@ -57,6 +57,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 7b6262142..be399779f 100644 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ b/openbsc/src/osmo-bsc_mgcp/Makefile.am @@ -1,11 +1,17 @@ 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) \ - $(LIBOSMONETIF_LIBS) + $(LIBOSMONETIF_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 000000000..cb35fc651 --- /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 14ec22143..6b7296591 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 000000000..cadc8769e --- /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; + enum audio_format src_fmt, dst_fmt; + + /* cleanup first */ + if (dst_end->rtp_process_data) { + talloc_free(dst_end->rtp_process_data); + 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 000000000..2dfb06abf --- /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 */