Merge remote-tracking branch 'origin/jerlbeck/features/mgcp-transcoding'
This commit is contained in:
commit
efd266ae12
|
@ -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)
|
||||
|
|
|
@ -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 $@ $<
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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@'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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)
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue