Initially implement the new osmo-mgw and libosmo-mgcp

Leave the old osmo-bsc_mgcp and libosmo-legacy-mgcp as it is; on a copy thereof
(added by a previous commit), apply changes to initially implement the new
osmo-mgw.

Adjust build system and debian packaging to accomodate the new libosmo-mgcp and
osmo-mgw.

The main differences:

*) use a list to manage rtp connections.

Aggregate all rtp related information inside a single struct.

Use a linked list to manage the both connections (net and bts).
The idea behind using a list is that we might support conference
calls at some later point.

Store the linked list in struct mgcp_endpoint, have a private linked
list for each endpoint. The list contains connection items which are
implemented in struct mgcp_conn. A connection is allocated and freed
using the functions in mgcp_conn.c. A connection is allocated on the
reception of a CRCX command and freed with the reception of a DLCX
command.

*) remove external transcoder feature

Fortunatelly the external transcoder feature is not needed
anymore. This patch removes the related code.

*) vty: get rid of CONN_BTS and CONN_NET

Since the new connection model does not make a difference
between BTS and NET connections the VTY should not use
the fixed CONN_BTS and CONN_NET constants.

- Handle the conns list inside the endpoint directly
- introduce function to dump basic rtp connection info
- introduce human readable names for connections

Parts of the code adjusted to use generalized connections instead of explicit
BTS/NET ones:

- teach mgcp_send_dummy() to send dummy packets to any RTP connection
- network: generalize mgcp_bind_net/bts_rtp_port()
- network: generalize mgcp_send()
- tap: generalize call tapping feature
- stat: generalize statistics
- Replace rtp_data_net() and rtp_data_bts() with generalized rtp_data_rx()

*) mgcp_protocol.c fixes:

- check ci string before it is converted:
  In case of missing ci, a nullpointer is delivered to strtoul().
  Add a function that takes ci, checks it and converts it to an
  uint32_t. Use the return code to react on missing ci.
- output error message on missing CI.
- when parsing the mode, print log message when mode is missing.
- use mode_orig when mode is missing.
- fix ptime formatstring to use %u rather than %d.
- cosmetic: log when connection is deleted on DLCX.
- change loglevels of CRCX, MDCX, DLCX events from DEBUG to NOTICE.

*) mgcp_test

- apply rename of strline_r() to mgcp_strline().
- MGCP command macros:
  - Add 'I: 1' parameters.
  - Use proper port numbers:
    from m=audio 0 RTP/AVP 126
    to   m=audio 16002 RTP/AVP 128
  - Change ptime to 'a=ptime:40' because this is what the MGW currently
    returns.  CRCX generally feed a ptime:40 and this is expected to be
    returned.
- struct mgcp_test: Use only one ptype, there are no explicit BTS and NET
  endpoints anymore.
  Hence remove one column from tests[].
- test_messages():
  - Enable: remove '#if 0'
  - Remove concept of BTS and NET endpoints: test only one conn, as they are
    now interchangeable anyway.
  - remove endpoint init, now done internally.
  - add false asserts in error cases.
- test_retransmission():
  - remove endpoint init, now done internally.
  - add false asserts in error cases.
- test_packet_error_detection():
  - Remove concept of BTS and NET endpoints: test only one conn, as they are
    now interchangeable anyway. Use arbitrary conn ids (e.g. 4711).
  - remove endpoint init, now done internally.
  - add false assert in error case.
  - Assert that a conn really vanishes on DLCX, previously the conn would
    remain and just be unused, now it is actually discarded.
- test_no_cycle()
  - Remove concept of BTS and NET endpoints: test only one conn, as they are
    now interchangeable anyway. Use arbitrary conn ids (e.g. 4711).
- test_no_name()
  - Enable: remove '#if 0'.
  - remove endpoint init, now done internally.
  - add false assert in error case.
- mgcp_test.ok: adjust expected results to status quo:
  - We now see two dummy packets instead of one, now sent to both sides because
    we don't know of BTS or NET side. (maybe drop dummy packets later...)
  - packet duration, conn mode: now sane defaults show instead of unset.
- various whitespace and formatting changes from lindent.

Change-Id: Ie008599136c7ed8a0dfbb0cf803188975a499fc5
This commit is contained in:
Philipp Maier 2017-08-22 16:35:41 +02:00 committed by Harald Welte
parent f83ec56212
commit 87bd9be0b0
42 changed files with 3402 additions and 4597 deletions

View File

@ -20,6 +20,7 @@ pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = \
libosmo-legacy-mgcp.pc \
libosmo-mgcp-client.pc \
libosmo-mgcp.pc \
$(NULL)
BUILT_SOURCES = $(top_srcdir)/.version

View File

@ -119,18 +119,23 @@ AM_CONFIG_HEADER(bscconfig.h)
AC_OUTPUT(
libosmo-legacy-mgcp.pc
libosmo-mgcp-client.pc
libosmo-mgcp.pc
include/Makefile
include/osmocom/Makefile
include/osmocom/legacy_mgcp/Makefile
include/osmocom/mgcp_client/Makefile
include/osmocom/mgcp/Makefile
src/Makefile
src/libosmo-legacy-mgcp/Makefile
src/libosmo-mgcp-client/Makefile
src/libosmo-mgcp/Makefile
src/osmo-bsc_mgcp/Makefile
src/osmo-mgw/Makefile
tests/Makefile
tests/atlocal
tests/legacy_mgcp/Makefile
tests/mgcp_client/Makefile
tests/mgcp/Makefile
doc/Makefile
doc/examples/Makefile
contrib/Makefile

33
debian/control vendored
View File

@ -16,23 +16,23 @@ Homepage: https://osmocom.org/projects/osmo-mgw
Package: osmo-mgw
Architecture: any
Multi-Arch: foreign
Depends: libosmo-legacy-mgcp0, ${misc:Depends}, ${shlibs:Depends}
Depends: libosmo-mgcp0, ${misc:Depends}, ${shlibs:Depends}
Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks
Package: libosmo-legacy-mgcp0
Package: libosmo-mgcp0
Section: libs
Architecture: any
Multi-Arch: same
Pre-Depends: ${misc:Pre-Depends}
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: libosmo-legacy-mgcp: Osmocom's Media Gateway server library
Description: libosmo-mgcp: Osmocom's Media Gateway server library
Package: libosmo-legacy-mgcp-dev
Package: libosmo-mgcp-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-legacy-mgcp0 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-legacy-mgcp: Osmocom's Media Gateway server library
Depends: libosmo-mgcp0 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-mgcp: Osmocom's Media Gateway server library
Package: libosmo-mgcp-client0
Section: libs
@ -48,3 +48,24 @@ Architecture: any
Multi-Arch: same
Depends: libosmo-mgcp-client0 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities
Package: osmo-bsc-mgcp
Architecture: any
Multi-Arch: foreign
Depends: libosmo-legacy-mgcp0, ${misc:Depends}, ${shlibs:Depends}
Description: OsmoBSC-MGCP: Osmocom's Legacy Media Gateway; use osmo-mgw instead.
Package: libosmo-legacy-mgcp0
Section: libs
Architecture: any
Multi-Arch: same
Pre-Depends: ${misc:Pre-Depends}
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.
Package: libosmo-legacy-mgcp-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libosmo-legacy-mgcp0 (= ${binary:Version}), ${misc:Depends}
Description: libosmo-legacy-mgcp: Osmocom's Legacy Media Gateway server library; use libosmo-mgcp instead.

4
debian/libosmo-mgcp-dev.install vendored Normal file
View File

@ -0,0 +1,4 @@
usr/include/osmocom/mgcp
usr/lib/*/libosmo-mgcp.so
usr/lib/*/libosmo-mgcp.a
usr/lib/*/pkgconfig/libosmo-mgcp.pc

1
debian/libosmo-mgcp0.install vendored Normal file
View File

@ -0,0 +1 @@
usr/lib/*/libosmo-mgcp.so.*

View File

@ -6,7 +6,6 @@ mgcp
!bts ip 10.24.24.1
!bind ip 10.23.24.1
bind port 2427
rtp base 4000
rtp force-ptime 20
sdp audio payload number 98
sdp audio payload name AMR/8000

View File

@ -8,4 +8,7 @@ nobase_include_HEADERS = \
osmocom/legacy_mgcp/osmux.h \
osmocom/mgcp_client/mgcp_client.h \
osmocom/mgcp_client/mgcp_common.h \
osmocom/mgcp/mgcp.h \
osmocom/mgcp/mgcp_internal.h \
osmocom/mgcp/osmux.h \
$(NULL)

View File

@ -1,4 +1,5 @@
SUBDIRS = \
legacy_mgcp \
mgcp_client \
mgcp \
$(NULL)

View File

@ -1,4 +1,7 @@
noinst_HEADERS = \
mgcp_transcode.h \
vty.h \
mgcp_msg.h \
mgcp_conn.h \
mgcp_stat.h \
mgcp_ep.h \
$(NULL)

View File

@ -20,21 +20,21 @@
*
*/
#ifndef OPENBSC_MGCP_H
#define OPENBSC_MGCP_H
#pragma once
#include <osmocom/core/msgb.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/logging.h>
#include <osmocom/mgcp/mgcp_ep.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define RTP_PORT_DEFAULT 4000
#define RTP_PORT_NET_DEFAULT 16000
#define RTP_PORT_DEFAULT_RANGE_START 16002
#define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64
/**
* Calculate the RTP audio port for the given multiplex
@ -102,26 +102,20 @@ typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
struct mgcp_rtp_end *src_end);
struct mgcp_conn_rtp;
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
const char**fmtp_extra,
struct mgcp_conn_rtp *conn);
/**
* This holds information on how to allocate ports
*/
struct mgcp_port_range {
int mode;
/* addr or NULL to fall-back to default */
char *bind_addr;
/* pre-allocated from a base? */
int base_port;
/* dynamically allocated */
int range_start;
int range_end;
@ -160,6 +154,10 @@ struct mgcp_trunk_config {
/* timer */
struct osmo_timer_list keepalive_timer;
/* When set, incoming RTP packets are not filtered
* when ports and ip-address do not match (debug) */
int rtp_accept_all;
unsigned int number_endpoints;
struct mgcp_endpoint *endpoints;
};
@ -188,16 +186,8 @@ struct mgcp_config {
int source_port;
char *local_ip;
char *source_addr;
char *bts_ip;
char *call_agent_addr;
struct in_addr bts_in;
/* transcoder handling */
char *transcoder_ip;
struct in_addr transcoder_in;
int transcoder_remote_base;
/* RTP processing */
mgcp_processing rtp_processing_cb;
mgcp_processing_setup setup_rtp_processing_cb;
@ -206,12 +196,10 @@ struct mgcp_config {
struct osmo_wqueue gw_fd;
struct mgcp_port_range bts_ports;
struct mgcp_port_range net_ports;
struct mgcp_port_range transcoder_ports;
int endp_dscp;
int bts_force_ptime;
int force_ptime;
mgcp_change change_cb;
mgcp_policy policy_cb;
@ -226,10 +214,6 @@ struct mgcp_config {
struct mgcp_trunk_config trunk;
struct llist_head trunks;
/* only used for start with a static configuration */
int last_net_port;
int last_bts_port;
enum mgcp_role role;
/* osmux translator: 0 means disabled, 1 means enabled */
@ -259,11 +243,6 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
int mgcp_vty_init(void);
int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg);
void mgcp_release_endp(struct mgcp_endpoint *endp);
void mgcp_initialize_endp(struct mgcp_endpoint *endp);
int mgcp_reset_transcoder(struct mgcp_config *cfg);
void mgcp_format_stats(struct mgcp_endpoint *endp, char *stats, size_t size);
int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter);
void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
/*
@ -293,7 +272,4 @@ int mgcp_send_reset_all(struct mgcp_config *cfg);
int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port);
int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, struct sockaddr_in *addr, char *buf, int rc);
int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len);
#endif

View File

@ -0,0 +1,40 @@
/* Message connection list handling */
/*
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* 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/>.
*
*/
#pragma once
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/core/linuxlist.h>
#include <inttypes.h>
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
uint32_t id, enum mgcp_conn_type type,
char *name);
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id);
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp,
uint32_t id);
void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id);
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp);
void mgcp_conn_free_all(struct mgcp_endpoint *endp);
char *mgcp_conn_dump(struct mgcp_conn *conn);
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn);

View File

@ -0,0 +1,50 @@
/* Endpoint types */
/*
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* 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/>.
*
*/
#pragma once
struct sockaddr_in;
struct mgcp_conn;
/* Callback type for RTP dispatcher functions
(e.g mgcp_dispatch_rtp_bridge_cb, see below) */
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in * addr,
char *buf, unsigned int buf_size,
struct mgcp_conn * conn);
/*! MGCP endpoint properties */
struct mgcp_endpoint_type {
/*!< maximum number of connections */
int max_conns;
/*!< callback that defines how to dispatch incoming RTP data */
mgcp_dispatch_rtp_cb dispatch_rtp_cb;
};
/*! MGCP endpoint typeset */
struct mgcp_endpoint_typeset {
struct mgcp_endpoint_type rtp;
};
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
extern const struct mgcp_endpoint_typeset ep_typeset;

View File

@ -23,11 +23,16 @@
#pragma once
#include <string.h>
#include <inttypes.h>
#include <osmocom/core/select.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/core/linuxlist.h>
#define CI_UNUSED 0
#define CONN_ID_BTS 0
#define CONN_ID_NET 1
enum mgcp_trunk_type {
MGCP_TRUNK_VIRTUAL,
MGCP_TRUNK_E1,
@ -80,8 +85,10 @@ struct mgcp_rtp_codec {
struct mgcp_rtp_end {
/* statistics */
unsigned int packets;
unsigned int octets;
unsigned int packets_rx;
unsigned int octets_rx;
unsigned int packets_tx;
unsigned int octets_tx;
unsigned int dropped_packets;
struct in_addr addr;
@ -104,24 +111,11 @@ struct mgcp_rtp_end {
int force_aligned_timing;
void *rtp_process_data;
/*
* Each end has a socket...
*/
/* Each end has a separate socket for RTP and RTCP */
struct osmo_fd rtp;
struct osmo_fd rtcp;
int local_port;
int local_alloc;
};
enum {
MGCP_TAP_BTS_IN,
MGCP_TAP_BTS_OUT,
MGCP_TAP_NET_IN,
MGCP_TAP_NET_OUT,
/* last element */
MGCP_TAP_COUNT
};
struct mgcp_rtp_tap {
@ -136,51 +130,36 @@ struct mgcp_lco {
int pkt_period_max; /* time in ms */
};
enum mgcp_type {
/* Specific rtp connection type (see struct mgcp_conn_rtp) */
enum mgcp_conn_rtp_type {
MGCP_RTP_DEFAULT = 0,
MGCP_RTP_TRANSCODED,
MGCP_OSMUX_BSC,
MGCP_OSMUX_BSC_NAT,
};
#include <osmocom/legacy_mgcp/osmux.h>
#include <osmocom/mgcp/osmux.h>
struct mgcp_conn;
struct mgcp_endpoint {
int allocated;
uint32_t ci;
char *callid;
struct mgcp_lco local_options;
int conn_mode;
int orig_mode;
/* MGCP connection (RTP) */
struct mgcp_conn_rtp {
/* backpointer */
struct mgcp_config *cfg;
struct mgcp_trunk_config *tcfg;
/* Backpointer to conn struct */
struct mgcp_conn *conn;
/* port status for bts/net */
struct mgcp_rtp_end bts_end;
struct mgcp_rtp_end net_end;
/* Specific connection type */
enum mgcp_conn_rtp_type type;
/*
* For transcoding we will send from the local_port
* of trans_bts and it will arrive at trans_net from
* where we will forward it to the network.
*/
struct mgcp_rtp_end trans_bts;
struct mgcp_rtp_end trans_net;
enum mgcp_type type;
/* Port status */
struct mgcp_rtp_end end;
/* sequence bits */
struct mgcp_rtp_state net_state;
struct mgcp_rtp_state bts_state;
/* Sequence bits */
struct mgcp_rtp_state state;
/* fields for re-transmission */
char *last_trans;
char *last_response;
/* tap for the endpoint */
struct mgcp_rtp_tap taps[MGCP_TAP_COUNT];
/* taps for the rtp connection */
struct mgcp_rtp_tap tap_in;
struct mgcp_rtp_tap tap_out;
/* Osmux states (optional) */
struct {
/* Osmux state: disabled, activating, active */
enum osmux_state state;
@ -200,38 +179,64 @@ struct mgcp_endpoint {
} osmux;
};
#define for_each_line(line, save) \
for (line = strline_r(NULL, &save); line;\
line = strline_r(NULL, &save))
/*! Connection type, specifies which member of the union "u" in mgcp_conn
* contains a useful connection description (currently only RTP) */
enum mgcp_conn_type {
MGCP_CONN_TYPE_RTP,
};
static inline char *strline_r(char *str, char **saveptr)
{
char *result;
/*! MGCP connection (untyped) */
struct mgcp_conn {
/*!< list head */
struct llist_head entry;
if (str)
*saveptr = str;
/*!< Backpointer to the endpoint where the conn belongs to */
struct mgcp_endpoint *endp;
result = *saveptr;
/*!< type of the connection (union) */
enum mgcp_conn_type type;
if (*saveptr != NULL) {
*saveptr = strpbrk(*saveptr, "\r\n");
/*!< mode of the connection */
enum mgcp_connection_mode mode;
if (*saveptr != NULL) {
char *eos = *saveptr;
/*!< copy of the mode to restore the original setting (VTY) */
enum mgcp_connection_mode mode_orig;
if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n')
(*saveptr)++;
(*saveptr)++;
if ((*saveptr)[0] == '\0')
*saveptr = NULL;
/*!< connection id to identify the conntion */
uint32_t id;
*eos = '\0';
}
}
/*!< human readable name (vty, logging) */
char name[256];
return result;
}
/*!< union with connection description */
union {
struct mgcp_conn_rtp rtp;
} u;
/*!< pointer to optional private data */
void *priv;
};
#include <osmocom/mgcp/mgcp_conn.h>
struct mgcp_endpoint_type;
struct mgcp_endpoint {
char *callid;
struct mgcp_lco local_options;
struct llist_head conns;
/* backpointer */
struct mgcp_config *cfg;
struct mgcp_trunk_config *tcfg;
const struct mgcp_endpoint_type *type;
/* fields for re-transmission */
char *last_trans;
char *last_response;
};
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
@ -247,12 +252,15 @@ struct mgcp_parse_data {
int found;
};
int mgcp_send_dummy(struct mgcp_endpoint *endp);
int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port);
int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *enp, int rtp_port);
int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *enp, int rtp_port);
int mgcp_free_rtp_port(struct mgcp_rtp_end *end);
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
char *buf, int rc, struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst);
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr, char *buf,
unsigned int buf_size, struct mgcp_conn *conn);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn);
void mgcp_free_rtp_port(struct mgcp_rtp_end *end);
/* For transcoding we need to manage an in and an output that are connected */
static inline int endp_back_channel(int endpoint)
@ -268,10 +276,6 @@ void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *rtp);
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);
@ -282,8 +286,9 @@ int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
int *payload_type,
const char**subtype_name,
const char**fmtp_extra);
const char**audio_name,
const char**fmtp_extra,
struct mgcp_conn_rtp *conn);
/* internal RTP Annex A counting */
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
@ -316,10 +321,9 @@ int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, st
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
int payload_type, const char *audio_name);
/**
* Internal network related
*/
/*! get the ip-address where the mgw application is bound on.
* \param[in] endp mgcp endpoint, that holds a copy of the VTY parameters
* \returns pointer to a string that contains the source ip-address */
static inline const char *mgcp_net_src_addr(struct mgcp_endpoint *endp)
{
if (endp->cfg->net_ports.bind_addr)
@ -327,11 +331,4 @@ static inline const char *mgcp_net_src_addr(struct mgcp_endpoint *endp)
return endp->cfg->source_addr;
}
static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp)
{
if (endp->cfg->bts_ports.bind_addr)
return endp->cfg->bts_ports.bind_addr;
return endp->cfg->source_addr;
}
int mgcp_msg_terminate_nul(struct msgb *msg);

View File

@ -0,0 +1,58 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* Message parser/generator utilities */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* 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/>.
*
*/
#pragma once
#include <stdint.h>
struct mgcp_conn;
struct mgcp_parse_data;
struct mgcp_endpoint;
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble);
int mgcp_parse_conn_mode(const char *msg, struct mgcp_endpoint *endp,
struct mgcp_conn *conn);
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data);
int mgcp_parse_osmux_cid(const char *line);
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line);
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid);
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *ci);
char *mgcp_strline(char *str, char **saveptr);
#define for_each_line(line, save)\
for (line = mgcp_strline(NULL, &save); line;\
line = mgcp_strline(NULL, &save))
#define for_each_non_empty_line(line, save)\
for (line = strtok_r(NULL, "\r\n", &save); line;\
line = strtok_r(NULL, "\r\n", &save))
int mgcp_parse_ci(uint32_t *conn_id, const char *ci);

View File

@ -0,0 +1,37 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* The statistics generator */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* 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/>.
*
*/
#pragma once
#include <osmocom/mgcp/mgcp_internal.h>
#include <inttypes.h>
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn);
/* Exposed for test purposes only, do not use actively */
void calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
uint32_t *expected, int *loss);
/* Exposed for test purposes only, do not use actively */
uint32_t calc_jitter(struct mgcp_rtp_state *);

View File

@ -1,90 +0,0 @@
/*
* (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
#include "bscconfig.h"
#include <gsm.h>
#ifdef HAVE_BCG729
#include <bcg729/decoder.h>
#include <bcg729/encoder.h>
#endif
enum audio_format {
AF_INVALID,
AF_S16,
AF_L16,
AF_GSM,
AF_G729,
AF_PCMA,
AF_PCMU
};
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_setup(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
struct mgcp_rtp_end *src_end);
void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp,
int *payload_type,
const char**audio_name,
const char**fmtp_extra);
int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
char *data, int *len, int buf_size);
int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst);
#endif /* OPENBSC_MGCP_TRANSCODE_H */

View File

@ -1,7 +1,8 @@
#ifndef _OPENBSC_OSMUX_H_
#define _OPENBSC_OSMUX_H_
#pragma once
#include <osmocom/netif/osmux.h>
struct mgcp_conn_rtp;
#define OSMUX_PORT 1984
@ -11,16 +12,13 @@ enum {
};
int osmux_init(int role, struct mgcp_config *cfg);
int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port);
void osmux_disable_endpoint(struct mgcp_endpoint *endp);
void osmux_allocate_cid(struct mgcp_endpoint *endp);
void osmux_release_cid(struct mgcp_endpoint *endp);
int osmux_xfrm_to_rtp(struct mgcp_endpoint *endp, int type, char *buf, int rc);
int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp);
int osmux_send_dummy(struct mgcp_endpoint *endp);
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
struct in_addr *addr, uint16_t port);
void osmux_disable_conn(struct mgcp_conn_rtp *conn);
void osmux_allocate_cid(struct mgcp_conn_rtp *conn);
void osmux_release_cid(struct mgcp_conn_rtp *conn);
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn);
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
int osmux_get_cid(void);
void osmux_put_cid(uint8_t osmux_cid);
int osmux_used_cid(void);
@ -38,4 +36,3 @@ enum osmux_usage {
OSMUX_USAGE_ONLY = 2,
};
#endif

10
libosmo-mgcp.pc.in Normal file
View File

@ -0,0 +1,10 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: Osmocom Media Gateway Control Protocol library
Description: C Utility Library
Version: @VERSION@
Libs: -L${libdir} -losmo-mgcp
Cflags: -I${includedir}/

View File

@ -16,13 +16,15 @@
app_configs = {
"mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"],
"osmo-mgw": ["doc/examples/osmo-mgw/osmo-mgw.cfg"],
"osmo-bsc_mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"],
}
apps = [(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp"),
apps = [(4243, "src/osmo-mgw/osmo-mgw", "OsmoMGW", "osmo-mgw"),
(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "osmo-bsc_mgcp"),
]
vty_command = ["./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "-c",
"doc/examples/osmo-bsc_mgcp/mgcp.cfg"]
vty_command = ["./src/osmo-mgw/osmo-mgw", "-c",
"doc/examples/osmo-mgw/osmo-mgw.cfg"]
vty_app = apps[0]

View File

@ -23,9 +23,11 @@ AM_LDFLAGS = \
SUBDIRS = \
libosmo-legacy-mgcp \
libosmo-mgcp-client \
libosmo-mgcp \
$(NULL)
# Programs
SUBDIRS += \
osmo-bsc_mgcp \
osmo-mgw \
$(NULL)

View File

@ -10,7 +10,6 @@ AM_CFLAGS = \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(LIBBCG729_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
@ -18,33 +17,31 @@ AM_LDFLAGS = \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(COVERAGE_LDFLAGS) \
$(LIBBCG729_LIBS) \
$(NULL)
# This is not at all related to the release version, but a range of supported
# API versions. Read TODO_RELEASE in the source tree's root!
LEGACY_MGCP_LIBVERSION=0:0:0
MGCP_LIBVERSION=0:0:0
lib_LTLIBRARIES = \
libosmo-legacy-mgcp.la \
libosmo-mgcp.la \
$(NULL)
noinst_HEADERS = \
g711common.h \
$(NULL)
libosmo_legacy_mgcp_la_SOURCES = \
libosmo_mgcp_la_SOURCES = \
mgcp_common.c \
mgcp_protocol.c \
mgcp_network.c \
mgcp_vty.c \
mgcp_osmux.c \
mgcp_sdp.c \
mgcp_msg.c \
mgcp_conn.c \
mgcp_stat.c \
mgcp_ep.c \
$(NULL)
if BUILD_MGCP_TRANSCODING
libosmo_legacy_mgcp_la_SOURCES += \
mgcp_transcode.c \
$(NULL)
endif
libosmo_legacy_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LEGACY_MGCP_LIBVERSION)
libosmo_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_LIBVERSION)

View File

@ -23,7 +23,7 @@
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmocom/legacy_mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp.h>
const struct value_string mgcp_connection_mode_strs[] = {
{ MGCP_CONN_NONE, "none" },

View File

@ -0,0 +1,286 @@
/* Message connection list handling */
/*
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* 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 <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_ep.h>
/* Reset codec state and free memory */
static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec)
{
codec->payload_type = -1;
codec->subtype_name = NULL;
codec->audio_name = NULL;
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
/* see also mgcp_sdp.c, mgcp_set_audio_info() */
talloc_free(codec->subtype_name);
talloc_free(codec->audio_name);
}
/* Reset states, free memory, set defaults and reset codec state */
static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn)
{
struct mgcp_rtp_end *end = &conn->end;
conn->type = MGCP_RTP_DEFAULT;
conn->osmux.allocated_cid = -1;
end->rtp.fd = -1;
end->rtcp.fd = -1;
end->local_port = 0;
end->packets_rx = 0;
end->octets_rx = 0;
end->packets_tx = 0;
end->octets_tx = 0;
end->dropped_packets = 0;
end->rtp_port = end->rtcp_port = 0;
talloc_free(end->fmtp_extra);
end->fmtp_extra = NULL;
/* Set default values */
end->frames_per_packet = 0; /* unknown */
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
end->output_enabled = 0;
mgcp_rtp_codec_reset(&end->codec);
mgcp_rtp_codec_reset(&end->alt_codec);
}
/*! allocate a new connection list entry.
* \param[in] ctx talloc context
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \param[in] type connection type (e.g. MGCP_CONN_TYPE_RTP)
* \returns pointer to allocated connection, NULL on error */
struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
uint32_t id, enum mgcp_conn_type type,
char *name)
{
struct mgcp_conn *conn;
OSMO_ASSERT(endp);
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
OSMO_ASSERT(strlen(name) < sizeof(conn->name));
/* Do not allow more then two connections */
if (llist_count(&endp->conns) >= endp->type->max_conns)
return NULL;
/* Prevent duplicate connection IDs */
if (mgcp_conn_get(endp, id))
return NULL;
/* Create new connection and add it to the list */
conn = talloc_zero(ctx, struct mgcp_conn);
if (!conn)
return NULL;
conn->endp = endp;
conn->type = type;
conn->mode = MGCP_CONN_NONE;
conn->mode_orig = MGCP_CONN_NONE;
conn->id = id;
conn->u.rtp.conn = conn;
strcpy(conn->name, name);
switch (type) {
case MGCP_CONN_TYPE_RTP:
mgcp_rtp_conn_reset(&conn->u.rtp);
break;
default:
/* NOTE: This should never be called with an
* invalid type, its up to the programmer
* to ensure propery types */
OSMO_ASSERT(false);
}
llist_add(&conn->entry, &endp->conns);
return conn;
}
/*! find a connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id)
{
OSMO_ASSERT(endp);
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
struct mgcp_conn *conn;
llist_for_each_entry(conn, &endp->conns, entry) {
if (conn->id == id)
return conn;
}
return NULL;
}
/*! find an RTP connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection
* \returns pointer to allocated connection, NULL if not found */
struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, uint32_t id)
{
OSMO_ASSERT(endp);
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
struct mgcp_conn *conn;
conn = mgcp_conn_get(endp, id);
if (!conn)
return NULL;
if (conn->type == MGCP_CONN_TYPE_RTP)
return &conn->u.rtp;
return NULL;
}
/*! free a connection by its ID.
* \param[in] endp associated endpoint
* \param[in] id identification number of the connection */
void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id)
{
OSMO_ASSERT(endp);
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
struct mgcp_conn *conn;
conn = mgcp_conn_get(endp, id);
if (!conn)
return;
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
osmux_disable_conn(&conn->u.rtp);
osmux_release_cid(&conn->u.rtp);
mgcp_free_rtp_port(&conn->u.rtp.end);
break;
default:
/* NOTE: This should never be called with an
* invalid type, its up to the programmer
* to ensure propery types */
OSMO_ASSERT(false);
}
llist_del(&conn->entry);
talloc_free(conn);
}
/*! free oldest connection in the list.
* \param[in] endp associated endpoint */
void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
{
OSMO_ASSERT(endp);
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
struct mgcp_conn *conn;
if (llist_empty(&endp->conns))
return;
conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
if (!conn)
return;
mgcp_conn_free(endp, conn->id);
}
/*! free all connections at once.
* \param[in] endp associated endpoint */
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
{
OSMO_ASSERT(endp);
OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
struct mgcp_conn *conn;
struct mgcp_conn *conn_tmp;
/* Drop all items in the list */
llist_for_each_entry_safe(conn, conn_tmp, &endp->conns, entry) {
mgcp_conn_free(endp, conn->id);
}
return;
}
/*! dump basic connection information to human readble string.
* \param[in] conn to dump
* \returns human readble string */
char *mgcp_conn_dump(struct mgcp_conn *conn)
{
static char str[256];
if (!conn) {
snprintf(str, sizeof(str), "(null connection)");
return str;
}
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
/* Dump RTP connection */
snprintf(str, sizeof(str), "(%s/rtp, id:%u, ip:%s, "
"rtp:%u rtcp:%u)",
conn->name,
conn->id,
inet_ntoa(conn->u.rtp.end.addr),
ntohs(conn->u.rtp.end.rtp_port),
ntohs(conn->u.rtp.end.rtcp_port));
break;
default:
/* Should not happen, we should be able to dump
* every possible connection type. */
snprintf(str, sizeof(str), "(unknown connection type)");
break;
}
return str;
}
/*! find destination connection on a specific endpoint.
* \param[in] conn to search a destination for
* \returns destination connection, NULL on failure */
struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
{
struct mgcp_endpoint *endp;
struct mgcp_conn *partner_conn;
endp = conn->endp;
/*! NOTE: This simply works by grabbing the first connection that is
* not the supplied connection, which is suitable for endpoints that
* do not serve more than two connections. */
llist_for_each_entry(partner_conn, &endp->conns, entry) {
if (conn != partner_conn) {
return partner_conn;
}
}
return NULL;
}

View File

@ -0,0 +1,32 @@
/* Endpoint types */
/*
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* 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 <osmocom/mgcp/mgcp_ep.h>
#include <osmocom/mgcp/mgcp_internal.h>
/* Endpoint typeset definition */
const struct mgcp_endpoint_typeset ep_typeset = {
/* Specify endpoint properties for RTP endpoint */
.rtp.max_conns = 2,
.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb
};

404
src/libosmo-mgcp/mgcp_msg.c Normal file
View File

@ -0,0 +1,404 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* Message parser/generator utilities */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* 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 <limits.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <osmocom/mgcp/mgcp_conn.h>
/*! Display an mgcp message on the log output.
* \param[in] message mgcp message string
* \param[in] len message mgcp message string length
* \param[in] preamble string to display in logtext in front of each line */
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
{
unsigned char line[80];
unsigned char *ptr;
unsigned int consumed = 0;
unsigned int consumed_line = 0;
unsigned int line_count = 0;
if (!log_check_level(DLMGCP, LOGL_DEBUG))
return;
while (1) {
memset(line, 0, sizeof(line));
ptr = line;
consumed_line = 0;
do {
if (*message != '\n' && *message != '\r') {
*ptr = *message;
ptr++;
}
message++;
consumed++;
consumed_line++;
} while (*message != '\n' && consumed < len
&& consumed_line < sizeof(line));
if (strlen((const char *)line)) {
LOGP(DLMGCP, LOGL_DEBUG, "%s: line #%02u: %s\n",
preamble, line_count, line);
line_count++;
}
if (consumed >= len)
return;
}
}
/*! Parse connection mode.
* \param[in] mode as string (recvonly, sendrecv, sendonly or loopback)
* \param[in] endp pointer to endpoint (only used for log output)
* \param[out] associated connection to be modified accordingly
* \returns 0 on success, -1 on error */
int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
struct mgcp_conn *conn)
{
int ret = 0;
if (!mode) {
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:%x missing connection mode\n",
ENDPOINT_NUMBER(endp));
return -1;
}
if (!conn)
return -1;
if (!endp)
return -1;
if (strcmp(mode, "recvonly") == 0)
conn->mode = MGCP_CONN_RECV_ONLY;
else if (strcmp(mode, "sendrecv") == 0)
conn->mode = MGCP_CONN_RECV_SEND;
else if (strcmp(mode, "sendonly") == 0)
conn->mode = MGCP_CONN_SEND_ONLY;
else if (strcmp(mode, "loopback") == 0)
conn->mode = MGCP_CONN_LOOPBACK;
else {
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:%x unknown connection mode: '%s'\n",
ENDPOINT_NUMBER(endp), mode);
ret = -1;
}
/* Special handling für RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
conn->u.rtp.end.output_enabled =
conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
}
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint:%x conn:%s\n",
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn));
LOGP(DLMGCP, LOGL_DEBUG,
"endpoint:%x connection mode '%s' %d\n",
ENDPOINT_NUMBER(endp), mode, conn->mode);
/* Special handling für RTP connections */
if (conn->type == MGCP_CONN_TYPE_RTP) {
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:%x output_enabled %d\n",
ENDPOINT_NUMBER(endp), conn->u.rtp.end.output_enabled);
}
/* The VTY might change the connection mode at any time, so we have
* to hold a copy of the original connection mode */
conn->mode_orig = conn->mode;
return ret;
}
/* We have a null terminated string with the endpoint name here. We only
* support two kinds. Simple ones as seen on the BSC level and the ones
* seen on the trunk side. (helper function for find_endpoint()) */
static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
const char *mgcp)
{
char *rest = NULL;
struct mgcp_trunk_config *tcfg;
int trunk, endp;
trunk = strtoul(mgcp + 6, &rest, 10);
if (rest == NULL || rest[0] != '/' || trunk < 1) {
LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
return NULL;
}
endp = strtoul(rest + 1, &rest, 10);
if (rest == NULL || rest[0] != '@') {
LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
return NULL;
}
/* signalling is on timeslot 1 */
if (endp == 1)
return NULL;
tcfg = mgcp_trunk_num(cfg, trunk);
if (!tcfg) {
LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
trunk);
return NULL;
}
if (!tcfg->endpoints) {
LOGP(DLMGCP, LOGL_ERROR,
"Endpoints of trunk %d not allocated.\n", trunk);
return NULL;
}
if (endp < 1 || endp >= tcfg->number_endpoints) {
LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
mgcp);
return NULL;
}
return &tcfg->endpoints[endp];
}
/* Search the endpoint pool for the endpoint that had been selected via the
* MGCP message (helper function for mgcp_analyze_header()) */
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
const char *mgcp)
{
char *endptr = NULL;
unsigned int gw = INT_MAX;
if (strncmp(mgcp, "ds/e1", 5) == 0)
return find_e1_endpoint(cfg, mgcp);
gw = strtoul(mgcp, &endptr, 16);
if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@')
return &cfg->trunk.endpoints[gw];
LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
return NULL;
}
/*! Analyze and parse the the hader of an MGCP messeage string.
* \param[out] pdata caller provided memory to store the parsing results
* \param[in] data mgcp message string
* \returns when the status line was complete and transaction_id and
* endp out parameters are set, -1 on error */
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
{
int i = 0;
char *elem, *save = NULL;
/*! This function will parse the header part of the received
* MGCP message. The parsing results are stored in pdata.
* The function will also automatically search the pool with
* available endpoints in order to find an endpoint that matches
* the endpoint string in in the header */
OSMO_ASSERT(data);
pdata->trans = "000000";
for (elem = strtok_r(data, " ", &save); elem;
elem = strtok_r(NULL, " ", &save)) {
switch (i) {
case 0:
pdata->trans = elem;
break;
case 1:
pdata->endp = find_endpoint(pdata->cfg, elem);
if (!pdata->endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Unable to find Endpoint `%s'\n", elem);
return -1;
}
break;
case 2:
if (strcmp("MGCP", elem)) {
LOGP(DLMGCP, LOGL_ERROR,
"MGCP header parsing error\n");
return -1;
}
break;
case 3:
if (strcmp("1.0", elem)) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
"not supported\n", elem);
return -1;
}
break;
}
i++;
}
if (i != 4) {
LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
pdata->trans = "000000";
pdata->endp = NULL;
return -1;
}
return 0;
}
/*! Extract OSMUX CID from an MGCP parameter line (string).
* \param[in] line single parameter line from the MGCP message
* \returns OSMUX CID, -1 on error */
int mgcp_parse_osmux_cid(const char *line)
{
int osmux_cid;
if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
return -1;
if (osmux_cid > OSMUX_CID_MAX) {
LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
osmux_cid, OSMUX_CID_MAX);
return -1;
}
LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
return osmux_cid;
}
/*! Check MGCP parameter line (string) for plausibility.
* \param[in] endp pointer to endpoint (only used for log output)
* \param[in] line single parameter line from the MGCP message
* \returns 1 when line seems plausible, 0 on error */
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
{
const size_t line_len = strlen(line);
if (line[0] != '\0' && line_len < 2) {
LOGP(DLMGCP, LOGL_ERROR,
"Wrong MGCP option format: '%s' on 0x%x\n",
line, ENDPOINT_NUMBER(endp));
return 0;
}
/* FIXME: A couple more checks wouldn't hurt... */
return 1;
}
/*! Check if the specified callid seems plausible.
* \param[in] endp pointer to endpoint
* \param{in] callid to verify
* \returns 1 when callid seems plausible, 0 on error */
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
{
/*! This function compares the supplied callid with the called that is
* stored in the endpoint structure. */
if (!endp)
return -1;
if (!callid)
return -1;
if (!endp->callid)
return -1;
if (strcmp(endp->callid, callid) != 0) {
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:%x CallIDs does not match '%s' != '%s'\n",
ENDPOINT_NUMBER(endp), endp->callid, callid);
return -1;
}
return 0;
}
/*! Check if the specified connection id seems plausible.
* \param[in] endp pointer to endpoint
* \param{in] connection id to verify
* \returns 1 when connection id seems plausible, 0 on error */
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *ci)
{
uint32_t id;
if (!endp)
return -1;
id = strtoul(ci, NULL, 10);
if (mgcp_conn_get(endp, id))
return 0;
LOGP(DLMGCP, LOGL_ERROR,
"endpoint:%x No connection found under ConnectionIdentifier %u\n",
ENDPOINT_NUMBER(endp), id);
return -1;
}
/*! Extract individual lines from MCGP message.
* \param[in] str MGCP message string, consisting of multiple lines
* \param{in] saveptr pointer to next line in str
* \returns line, NULL when done */
char *mgcp_strline(char *str, char **saveptr)
{
char *result;
/*! The function must be called with *str set to the input string
* for the first line. After that saveptr will be initalized.
* all consecutive lines are extracted by calling the function
* with str set to NULL. When done, the function will return NULL
* to indicate that all lines have been parsed. */
if (str)
*saveptr = str;
result = *saveptr;
if (*saveptr != NULL) {
*saveptr = strpbrk(*saveptr, "\r\n");
if (*saveptr != NULL) {
char *eos = *saveptr;
if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n')
(*saveptr)++;
(*saveptr)++;
if ((*saveptr)[0] == '\0')
*saveptr = NULL;
*eos = '\0';
}
}
return result;
}
/*! Parse CI from a given string.
* \param[out] caller provided memory to store the result
* \param{in] string containing the connection id
* \returns 0 on success, -1 on error */
int mgcp_parse_ci(uint32_t *conn_id, const char *ci)
{
OSMO_ASSERT(conn_id);
if (!ci)
return -1;
*conn_id = strtoul(ci, NULL, 10);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -20,9 +20,10 @@
#include <osmocom/netif/osmux.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/legacy_mgcp/mgcp.h>
#include <osmocom/legacy_mgcp/mgcp_internal.h>
#include <osmocom/legacy_mgcp/osmux.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/osmux.h>
#include <osmocom/mgcp/mgcp_conn.h>
static struct osmo_fd osmux_fd;
@ -38,7 +39,8 @@ struct osmux_handle {
static void *osmux;
static void osmux_deliver(struct msgb *batch_msg, void *data)
/* Deliver OSMUX batch to the remote end */
static void osmux_deliver_cb(struct msgb *batch_msg, void *data)
{
struct osmux_handle *handle = data;
struct sockaddr_in out = {
@ -52,12 +54,12 @@ static void osmux_deliver(struct msgb *batch_msg, void *data)
msgb_free(batch_msg);
}
/* Lookup existing OSMUX handle for specified destination address. */
static struct osmux_handle *
osmux_handle_find_get(struct in_addr *addr, int rem_port)
{
struct osmux_handle *h;
/* Lookup for existing OSMUX handle for this destination address. */
llist_for_each_entry(h, &osmux_handle_list, head) {
if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 &&
h->rem_port == rem_port) {
@ -72,11 +74,11 @@ osmux_handle_find_get(struct in_addr *addr, int rem_port)
return NULL;
}
/* Put down no longer needed OSMUX handle */
static void osmux_handle_put(struct osmux_in_handle *in)
{
struct osmux_handle *h;
/* Lookup for existing OSMUX handle for this destination address. */
llist_for_each_entry(h, &osmux_handle_list, head) {
if (h->in == in) {
if (--h->refcnt == 0) {
@ -101,6 +103,7 @@ static void osmux_handle_put(struct osmux_in_handle *in)
LOGP(DLMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in);
}
/* Allocate free OSMUX handle */
static struct osmux_handle *
osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
{
@ -119,11 +122,14 @@ osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
return NULL;
}
h->in->osmux_seq = 0; /* sequence number to start OSmux message from */
/* sequence number to start OSMUX message from */
h->in->osmux_seq = 0;
h->in->batch_factor = cfg->osmux_batch;
/* If batch size is zero, the library defaults to 1470 bytes. */
h->in->batch_size = cfg->osmux_batch_size;
h->in->deliver = osmux_deliver;
h->in->deliver = osmux_deliver_cb;
osmux_xfrm_input_init(h->in);
h->in->data = h;
@ -135,6 +141,8 @@ osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
return h;
}
/* Lookup existing handle for a specified address, if the handle can not be
* foud a the function will automatically allocate one */
static struct osmux_in_handle *
osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
{
@ -151,47 +159,60 @@ osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
return h->in;
}
int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp)
/*! send RTP packet through OSMUX connection.
* \param[in] buf rtp data
* \param[in] buf_len length of rtp data
* \param[in] conn associated RTP connection
* \returns 0 on success, -1 on ERROR */
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn)
{
int ret;
struct msgb *msg;
msg = msgb_alloc(4096, "RTP");
if (!msg)
return 0;
return -1;
memcpy(msg->data, buf, rc);
msgb_put(msg, rc);
memcpy(msg->data, buf, buf_len);
msgb_put(msg, buf_len);
while ((ret = osmux_xfrm_input(endp->osmux.in, msg, endp->osmux.cid)) > 0) {
while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.cid)) > 0) {
/* batch full, build and deliver it */
osmux_xfrm_input_deliver(endp->osmux.in);
osmux_xfrm_input_deliver(conn->osmux.in);
}
return 0;
}
/* Lookup the endpoint that corresponds to the specified address (port) */
static struct mgcp_endpoint *
endpoint_lookup(struct mgcp_config *cfg, int cid,
struct in_addr *from_addr, int type)
{
struct mgcp_endpoint *tmp = NULL;
struct mgcp_endpoint *endp = NULL;
int i;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
/* Lookup for the endpoint that corresponds to this port */
for (i=0; i<cfg->trunk.number_endpoints; i++) {
struct in_addr *this;
tmp = &cfg->trunk.endpoints[i];
endp = &cfg->trunk.endpoints[i];
#if 0
if (!tmp->allocated)
continue;
#endif
switch(type) {
case MGCP_DEST_NET:
this = &tmp->net_end.addr;
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
this = &conn_net->end.addr;
break;
case MGCP_DEST_BTS:
this = &tmp->bts_end.addr;
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
this = &conn_bts->end.addr;
break;
default:
/* Should not ever happen */
@ -199,8 +220,10 @@ endpoint_lookup(struct mgcp_config *cfg, int cid,
return NULL;
}
if (tmp->osmux.cid == cid && this->s_addr == from_addr->s_addr)
return tmp;
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (conn_net->osmux.cid == cid && this->s_addr == from_addr->s_addr)
return endp;
}
LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
@ -211,30 +234,54 @@ endpoint_lookup(struct mgcp_config *cfg, int cid,
static void scheduled_tx_net_cb(struct msgb *msg, void *data)
{
struct mgcp_endpoint *endp = data;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_bts || !conn_net)
return;
struct sockaddr_in addr = {
.sin_addr = endp->net_end.addr,
.sin_port = endp->net_end.rtp_port,
.sin_addr = conn_net->end.addr,
.sin_port = conn_net->end.rtp_port,
};
endp->bts_end.octets += msg->len;
endp->bts_end.packets++;
conn_bts->end.octets_tx += msg->len;
conn_bts->end.packets_tx++;
mgcp_send(endp, MGCP_DEST_NET, 1, &addr, (char *)msg->data, msg->len);
/* Send RTP data to NET */
/* FIXME: Get rid of conn_bts and conn_net! */
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
conn_bts, conn_net);
msgb_free(msg);
}
static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
{
struct mgcp_endpoint *endp = data;
struct mgcp_conn_rtp *conn_net = NULL;
struct mgcp_conn_rtp *conn_bts = NULL;
/* FIXME: Get rid of CONN_ID_XXX! */
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_bts || !conn_net)
return;
struct sockaddr_in addr = {
.sin_addr = endp->bts_end.addr,
.sin_port = endp->bts_end.rtp_port,
.sin_addr = conn_bts->end.addr,
.sin_port = conn_bts->end.rtp_port,
};
endp->net_end.octets += msg->len;
endp->net_end.packets++;
conn_net->end.octets_tx += msg->len;
conn_net->end.packets_tx++;
mgcp_send(endp, MGCP_DEST_BTS, 1, &addr, (char *)msg->data, msg->len);
/* Send RTP data to BTS */
/* FIXME: Get rid of conn_bts and conn_net! */
mgcp_send(endp, 1, &addr, (char *)msg->data, msg->len,
conn_net, conn_bts);
msgb_free(msg);
}
@ -271,6 +318,7 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
struct mgcp_conn_rtp *conn_net = NULL;
msg = osmux_recv(ofd, &addr);
if (!msg)
@ -287,17 +335,23 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
/* Yes, we use MGCP_DEST_NET to locate the origin */
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
&addr.sin_addr, MGCP_DEST_NET);
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_net)
goto out;
if (!endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find an endpoint for circuit_id=%d\n",
osmuxh->circuit_id);
goto out;
}
endp->osmux.stats.octets += osmux_chunk_length(msg, rem);
endp->osmux.stats.chunks++;
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
conn_net->osmux.stats.chunks++;
rem = msg->len;
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
}
out:
@ -311,6 +365,7 @@ static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
{
struct mgcp_endpoint *endp;
uint8_t osmux_cid;
struct mgcp_conn_rtp *conn_net = NULL;
if (msg->len < 1 + sizeof(osmux_cid)) {
LOGP(DLMGCP, LOGL_ERROR,
@ -337,10 +392,14 @@ static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
goto out;
}
if (endp->osmux.state == OSMUX_STATE_ENABLED)
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_net)
goto out;
if (osmux_enable_endpoint(endp, &addr->sin_addr, addr->sin_port) < 0 ) {
if (conn_net->osmux.state == OSMUX_STATE_ENABLED)
goto out;
if (osmux_enable_conn(endp, conn_net, &addr->sin_addr, addr->sin_port) < 0 ) {
LOGP(DLMGCP, LOGL_ERROR,
"Could not enable osmux in endpoint %d\n",
ENDPOINT_NUMBER(endp));
@ -363,6 +422,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
struct sockaddr_in addr;
struct mgcp_config *cfg = ofd->data;
uint32_t rem;
struct mgcp_conn_rtp *conn_net = NULL;
msg = osmux_recv(ofd, &addr);
if (!msg)
@ -379,17 +439,23 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
/* Yes, we use MGCP_DEST_BTS to locate the origin */
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
&addr.sin_addr, MGCP_DEST_BTS);
/* FIXME: Get rid of CONN_ID_XXX! */
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
if (!conn_net)
goto out;
if (!endp) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot find an endpoint for circuit_id=%d\n",
osmuxh->circuit_id);
goto out;
}
endp->osmux.stats.octets += osmux_chunk_length(msg, rem);
endp->osmux.stats.chunks++;
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
conn_net->osmux.stats.chunks++;
rem = msg->len;
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
}
out:
@ -432,116 +498,155 @@ int osmux_init(int role, struct mgcp_config *cfg)
return 0;
}
int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port)
/*! enable OSXMUX circuit for a specified connection.
* \param[in] endp mgcp endpoint (configuration)
* \param[in] conn connection to disable
* \param[in] addr IP address of remote OSMUX endpoint
* \param[in] port portnumber of the remote OSMUX endpoint
* \returns 0 on success, -1 on ERROR */
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
struct in_addr *addr, uint16_t port)
{
/* If osmux is enabled, initialize the output handler. This handler is
* used to reconstruct the RTP flow from osmux. The RTP SSRC is
* allocated based on the circuit ID (endp->osmux.cid), which is unique
* in the local scope to the BSC/BSC-NAT. We use it to divide the RTP
* SSRC space (2^32) by the 256 possible circuit IDs, then randomly
* select one value from that window. Thus, we have no chance to have
* overlapping RTP SSRC traveling to the BTSes behind the BSC,
* similarly, for flows traveling to the MSC.
/*! If osmux is enabled, initialize the output handler. This handler is
* used to reconstruct the RTP flow from osmux. The RTP SSRC is
* allocated based on the circuit ID (conn_net->osmux.cid), which is unique
* in the local scope to the BSC/BSC-NAT. We use it to divide the RTP
* SSRC space (2^32) by the 256 possible circuit IDs, then randomly
* select one value from that window. Thus, we have no chance to have
* overlapping RTP SSRC traveling to the BTSes behind the BSC,
* similarly, for flows traveling to the MSC.
*/
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256;
uint16_t osmux_dummy = endp->cfg->osmux_dummy;
if (endp->osmux.state == OSMUX_STATE_DISABLED) {
LOGP(DLMGCP, LOGL_ERROR, "Endpoint %u didn't request Osmux\n",
ENDPOINT_NUMBER(endp));
/* Check if osmux is enabled for the specified connection */
if (conn->osmux.state == OSMUX_STATE_DISABLED) {
LOGP(DLMGCP, LOGL_ERROR, "OSMUX not enabled for conn:%s\n",
mgcp_conn_dump(conn->conn));
return -1;
}
osmux_xfrm_output_init(&endp->osmux.out,
(endp->osmux.cid * rtp_ssrc_winlen) +
osmux_xfrm_output_init(&conn->osmux.out,
(conn->osmux.cid * rtp_ssrc_winlen) +
(random() % rtp_ssrc_winlen));
endp->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
if (!endp->osmux.in) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle\n");
conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
if (!conn->osmux.in) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle for conn:%s\n",
mgcp_conn_dump(conn->conn));
return -1;
}
if (!osmux_xfrm_input_open_circuit(endp->osmux.in, endp->osmux.cid,
endp->cfg->osmux_dummy)) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u\n",
endp->osmux.cid);
if (!osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy)) {
LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u for conn:%s\n",
conn->osmux.cid, mgcp_conn_dump(conn->conn));
return -1;
}
switch (endp->cfg->role) {
case MGCP_BSC_NAT:
endp->type = MGCP_OSMUX_BSC_NAT;
conn->type = MGCP_OSMUX_BSC_NAT;
break;
case MGCP_BSC:
endp->type = MGCP_OSMUX_BSC;
conn->type = MGCP_OSMUX_BSC;
break;
}
endp->osmux.state = OSMUX_STATE_ENABLED;
conn->osmux.state = OSMUX_STATE_ENABLED;
return 0;
}
void osmux_disable_endpoint(struct mgcp_endpoint *endp)
/*! disable OSXMUX circuit for a specified connection.
* \param[in] conn connection to disable */
void osmux_disable_conn(struct mgcp_conn_rtp *conn)
{
LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n",
ENDPOINT_NUMBER(endp), endp->osmux.cid);
osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid);
endp->osmux.state = OSMUX_STATE_DISABLED;
endp->osmux.cid = -1;
osmux_handle_put(endp->osmux.in);
if (!conn)
return;
if (conn->osmux.state != OSMUX_STATE_ENABLED)
return;
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %u using Osmux CID %u\n",
conn->conn->id, conn->osmux.cid);
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
conn->osmux.state = OSMUX_STATE_DISABLED;
conn->osmux.cid = -1;
osmux_handle_put(conn->osmux.in);
}
void osmux_release_cid(struct mgcp_endpoint *endp)
/*! relase OSXMUX cid, that had been allocated to this connection.
* \param[in] conn connection with OSMUX cid to release */
void osmux_release_cid(struct mgcp_conn_rtp *conn)
{
if (endp->osmux.allocated_cid >= 0)
osmux_put_cid(endp->osmux.allocated_cid);
endp->osmux.allocated_cid = -1;
if (!conn)
return;
if (conn->osmux.state != OSMUX_STATE_ENABLED)
return;
if (conn->osmux.allocated_cid >= 0)
osmux_put_cid(conn->osmux.allocated_cid);
conn->osmux.allocated_cid = -1;
}
void osmux_allocate_cid(struct mgcp_endpoint *endp)
/*! allocate OSXMUX cid to connection.
* \param[in] conn connection for which we allocate the OSMUX cid*/
void osmux_allocate_cid(struct mgcp_conn_rtp *conn)
{
osmux_release_cid(endp);
endp->osmux.allocated_cid = osmux_get_cid();
osmux_release_cid(conn);
conn->osmux.allocated_cid = osmux_get_cid();
}
/* We don't need to send the dummy load for osmux so often as another endpoint
* may have already punched the hole in the firewall. This approach is simple
* though.
*/
int osmux_send_dummy(struct mgcp_endpoint *endp)
/*! send RTP dummy packet to OSMUX connection port.
* \param[in] endp mcgp endpoint that holds the RTP connection
* \param[in] conn associated RTP connection
* \returns bytes sent, -1 on error */
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
{
char buf[1 + sizeof(uint8_t)];
struct in_addr addr_unset = {};
/*! The dummy packet will not be sent via the actual OSMUX connection,
* instead it is sent out of band to port where the remote OSMUX
* multplexer is listening. The goal is to ensure that the connection
* is kept open */
/*! We don't need to send the dummy load for osmux so often as another
* endpoint may have already punched the hole in the firewall. This
* approach is simple though. */
buf[0] = MGCP_DUMMY_LOAD;
memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid));
memcpy(&buf[1], &conn->osmux.cid, sizeof(conn->osmux.cid));
/* Wait until we have the connection information from MDCX */
if (memcmp(&endp->net_end.addr, &addr_unset, sizeof(addr_unset)) == 0)
if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0)
return 0;
if (endp->osmux.state == OSMUX_STATE_ACTIVATING) {
if (osmux_enable_endpoint(endp, &endp->net_end.addr,
htons(endp->cfg->osmux_port)) < 0) {
if (conn->osmux.state == OSMUX_STATE_ACTIVATING) {
if (osmux_enable_conn(endp, conn, &conn->end.addr,
htons(endp->cfg->osmux_port)) < 0) {
LOGP(DLMGCP, LOGL_ERROR,
"Could not activate osmux in endpoint %d\n",
ENDPOINT_NUMBER(endp));
"Could not activate osmux for conn:%s\n",
mgcp_conn_dump(conn->conn));
}
LOGP(DLMGCP, LOGL_ERROR,
"Osmux CID %u for %s:%u is now enabled\n",
endp->osmux.cid, inet_ntoa(endp->net_end.addr),
conn->osmux.cid, inet_ntoa(conn->end.addr),
endp->cfg->osmux_port);
}
LOGP(DLMGCP, LOGL_DEBUG,
"sending OSMUX dummy load to %s CID %u\n",
inet_ntoa(endp->net_end.addr), endp->osmux.cid);
inet_ntoa(conn->end.addr), conn->osmux.cid);
return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr,
return mgcp_udp_send(osmux_fd.fd, &conn->end.addr,
htons(endp->cfg->osmux_port), buf, sizeof(buf));
}
/* bsc-nat allocates/releases the Osmux circuit ID */
/*! bsc-nat allocates/releases the OSMUX cids (Circuit IDs). */
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
/*! count the number of taken OSMUX cids.
* \returns number of OSMUX cids in use */
int osmux_used_cid(void)
{
int i, j, used = 0;
@ -556,6 +661,8 @@ int osmux_used_cid(void)
return used;
}
/*! take a free OSMUX cid.
* \returns OSMUX cid */
int osmux_get_cid(void)
{
int i, j;
@ -576,6 +683,8 @@ int osmux_get_cid(void)
return -1;
}
/*! put back a no longer used OSMUX cid.
* \param[in] osmux_cid OSMUX cid */
void osmux_put_cid(uint8_t osmux_cid)
{
LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);

File diff suppressed because it is too large Load Diff

View File

@ -20,8 +20,9 @@
*
*/
#include <osmocom/legacy_mgcp/mgcp.h>
#include <osmocom/legacy_mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/mgcp_msg.h>
#include <errno.h>

View File

@ -0,0 +1,128 @@
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
/* The statistics generator */
/*
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
* (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* 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 <osmocom/mgcp/mgcp_stat.h>
#include <limits.h>
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
void calc_loss(struct mgcp_rtp_state *state,
struct mgcp_rtp_end *end, uint32_t *expected,
int *loss)
{
*expected = state->stats_cycles + state->stats_max_seq;
*expected = *expected - state->stats_base_seq + 1;
if (!state->stats_initialized) {
*expected = 0;
*loss = 0;
return;
}
/*
* Make sure the sign is correct and use the biggest
* positive/negative number that fits.
*/
*loss = *expected - end->packets_rx;
if (*expected < end->packets_rx) {
if (*loss > 0)
*loss = INT_MIN;
} else {
if (*loss < 0)
*loss = INT_MAX;
}
}
/* Helper function for mgcp_format_stats_rtp() to calculate jitter */
uint32_t calc_jitter(struct mgcp_rtp_state *state)
{
if (!state->stats_initialized)
return 0;
return state->stats_jitter >> 4;
}
/* Generate statistics for an RTP connection */
static void mgcp_format_stats_rtp(char *str, size_t str_len,
struct mgcp_conn_rtp *conn)
{
uint32_t expected, jitter;
int ploss;
int nchars;
calc_loss(&conn->state, &conn->end, &expected, &ploss);
jitter = calc_jitter(&conn->state);
nchars = snprintf(str, str_len,
"\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
conn->end.packets_tx, conn->end.octets_tx,
conn->end.packets_rx, conn->end.octets_rx,
ploss, jitter);
if (nchars < 0 || nchars >= str_len)
goto truncate;
str += nchars;
str_len -= nchars;
/* Error Counter */
nchars = snprintf(str, str_len,
"\r\nX-Osmo-CP: EC TI=%u, TO=%u",
conn->state.in_stream.err_ts_counter,
conn->state.out_stream.err_ts_counter);
if (nchars < 0 || nchars >= str_len)
goto truncate;
str += nchars;
str_len -= nchars;
if (conn->osmux.state == OSMUX_STATE_ENABLED) {
snprintf(str, str_len,
"\r\nX-Osmux-ST: CR=%u, BR=%u",
conn->osmux.stats.chunks, conn->osmux.stats.octets);
}
truncate:
str[str_len - 1] = '\0';
}
/*! format statistics into an mgcp parameter string.
* \param[out] str resulting string
* \param[in] str_len length of the string buffer
* \param[in] conn connection to evaluate */
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn)
{
memset(str, 0, str_len);
if (!conn)
return;
/* NOTE: At the moment we only support generating statistics for
* RTP connections. However, in the future we may also want to
* generate statistics for other connection types as well. Lets
* keep this option open: */
switch (conn->type) {
case MGCP_CONN_TYPE_RTP:
mgcp_format_stats_rtp(str, str_len, &conn->u.rtp);
break;
default:
break;
}
}

View File

@ -1,611 +0,0 @@
/*
* (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 "g711common.h"
#include <osmocom/legacy_mgcp/mgcp.h>
#include <osmocom/legacy_mgcp/mgcp_internal.h>
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
#include <osmocom/core/talloc.h>
#include <osmocom/netif/rtp.h>
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_codec *codec)
{
if (codec->subtype_name) {
if (!strcasecmp("GSM", codec->subtype_name))
return AF_GSM;
if (!strcasecmp("PCMA", codec->subtype_name))
return AF_PCMA;
if (!strcasecmp("PCMU", codec->subtype_name))
return AF_PCMU;
#ifdef HAVE_BCG729
if (!strcasecmp("G729", codec->subtype_name))
return AF_G729;
#endif
if (!strcasecmp("L16", codec->subtype_name))
return AF_L16;
}
switch (codec->payload_type) {
case 0 /* PCMU */:
return AF_PCMU;
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 void ulaw_encode(short *sample, unsigned char *buf, size_t n)
{
for (; n > 0; --n)
*(buf++) = s16_to_ulaw(*(sample++));
}
static void ulaw_decode(unsigned char *buf, short *sample, size_t n)
{
for (; n > 0; --n)
*(sample++) = ulaw_to_s16(*(buf++));
}
static int processing_state_destructor(struct mgcp_process_rtp_state *state)
{
switch (state->src_fmt) {
case AF_GSM:
if (state->src.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;
const struct mgcp_rtp_codec *dst_codec = &dst_end->codec;
/* 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;
const struct mgcp_rtp_codec *src_codec = &src_end->codec;
if (endp->tcfg->no_audio_transcoding) {
LOGP(DLMGCP, LOGL_NOTICE,
"Transcoding disabled on endpoint 0x%x\n",
ENDPOINT_NUMBER(endp));
return 0;
}
src_fmt = get_audio_format(src_codec);
dst_fmt = get_audio_format(dst_codec);
LOGP(DLMGCP, LOGL_ERROR,
"Checking transcoding: %s (%d) -> %s (%d)\n",
src_codec->subtype_name, src_codec->payload_type,
dst_codec->subtype_name, dst_codec->payload_type);
if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) {
if (!src_codec->subtype_name || !dst_codec->subtype_name)
/* Not enough info, do nothing */
return 0;
if (strcasecmp(src_codec->subtype_name, dst_codec->subtype_name) == 0)
/* Nothing to do */
return 0;
LOGP(DLMGCP, LOGL_ERROR,
"Cannot transcode: %s codec not supported (%s -> %s).\n",
src_fmt != AF_INVALID ? "destination" : "source",
src_codec->audio_name, dst_codec->audio_name);
return -EINVAL;
}
if (src_codec->rate && dst_codec->rate && src_codec->rate != dst_codec->rate) {
LOGP(DLMGCP, LOGL_ERROR,
"Cannot transcode: rate conversion (%d -> %d) not supported.\n",
src_codec->rate, dst_codec->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(DLMGCP, 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(DLMGCP, LOGL_ERROR,
"Failed to initialize G.729 decoder.\n");
return -EINVAL;
}
break;
#endif
case AF_PCMU:
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(DLMGCP, 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(DLMGCP, LOGL_ERROR,
"Failed to initialize G.729 decoder.\n");
return -EINVAL;
}
break;
#endif
case AF_PCMU:
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(DLMGCP, LOGL_INFO,
"Initialized RTP processing on: 0x%x "
"conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n",
ENDPOINT_NUMBER(endp),
src_fmt, src_codec->payload_type, src_codec->rate, src_end->fmtp_extra,
dst_fmt, dst_codec->payload_type, dst_codec->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;
struct mgcp_rtp_codec *net_codec = &endp->net_end.codec;
struct mgcp_rtp_codec *bts_codec = &endp->bts_end.codec;
if (!state || net_codec->payload_type < 0) {
*payload_type = bts_codec->payload_type;
*audio_name = bts_codec->audio_name;
*fmtp_extra = endp->bts_end.fmtp_extra;
return;
}
*payload_type = net_codec->payload_type;
*audio_name = net_codec->audio_name;
*fmtp_extra = endp->net_end.fmtp_extra;
}
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(DLMGCP, LOGL_ERROR,
"Sample buffer too small: %zu > %zu.\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(DLMGCP, 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_PCMU:
ulaw_decode(*src, state->samples + state->sample_cnt,
state->src_samples_per_frame);
break;
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(DLMGCP, LOGL_INFO,
"Encoding (RTP) buffer too small: %zu > %zu.\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_PCMU:
ulaw_encode(state->samples + state->sample_offs, dst,
state->src_samples_per_frame);
break;
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;
}
static struct mgcp_rtp_end *source_for_dest(struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end)
{
if (&endp->bts_end == dst_end)
return &endp->net_end;
else if (&endp->net_end == dst_end)
return &endp->bts_end;
OSMO_ASSERT(0);
}
/*
* With some modems we get offered multiple codecs
* and we have selected one of them. It might not
* be the right one and we need to detect this with
* the first audio packets. One difficulty is that
* we patch the rtp payload type in place, so we
* need to discuss this.
*/
struct mgcp_process_rtp_state *check_transcode_state(
struct mgcp_endpoint *endp,
struct mgcp_rtp_end *dst_end,
struct rtp_hdr *rtp_hdr)
{
struct mgcp_rtp_end *src_end;
/* Only deal with messages from net to bts */
if (&endp->bts_end != dst_end)
goto done;
src_end = source_for_dest(endp, dst_end);
/* Already patched */
if (rtp_hdr->payload_type == dst_end->codec.payload_type)
goto done;
/* The payload we expect */
if (rtp_hdr->payload_type == src_end->codec.payload_type)
goto done;
/* The matching alternate payload type? Then switch */
if (rtp_hdr->payload_type == src_end->alt_codec.payload_type) {
struct mgcp_config *cfg = endp->cfg;
struct mgcp_rtp_codec tmp_codec = src_end->alt_codec;
src_end->alt_codec = src_end->codec;
src_end->codec = tmp_codec;
cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end);
cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end);
}
done:
return dst_end->rtp_process_data;
}
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;
const size_t rtp_hdr_size = sizeof(struct rtp_hdr);
struct rtp_hdr *rtp_hdr = (struct rtp_hdr *) data;
char *payload_data = (char *) &rtp_hdr->data[0];
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;
state = check_transcode_state(endp, dst_end, rtp_hdr);
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(rtp_hdr->timestamp);
if (!state->is_running) {
state->next_seq = ntohs(rtp_hdr->sequence);
state->next_time = ts_no;
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.
*/
LOGP(DLMGCP, LOGL_NOTICE,
"0x%x dropping sample buffer due delta=%d sample_cnt=%zu\n",
ENDPOINT_NUMBER(endp), delta, state->sample_cnt);
state->sample_cnt = 0;
state->next_time = ts_no;
} else if (delta < 0) {
LOGP(DLMGCP, 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(DLMGCP, LOGL_NOTICE,
"Skipped audio frame in RTP packet: %zu 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);
/*
* There were no samples to encode?
* TODO: how does this work for comfort noise?
*/
if (rc == 0)
return -ENOMSG;
/* Any other error during the encoding */
if (rc < 0)
return rc;
nsamples -= state->sample_cnt;
*len = rtp_hdr_size + rc;
rtp_hdr->sequence = htons(state->next_seq);
rtp_hdr->timestamp = htonl(ts_no);
state->next_seq += 1;
state->next_time = ts_no + nsamples;
/*
* XXX: At this point we should always have consumed
* samples. So doing OSMO_ASSERT(nsamples > 0) and returning
* rtp_hdr_size should be fine.
*/
return nsamples ? rtp_hdr_size : 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -9,23 +9,20 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(LIBBCG729_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
bin_PROGRAMS = \
osmo-bsc_mgcp \
osmo-mgw \
$(NULL)
osmo_bsc_mgcp_SOURCES = \
mgcp_main.c \
osmo_mgw_SOURCES = \
mgw_main.c \
$(NULL)
osmo_bsc_mgcp_LDADD = \
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
osmo_mgw_LDADD = \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMONETIF_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBBCG729_LIBS) \
$(LIBRARY_GSM) \
$(NULL)

View File

@ -32,9 +32,9 @@
#include <sys/socket.h>
#include <osmocom/legacy_mgcp/mgcp.h>
#include <osmocom/legacy_mgcp/mgcp_internal.h>
#include <osmocom/legacy_mgcp/vty.h>
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_internal.h>
#include <osmocom/mgcp/vty.h>
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
@ -52,14 +52,10 @@
#include "../../bscconfig.h"
#ifdef BUILD_MGCP_TRANSCODING
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
#endif
#define _GNU_SOURCE
#include <getopt.h>
#warning "Make use of the rtp proxy code"
/* FIXME: Make use of the rtp proxy code */
static struct mgcp_config *cfg;
static struct mgcp_trunk_config *reset_trunk;
@ -68,6 +64,7 @@ static int daemonize = 0;
const char *openbsc_copyright =
"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
"Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
@ -132,10 +129,14 @@ static void handle_options(int argc, char **argv)
}
}
/* simply remember this */
/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
* command is received */
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
{
/* Set flag so that, when read_call_agent() is called next time
* the reset can progress */
reset_endpoints = 1;
reset_trunk = tcfg;
return 0;
@ -173,13 +174,17 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
msgb_free(resp);
}
/* reset endpoints */
if (reset_endpoints) {
LOGP(DLMGCP, LOGL_NOTICE,
"Asked to reset endpoints: %d/%d\n",
reset_trunk->trunk_nr, reset_trunk->trunk_type);
/* reset flag */
reset_endpoints = 0;
/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
/* Walk over all endpoints and trigger a release, this will release all
* endpoints, possible open connections are forcefully dropped */
for (i = 1; i < reset_trunk->number_endpoints; ++i)
mgcp_release_endp(&reset_trunk->endpoints[i]);
}
@ -220,7 +225,7 @@ int mgcp_vty_go_parent(struct vty *vty)
static struct vty_app_info vty_info = {
.name = "OpenBSC MGCP",
.name = "OsmoMGW",
.version = PACKAGE_VERSION,
.go_parent_cb = mgcp_vty_go_parent,
.is_config_node = mgcp_vty_is_config_node,
@ -250,14 +255,6 @@ 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
cfg->trunk.force_realloc = 1;
vty_info.copyright = openbsc_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(NULL);
@ -279,7 +276,8 @@ int main(int argc, char **argv)
if (rc < 0)
return rc;
/* set some callbacks */
/* Set the reset callback function. This functions is called when the
* mgcp-command "RSIP" (Reset in Progress) is received */
cfg->reset_cb = mgcp_rsip_cb;
/* we need to bind a socket */

View File

@ -1,6 +1,7 @@
SUBDIRS = \
legacy_mgcp \
mgcp_client \
mgcp \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.

View File

@ -489,11 +489,11 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp)
static void test_values(void)
{
/* Check that NONE disables all output */
OSMO_ASSERT((MGCP_CONN_NONE & MGCP_CONN_RECV_SEND) == 0)
OSMO_ASSERT((MGCP_CONN_NONE & MGCP_CONN_RECV_SEND) == 0);
/* Check that LOOPBACK enables all output */
OSMO_ASSERT((MGCP_CONN_LOOPBACK & MGCP_CONN_RECV_SEND) ==
MGCP_CONN_RECV_SEND)
MGCP_CONN_RECV_SEND);
}

View File

@ -9,7 +9,6 @@ AM_CFLAGS = \
-ggdb3 \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMONETIF_CFLAGS) \
$(LIBBCG729_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
@ -19,43 +18,21 @@ AM_LDFLAGS = \
EXTRA_DIST = \
mgcp_test.ok \
mgcp_transcoding_test.ok \
$(NULL)
noinst_PROGRAMS = \
mgcp_test \
$(NULL)
if BUILD_MGCP_TRANSCODING
noinst_PROGRAMS += \
mgcp_transcoding_test \
$(NULL)
endif
mgcp_test_SOURCES = \
mgcp_test.c \
$(NULL)
mgcp_test_LDADD = \
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBRARY_DL) \
$(LIBOSMONETIF_LIBS) \
$(LIBRARY_GSM) \
-lm \
$(NULL)
mgcp_transcoding_test_SOURCES = \
mgcp_transcoding_test.c \
$(NULL)
mgcp_transcoding_test_LDADD = \
$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBBCG729_LIBS) \
$(LIBRARY_DL) \
$(LIBOSMONETIF_LIBS) \
$(LIBRARY_GSM) \
-lm \
$(NULL)

File diff suppressed because it is too large Load Diff

View File

@ -16,32 +16,32 @@ Testing AUEP2
Testing MDCX1
Testing MDCX2
Testing CRCX
Dummy packets: 1
Dummy packets: 2
Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 1: RECV
Testing MDCX3
Dummy packets: 1
Packet duration not set
Dummy packets: 2
Detected packet duration: 40
Requested packetization period not set
Connection mode not set
Connection mode: 1: RECV
Testing MDCX4
Dummy packets: 1
Dummy packets: 2
Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 3: SEND RECV
Testing MDCX4_PT1
Dummy packets: 1
Dummy packets: 2
Detected packet duration: 40
Requested packetetization period: 20-40
Connection mode: 3: SEND RECV
Testing MDCX4_PT2
Dummy packets: 1
Dummy packets: 2
Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 3: SEND RECV
Testing MDCX4_PT3
Dummy packets: 1
Dummy packets: 2
Detected packet duration: 40
Requested packetization period not set
Connection mode: 3: SEND RECV
@ -50,17 +50,14 @@ Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 2: SEND
Testing MDCX4_RO
Dummy packets: 1
Packet duration not set
Dummy packets: 2
Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 1: RECV
Testing DLCX
Detected packet duration: 20
Requested packetization period not set
Connection mode: 0: NONE
Testing CRCX_ZYN
Dummy packets: 1
Packet duration not set
Dummy packets: 2
Detected packet duration: 20
Requested packetization period not set
Connection mode: 1: RECV
Testing EMPTY
@ -71,23 +68,17 @@ Testing SHORT4
Testing RQNT1
Testing RQNT2
Testing DLCX
Detected packet duration: 20
Requested packetization period not set
Connection mode: 0: NONE
Testing CRCX
Dummy packets: 1
Dummy packets: 2
Detected packet duration: 40
Requested packetetization period: 20-20
Connection mode: 1: RECV
Testing MDCX3
Dummy packets: 1
Packet duration not set
Dummy packets: 2
Detected packet duration: 40
Requested packetization period not set
Connection mode not set
Connection mode: 1: RECV
Testing DLCX
Detected packet duration: 20
Requested packetization period not set
Connection mode: 0: NONE
Testing CRCX
Re-transmitting CRCX
Testing RQNT1

View File

@ -1,661 +0,0 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/application.h>
#include <osmocom/netif/rtp.h>
#include <osmocom/legacy_mgcp/mgcp.h>
#include <osmocom/legacy_mgcp/mgcp_internal.h>
#include "bscconfig.h"
#ifndef BUILD_MGCP_TRANSCODING
#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)"
#endif
#include <osmocom/legacy_mgcp/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"
},
/* RTP: SeqNo=26527, TS=232640 */
{0.020000, 92,
"\x80\x08\x67\x9f\x00\x03\x8c\xc0\x04\xaa\x67\x9f\xd5\xd5\xd5\xd5"
"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
"\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\xd5\xd5\x55\x55\xd5\xd5\x55\x55"
"\xd5\xd5\xd5\x55\x55\xd5\xd5\xd5\x55\x55\xd5\xd5"
},
/* RTP: SeqNo=26528, TS=232720 */
{0.020000, 92,
"\x80\x08\x67\xa0\x00\x03\x8d\x10\x04\xaa\x67\x9f\x55\xd5\xd5\x55"
"\xd5\x55\xd5\xd5\xd5\x55\xd5\x55\xd5\xd5\x55\xd5\x55\xd5\x55\xd5"
"\x55\x55\xd5\x55\xd5\xd5\x55\x55\x55\x55\x55\xd5\xd5\x55\xd5\xd5"
"\xd5\x55\xd5\xd5\xd5\x55\x54\x55\xd5\xd5\x55\xd5\xd5\xd5\xd5\x55"
"\x54\x55\xd5\x55\xd5\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd4"
"\xd5\x54\x55\xd5\xd4\xd5\x54\xd5\x55\xd5\xd5\xd5"
},
};
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 given_configured_endpoint(int in_samples, int out_samples,
const char *srcfmt, const char *dstfmt,
void **out_ctx, struct mgcp_endpoint **out_endp)
{
int rc;
struct mgcp_rtp_end *dst_end;
struct mgcp_rtp_end *src_end;
struct mgcp_config *cfg;
struct mgcp_trunk_config *tcfg;
struct mgcp_endpoint *endp;
cfg = mgcp_config_alloc();
tcfg = talloc_zero(cfg, struct mgcp_trunk_config);
endp = talloc_zero(tcfg, struct mgcp_endpoint);
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;
tcfg->endpoints = endp;
tcfg->number_endpoints = 1;
tcfg->cfg = cfg;
endp->tcfg = tcfg;
endp->cfg = cfg;
mgcp_initialize_endp(endp);
dst_end = &endp->bts_end;
dst_end->codec.payload_type = audio_name_to_type(dstfmt);
src_end = &endp->net_end;
src_end->codec.payload_type = audio_name_to_type(srcfmt);
if (out_samples) {
dst_end->codec.frame_duration_den = dst_end->codec.rate;
dst_end->codec.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) {
printf("setup failed: %s", strerror(-rc));
abort();
}
*out_ctx = cfg;
*out_endp = endp;
return 0;
}
static int transcode_test(const char *srcfmt, const char *dstfmt,
uint8_t *src_pkts, size_t src_pkt_size)
{
char buf[4096] = {0x80, 0};
void *ctx;
struct mgcp_rtp_end *dst_end;
struct mgcp_process_rtp_state *state;
struct mgcp_endpoint *endp;
int in_size;
const int in_samples = 160;
int len, cont;
printf("== Transcoding test ==\n");
printf("converting %s -> %s\n", srcfmt, dstfmt);
given_configured_endpoint(in_samples, 0, srcfmt, dstfmt, &ctx, &endp);
dst_end = &endp->bts_end;
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) {
printf("Nothing encoded due: %s\n", strerror(-cont));
talloc_free(ctx);
return -1;
}
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);
}
printf("counted: %d\n", cont);
talloc_free(ctx);
return 0;
}
static void test_rtp_seq_state(void)
{
char buf[4096];
int len;
int cont;
void *ctx;
struct mgcp_endpoint *endp;
struct mgcp_process_rtp_state *state;
struct rtp_hdr *hdr;
uint32_t ts_no;
uint16_t seq_no;
given_configured_endpoint(160, 0, "pcma", "l16", &ctx, &endp);
state = endp->bts_end.rtp_process_data;
OSMO_ASSERT(!state->is_running);
OSMO_ASSERT(state->next_seq == 0);
OSMO_ASSERT(state->next_time == 0);
/* initialize packet */
len = audio_packets_pcma[0].len;
memcpy(buf, audio_packets_pcma[0].data, len);
cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len);
OSMO_ASSERT(cont >= 0);
OSMO_ASSERT(state->is_running);
OSMO_ASSERT(state->next_seq == 2);
OSMO_ASSERT(state->next_time == 240);
/* verify that the right timestamp was written */
OSMO_ASSERT(len == audio_packets_pcma[0].len);
hdr = (struct rtp_hdr *) &buf[0];
memcpy(&ts_no, &hdr->timestamp, sizeof(ts_no));
OSMO_ASSERT(htonl(ts_no) == 160);
memcpy(&seq_no, &hdr->sequence, sizeof(seq_no));
OSMO_ASSERT(htons(seq_no) == 1);
/* Check the right sequence number is written */
state->next_seq = 1234;
len = audio_packets_pcma[0].len;
memcpy(buf, audio_packets_pcma[0].data, len);
cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len);
OSMO_ASSERT(cont >= 0);
OSMO_ASSERT(len == audio_packets_pcma[0].len);
hdr = (struct rtp_hdr *) &buf[0];
memcpy(&seq_no, &hdr->sequence, sizeof(seq_no));
OSMO_ASSERT(htons(seq_no) == 1234);
talloc_free(ctx);
}
static void test_transcode_result(void)
{
char buf[4096];
int len, res;
void *ctx;
struct mgcp_endpoint *endp;
struct mgcp_process_rtp_state *state;
{
/* from GSM to PCMA and same ptime */
given_configured_endpoint(160, 0, "gsm", "pcma", &ctx, &endp);
state = endp->bts_end.rtp_process_data;
/* result */
len = audio_packets_gsm[0].len;
memcpy(buf, audio_packets_gsm[0].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
OSMO_ASSERT(state->sample_cnt == 0);
len = res;
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == -ENOMSG);
talloc_free(ctx);
}
{
/* from GSM to PCMA and same ptime */
given_configured_endpoint(160, 160, "gsm", "pcma", &ctx, &endp);
state = endp->bts_end.rtp_process_data;
/* result */
len = audio_packets_gsm[0].len;
memcpy(buf, audio_packets_gsm[0].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
OSMO_ASSERT(state->sample_cnt == 0);
len = res;
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == -EAGAIN);
talloc_free(ctx);
}
{
/* from PCMA to GSM and wrong different ptime */
given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp);
state = endp->bts_end.rtp_process_data;
/* Add the first sample */
len = audio_packets_pcma[1].len;
memcpy(buf, audio_packets_pcma[1].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(state->sample_cnt == 80);
OSMO_ASSERT(state->next_time == 232640);
OSMO_ASSERT(res < 0);
/* Add the second sample and it should be consumable */
len = audio_packets_pcma[2].len;
memcpy(buf, audio_packets_pcma[2].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(state->sample_cnt == 0);
OSMO_ASSERT(state->next_time == 232640 + 80 + 160);
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
talloc_free(ctx);
}
{
/* from PCMA to GSM with a big time jump */
struct rtp_hdr *hdr;
uint32_t ts;
given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp);
state = endp->bts_end.rtp_process_data;
/* Add the first sample */
len = audio_packets_pcma[1].len;
memcpy(buf, audio_packets_pcma[1].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(state->sample_cnt == 80);
OSMO_ASSERT(state->next_time == 232640);
OSMO_ASSERT(state->next_seq == 26527);
OSMO_ASSERT(res < 0);
/* Add a skip to the packet to force a 'resync' */
len = audio_packets_pcma[2].len;
memcpy(buf, audio_packets_pcma[2].data, len);
hdr = (struct rtp_hdr *) &buf[0];
/* jump the time and add alignment error */
ts = ntohl(hdr->timestamp) + 123 * 80 + 2;
hdr->timestamp = htonl(ts);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res < 0);
OSMO_ASSERT(state->sample_cnt == 80);
OSMO_ASSERT(state->next_time == ts);
OSMO_ASSERT(state->next_seq == 26527);
/* TODO: this can create alignment errors */
/* Now attempt to consume 160 samples */
len = audio_packets_pcma[2].len;
memcpy(buf, audio_packets_pcma[2].data, len);
hdr = (struct rtp_hdr *) &buf[0];
ts += 80;
hdr->timestamp = htonl(ts);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == 12);
OSMO_ASSERT(state->sample_cnt == 0);
OSMO_ASSERT(state->next_time == ts + 160);
OSMO_ASSERT(state->next_seq == 26528);
talloc_free(ctx);
}
}
static void test_transcode_change(void)
{
char buf[4096] = {0x80, 0};
void *ctx;
struct mgcp_endpoint *endp;
struct mgcp_process_rtp_state *state;
struct rtp_hdr *hdr;
int len, res;
{
/* from GSM to PCMA and same ptime */
printf("Testing Initial L16->GSM, PCMA->GSM\n");
given_configured_endpoint(160, 0, "l16", "gsm", &ctx, &endp);
endp->net_end.alt_codec = endp->net_end.codec;
endp->net_end.alt_codec.payload_type = audio_name_to_type("pcma");
state = endp->bts_end.rtp_process_data;
/* initial transcoding work */
OSMO_ASSERT(state->src_fmt == AF_L16);
OSMO_ASSERT(state->dst_fmt == AF_GSM);
OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 8);
OSMO_ASSERT(endp->net_end.codec.payload_type == 11);
/* result */
len = audio_packets_pcma[0].len;
memcpy(buf, audio_packets_pcma[0].data, len);
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
state = endp->bts_end.rtp_process_data;
OSMO_ASSERT(res == sizeof(struct rtp_hdr));
OSMO_ASSERT(state->sample_cnt == 0);
OSMO_ASSERT(state->src_fmt == AF_PCMA);
OSMO_ASSERT(state->dst_fmt == AF_GSM);
OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11);
OSMO_ASSERT(endp->net_end.codec.payload_type == 8);
len = res;
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(res == -ENOMSG);
OSMO_ASSERT(state == endp->bts_end.rtp_process_data);
/* now check that comfort noise doesn't change anything */
len = audio_packets_pcma[1].len;
memcpy(buf, audio_packets_pcma[1].data, len);
hdr = (struct rtp_hdr *) buf;
hdr->payload_type = 12;
res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
OSMO_ASSERT(state == endp->bts_end.rtp_process_data);
OSMO_ASSERT(state->sample_cnt == 80);
OSMO_ASSERT(state->src_fmt == AF_PCMA);
OSMO_ASSERT(state->dst_fmt == AF_GSM);
OSMO_ASSERT(endp->net_end.alt_codec.payload_type == 11);
OSMO_ASSERT(endp->net_end.codec.payload_type == 8);
talloc_free(ctx);
}
}
static int test_repacking(int in_samples, int out_samples, int no_transcode)
{
char buf[4096] = {0x80, 0};
int cc;
struct mgcp_endpoint *endp;
void *ctx;
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";
printf("== Transcoding test ==\n");
printf("converting %s -> %s\n", srcfmt, dstfmt);
given_configured_endpoint(in_samples, out_samples, srcfmt, dstfmt, &ctx, &endp);
state = endp->bts_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] = endp->net_end.codec.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, &endp->bts_end,
buf, &len, sizeof(buf));
if (cont == -EAGAIN) {
fprintf(stderr, "Got EAGAIN\n");
break;
}
if (cont < 0) {
printf("processing failed: %s", strerror(-cont));
abort();
}
len -= 12; /* ignore RTP header */
printf("got %d %s output frames (%d octets) count=%d\n",
len / out_size, dstfmt, len, cont);
len = cont;
} while (len > 0);
}
talloc_free(ctx);
return 0;
}
static const struct log_info_cat log_categories[] = {
};
const struct log_info log_info = {
.cat = log_categories,
.num_cat = ARRAY_SIZE(log_categories),
};
int main(int argc, char **argv)
{
int rc;
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");
rc = transcode_test("gsm", "pcma",
(uint8_t *)audio_packets_gsm_invalid_size[0].data,
audio_packets_gsm_invalid_size[0].len);
OSMO_ASSERT(rc < 0);
printf("Invalid data:\n");
rc = transcode_test("gsm", "pcma",
(uint8_t *)audio_packets_gsm_invalid_data[0].data,
audio_packets_gsm_invalid_data[0].len);
OSMO_ASSERT(rc < 0);
printf("Invalid payload type:\n");
rc = transcode_test("gsm", "pcma",
(uint8_t *)audio_packets_gsm_invalid_ptype[0].data,
audio_packets_gsm_invalid_ptype[0].len);
OSMO_ASSERT(rc == 0);
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);
test_rtp_seq_state();
test_transcode_result();
test_transcode_change();
return 0;
}

View File

@ -1,539 +0,0 @@
=== 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
counted: 0
== Transcoding test ==
converting l16 -> gsm
encoded:
80 0b 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
counted: 12
== Transcoding test ==
converting l16 -> pcma
encoded:
80 0b 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
counted: 12
== Transcoding test ==
converting gsm -> l16
encoded:
80 03 00 01 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
counted: 12
== 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
counted: 0
== Transcoding test ==
converting gsm -> pcma
encoded:
80 03 00 01 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
counted: 12
== Transcoding test ==
converting pcma -> l16
encoded:
80 08 00 01 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
counted: 12
== Transcoding test ==
converting pcma -> gsm
encoded:
80 08 00 01 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
counted: 12
== 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
counted: 0
=== Transcoding Bad Cases ===
Invalid size:
== Transcoding test ==
converting gsm -> pcma
Nothing encoded due: No message of desired type
Invalid data:
== Transcoding test ==
converting gsm -> pcma
Nothing encoded due: No message of desired type
Invalid payload type:
== Transcoding test ==
converting gsm -> pcma
encoded:
80 08 00 01 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
counted: 12
=== Repacking ===
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
generating 160 pcma input samples
got 2 l16 output frames (320 octets) count=12
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
generating 160 pcma input samples
got 2 pcma output frames (160 octets) count=12
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 l16 output frames (640 octets) count=12
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 4 pcma output frames (320 octets) count=12
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 l16 output frames (480 octets) count=12
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
generating 160 pcma input samples
generating 160 pcma input samples
got 3 pcma output frames (240 octets) count=12
== Transcoding test ==
converting pcma -> l16
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
generating 160 pcma input samples
got 1 l16 output frames (160 octets) count=12
got 1 l16 output frames (160 octets) count=12
== Transcoding test ==
converting pcma -> pcma
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
generating 160 pcma input samples
got 1 pcma output frames (80 octets) count=12
got 1 pcma output frames (80 octets) count=12
Testing Initial L16->GSM, PCMA->GSM

View File

@ -20,3 +20,9 @@ cat $abs_srcdir/mgcp_client/mgcp_client_test.ok > expout
cat $abs_srcdir/mgcp_client/mgcp_client_test.err > experr
AT_CHECK([$abs_top_builddir/tests/mgcp_client/mgcp_client_test], [], [expout], [experr])
AT_CLEANUP
AT_SETUP([mgcp])
AT_KEYWORDS([mgcp])
cat $abs_srcdir/mgcp/mgcp_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_test], [], [expout], [ignore])
AT_CLEANUP