Merge remote-tracking branch 'origin/jerlbeck/features/mgcp-transcoding'

This commit is contained in:
Jacob Erlbeck 2014-06-05 14:45:07 +02:00
commit efd266ae12
22 changed files with 2249 additions and 65 deletions

View File

@ -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)

View File

@ -0,0 +1,16 @@
OBJS = testconv_main.o
CC = gcc
CFLAGS = -O0 -ggdb -Wall
LDFLAGS =
CPPFLAGS = -I../.. -I../../include $(shell pkg-config --cflags libosmocore) $(shell pkg-config --cflags libbcg729)
LIBS = ../../src/libmgcp/libmgcp.a ../../src/libcommon/libcommon.a $(shell pkg-config --libs libosmocore) $(shell pkg-config --libs libbcg729) -lgsm -lrt
testconv: $(OBJS)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
testconv_main.o: testconv_main.c
$(OBJS):
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<

View File

@ -0,0 +1,133 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/mgcp.h>
#include <openbsc/mgcp_internal.h>
#include "bscconfig.h"
#ifndef BUILD_MGCP_TRANSCODING
#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)"
#endif
#include "openbsc/mgcp_transcode.h"
static int audio_name_to_type(const char *name)
{
if (!strcasecmp(name, "gsm"))
return 3;
#ifdef HAVE_BCG729
else if (!strcasecmp(name, "g729"))
return 18;
#endif
else if (!strcasecmp(name, "pcma"))
return 8;
else if (!strcasecmp(name, "l16"))
return 11;
return -1;
}
int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst);
int main(int argc, char **argv)
{
char buf[4096] = {0x80, 0};
int cc, rc;
struct mgcp_rtp_end *dst_end;
struct mgcp_rtp_end *src_end;
struct mgcp_trunk_config tcfg = {{0}};
struct mgcp_endpoint endp = {0};
struct mgcp_process_rtp_state *state;
int in_size;
int in_samples = 160;
int out_samples = 0;
uint32_t ts = 0;
uint16_t seq = 0;
osmo_init_logging(&log_info);
tcfg.endpoints = &endp;
tcfg.number_endpoints = 1;
endp.tcfg = &tcfg;
mgcp_free_endp(&endp);
dst_end = &endp.bts_end;
src_end = &endp.net_end;
if (argc <= 2)
errx(1, "Usage: {gsm|g729|pcma|l16} {gsm|g729|pcma|l16} [SPP]");
if ((src_end->payload_type = audio_name_to_type(argv[1])) == -1)
errx(1, "invalid input format '%s'", argv[1]);
if ((dst_end->payload_type = audio_name_to_type(argv[2])) == -1)
errx(1, "invalid output format '%s'", argv[2]);
if (argc > 3)
out_samples = atoi(argv[3]);
if (out_samples) {
dst_end->frame_duration_den = dst_end->rate;
dst_end->frame_duration_num = out_samples;
dst_end->frames_per_packet = 1;
}
rc = mgcp_transcoding_setup(&endp, dst_end, src_end);
if (rc < 0)
errx(1, "setup failed: %s", strerror(-rc));
state = dst_end->rtp_process_data;
OSMO_ASSERT(state != NULL);
in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
OSMO_ASSERT(sizeof(buf) >= in_size + 12);
buf[1] = src_end->payload_type;
*(uint16_t*)(buf+2) = htons(1);
*(uint32_t*)(buf+4) = htonl(0);
*(uint32_t*)(buf+8) = htonl(0xaabbccdd);
while ((cc = read(0, buf + 12, in_size))) {
int cont;
int len;
if (cc != in_size)
err(1, "read");
*(uint16_t*)(buf+2) = htonl(seq);
*(uint32_t*)(buf+4) = htonl(ts);
seq += 1;
ts += in_samples;
cc += 12; /* include RTP header */
len = cc;
do {
cont = mgcp_transcoding_process_rtp(&endp, dst_end,
buf, &len, sizeof(buf));
if (cont == -EAGAIN) {
fprintf(stderr, "Got EAGAIN\n");
break;
}
if (cont < 0)
errx(1, "processing failed: %s", strerror(-cont));
len -= 12; /* ignore RTP header */
if (write(1, buf + 12, len) != len)
err(1, "write");
len = cont;
} while (len > 0);
}
return 0;
}

View File

@ -14,7 +14,7 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \
osmo_msc_data.h osmo_bsc_grace.h sms_queue.h abis_om2000.h \
bss.h gsm_data_shared.h control_cmd.h ipaccess.h mncc_int.h \
arfcn_range_encode.h nat_rewrite_trie.h bsc_nat_callstats.h \
osmux.h
osmux.h mgcp_transcode.h
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h
openbscdir = $(includedir)/openbsc

View File

@ -65,6 +65,7 @@ static inline int rtp_calculate_port(int multiplex, int base)
struct mgcp_endpoint;
struct mgcp_config;
struct mgcp_trunk_config;
struct mgcp_rtp_end;
#define MGCP_ENDP_CRCX 1
#define MGCP_ENDP_DLCX 2
@ -86,6 +87,18 @@ typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int stat
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
struct mgcp_rtp_end *src_end);
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
int *payload_type,
const char**subtype_name,
const char**fmtp_extra);
#define PORT_ALLOC_STATIC 0
#define PORT_ALLOC_DYNAMIC 1
@ -156,6 +169,12 @@ struct mgcp_config {
struct in_addr transcoder_in;
int transcoder_remote_base;
/* RTP processing */
mgcp_processing rtp_processing_cb;
mgcp_processing_setup setup_rtp_processing_cb;
mgcp_get_format get_net_downlink_format_cb;
struct osmo_wqueue gw_fd;
struct mgcp_port_range bts_ports;
@ -163,6 +182,8 @@ struct mgcp_config {
struct mgcp_port_range transcoder_ports;
int endp_dscp;
int bts_force_ptime;
mgcp_change change_cb;
mgcp_policy policy_cb;
mgcp_reset reset_cb;

View File

@ -80,16 +80,21 @@ struct mgcp_rtp_end {
/* per endpoint data */
int payload_type;
uint32_t rate;
int channels;
uint32_t frame_duration_num;
uint32_t frame_duration_den;
int frames_per_packet;
uint32_t packet_duration_ms;
char *fmtp_extra;
char *audio_name;
char *subtype_name;
int output_enabled;
int force_output_ptime;
/* RTP patching */
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
int force_aligned_timing;
void *rtp_process_data;
/*
* Each end has a socket...
@ -118,6 +123,7 @@ struct mgcp_rtp_tap {
struct mgcp_lco {
char *string;
char *codec;
int pkt_period_min; /* time in ms */
int pkt_period_max; /* time in ms */
};
@ -205,6 +211,19 @@ void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
uint32_t *expected, int *loss);
uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *);
/* payload processing default functions */
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
struct mgcp_rtp_end *src_end);
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
int *payload_type,
const char**subtype_name,
const char**fmtp_extra);
enum {
MGCP_DEST_NET = 0,
MGCP_DEST_BTS,

View File

@ -0,0 +1,36 @@
/*
* (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 <http://www.gnu.org/licenses/>.
*
*/
#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);
int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst);
#endif /* OPENBSC_MGCP_TRANSCODE_H */

View File

@ -1,9 +1,15 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) $(COVERAGE_CFLAGS) $(LIBBCG729_CFLAGS)
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) \
$(LIBOSMONETIF_LIBS) $(COVERAGE_LDFLAGS)
$(LIBOSMONETIF_LIBS) $(COVERAGE_LDFLAGS) $(LIBBCG729_LIBS)
noinst_LIBRARIES = libmgcp.a
noinst_HEADERS = g711common.h
libmgcp_a_SOURCES = mgcp_protocol.c mgcp_network.c mgcp_vty.c osmux.c
if BUILD_MGCP_TRANSCODING
libmgcp_a_SOURCES += mgcp_transcode.c
endif

View File

@ -0,0 +1,187 @@
/*
* PCM - A-Law conversion
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
*
* Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
*
*
* 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));
}

View File

@ -79,6 +79,7 @@ struct rtp_hdr {
#define RTP_SEQ_MOD (1 << 16)
#define RTP_MAX_DROPOUT 3000
#define RTP_MAX_MISORDER 100
#define RTP_BUF_SIZE 4096
enum {
MGCP_PROTO_RTP,
@ -339,6 +340,31 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp,
return timestamp_error;
}
int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size)
{
return 0;
}
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
struct mgcp_rtp_end *src_end)
{
return 0;
}
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
int *payload_type,
const char**audio_name,
const char**fmtp_extra)
{
/* Use the BTS side parameters when passing the SDP data (for
* downlink) to the net peer.
*/
*payload_type = endp->bts_end.payload_type;
*audio_name = endp->bts_end.audio_name;
*fmtp_extra = endp->bts_end.fmtp_extra;
}
/**
* The RFC 3550 Appendix A assumes there are multiple sources but
@ -588,11 +614,28 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
if (!rtp_end->output_enabled)
rtp_end->dropped_packets += 1;
else if (is_rtp) {
mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, rc);
forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx], buf, rc);
return mgcp_udp_send(rtp_end->rtp.fd,
&rtp_end->addr,
rtp_end->rtp_port, buf, rc);
int cont;
int nbytes = 0;
int len = rc;
mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, len);
do {
cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
buf, &len, RTP_BUF_SIZE);
if (cont < 0)
break;
forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx],
buf, len);
rc = mgcp_udp_send(rtp_end->rtp.fd,
&rtp_end->addr,
rtp_end->rtp_port, buf, len);
if (rc <= 0)
return rc;
nbytes += rc;
len = cont;
} while (len > 0);
return nbytes;
} else if (!tcfg->omit_rtcp) {
return mgcp_udp_send(rtp_end->rtcp.fd,
&rtp_end->addr,
@ -627,7 +670,7 @@ static int receive_from(struct mgcp_endpoint *endp, int fd, struct sockaddr_in *
static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
{
char buf[4096];
char buf[RTP_BUF_SIZE];
struct sockaddr_in addr;
struct mgcp_endpoint *endp;
int rc, proto;
@ -723,7 +766,7 @@ static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_
static int rtp_data_bts(struct osmo_fd *fd, unsigned int what)
{
char buf[4096];
char buf[RTP_BUF_SIZE];
struct sockaddr_in addr;
struct mgcp_endpoint *endp;
int rc, proto;
@ -790,7 +833,7 @@ static int rtp_data_bts(struct osmo_fd *fd, unsigned int what)
static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_endp,
int dest, struct osmo_fd *fd)
{
char buf[4096];
char buf[RTP_BUF_SIZE];
struct sockaddr_in addr;
struct mgcp_config *cfg;
int rc, proto;

View File

@ -28,6 +28,7 @@
#include <time.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
@ -77,6 +78,9 @@ char *strline_r(char *str, char **saveptr)
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
#define PTYPE_UNDEFINED (-1)
static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end);
@ -107,6 +111,8 @@ static struct msgb *handle_noti_req(struct mgcp_parse_data *data);
static void create_transcoder(struct mgcp_endpoint *endp);
static void delete_transcoder(struct mgcp_endpoint *endp);
static void setup_rtp_processing(struct mgcp_endpoint *endp);
static int mgcp_analyze_header(struct mgcp_parse_data *parse, char *data);
static int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
@ -237,50 +243,106 @@ static struct msgb *create_err_response(struct mgcp_endpoint *endp,
return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
}
static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
const char *msg, const char *trans_id)
static int write_response_sdp(struct mgcp_endpoint *endp,
char *sdp_record, size_t size, const char *addr)
{
const char *addr = endp->cfg->local_ip;
const char *fmtp_extra = endp->bts_end.fmtp_extra;
char sdp_record[4096];
const char *fmtp_extra;
const char *audio_name;
int payload_type;
int len;
int nchars;
if (!addr)
addr = endp->cfg->source_addr;
endp->cfg->get_net_downlink_format_cb(endp, &payload_type,
&audio_name, &fmtp_extra);
len = snprintf(sdp_record, sizeof(sdp_record) - 1,
"I: %u%s\n\n"
len = snprintf(sdp_record, size,
"v=0\r\n"
"o=- %u 23 IN IP4 %s\r\n"
"c=IN IP4 %s\r\n"
"t=0 0\r\n"
"m=audio %d RTP/AVP %d\r\n"
"a=rtpmap:%d %s\r\n"
"%s%s",
endp->ci, endp->cfg->osmux && endp->osmux ? "\nX-Osmux: On" : "",
endp->ci, addr, addr,
endp->net_end.local_port, endp->bts_end.payload_type,
endp->bts_end.payload_type, endp->tcfg->audio_name,
fmtp_extra ? fmtp_extra : "", fmtp_extra ? "\r\n" : "");
"t=0 0\r\n",
endp->ci, addr, addr);
if (len < 0 || len >= sizeof(sdp_record))
if (len < 0 || len >= size)
goto buffer_too_small;
if (payload_type >= 0) {
nchars = snprintf(sdp_record + len, size - len,
"m=audio %d RTP/AVP %d\r\n",
endp->net_end.local_port, payload_type);
if (nchars < 0 || nchars >= size - len)
goto buffer_too_small;
len += nchars;
if (audio_name) {
nchars = snprintf(sdp_record + len, size - len,
"a=rtpmap:%d %s\r\n",
payload_type, audio_name);
if (nchars < 0 || nchars >= size - len)
goto buffer_too_small;
len += nchars;
}
if (fmtp_extra) {
nchars = snprintf(sdp_record + len, size - len,
"a=rtpmap:%d %s\r\n",
payload_type, audio_name);
if (nchars < 0 || nchars >= size - len)
goto buffer_too_small;
len += nchars;
}
}
if (endp->bts_end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
int nchars = snprintf(sdp_record + len, sizeof(sdp_record) - len,
"a=ptime:%d\r\n",
endp->bts_end.packet_duration_ms);
if (nchars < 0 || nchars >= sizeof(sdp_record) - len)
nchars = snprintf(sdp_record + len, size - len,
"a=ptime:%d\r\n",
endp->bts_end.packet_duration_ms);
if (nchars < 0 || nchars >= size - len)
goto buffer_too_small;
len += nchars;
}
return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record);
return len;
buffer_too_small:
LOGP(DMGCP, LOGL_ERROR, "SDP buffer too small: %d (needed %d)\n",
sizeof(sdp_record), len);
return NULL;
size, len);
return -1;
}
static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
const char *msg, const char *trans_id)
{
const char *addr = endp->cfg->local_ip;
char sdp_record[4096];
int len;
int nchars;
if (!addr)
addr = endp->cfg->source_addr;
len = snprintf(sdp_record, sizeof(sdp_record),
"I: %u%s\n\n",
endp->ci,
endp->cfg->osmux && endp->osmux ? "\nX-Osmux: On" : "");
if (len < 0)
return NULL;
nchars = write_response_sdp(endp, sdp_record + len,
sizeof(sdp_record) - len - 1, addr);
if (nchars < 0)
return NULL;
len += nchars;
sdp_record[sizeof(sdp_record) - 1] = '\0';
return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record);
}
/*
@ -526,6 +588,72 @@ static int parse_conn_mode(const char *msg, struct mgcp_endpoint *endp)
return ret;
}
static int set_audio_info(void *ctx, struct mgcp_rtp_end *rtp,
int payload_type, const char *audio_name)
{
int rate = rtp->rate;
int channels = rtp->channels;
char audio_codec[64];
talloc_free(rtp->subtype_name);
rtp->subtype_name = NULL;
talloc_free(rtp->audio_name);
rtp->audio_name = NULL;
if (payload_type != PTYPE_UNDEFINED)
rtp->payload_type = payload_type;
if (!audio_name) {
switch (payload_type) {
case 3: audio_name = "GSM/8000/1"; break;
case 8: audio_name = "PCMA/8000/1"; break;
case 18: audio_name = "G729/8000/1"; break;
default:
/* Payload type is unknown, don't change rate and
* channels. */
/* TODO: return value? */
return 0;
}
}
if (sscanf(audio_name, "%63[^/]/%d/%d",
audio_codec, &rate, &channels) < 1)
return -EINVAL;
rtp->rate = rate;
rtp->channels = channels;
rtp->subtype_name = talloc_strdup(ctx, audio_codec);
rtp->audio_name = talloc_strdup(ctx, audio_name);
if (!strcmp(audio_codec, "G729")) {
rtp->frame_duration_num = 10;
rtp->frame_duration_den = 1000;
} else {
rtp->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
rtp->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
}
if (payload_type < 0) {
payload_type = 96;
if (rate == 8000 && channels == 1) {
if (!strcmp(audio_codec, "GSM"))
payload_type = 3;
else if (!strcmp(audio_codec, "PCMA"))
payload_type = 8;
else if (!strcmp(audio_codec, "G729"))
payload_type = 18;
}
rtp->payload_type = payload_type;
}
if (channels != 1)
LOGP(DMGCP, LOGL_NOTICE,
"Channels != 1 in SDP: '%s'\n", audio_name);
return 0;
}
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end,
struct mgcp_port_range *range,
int (*alloc)(struct mgcp_endpoint *endp, int port))
@ -612,11 +740,8 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
break;
case 'a': {
int payload;
int rate;
int channels = 1;
int ptime, ptime2 = 0;
char audio_name[64];
char audio_codec[64];
if (audio_payload == -1)
break;
@ -626,15 +751,7 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
if (payload != audio_payload)
break;
if (sscanf(audio_name, "%[^/]/%d/%d",
audio_codec, &rate, &channels) < 2)
break;
rtp->rate = rate;
if (channels != 1)
LOGP(DMGCP, LOGL_NOTICE,
"Channels != 1 in SDP: '%s' on 0x%x\n",
line, ENDPOINT_NUMBER(p->endp));
set_audio_info(p->cfg, rtp, payload, audio_name);
} else if (sscanf(line, "a=ptime:%d-%d",
&ptime, &ptime2) >= 1) {
if (ptime2 > 0 && ptime2 != ptime)
@ -657,8 +774,8 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
&port, &audio_payload) == 2) {
rtp->rtp_port = htons(port);
rtp->rtcp_port = htons(port + 1);
rtp->payload_type = audio_payload;
found_media = 1;
set_audio_info(p->cfg, rtp, audio_payload, NULL);
}
break;
}
@ -685,9 +802,10 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
if (found_media)
LOGP(DMGCP, LOGL_NOTICE,
"Got media info via SDP: port %d, payload %d, "
"Got media info via SDP: port %d, payload %d (%s), "
"duration %d, addr %s\n",
ntohs(rtp->rtp_port), rtp->payload_type,
rtp->subtype_name ? rtp->subtype_name : "unknown",
rtp->packet_duration_ms, inet_ntoa(rtp->addr));
return found_media;
@ -700,9 +818,12 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
const char *options)
{
char *p_opt;
char *p_opt, *a_opt;
char codec[9];
talloc_free(lco->string);
talloc_free(lco->codec);
lco->codec = NULL;
lco->pkt_period_min = lco->pkt_period_max = 0;
lco->string = talloc_strdup(ctx, options ? options : "");
@ -710,6 +831,10 @@ static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
if (p_opt && sscanf(p_opt, "p:%d-%d",
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
lco->pkt_period_max = lco->pkt_period_min;
a_opt = strstr(lco->string, "a:");
if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
lco->codec = talloc_strdup(ctx, codec);
}
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
@ -849,11 +974,21 @@ mgcp_header_done:
endp->allocated = 1;
/* set up RTP media parameters */
endp->bts_end.payload_type = tcfg->audio_payload;
set_audio_info(p->cfg, &endp->bts_end, tcfg->audio_payload, tcfg->audio_name);
endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints,
tcfg->audio_fmtp_extra);
if (have_sdp)
parse_sdp_data(&endp->net_end, p);
else if (endp->local_options.codec)
set_audio_info(p->cfg, &endp->net_end,
PTYPE_UNDEFINED, endp->local_options.codec);
if (p->cfg->bts_force_ptime) {
endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime;
endp->bts_end.force_output_ptime = 1;
}
setup_rtp_processing(endp);
/* policy CB */
if (p->cfg->policy_cb) {
@ -904,6 +1039,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
struct mgcp_endpoint *endp = p->endp;
int error_code = 500;
int silent = 0, osmux = 0;
int have_sdp = 0;
char *line;
const char *local_options = NULL;
@ -950,6 +1086,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
break;
case '\0':
/* SDP file begins */
have_sdp = 1;
parse_sdp_data(&endp->net_end, p);
/* This will exhaust p->save, so the loop will
* terminate next time.
@ -980,6 +1117,12 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
local_options);
if (!have_sdp && endp->local_options.codec)
set_audio_info(p->cfg, &endp->net_end,
PTYPE_UNDEFINED, endp->local_options.codec);
setup_rtp_processing(endp);
/* policy CB */
if (p->cfg->policy_cb) {
int rc;
@ -1223,6 +1366,11 @@ struct mgcp_config *mgcp_config_alloc(void)
cfg->bts_ports.base_port = RTP_PORT_DEFAULT;
cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT;
cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
/* default trunk handling */
cfg->trunk.cfg = cfg;
cfg->trunk.trunk_nr = 0;
@ -1288,6 +1436,12 @@ static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end)
end->local_alloc = -1;
talloc_free(end->fmtp_extra);
end->fmtp_extra = NULL;
talloc_free(end->subtype_name);
end->subtype_name = NULL;
talloc_free(end->audio_name);
end->audio_name = NULL;
talloc_free(end->rtp_process_data);
end->rtp_process_data = NULL;
/* Set default values */
end->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
@ -1295,6 +1449,7 @@ static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end)
end->frames_per_packet = 0; /* unknown */
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
end->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
end->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
end->output_enabled = 0;
}
@ -1340,6 +1495,8 @@ void mgcp_free_endp(struct mgcp_endpoint *endp)
talloc_free(endp->local_options.string);
endp->local_options.string = NULL;
talloc_free(endp->local_options.codec);
endp->local_options.codec = NULL;
mgcp_rtp_end_reset(&endp->bts_end);
mgcp_rtp_end_reset(&endp->net_end);
@ -1372,23 +1529,26 @@ static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port,
{
char buf[2096];
int len;
int nchars;
/* hardcoded to AMR right now, we do not know the real type at this point */
len = snprintf(buf, sizeof(buf),
"%s 42 %x@mgw MGCP 1.0\r\n"
"C: 4256\r\n"
"M: %s\r\n"
"\r\n"
"c=IN IP4 %s\r\n"
"m=audio %d RTP/AVP %d\r\n"
"a=rtpmap:%d %s\r\n",
msg, endpoint, mode, endp->cfg->source_addr,
port, endp->tcfg->audio_payload,
endp->tcfg->audio_payload, endp->tcfg->audio_name);
"\r\n",
msg, endpoint, mode);
if (len < 0)
return;
nchars = write_response_sdp(endp, buf + len, sizeof(buf) + len - 1,
endp->cfg->source_addr);
if (nchars < 0)
return;
len += nchars;
buf[sizeof(buf) - 1] = '\0';
send_trans(endp->cfg, buf, len);
@ -1442,6 +1602,27 @@ int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint)
return send_agent(endp->cfg, buf, len);
}
static void setup_rtp_processing(struct mgcp_endpoint *endp)
{
struct mgcp_config *cfg = endp->cfg;
if (endp->type != MGCP_RTP_DEFAULT)
return;
if (endp->conn_mode == MGCP_CONN_LOOPBACK)
return;
if (endp->conn_mode & MGCP_CONN_SEND_ONLY)
cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end);
else
cfg->setup_rtp_processing_cb(endp, &endp->net_end, NULL);
if (endp->conn_mode & MGCP_CONN_RECV_ONLY)
cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end);
else
cfg->setup_rtp_processing_cb(endp, &endp->bts_end, NULL);
}
static void create_transcoder(struct mgcp_endpoint *endp)
{
int port;

View File

@ -0,0 +1,550 @@
/*
* (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 <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "../../bscconfig.h"
#include "g711common.h"
#include <gsm.h>
#ifdef HAVE_BCG729
#include <bcg729/decoder.h>
#include <bcg729/encoder.h>
#endif
#include <openbsc/debug.h>
#include <openbsc/mgcp.h>
#include <openbsc/mgcp_internal.h>
#include <osmocom/core/talloc.h>
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;
int dst_packet_duration;
int is_running;
uint16_t next_seq;
uint32_t next_time;
int16_t samples[10*160];
size_t sample_cnt;
size_t sample_offs;
};
int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst)
{
struct mgcp_process_rtp_state *state = state_;
if (dst)
return (nsamples >= 0 ?
nsamples / state->dst_samples_per_frame :
1) * state->dst_frame_size;
else
return (nsamples >= 0 ?
nsamples / state->src_samples_per_frame :
1) * state->src_frame_size;
}
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;
}
if (dst_end->force_output_ptime)
state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end);
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;
}
static int decode_audio(struct mgcp_process_rtp_state *state,
uint8_t **src, size_t *nbytes)
{
while (*nbytes >= state->src_frame_size) {
if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) {
LOGP(DMGCP, LOGL_ERROR,
"Sample buffer too small: %d > %d.\n",
state->sample_cnt + state->src_samples_per_frame,
ARRAY_SIZE(state->samples));
return -ENOSPC;
}
switch (state->src_fmt) {
case AF_GSM:
if (gsm_decode(state->src.gsm_handle,
(gsm_byte *)*src, state->samples + state->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, state->samples + state->sample_cnt);
break;
#endif
case AF_PCMA:
alaw_decode(*src, state->samples + state->sample_cnt,
state->src_samples_per_frame);
break;
case AF_S16:
memmove(state->samples + state->sample_cnt, *src,
state->src_frame_size);
break;
case AF_L16:
l16_decode(*src, state->samples + state->sample_cnt,
state->src_samples_per_frame);
break;
default:
break;
}
*src += state->src_frame_size;
*nbytes -= state->src_frame_size;
state->sample_cnt += state->src_samples_per_frame;
}
return 0;
}
static int encode_audio(struct mgcp_process_rtp_state *state,
uint8_t *dst, size_t buf_size, size_t max_samples)
{
int nbytes = 0;
size_t nsamples = 0;
/* Encode samples into dst */
while (nsamples + state->dst_samples_per_frame <= max_samples) {
if (nbytes + state->dst_frame_size > buf_size) {
if (nbytes > 0)
break;
/* Not even one frame fits into the buffer */
LOGP(DMGCP, LOGL_INFO,
"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,
state->samples + state->sample_offs, dst);
break;
#ifdef HAVE_BCG729
case AF_G729:
bcg729Encoder(state->dst.g729_enc,
state->samples + state->sample_offs, dst);
break;
#endif
case AF_PCMA:
alaw_encode(state->samples + state->sample_offs, dst,
state->src_samples_per_frame);
break;
case AF_S16:
memmove(dst, state->samples + state->sample_offs,
state->dst_frame_size);
break;
case AF_L16:
l16_encode(state->samples + state->sample_offs, dst,
state->src_samples_per_frame);
break;
default:
break;
}
dst += state->dst_frame_size;
nbytes += state->dst_frame_size;
state->sample_offs += state->dst_samples_per_frame;
nsamples += state->dst_samples_per_frame;
}
state->sample_cnt -= nsamples;
return nbytes;
}
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;
uint8_t *src = (uint8_t *)payload_data;
uint8_t *dst = (uint8_t *)payload_data;
size_t nbytes = payload_len;
size_t nsamples;
size_t max_samples;
uint32_t ts_no;
int rc;
if (!state)
return 0;
if (state->src_fmt == state->dst_fmt) {
if (!state->dst_packet_duration)
return 0;
/* TODO: repackage without transcoding */
}
/* If the remaining samples do not fit into a fixed ptime,
* a) discard them, if the next packet is much later
* b) add silence and * send it, if the current packet is not
* yet too late
* c) append the sample data, if the timestamp matches exactly
*/
/* TODO: check payload type (-> G.711 comfort noise) */
if (payload_len > 0) {
ts_no = ntohl(*(uint32_t*)(data+4));
if (!state->is_running)
state->next_seq = ntohs(*(uint32_t*)(data+4));
state->is_running = 1;
if (state->sample_cnt > 0) {
int32_t delta = ts_no - state->next_time;
/* TODO: check sequence? reordering? packet loss? */
if (delta > state->sample_cnt)
/* There is a time gap between the last packet
* and the current one. Just discard the
* partial data that is left in the buffer.
* TODO: This can be improved by adding silence
* instead if the delta is small enough.
*/
state->sample_cnt = 0;
else if (delta < 0) {
LOGP(DMGCP, LOGL_NOTICE,
"RTP time jumps backwards, delta = %d, "
"discarding buffered samples\n",
delta);
state->sample_cnt = 0;
state->sample_offs = 0;
return -EAGAIN;
}
/* Make sure the samples start without offset */
if (state->sample_offs && state->sample_cnt)
memmove(&state->samples[0],
&state->samples[state->sample_offs],
state->sample_cnt *
sizeof(state->samples[0]));
}
state->sample_offs = 0;
/* Append decoded audio to samples */
decode_audio(state, &src, &nbytes);
if (nbytes > 0)
LOGP(DMGCP, LOGL_NOTICE,
"Skipped audio frame in RTP packet: %d octets\n",
nbytes);
} else
ts_no = state->next_time;
if (state->sample_cnt < state->dst_packet_duration)
return -EAGAIN;
max_samples =
state->dst_packet_duration ?
state->dst_packet_duration : state->sample_cnt;
nsamples = state->sample_cnt;
rc = encode_audio(state, dst, buf_size, max_samples);
if (rc <= 0)
return rc;
nsamples -= state->sample_cnt;
*len = rtp_hdr_size + rc;
*(uint16_t*)(data+2) = htonl(state->next_seq);
*(uint32_t*)(data+4) = htonl(ts_no);
state->next_seq += 1;
state->next_time = ts_no + nsamples;
return nsamples ? rtp_hdr_size : 0;
}

View File

@ -366,6 +366,26 @@ ALIAS_DEPRECATED(cfg_mgcp_rtp_ip_dscp, cfg_mgcp_rtp_ip_tos_cmd,
RTP_STR
"Apply IP_TOS to the audio stream\n" "The DSCP value\n")
#define FORCE_PTIME_STR "Force a fixed ptime for packets sent to the BTS"
DEFUN(cfg_mgcp_rtp_force_ptime,
cfg_mgcp_rtp_force_ptime_cmd,
"rtp force-ptime (10|20|40)",
RTP_STR FORCE_PTIME_STR
"The required ptime (packet duration) in ms\n")
{
g_cfg->bts_force_ptime = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_no_rtp_force_ptime,
cfg_mgcp_no_rtp_force_ptime_cmd,
"no rtp force-ptime",
NO_STR RTP_STR FORCE_PTIME_STR)
{
g_cfg->bts_force_ptime = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_mgcp_sdp_fmtp_extra,
cfg_mgcp_sdp_fmtp_extra_cmd,
"sdp audio fmtp-extra .NAME",
@ -1123,6 +1143,8 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_base_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_force_ptime_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_rtp_force_ptime_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_cmd);
install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_once_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_rtp_keepalive_cmd);

View File

@ -5,7 +5,8 @@ AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) \
bin_PROGRAMS = osmo-bsc_mgcp
osmo_bsc_mgcp_SOURCES = mgcp_main.c
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)

View File

@ -49,6 +49,10 @@
#include "../../bscconfig.h"
#ifdef BUILD_MGCP_TRANSCODING
#include "openbsc/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);

View File

@ -1,3 +1,4 @@
enable_nat_test='@osmo_ac_build_nat@'
enable_smpp_test='@osmo_ac_build_smpp@'
enable_bsc_test='@osmo_ac_build_bsc@'
enable_mgcp_transcoding_test='@osmo_ac_mgcp_transcoding@'

View File

@ -1,11 +1,15 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS)
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_srcdir)
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(COVERAGE_CFLAGS) $(LIBBCG729_CFLAGS)
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
EXTRA_DIST = mgcp_test.ok
EXTRA_DIST = mgcp_test.ok mgcp_transcoding_test.ok
noinst_PROGRAMS = mgcp_test
if BUILD_MGCP_TRANSCODING
noinst_PROGRAMS += mgcp_transcoding_test
endif
mgcp_test_SOURCES = mgcp_test.c
mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
@ -13,3 +17,12 @@ mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBOSMOCORE_LIBS) -lrt -lm $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \
$(LIBRARY_DL) $(LIBOSMONETIF_LIBS)
mgcp_transcoding_test_SOURCES = mgcp_transcoding_test.c
mgcp_transcoding_test_LDADD = \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libmgcp/libmgcp.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBOSMOCORE_LIBS) $(LIBBCG729_LIBS) -lrt -lm $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \
$(LIBRARY_DL) $(LIBOSMONETIF_LIBS)

View File

@ -168,6 +168,12 @@ static void test_strline(void)
"a=rtpmap:99 AMR/8000\r\n" \
"a=ptime:40\r\n"
#define MDCX4_RO "MDCX 18983221 1@mgw MGCP 1.0\r\n" \
"M: recvonly\r" \
"C: 2\r\n" \
"I: 1\r\n" \
"L: p:20, a:AMR, nt:IN\r\n"
#define SHORT2 "CRCX 1"
#define SHORT2_RET "510 000000 FAIL\r\n"
#define SHORT3 "CRCX 1 1@mgw"
@ -258,6 +264,7 @@ static const struct mgcp_test tests[] = {
{ "MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99, 126 },
{ "MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99, 126 },
{ "MDCX4_SO", MDCX4_SO, MDCX4_RET("18983220"), 99, 126 },
{ "MDCX4_RO", MDCX4_RO, MDCX4_RET("18983221"), PTYPE_IGNORE, 126 },
{ "DLCX", DLCX, DLCX_RET, -1, -1 },
{ "CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97, 126 },
{ "EMPTY", EMPTY, EMPTY_RET },

View File

@ -49,6 +49,11 @@ Testing MDCX4_SO
Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 2: SEND
Testing MDCX4_RO
Dummy packets: 1
Packet duration not set
Requested packetetization period: 20-20
Connection mode: 1: RECV
Testing DLCX
Detected packet duration: 20
Requested packetization period not set

View File

@ -0,0 +1,377 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/mgcp.h>
#include <openbsc/mgcp_internal.h>
#include "bscconfig.h"
#ifndef BUILD_MGCP_TRANSCODING
#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)"
#endif
#include "openbsc/mgcp_transcode.h"
uint8_t *audio_frame_l16[] = {
};
struct rtp_packets {
float t;
int len;
char *data;
};
struct rtp_packets audio_packets_l16[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 332,
"\x80\x0B\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
},
};
struct rtp_packets audio_packets_gsm[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 45,
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
"\xDE"
},
};
struct rtp_packets audio_packets_gsm_invalid_size[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 41,
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
"\xDE"
},
};
struct rtp_packets audio_packets_gsm_invalid_data[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 45,
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
"\xEE"
},
};
struct rtp_packets audio_packets_gsm_invalid_ptype[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 45,
"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
"\xDE"
},
};
struct rtp_packets audio_packets_g729[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 32,
"\x80\x12\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xAF\xC2\x81\x40\x00\xFA\xCE\xA4\x21\x7C\xC5\xC3\x4F\xA5\x98\xF5"
"\xB2\x95\xC4\xAD"
},
};
struct rtp_packets audio_packets_pcma[] = {
/* RTP: SeqNo=1, TS=160 */
{0.020000, 172,
"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
},
};
static int audio_name_to_type(const char *name)
{
if (!strcasecmp(name, "gsm"))
return 3;
#ifdef HAVE_BCG729
else if (!strcasecmp(name, "g729"))
return 18;
#endif
else if (!strcasecmp(name, "pcma"))
return 8;
else if (!strcasecmp(name, "l16"))
return 11;
return -1;
}
int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst);
static int transcode_test(const char *srcfmt, const char *dstfmt,
uint8_t *src_pkts, size_t src_pkt_size)
{
char buf[4096] = {0x80, 0};
int rc;
struct mgcp_rtp_end *dst_end;
struct mgcp_rtp_end *src_end;
struct mgcp_trunk_config tcfg = {{0}};
struct mgcp_endpoint endp = {0};
struct mgcp_process_rtp_state *state;
int in_size;
int in_samples = 160;
int len, cont;
printf("== Transcoding test ==\n");
printf("converting %s -> %s\n", srcfmt, dstfmt);
tcfg.endpoints = &endp;
tcfg.number_endpoints = 1;
endp.tcfg = &tcfg;
mgcp_free_endp(&endp);
dst_end = &endp.bts_end;
src_end = &endp.net_end;
src_end->payload_type = audio_name_to_type(srcfmt);
dst_end->payload_type = audio_name_to_type(dstfmt);
rc = mgcp_transcoding_setup(&endp, dst_end, src_end);
if (rc < 0)
errx(1, "setup failed: %s", strerror(-rc));
state = dst_end->rtp_process_data;
OSMO_ASSERT(state != NULL);
in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
OSMO_ASSERT(sizeof(buf) >= in_size + 12);
memcpy(buf, src_pkts, src_pkt_size);
len = src_pkt_size;
cont = mgcp_transcoding_process_rtp(&endp, dst_end,
buf, &len, sizeof(buf));
if (cont < 0)
errx(1, "processing failed: %s", strerror(-cont));
if (len < 24) {
printf("encoded: %s\n", osmo_hexdump((unsigned char *)buf, len));
} else {
const char *str = osmo_hexdump((unsigned char *)buf, len);
int i = 0;
const int prefix = 4;
const int cutlen = 48;
int nchars = 0;
printf("encoded:\n");
do {
nchars = printf("%*s%-.*s", prefix, "", cutlen, str + i);
i += nchars - prefix;
printf("\n");
} while (nchars - prefix >= cutlen);
}
return 0;
}
static int test_repacking(int in_samples, int out_samples, int no_transcode)
{
char buf[4096] = {0x80, 0};
int cc, rc;
struct mgcp_rtp_end *dst_end;
struct mgcp_rtp_end *src_end;
struct mgcp_config *cfg;
struct mgcp_trunk_config tcfg = {{0}};
struct mgcp_endpoint endp = {0};
struct mgcp_process_rtp_state *state;
int in_cnt;
int out_size;
int in_size;
uint32_t ts = 0;
uint16_t seq = 0;
const char *srcfmt = "pcma";
const char *dstfmt = no_transcode ? "pcma" : "l16";
cfg = mgcp_config_alloc();
tcfg.endpoints = &endp;
tcfg.number_endpoints = 1;
tcfg.cfg = cfg;
endp.tcfg = &tcfg;
endp.cfg = cfg;
mgcp_free_endp(&endp);
dst_end = &endp.bts_end;
src_end = &endp.net_end;
printf("== Transcoding test ==\n");
printf("converting %s -> %s\n", srcfmt, dstfmt);
src_end->payload_type = audio_name_to_type(srcfmt);
dst_end->payload_type = audio_name_to_type(dstfmt);
if (out_samples) {
dst_end->frame_duration_den = dst_end->rate;
dst_end->frame_duration_num = out_samples;
dst_end->frames_per_packet = 1;
dst_end->force_output_ptime = 1;
}
rc = mgcp_transcoding_setup(&endp, dst_end, src_end);
if (rc < 0)
errx(1, "setup failed: %s", strerror(-rc));
state = dst_end->rtp_process_data;
OSMO_ASSERT(state != NULL);
in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
OSMO_ASSERT(sizeof(buf) >= in_size + 12);
out_size = mgcp_transcoding_get_frame_size(state, -1, 1);
OSMO_ASSERT(sizeof(buf) >= out_size + 12);
buf[1] = src_end->payload_type;
*(uint16_t*)(buf+2) = htons(1);
*(uint32_t*)(buf+4) = htonl(0);
*(uint32_t*)(buf+8) = htonl(0xaabbccdd);
for (in_cnt = 0; in_cnt < 16; in_cnt++) {
int cont;
int len;
/* fake PCMA data */
printf("generating %d %s input samples\n", in_samples, srcfmt);
for (cc = 0; cc < in_samples; cc++)
buf[12+cc] = cc;
*(uint16_t*)(buf+2) = htonl(seq);
*(uint32_t*)(buf+4) = htonl(ts);
seq += 1;
ts += in_samples;
cc += 12; /* include RTP header */
len = cc;
do {
cont = mgcp_transcoding_process_rtp(&endp, dst_end,
buf, &len, sizeof(buf));
if (cont == -EAGAIN) {
fprintf(stderr, "Got EAGAIN\n");
break;
}
if (cont < 0)
errx(1, "processing failed: %s", strerror(-cont));
len -= 12; /* ignore RTP header */
printf("got %d %s output frames (%d octets)\n",
len / out_size, dstfmt, len);
len = cont;
} while (len > 0);
}
return 0;
}
int main(int argc, char **argv)
{
osmo_init_logging(&log_info);
printf("=== Transcoding Good Cases ===\n");
transcode_test("l16", "l16",
(uint8_t *)audio_packets_l16[0].data,
audio_packets_l16[0].len);
transcode_test("l16", "gsm",
(uint8_t *)audio_packets_l16[0].data,
audio_packets_l16[0].len);
transcode_test("l16", "pcma",
(uint8_t *)audio_packets_l16[0].data,
audio_packets_l16[0].len);
transcode_test("gsm", "l16",
(uint8_t *)audio_packets_gsm[0].data,
audio_packets_gsm[0].len);
transcode_test("gsm", "gsm",
(uint8_t *)audio_packets_gsm[0].data,
audio_packets_gsm[0].len);
transcode_test("gsm", "pcma",
(uint8_t *)audio_packets_gsm[0].data,
audio_packets_gsm[0].len);
transcode_test("pcma", "l16",
(uint8_t *)audio_packets_pcma[0].data,
audio_packets_pcma[0].len);
transcode_test("pcma", "gsm",
(uint8_t *)audio_packets_pcma[0].data,
audio_packets_pcma[0].len);
transcode_test("pcma", "pcma",
(uint8_t *)audio_packets_pcma[0].data,
audio_packets_pcma[0].len);
printf("=== Transcoding Bad Cases ===\n");
printf("Invalid size:\n");
transcode_test("gsm", "pcma",
(uint8_t *)audio_packets_gsm_invalid_size[0].data,
audio_packets_gsm_invalid_size[0].len);
printf("Invalid data:\n");
transcode_test("gsm", "pcma",
(uint8_t *)audio_packets_gsm_invalid_data[0].data,
audio_packets_gsm_invalid_data[0].len);
printf("Invalid payload type:\n");
transcode_test("gsm", "pcma",
(uint8_t *)audio_packets_gsm_invalid_ptype[0].data,
audio_packets_gsm_invalid_ptype[0].len);
printf("=== Repacking ===\n");
test_repacking(160, 160, 0);
test_repacking(160, 160, 1);
test_repacking(160, 80, 0);
test_repacking(160, 80, 1);
test_repacking(160, 320, 0);
test_repacking(160, 320, 1);
test_repacking(160, 240, 0);
test_repacking(160, 240, 1);
test_repacking(160, 100, 0);
test_repacking(160, 100, 1);
return 0;
}

View File

@ -0,0 +1,534 @@
=== Transcoding Good Cases ===
== Transcoding test ==
converting l16 -> l16
encoded:
80 0b 00 01 00 00 00 a0 11 22 33 44 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed 00 00 40 13
5a 9e 40 13 00 00 bf ed a5 62 bf ed
== Transcoding test ==
converting l16 -> gsm
encoded:
80 0b 00 00 00 00 00 a0 11 22 33 44 d4 7c e3 e9
62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc
69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de
== Transcoding test ==
converting l16 -> pcma
encoded:
80 0b 00 00 00 00 00 a0 11 22 33 44 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25
== Transcoding test ==
converting gsm -> l16
encoded:
80 03 00 00 00 00 00 a0 11 22 33 44 00 00 54 00
59 f0 34 20 c4 c8 b9 f8 e2 18 f1 e8 f2 28 f0 e0
46 08 4f 80 2c a0 a9 c8 80 00 c0 58 3f 80 63 c0
24 b8 fa b8 f6 88 0b a0 c8 70 a8 b0 c8 c0 3b a8
66 a0 2e 38 d1 f8 98 98 aa 18 e8 30 26 a0 37 40
37 e8 17 00 ee 50 b7 80 b1 88 de 28 18 40 45 b0
4f 48 21 d8 df 78 ae 68 ab 98 d6 b8 18 18 48 90
4e 70 27 40 e8 10 b5 b0 ac 80 d4 60 14 50 48 48
50 10 2a 00 ec 08 ba 00 af 58 d1 c0 10 60 45 c8
54 10 30 78 f1 a8 bb 18 ad 48 ce 30 0a e8 3f 30
4f 10 32 18 f6 18 bf 20 ac 30 cd 80 0b d0 43 d8
55 e0 34 a0 f5 78 bc 98 ad 98 cd c8 0a 80 40 58
51 c0 35 40 f9 60 c1 68 ac c8 cb 38 08 00 40 98
51 e0 34 d8 fa 28 c2 f0 ae 40 c7 70 02 d0 3c a8
54 78 38 a0 fc 68 c2 08 ad 50 c7 78 01 60 39 c0
51 38 3a e8 00 e8 c6 38 ab d8 c4 00 fe 08 39 18
50 30 39 50 01 d8 ca 70 b1 80 c4 c8 fc 58 36 40
51 d8 3b 08 02 80 c8 58 b0 60 c5 a8 fb d0 33 e8
4e 80 3c e0 06 10 cb 90 ae 48 c2 60 f9 58 34 08
4d a0 3a a8 06 48 cf 80 b4 60 c3 e8 f7 90 30 18
4d a0 3b 98 07 90 cf 18 b4 68 c4 88
== Transcoding test ==
converting gsm -> gsm
encoded:
80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9
62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc
69 9c d1 f0 66 7a ec 49 7a 33 3d 0a de
== Transcoding test ==
converting gsm -> pcma
encoded:
80 03 00 00 00 00 00 a0 11 22 33 44 d5 a0 a3 bf
38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60
17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82
04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6
02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd
19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf
10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf
62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8
db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8
f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8
ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38
== Transcoding test ==
converting pcma -> l16
encoded:
80 08 00 00 00 00 00 a0 11 22 33 44 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00 00 08 42 00
5a 00 42 00 00 08 be 00 a6 00 be 00
== Transcoding test ==
converting pcma -> gsm
encoded:
80 08 00 00 00 00 00 a0 11 22 33 44 d4 b9 f4 5d
d9 50 5a e1 a0 cd 76 ea 52 0e 87 53 ad d4 ea a2
0a 63 ca e9 60 79 e2 2a 25 d2 c0 f3 39
== Transcoding test ==
converting pcma -> pcma
encoded:
80 08 00 01 00 00 00 a0 11 22 33 44 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25 d5 a5 a3 a5
d5 25 23 25 d5 a5 a3 a5 d5 25 23 25
=== Transcoding Bad Cases ===
Invalid size:
== Transcoding test ==
converting gsm -> pcma
encoded:
80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9
62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc
69 9c d1 f0 66 7a ec 49 7a
Invalid data:
== Transcoding test ==
converting gsm -> pcma
encoded:
80 03 00 01 00 00 00 a0 11 22 33 44 ee ee ee ee
ee ee ee ee ee ee ee ee ee ee ee ee ee ee ee ee
ee ee ee ee ee ee ee ee ee ee ee ee ee
Invalid payload type:
== Transcoding test ==
converting gsm -> pcma
encoded:
80 08 00 00 00 00 00 a0 11 22 33 44 d5 a0 a3 bf
38 24 08 19 1e 1b a4 a6 b3 20 2a 3a ba ad b7 60
17 92 3e 20 3e b8 ac b2 32 2c 20 02 b6 be be 82
04 27 26 35 8d a4 a6 b5 35 21 20 31 8d a7 a6 b6
02 27 21 30 81 a7 a1 b0 06 24 21 32 85 a4 a0 bd
19 24 21 3d 90 ba a6 bc 16 25 21 3c 92 a5 a0 bf
10 25 21 3c 90 a5 a1 bf 6f 3a 21 3f 95 a5 a1 bf
62 3b 21 39 f3 bb a0 b9 79 3b 21 39 c3 b9 a1 b8
db 39 20 3b 4a b9 a1 b9 c8 3f 26 38 78 be a1 b8
f1 3e 26 38 65 bc a6 bb ed 3f 21 3b 6f bf a6 b8
ec 3d 27 3b 15 bd a6 b8 eb 3d 27 38
=== Repacking ===
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
generating 160 pcma input samples
got 2 l16 output frames (320 octets)
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
generating 160 pcma input samples
got 2 pcma output frames (160 octets)
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets)
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets)
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets)
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets)
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets)
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
generating 160 pcma input samples
got 1 l16 output frames (160 octets)
got 1 l16 output frames (160 octets)
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)
generating 160 pcma input samples
got 1 pcma output frames (80 octets)
got 1 pcma output frames (80 octets)

View File

@ -27,6 +27,13 @@ cat $abs_srcdir/mgcp/mgcp_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([mgcp-trans])
AT_KEYWORDS([mgcp-trans])
AT_CHECK([test "$enable_mgcp_transcoding_test" == yes || exit 77])
cat $abs_srcdir/mgcp/mgcp_transcoding_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_transcoding_test], [], [expout], [ignore])
AT_CLEANUP
AT_SETUP([gprs])
AT_KEYWORDS([gprs])
cat $abs_srcdir/gprs/gprs_test.ok > expout