move to osmo-mgw.git: osmo-bsc_mgcp and libmgcp as libosmo-legacy-mgcp
Rewire build and includes to libosmo-legacy-mgcp. Drop osmo-bsc_mgcp and related python tests, now found in osmo-mgw.git. libosmo-legacy-mgcp is installed from osmo-mgw, hence add the dependency to jenkins.sh (so far using the pre_release branch). Change-Id: Ic99d681759edce11564da62500c2aac5cf5fffe2
This commit is contained in:
parent
c4c794c6c7
commit
becfc4cb1b
|
@ -48,6 +48,7 @@ PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.6.4)
|
||||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.0.1)
|
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.0.1)
|
||||||
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran) # TODO version?
|
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran) # TODO version?
|
||||||
PKG_CHECK_MODULES(LIBCRYPTO, libcrypto >= 0.9.5)
|
PKG_CHECK_MODULES(LIBCRYPTO, libcrypto >= 0.9.5)
|
||||||
|
PKG_CHECK_MODULES(LIBOSMOLEGACYMGCP, libosmo-legacy-mgcp >= 0.0.1)
|
||||||
|
|
||||||
# Enabke/disable the NAT?
|
# Enabke/disable the NAT?
|
||||||
AC_ARG_ENABLE([nat], [AS_HELP_STRING([--enable-nat], [Build the BSC NAT. Requires SCCP])],
|
AC_ARG_ENABLE([nat], [AS_HELP_STRING([--enable-nat], [Build the BSC NAT. Requires SCCP])],
|
||||||
|
@ -230,14 +231,12 @@ AC_OUTPUT(
|
||||||
src/libbsc/Makefile
|
src/libbsc/Makefile
|
||||||
src/libmsc/Makefile
|
src/libmsc/Makefile
|
||||||
src/libvlr/Makefile
|
src/libvlr/Makefile
|
||||||
src/libmgcp/Makefile
|
|
||||||
src/libcommon/Makefile
|
src/libcommon/Makefile
|
||||||
src/libfilter/Makefile
|
src/libfilter/Makefile
|
||||||
src/libcommon-cs/Makefile
|
src/libcommon-cs/Makefile
|
||||||
src/osmo-msc/Makefile
|
src/osmo-msc/Makefile
|
||||||
src/osmo-bsc/Makefile
|
src/osmo-bsc/Makefile
|
||||||
src/osmo-bsc_nat/Makefile
|
src/osmo-bsc_nat/Makefile
|
||||||
src/osmo-bsc_mgcp/Makefile
|
|
||||||
src/ipaccess/Makefile
|
src/ipaccess/Makefile
|
||||||
src/utils/Makefile
|
src/utils/Makefile
|
||||||
src/gprs/Makefile
|
src/gprs/Makefile
|
||||||
|
@ -248,7 +247,6 @@ AC_OUTPUT(
|
||||||
tests/bsc/Makefile
|
tests/bsc/Makefile
|
||||||
tests/bsc-nat/Makefile
|
tests/bsc-nat/Makefile
|
||||||
tests/bsc-nat-trie/Makefile
|
tests/bsc-nat-trie/Makefile
|
||||||
tests/mgcp/Makefile
|
|
||||||
tests/gprs/Makefile
|
tests/gprs/Makefile
|
||||||
tests/gbproxy/Makefile
|
tests/gbproxy/Makefile
|
||||||
tests/abis/Makefile
|
tests/abis/Makefile
|
||||||
|
|
|
@ -29,6 +29,7 @@ osmo-build-dep.sh libosmo-netif
|
||||||
osmo-build-dep.sh libosmo-sccp
|
osmo-build-dep.sh libosmo-sccp
|
||||||
PARALLEL_MAKE="" osmo-build-dep.sh libsmpp34
|
PARALLEL_MAKE="" osmo-build-dep.sh libsmpp34
|
||||||
osmo-build-dep.sh openggsn
|
osmo-build-dep.sh openggsn
|
||||||
|
osmo-build-dep.sh osmo-mgw pre_release # move to 'master' once the split is complete
|
||||||
|
|
||||||
if [ "x$IU" = "x--enable-iu" ]; then
|
if [ "x$IU" = "x--enable-iu" ]; then
|
||||||
osmo-build-dep.sh libasn1c
|
osmo-build-dep.sh libasn1c
|
||||||
|
|
|
@ -54,10 +54,6 @@ noinst_HEADERS = \
|
||||||
iu_dummy.h \
|
iu_dummy.h \
|
||||||
meas_feed.h \
|
meas_feed.h \
|
||||||
meas_rep.h \
|
meas_rep.h \
|
||||||
mgcp.h \
|
|
||||||
mgcp_internal.h \
|
|
||||||
mgcp_transcode.h \
|
|
||||||
mgcpgw_client.h \
|
|
||||||
misdn.h \
|
misdn.h \
|
||||||
mncc.h \
|
mncc.h \
|
||||||
mncc_int.h \
|
mncc_int.h \
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
#ifndef BSC_NAT_H
|
#ifndef BSC_NAT_H
|
||||||
#define BSC_NAT_H
|
#define BSC_NAT_H
|
||||||
|
|
||||||
#include "mgcp.h"
|
#include <osmocom/legacy_mgcp/mgcp.h>
|
||||||
#include "bsc_msg_filter.h"
|
|
||||||
|
|
||||||
|
#include "bsc_msg_filter.h"
|
||||||
|
|
||||||
#include <osmocom/core/select.h>
|
#include <osmocom/core/select.h>
|
||||||
#include <osmocom/core/msgb.h>
|
#include <osmocom/core/msgb.h>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include <openbsc/common.h>
|
#include <openbsc/common.h>
|
||||||
#include <openbsc/rest_octets.h>
|
#include <openbsc/rest_octets.h>
|
||||||
#include <openbsc/common_cs.h>
|
#include <openbsc/common_cs.h>
|
||||||
#include <openbsc/mgcpgw_client.h>
|
#include <osmocom/legacy_mgcp/mgcpgw_client.h>
|
||||||
|
|
||||||
|
|
||||||
/** annotations for msgb ownership */
|
/** annotations for msgb ownership */
|
||||||
|
|
|
@ -1,300 +0,0 @@
|
||||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
||||||
* (C) 2009-2012 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_H
|
|
||||||
#define OPENBSC_MGCP_H
|
|
||||||
|
|
||||||
#include <osmocom/core/msgb.h>
|
|
||||||
#include <osmocom/core/write_queue.h>
|
|
||||||
#include <osmocom/core/timer.h>
|
|
||||||
|
|
||||||
#include "debug.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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the RTP audio port for the given multiplex
|
|
||||||
* and the direction. This allows a semi static endpoint
|
|
||||||
* to port calculation removing the need for the BSC
|
|
||||||
* and the MediaGateway to communicate.
|
|
||||||
*
|
|
||||||
* Port usage explained:
|
|
||||||
* base + (multiplex * 2) + 0 == local port to wait for network packets
|
|
||||||
* base + (multiplex * 2) + 1 == local port for rtcp
|
|
||||||
*
|
|
||||||
* The above port will receive packets from the BTS that need
|
|
||||||
* to be patched and forwarded to the network.
|
|
||||||
* The above port will receive packets from the network that
|
|
||||||
* need to be patched and forwarded to the BTS.
|
|
||||||
*
|
|
||||||
* We assume to have a static BTS IP address so we can differentiate
|
|
||||||
* network and BTS.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static inline int rtp_calculate_port(int multiplex, int base)
|
|
||||||
{
|
|
||||||
return base + (multiplex * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handling of MGCP Endpoints and the MGCP Config
|
|
||||||
*/
|
|
||||||
struct mgcp_endpoint;
|
|
||||||
struct mgcp_config;
|
|
||||||
struct mgcp_trunk_config;
|
|
||||||
struct mgcp_rtp_end;
|
|
||||||
|
|
||||||
#define MGCP_ENDP_CRCX 1
|
|
||||||
#define MGCP_ENDP_DLCX 2
|
|
||||||
#define MGCP_ENDP_MDCX 3
|
|
||||||
|
|
||||||
/*
|
|
||||||
* what to do with the msg?
|
|
||||||
* - continue as usual?
|
|
||||||
* - reject and send a failure code?
|
|
||||||
* - defer? do not send anything
|
|
||||||
*/
|
|
||||||
#define MGCP_POLICY_CONT 4
|
|
||||||
#define MGCP_POLICY_REJECT 5
|
|
||||||
#define MGCP_POLICY_DEFER 6
|
|
||||||
|
|
||||||
typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint);
|
|
||||||
typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state);
|
|
||||||
typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id);
|
|
||||||
typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
|
|
||||||
typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return:
|
|
||||||
* < 0 in case no audio was processed
|
|
||||||
* >= 0 in case audio was processed. The remaining payload
|
|
||||||
* length will be returned.
|
|
||||||
*/
|
|
||||||
typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end,
|
|
||||||
char *data, int *len, int buf_size);
|
|
||||||
typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end,
|
|
||||||
struct mgcp_rtp_end *src_end);
|
|
||||||
|
|
||||||
typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp,
|
|
||||||
int *payload_type,
|
|
||||||
const char**subtype_name,
|
|
||||||
const char**fmtp_extra);
|
|
||||||
|
|
||||||
#define PORT_ALLOC_STATIC 0
|
|
||||||
#define PORT_ALLOC_DYNAMIC 1
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
int last_port;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MGCP_KEEPALIVE_ONCE (-1)
|
|
||||||
|
|
||||||
struct mgcp_trunk_config {
|
|
||||||
struct llist_head entry;
|
|
||||||
|
|
||||||
struct mgcp_config *cfg;
|
|
||||||
|
|
||||||
int trunk_nr;
|
|
||||||
int trunk_type;
|
|
||||||
|
|
||||||
char *audio_fmtp_extra;
|
|
||||||
char *audio_name;
|
|
||||||
int audio_payload;
|
|
||||||
int audio_send_ptime;
|
|
||||||
int audio_send_name;
|
|
||||||
int audio_loop;
|
|
||||||
|
|
||||||
int no_audio_transcoding;
|
|
||||||
|
|
||||||
int omit_rtcp;
|
|
||||||
int keepalive_interval;
|
|
||||||
|
|
||||||
/* RTP patching */
|
|
||||||
int force_constant_ssrc; /* 0: don't, 1: once */
|
|
||||||
int force_aligned_timing;
|
|
||||||
|
|
||||||
/* spec handling */
|
|
||||||
int force_realloc;
|
|
||||||
|
|
||||||
/* timer */
|
|
||||||
struct osmo_timer_list keepalive_timer;
|
|
||||||
|
|
||||||
unsigned int number_endpoints;
|
|
||||||
struct mgcp_endpoint *endpoints;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mgcp_role {
|
|
||||||
MGCP_BSC = 0,
|
|
||||||
MGCP_BSC_NAT,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mgcp_connection_mode {
|
|
||||||
MGCP_CONN_NONE = 0,
|
|
||||||
MGCP_CONN_RECV_ONLY = 1,
|
|
||||||
MGCP_CONN_SEND_ONLY = 2,
|
|
||||||
MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
|
|
||||||
MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND,
|
|
||||||
};
|
|
||||||
|
|
||||||
extern const struct value_string mgcp_connection_mode_strs[];
|
|
||||||
|
|
||||||
static inline const char *mgcp_cmode_name(enum mgcp_connection_mode mode)
|
|
||||||
{
|
|
||||||
return get_value_string(mgcp_connection_mode_strs, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
mgcp_get_format get_net_downlink_format_cb;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
mgcp_change change_cb;
|
|
||||||
mgcp_policy policy_cb;
|
|
||||||
mgcp_reset reset_cb;
|
|
||||||
mgcp_realloc realloc_cb;
|
|
||||||
mgcp_rqnt rqnt_cb;
|
|
||||||
void *data;
|
|
||||||
|
|
||||||
uint32_t last_call_id;
|
|
||||||
|
|
||||||
/* trunk handling */
|
|
||||||
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 */
|
|
||||||
int osmux;
|
|
||||||
/* addr to bind the server to */
|
|
||||||
char *osmux_addr;
|
|
||||||
/* The BSC-NAT may ask for enabling osmux on demand. This tells us if
|
|
||||||
* the osmux socket is already initialized.
|
|
||||||
*/
|
|
||||||
int osmux_init;
|
|
||||||
/* osmux batch factor: from 1 to 4 maximum */
|
|
||||||
int osmux_batch;
|
|
||||||
/* osmux batch size (in bytes) */
|
|
||||||
int osmux_batch_size;
|
|
||||||
/* osmux port */
|
|
||||||
uint16_t osmux_port;
|
|
||||||
/* Pad circuit with dummy messages until we see the first voice
|
|
||||||
* message.
|
|
||||||
*/
|
|
||||||
uint16_t osmux_dummy;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* config management */
|
|
||||||
struct mgcp_config *mgcp_config_alloc(void);
|
|
||||||
int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg,
|
|
||||||
enum mgcp_role role);
|
|
||||||
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);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* format helper functions
|
|
||||||
*/
|
|
||||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
|
|
||||||
|
|
||||||
/* adc helper */
|
|
||||||
static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot)
|
|
||||||
{
|
|
||||||
if (timeslot == 0) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Timeslot should not be 0\n");
|
|
||||||
timeslot = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
return timeslot + (32 * multiplex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void mgcp_endpoint_to_timeslot(int endpoint, int *multiplex, int *timeslot)
|
|
||||||
{
|
|
||||||
*multiplex = endpoint / 32;
|
|
||||||
*timeslot = endpoint % 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint);
|
|
||||||
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
|
|
|
@ -1,337 +0,0 @@
|
||||||
/* MGCP Private Data */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
||||||
* (C) 2009-2012 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/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/select.h>
|
|
||||||
|
|
||||||
#define CI_UNUSED 0
|
|
||||||
|
|
||||||
enum mgcp_trunk_type {
|
|
||||||
MGCP_TRUNK_VIRTUAL,
|
|
||||||
MGCP_TRUNK_E1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_rtp_stream_state {
|
|
||||||
uint32_t ssrc;
|
|
||||||
uint16_t last_seq;
|
|
||||||
uint32_t last_timestamp;
|
|
||||||
uint32_t err_ts_counter;
|
|
||||||
int32_t last_tsdelta;
|
|
||||||
uint32_t last_arrival_time;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_rtp_state {
|
|
||||||
int initialized;
|
|
||||||
int patch_ssrc;
|
|
||||||
|
|
||||||
uint32_t orig_ssrc;
|
|
||||||
|
|
||||||
int seq_offset;
|
|
||||||
|
|
||||||
int32_t timestamp_offset;
|
|
||||||
uint32_t packet_duration;
|
|
||||||
|
|
||||||
struct mgcp_rtp_stream_state in_stream;
|
|
||||||
struct mgcp_rtp_stream_state out_stream;
|
|
||||||
|
|
||||||
/* jitter and packet loss calculation */
|
|
||||||
int stats_initialized;
|
|
||||||
uint16_t stats_base_seq;
|
|
||||||
uint16_t stats_max_seq;
|
|
||||||
uint32_t stats_ssrc;
|
|
||||||
uint32_t stats_jitter;
|
|
||||||
int32_t stats_transit;
|
|
||||||
int stats_cycles;
|
|
||||||
bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_rtp_codec {
|
|
||||||
uint32_t rate;
|
|
||||||
int channels;
|
|
||||||
uint32_t frame_duration_num;
|
|
||||||
uint32_t frame_duration_den;
|
|
||||||
|
|
||||||
int payload_type;
|
|
||||||
char *audio_name;
|
|
||||||
char *subtype_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_rtp_end {
|
|
||||||
/* statistics */
|
|
||||||
unsigned int packets;
|
|
||||||
unsigned int octets;
|
|
||||||
unsigned int dropped_packets;
|
|
||||||
struct in_addr addr;
|
|
||||||
|
|
||||||
/* in network byte order */
|
|
||||||
int rtp_port, rtcp_port;
|
|
||||||
|
|
||||||
/* audio codec information */
|
|
||||||
struct mgcp_rtp_codec codec;
|
|
||||||
struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */
|
|
||||||
|
|
||||||
/* per endpoint data */
|
|
||||||
int frames_per_packet;
|
|
||||||
uint32_t packet_duration_ms;
|
|
||||||
char *fmtp_extra;
|
|
||||||
int output_enabled;
|
|
||||||
int force_output_ptime;
|
|
||||||
|
|
||||||
/* RTP patching */
|
|
||||||
int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
|
|
||||||
int force_aligned_timing;
|
|
||||||
void *rtp_process_data;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Each end has a socket...
|
|
||||||
*/
|
|
||||||
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 {
|
|
||||||
int enabled;
|
|
||||||
struct sockaddr_in forward;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_lco {
|
|
||||||
char *string;
|
|
||||||
char *codec;
|
|
||||||
int pkt_period_min; /* time in ms */
|
|
||||||
int pkt_period_max; /* time in ms */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum mgcp_type {
|
|
||||||
MGCP_RTP_DEFAULT = 0,
|
|
||||||
MGCP_RTP_TRANSCODED,
|
|
||||||
MGCP_OSMUX_BSC,
|
|
||||||
MGCP_OSMUX_BSC_NAT,
|
|
||||||
};
|
|
||||||
|
|
||||||
#include <openbsc/osmux.h>
|
|
||||||
|
|
||||||
struct mgcp_endpoint {
|
|
||||||
int allocated;
|
|
||||||
uint32_t ci;
|
|
||||||
char *callid;
|
|
||||||
struct mgcp_lco local_options;
|
|
||||||
int conn_mode;
|
|
||||||
int orig_mode;
|
|
||||||
|
|
||||||
/* backpointer */
|
|
||||||
struct mgcp_config *cfg;
|
|
||||||
struct mgcp_trunk_config *tcfg;
|
|
||||||
|
|
||||||
/* port status for bts/net */
|
|
||||||
struct mgcp_rtp_end bts_end;
|
|
||||||
struct mgcp_rtp_end net_end;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
/* sequence bits */
|
|
||||||
struct mgcp_rtp_state net_state;
|
|
||||||
struct mgcp_rtp_state bts_state;
|
|
||||||
|
|
||||||
/* fields for re-transmission */
|
|
||||||
char *last_trans;
|
|
||||||
char *last_response;
|
|
||||||
|
|
||||||
/* tap for the endpoint */
|
|
||||||
struct mgcp_rtp_tap taps[MGCP_TAP_COUNT];
|
|
||||||
|
|
||||||
struct {
|
|
||||||
/* Osmux state: disabled, activating, active */
|
|
||||||
enum osmux_state state;
|
|
||||||
/* Allocated Osmux circuit ID for this endpoint */
|
|
||||||
int allocated_cid;
|
|
||||||
/* Used Osmux circuit ID for this endpoint */
|
|
||||||
uint8_t cid;
|
|
||||||
/* handle to batch messages */
|
|
||||||
struct osmux_in_handle *in;
|
|
||||||
/* handle to unbatch messages */
|
|
||||||
struct osmux_out_handle out;
|
|
||||||
/* statistics */
|
|
||||||
struct {
|
|
||||||
uint32_t chunks;
|
|
||||||
uint32_t octets;
|
|
||||||
} stats;
|
|
||||||
} osmux;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define for_each_line(line, save) \
|
|
||||||
for (line = strline_r(NULL, &save); line;\
|
|
||||||
line = strline_r(NULL, &save))
|
|
||||||
|
|
||||||
static inline char *strline_r(char *str, char **saveptr)
|
|
||||||
{
|
|
||||||
char *result;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal structure while parsing a request
|
|
||||||
*/
|
|
||||||
struct mgcp_parse_data {
|
|
||||||
struct mgcp_config *cfg;
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
char *trans;
|
|
||||||
char *save;
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* For transcoding we need to manage an in and an output that are connected */
|
|
||||||
static inline int endp_back_channel(int endpoint)
|
|
||||||
{
|
|
||||||
return endpoint + 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
|
|
||||||
struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
|
|
||||||
|
|
||||||
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
|
|
||||||
struct mgcp_rtp_end *rtp);
|
|
||||||
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);
|
|
||||||
|
|
||||||
int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp,
|
|
||||||
struct mgcp_rtp_end *dst_end,
|
|
||||||
struct mgcp_rtp_end *src_end);
|
|
||||||
|
|
||||||
void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
|
||||||
int *payload_type,
|
|
||||||
const char**subtype_name,
|
|
||||||
const char**fmtp_extra);
|
|
||||||
|
|
||||||
/* internal RTP Annex A counting */
|
|
||||||
void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
|
|
||||||
const uint16_t seq, const int32_t transit,
|
|
||||||
const uint32_t ssrc);
|
|
||||||
|
|
||||||
int mgcp_set_ip_tos(int fd, int tos);
|
|
||||||
|
|
||||||
enum {
|
|
||||||
MGCP_DEST_NET = 0,
|
|
||||||
MGCP_DEST_BTS,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#define MGCP_DUMMY_LOAD 0x23
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SDP related information
|
|
||||||
*/
|
|
||||||
/* Assume audio frame length of 20ms */
|
|
||||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
|
|
||||||
#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
|
|
||||||
#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
|
|
||||||
#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
|
|
||||||
#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
|
|
||||||
|
|
||||||
#define PTYPE_UNDEFINED (-1)
|
|
||||||
int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p);
|
|
||||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
|
||||||
int payload_type, const char *audio_name);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal network related
|
|
||||||
*/
|
|
||||||
static inline const char *mgcp_net_src_addr(struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
if (endp->cfg->net_ports.bind_addr)
|
|
||||||
return endp->cfg->net_ports.bind_addr;
|
|
||||||
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);
|
|
|
@ -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 */
|
|
|
@ -1,111 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/linuxlist.h>
|
|
||||||
#include <osmocom/core/write_queue.h>
|
|
||||||
|
|
||||||
enum mgcp_connection_mode;
|
|
||||||
|
|
||||||
struct msgb;
|
|
||||||
struct vty;
|
|
||||||
|
|
||||||
#define MGCPGW_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
|
|
||||||
#define MGCPGW_CLIENT_LOCAL_PORT_DEFAULT 0
|
|
||||||
#define MGCPGW_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
|
|
||||||
#define MGCPGW_CLIENT_REMOTE_PORT_DEFAULT 2427
|
|
||||||
|
|
||||||
#define MSGB_CB_MGCP_TRANS_ID 0
|
|
||||||
|
|
||||||
typedef unsigned int mgcp_trans_id_t;
|
|
||||||
|
|
||||||
struct mgcpgw_client_conf {
|
|
||||||
const char *local_addr;
|
|
||||||
int local_port;
|
|
||||||
const char *remote_addr;
|
|
||||||
int remote_port;
|
|
||||||
uint16_t first_endpoint;
|
|
||||||
uint16_t last_endpoint;
|
|
||||||
uint16_t bts_base;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_response_head {
|
|
||||||
int response_code;
|
|
||||||
mgcp_trans_id_t trans_id;
|
|
||||||
const char *comment;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_response {
|
|
||||||
char *body;
|
|
||||||
struct mgcp_response_head head;
|
|
||||||
uint16_t audio_port;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcpgw_client {
|
|
||||||
struct mgcpgw_client_conf actual;
|
|
||||||
uint32_t remote_addr;
|
|
||||||
struct osmo_wqueue wq;
|
|
||||||
mgcp_trans_id_t next_trans_id;
|
|
||||||
struct llist_head responses_pending;
|
|
||||||
struct llist_head inuse_endpoints;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct mgcp_inuse_endpoint {
|
|
||||||
struct llist_head entry;
|
|
||||||
uint16_t id;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Invoked when an MGCP response is received or sending failed. When the
|
|
||||||
* response is passed as NULL, this indicates failure during transmission. */
|
|
||||||
typedef void (* mgcp_response_cb_t )(struct mgcp_response *response, void *priv);
|
|
||||||
|
|
||||||
struct mgcp_response_pending {
|
|
||||||
struct llist_head entry;
|
|
||||||
|
|
||||||
mgcp_trans_id_t trans_id;
|
|
||||||
mgcp_response_cb_t response_cb;
|
|
||||||
void *priv;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf);
|
|
||||||
|
|
||||||
struct mgcpgw_client *mgcpgw_client_init(void *ctx,
|
|
||||||
struct mgcpgw_client_conf *conf);
|
|
||||||
int mgcpgw_client_connect(struct mgcpgw_client *mgcp);
|
|
||||||
|
|
||||||
const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp);
|
|
||||||
uint16_t mgcpgw_client_remote_port(struct mgcpgw_client *mgcp);
|
|
||||||
uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp);
|
|
||||||
|
|
||||||
/* Find and seize an unsused endpoint id */
|
|
||||||
int mgcpgw_client_next_endpoint(struct mgcpgw_client *client);
|
|
||||||
|
|
||||||
/* Release a seized endpoint id to make it available again for other calls */
|
|
||||||
void mgcpgw_client_release_endpoint(uint16_t id, struct mgcpgw_client *client);
|
|
||||||
|
|
||||||
int mgcp_response_parse_params(struct mgcp_response *r);
|
|
||||||
|
|
||||||
int mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg,
|
|
||||||
mgcp_response_cb_t response_cb, void *priv);
|
|
||||||
|
|
||||||
struct msgb *mgcp_msg_crcx(struct mgcpgw_client *mgcp,
|
|
||||||
uint16_t rtp_endpoint, unsigned int call_id,
|
|
||||||
enum mgcp_connection_mode mode);
|
|
||||||
|
|
||||||
struct msgb *mgcp_msg_mdcx(struct mgcpgw_client *mgcp,
|
|
||||||
uint16_t rtp_endpoint, const char *rtp_conn_addr,
|
|
||||||
uint16_t rtp_port, enum mgcp_connection_mode mode);
|
|
||||||
|
|
||||||
struct msgb *mgcp_msg_dlcx(struct mgcpgw_client *mgcp, uint16_t rtp_endpoint,
|
|
||||||
unsigned int call_id);
|
|
||||||
|
|
||||||
void mgcpgw_client_vty_init(int node, struct mgcpgw_client_conf *conf);
|
|
||||||
int mgcpgw_client_config_write(struct vty *vty, const char *indent);
|
|
||||||
|
|
||||||
struct mgcp_response_pending * mgcpgw_client_pending_add(
|
|
||||||
struct mgcpgw_client *mgcp,
|
|
||||||
mgcp_trans_id_t trans_id,
|
|
||||||
mgcp_response_cb_t response_cb,
|
|
||||||
void *priv);
|
|
||||||
int mgcpgw_client_rx(struct mgcpgw_client *mgcp, struct msgb *msg);
|
|
|
@ -30,7 +30,6 @@ nitb_e1_configs = [
|
||||||
app_configs = {
|
app_configs = {
|
||||||
"osmo-bsc": ["doc/examples/osmo-bsc/osmo-bsc.cfg"],
|
"osmo-bsc": ["doc/examples/osmo-bsc/osmo-bsc.cfg"],
|
||||||
"nat": ["doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"],
|
"nat": ["doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"],
|
||||||
"mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"],
|
|
||||||
"gbproxy": ["doc/examples/osmo-gbproxy/osmo-gbproxy.cfg",
|
"gbproxy": ["doc/examples/osmo-gbproxy/osmo-gbproxy.cfg",
|
||||||
"doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg"],
|
"doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg"],
|
||||||
"sgsn": ["doc/examples/osmo-sgsn/osmo-sgsn.cfg"],
|
"sgsn": ["doc/examples/osmo-sgsn/osmo-sgsn.cfg"],
|
||||||
|
@ -41,7 +40,6 @@ app_configs = {
|
||||||
|
|
||||||
apps = [(4242, "src/osmo-bsc/osmo-bsc", "OsmoBSC", "osmo-bsc"),
|
apps = [(4242, "src/osmo-bsc/osmo-bsc", "OsmoBSC", "osmo-bsc"),
|
||||||
(4244, "src/osmo-bsc_nat/osmo-bsc_nat", "OsmoBSCNAT", "nat"),
|
(4244, "src/osmo-bsc_nat/osmo-bsc_nat", "OsmoBSCNAT", "nat"),
|
||||||
(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp"),
|
|
||||||
(4246, "src/gprs/osmo-gbproxy", "OsmoGbProxy", "gbproxy"),
|
(4246, "src/gprs/osmo-gbproxy", "OsmoGbProxy", "gbproxy"),
|
||||||
(4245, "src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn"),
|
(4245, "src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn"),
|
||||||
(4254, "src/osmo-msc/osmo-msc", "OsmoMSC", "msc"),
|
(4254, "src/osmo-msc/osmo-msc", "OsmoMSC", "msc"),
|
||||||
|
@ -51,4 +49,4 @@ apps = [(4242, "src/osmo-bsc/osmo-bsc", "OsmoBSC", "osmo-bsc"),
|
||||||
vty_command = ["./src/osmo-msc/osmo-msc", "-c",
|
vty_command = ["./src/osmo-msc/osmo-msc", "-c",
|
||||||
"doc/examples/osmo-msc/osmo-msc.cfg"]
|
"doc/examples/osmo-msc/osmo-msc.cfg"]
|
||||||
|
|
||||||
vty_app = apps[5] # reference apps[] entry for osmo-nitb
|
vty_app = apps[4] # reference apps[] entry for osmo-msc
|
||||||
|
|
|
@ -23,7 +23,6 @@ AM_LDFLAGS = \
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
libcommon \
|
libcommon \
|
||||||
libvlr \
|
libvlr \
|
||||||
libmgcp \
|
|
||||||
libbsc \
|
libbsc \
|
||||||
libmsc \
|
libmsc \
|
||||||
libtrau \
|
libtrau \
|
||||||
|
@ -34,7 +33,6 @@ SUBDIRS = \
|
||||||
# Programs
|
# Programs
|
||||||
SUBDIRS += \
|
SUBDIRS += \
|
||||||
osmo-msc \
|
osmo-msc \
|
||||||
osmo-bsc_mgcp \
|
|
||||||
utils \
|
utils \
|
||||||
ipaccess \
|
ipaccess \
|
||||||
gprs \
|
gprs \
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include <openbsc/bsc_msg_filter.h>
|
#include <openbsc/bsc_msg_filter.h>
|
||||||
#include <openbsc/bsc_nat.h>
|
#include <openbsc/bsc_nat.h>
|
||||||
|
#include <openbsc/debug.h>
|
||||||
|
|
||||||
#include <osmocom/core/rate_ctr.h>
|
#include <osmocom/core/rate_ctr.h>
|
||||||
#include <osmocom/core/stats.h>
|
#include <osmocom/core/stats.h>
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
AM_CPPFLAGS = \
|
|
||||||
$(all_includes) \
|
|
||||||
-I$(top_srcdir)/include \
|
|
||||||
-I$(top_builddir) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_CFLAGS = \
|
|
||||||
-Wall \
|
|
||||||
$(LIBOSMOCORE_CFLAGS) \
|
|
||||||
$(LIBOSMOVTY_CFLAGS) \
|
|
||||||
$(LIBOSMONETIF_CFLAGS) \
|
|
||||||
$(COVERAGE_CFLAGS) \
|
|
||||||
$(LIBBCG729_CFLAGS) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_LDFLAGS = \
|
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(LIBOSMOGSM_LIBS) \
|
|
||||||
$(LIBOSMONETIF_LIBS) \
|
|
||||||
$(COVERAGE_LDFLAGS) \
|
|
||||||
$(LIBBCG729_LIBS) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
noinst_LIBRARIES = \
|
|
||||||
libmgcp.a \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
noinst_HEADERS = \
|
|
||||||
g711common.h \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
libmgcp_a_SOURCES = \
|
|
||||||
mgcp_common.c \
|
|
||||||
mgcp_protocol.c \
|
|
||||||
mgcp_network.c \
|
|
||||||
mgcp_vty.c \
|
|
||||||
mgcp_osmux.c \
|
|
||||||
mgcp_sdp.c \
|
|
||||||
mgcpgw_client.c \
|
|
||||||
mgcpgw_client_vty.c \
|
|
||||||
$(NULL)
|
|
||||||
if BUILD_MGCP_TRANSCODING
|
|
||||||
libmgcp_a_SOURCES += \
|
|
||||||
mgcp_transcode.c \
|
|
||||||
$(NULL)
|
|
||||||
endif
|
|
|
@ -1,187 +0,0 @@
|
||||||
/*
|
|
||||||
* PCM - A-Law conversion
|
|
||||||
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
|
||||||
*
|
|
||||||
* Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline int val_seg(int val)
|
|
||||||
{
|
|
||||||
int r = 0;
|
|
||||||
val >>= 7; /*7 = 4 + 3*/
|
|
||||||
if (val & 0xf0) {
|
|
||||||
val >>= 4;
|
|
||||||
r += 4;
|
|
||||||
}
|
|
||||||
if (val & 0x0c) {
|
|
||||||
val >>= 2;
|
|
||||||
r += 2;
|
|
||||||
}
|
|
||||||
if (val & 0x02)
|
|
||||||
r += 1;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
|
|
||||||
*
|
|
||||||
* s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
|
|
||||||
*
|
|
||||||
* Linear Input Code Compressed Code
|
|
||||||
* ------------------------ ---------------
|
|
||||||
* 0000000wxyza 000wxyz
|
|
||||||
* 0000001wxyza 001wxyz
|
|
||||||
* 000001wxyzab 010wxyz
|
|
||||||
* 00001wxyzabc 011wxyz
|
|
||||||
* 0001wxyzabcd 100wxyz
|
|
||||||
* 001wxyzabcde 101wxyz
|
|
||||||
* 01wxyzabcdef 110wxyz
|
|
||||||
* 1wxyzabcdefg 111wxyz
|
|
||||||
*
|
|
||||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
|
||||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
|
||||||
* G711 is designed for 13 bits input signal, this function add extra shifting to take this into account.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline unsigned char s16_to_alaw(int pcm_val)
|
|
||||||
{
|
|
||||||
int mask;
|
|
||||||
int seg;
|
|
||||||
unsigned char aval;
|
|
||||||
|
|
||||||
if (pcm_val >= 0) {
|
|
||||||
mask = 0xD5;
|
|
||||||
} else {
|
|
||||||
mask = 0x55;
|
|
||||||
pcm_val = -pcm_val;
|
|
||||||
if (pcm_val > 0x7fff)
|
|
||||||
pcm_val = 0x7fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pcm_val < 256) /*256 = 32 << 3*/
|
|
||||||
aval = pcm_val >> 4; /*4 = 1 + 3*/
|
|
||||||
else {
|
|
||||||
/* Convert the scaled magnitude to segment number. */
|
|
||||||
seg = val_seg(pcm_val);
|
|
||||||
aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
|
|
||||||
}
|
|
||||||
return aval ^ mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static inline int alaw_to_s16(unsigned char a_val)
|
|
||||||
{
|
|
||||||
int t;
|
|
||||||
int seg;
|
|
||||||
|
|
||||||
a_val ^= 0x55;
|
|
||||||
t = a_val & 0x7f;
|
|
||||||
if (t < 16)
|
|
||||||
t = (t << 4) + 8;
|
|
||||||
else {
|
|
||||||
seg = (t >> 4) & 0x07;
|
|
||||||
t = ((t & 0x0f) << 4) + 0x108;
|
|
||||||
t <<= seg -1;
|
|
||||||
}
|
|
||||||
return ((a_val & 0x80) ? t : -t);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* s16_to_ulaw() - Convert a linear PCM value to u-law
|
|
||||||
*
|
|
||||||
* In order to simplify the encoding process, the original linear magnitude
|
|
||||||
* is biased by adding 33 which shifts the encoding range from (0 - 8158) to
|
|
||||||
* (33 - 8191). The result can be seen in the following encoding table:
|
|
||||||
*
|
|
||||||
* Biased Linear Input Code Compressed Code
|
|
||||||
* ------------------------ ---------------
|
|
||||||
* 00000001wxyza 000wxyz
|
|
||||||
* 0000001wxyzab 001wxyz
|
|
||||||
* 000001wxyzabc 010wxyz
|
|
||||||
* 00001wxyzabcd 011wxyz
|
|
||||||
* 0001wxyzabcde 100wxyz
|
|
||||||
* 001wxyzabcdef 101wxyz
|
|
||||||
* 01wxyzabcdefg 110wxyz
|
|
||||||
* 1wxyzabcdefgh 111wxyz
|
|
||||||
*
|
|
||||||
* Each biased linear code has a leading 1 which identifies the segment
|
|
||||||
* number. The value of the segment number is equal to 7 minus the number
|
|
||||||
* of leading 0's. The quantization interval is directly available as the
|
|
||||||
* four bits wxyz. * The trailing bits (a - h) are ignored.
|
|
||||||
*
|
|
||||||
* Ordinarily the complement of the resulting code word is used for
|
|
||||||
* transmission, and so the code word is complemented before it is returned.
|
|
||||||
*
|
|
||||||
* For further information see John C. Bellamy's Digital Telephony, 1982,
|
|
||||||
* John Wiley & Sons, pps 98-111 and 472-476.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */
|
|
||||||
{
|
|
||||||
int mask;
|
|
||||||
int seg;
|
|
||||||
unsigned char uval;
|
|
||||||
|
|
||||||
if (pcm_val < 0) {
|
|
||||||
pcm_val = 0x84 - pcm_val;
|
|
||||||
mask = 0x7f;
|
|
||||||
} else {
|
|
||||||
pcm_val += 0x84;
|
|
||||||
mask = 0xff;
|
|
||||||
}
|
|
||||||
if (pcm_val > 0x7fff)
|
|
||||||
pcm_val = 0x7fff;
|
|
||||||
|
|
||||||
/* Convert the scaled magnitude to segment number. */
|
|
||||||
seg = val_seg(pcm_val);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Combine the sign, segment, quantization bits;
|
|
||||||
* and complement the code word.
|
|
||||||
*/
|
|
||||||
uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
|
|
||||||
return uval ^ mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM
|
|
||||||
*
|
|
||||||
* First, a biased linear code is derived from the code word. An unbiased
|
|
||||||
* output can then be obtained by subtracting 33 from the biased code.
|
|
||||||
*
|
|
||||||
* Note that this function expects to be passed the complement of the
|
|
||||||
* original code word. This is in keeping with ISDN conventions.
|
|
||||||
*/
|
|
||||||
static inline int ulaw_to_s16(unsigned char u_val)
|
|
||||||
{
|
|
||||||
int t;
|
|
||||||
|
|
||||||
/* Complement to obtain normal u-law value. */
|
|
||||||
u_val = ~u_val;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Extract and bias the quantization bits. Then
|
|
||||||
* shift up by the segment number and subtract out the bias.
|
|
||||||
*/
|
|
||||||
t = ((u_val & 0x0f) << 3) + 0x84;
|
|
||||||
t <<= (u_val & 0x70) >> 4;
|
|
||||||
|
|
||||||
return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84));
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
/* Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
|
||||||
/* Implementations useful both for the MGCP GW as well as MGCP GW clients */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (C) 2016 by sysmocom s.m.f.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 <errno.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/utils.h>
|
|
||||||
#include <openbsc/mgcp.h>
|
|
||||||
|
|
||||||
const struct value_string mgcp_connection_mode_strs[] = {
|
|
||||||
{ MGCP_CONN_NONE, "none" },
|
|
||||||
{ MGCP_CONN_RECV_SEND, "sendrecv" },
|
|
||||||
{ MGCP_CONN_SEND_ONLY, "sendonly" },
|
|
||||||
{ MGCP_CONN_RECV_ONLY, "recvonly" },
|
|
||||||
{ MGCP_CONN_LOOPBACK, "loopback" },
|
|
||||||
{ 0, NULL }
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Ensure that the msg->l2h is NUL terminated. */
|
|
||||||
int mgcp_msg_terminate_nul(struct msgb *msg)
|
|
||||||
{
|
|
||||||
unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
|
|
||||||
if (tail[-1] == '\0')
|
|
||||||
/* nothing to do */;
|
|
||||||
else if (msgb_tailroom(msg) > 0)
|
|
||||||
tail[0] = '\0';
|
|
||||||
else if (tail[-1] == '\r' || tail[-1] == '\n')
|
|
||||||
tail[-1] = '\0';
|
|
||||||
else {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: "
|
|
||||||
"Length: %d, Buffer size: %d\n",
|
|
||||||
msgb_l2len(msg), msg->data_len);
|
|
||||||
return -ENOTSUP;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,583 +0,0 @@
|
||||||
/*
|
|
||||||
* (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org>
|
|
||||||
* (C) 2012-2013 by On Waves ehf <http://www.on-waves.com>
|
|
||||||
* All rights not specifically granted under this license are 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h> /* for printf */
|
|
||||||
#include <string.h> /* for memcpy */
|
|
||||||
#include <stdlib.h> /* for abs */
|
|
||||||
#include <inttypes.h> /* for PRIu64 */
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <osmocom/core/msgb.h>
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
|
|
||||||
#include <osmocom/netif/osmux.h>
|
|
||||||
#include <osmocom/netif/rtp.h>
|
|
||||||
|
|
||||||
#include <openbsc/mgcp.h>
|
|
||||||
#include <openbsc/mgcp_internal.h>
|
|
||||||
#include <openbsc/osmux.h>
|
|
||||||
|
|
||||||
static struct osmo_fd osmux_fd;
|
|
||||||
|
|
||||||
static LLIST_HEAD(osmux_handle_list);
|
|
||||||
|
|
||||||
struct osmux_handle {
|
|
||||||
struct llist_head head;
|
|
||||||
struct osmux_in_handle *in;
|
|
||||||
struct in_addr rem_addr;
|
|
||||||
int rem_port;
|
|
||||||
int refcnt;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *osmux;
|
|
||||||
|
|
||||||
static void osmux_deliver(struct msgb *batch_msg, void *data)
|
|
||||||
{
|
|
||||||
struct osmux_handle *handle = data;
|
|
||||||
struct sockaddr_in out = {
|
|
||||||
.sin_family = AF_INET,
|
|
||||||
.sin_port = handle->rem_port,
|
|
||||||
};
|
|
||||||
|
|
||||||
memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr));
|
|
||||||
sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0,
|
|
||||||
(struct sockaddr *)&out, sizeof(out));
|
|
||||||
msgb_free(batch_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
LOGP(DMGCP, LOGL_DEBUG, "using existing OSMUX handle "
|
|
||||||
"for addr=%s:%d\n",
|
|
||||||
inet_ntoa(*addr), ntohs(rem_port));
|
|
||||||
h->refcnt++;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
LOGP(DMGCP, LOGL_INFO,
|
|
||||||
"Releasing unused osmux handle for %s:%d\n",
|
|
||||||
inet_ntoa(h->rem_addr),
|
|
||||||
ntohs(h->rem_port));
|
|
||||||
LOGP(DMGCP, LOGL_INFO, "Stats: "
|
|
||||||
"input RTP msgs: %u bytes: %"PRIu64" "
|
|
||||||
"output osmux msgs: %u bytes: %"PRIu64"\n",
|
|
||||||
in->stats.input_rtp_msgs,
|
|
||||||
in->stats.input_rtp_bytes,
|
|
||||||
in->stats.output_osmux_msgs,
|
|
||||||
in->stats.output_osmux_bytes);
|
|
||||||
llist_del(&h->head);
|
|
||||||
osmux_xfrm_input_fini(h->in);
|
|
||||||
talloc_free(h);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct osmux_handle *
|
|
||||||
osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
|
||||||
{
|
|
||||||
struct osmux_handle *h;
|
|
||||||
|
|
||||||
h = talloc_zero(osmux, struct osmux_handle);
|
|
||||||
if (!h)
|
|
||||||
return NULL;
|
|
||||||
h->rem_addr = *addr;
|
|
||||||
h->rem_port = rem_port;
|
|
||||||
h->refcnt++;
|
|
||||||
|
|
||||||
h->in = talloc_zero(h, struct osmux_in_handle);
|
|
||||||
if (!h->in) {
|
|
||||||
talloc_free(h);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
h->in->osmux_seq = 0; /* sequence number to start OSmux message from */
|
|
||||||
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;
|
|
||||||
osmux_xfrm_input_init(h->in);
|
|
||||||
h->in->data = h;
|
|
||||||
|
|
||||||
llist_add(&h->head, &osmux_handle_list);
|
|
||||||
|
|
||||||
LOGP(DMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n",
|
|
||||||
inet_ntoa(*addr), ntohs(rem_port));
|
|
||||||
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct osmux_in_handle *
|
|
||||||
osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port)
|
|
||||||
{
|
|
||||||
struct osmux_handle *h;
|
|
||||||
|
|
||||||
h = osmux_handle_find_get(addr, rem_port);
|
|
||||||
if (h != NULL)
|
|
||||||
return h->in;
|
|
||||||
|
|
||||||
h = osmux_handle_alloc(cfg, addr, rem_port);
|
|
||||||
if (h == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return h->in;
|
|
||||||
}
|
|
||||||
|
|
||||||
int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct msgb *msg;
|
|
||||||
|
|
||||||
msg = msgb_alloc(4096, "RTP");
|
|
||||||
if (!msg)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
memcpy(msg->data, buf, rc);
|
|
||||||
msgb_put(msg, rc);
|
|
||||||
|
|
||||||
while ((ret = osmux_xfrm_input(endp->osmux.in, msg, endp->osmux.cid)) > 0) {
|
|
||||||
/* batch full, build and deliver it */
|
|
||||||
osmux_xfrm_input_deliver(endp->osmux.in);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct mgcp_endpoint *
|
|
||||||
endpoint_lookup(struct mgcp_config *cfg, int cid,
|
|
||||||
struct in_addr *from_addr, int type)
|
|
||||||
{
|
|
||||||
struct mgcp_endpoint *tmp = NULL;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* 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];
|
|
||||||
|
|
||||||
if (!tmp->allocated)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
case MGCP_DEST_NET:
|
|
||||||
this = &tmp->net_end.addr;
|
|
||||||
break;
|
|
||||||
case MGCP_DEST_BTS:
|
|
||||||
this = &tmp->bts_end.addr;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Should not ever happen */
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tmp->osmux.cid == cid && this->s_addr == from_addr->s_addr)
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void scheduled_tx_net_cb(struct msgb *msg, void *data)
|
|
||||||
{
|
|
||||||
struct mgcp_endpoint *endp = data;
|
|
||||||
struct sockaddr_in addr = {
|
|
||||||
.sin_addr = endp->net_end.addr,
|
|
||||||
.sin_port = endp->net_end.rtp_port,
|
|
||||||
};
|
|
||||||
|
|
||||||
endp->bts_end.octets += msg->len;
|
|
||||||
endp->bts_end.packets++;
|
|
||||||
|
|
||||||
mgcp_send(endp, MGCP_DEST_NET, 1, &addr, (char *)msg->data, msg->len);
|
|
||||||
msgb_free(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void scheduled_tx_bts_cb(struct msgb *msg, void *data)
|
|
||||||
{
|
|
||||||
struct mgcp_endpoint *endp = data;
|
|
||||||
struct sockaddr_in addr = {
|
|
||||||
.sin_addr = endp->bts_end.addr,
|
|
||||||
.sin_port = endp->bts_end.rtp_port,
|
|
||||||
};
|
|
||||||
|
|
||||||
endp->net_end.octets += msg->len;
|
|
||||||
endp->net_end.packets++;
|
|
||||||
|
|
||||||
mgcp_send(endp, MGCP_DEST_BTS, 1, &addr, (char *)msg->data, msg->len);
|
|
||||||
msgb_free(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
socklen_t slen = sizeof(*addr);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
msg = msgb_alloc(4096, "OSMUX");
|
|
||||||
if (!msg) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "cannot allocate message\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0,
|
|
||||||
(struct sockaddr *)addr, &slen);
|
|
||||||
if (ret <= 0) {
|
|
||||||
msgb_free(msg);
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "cannot receive message\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
msgb_put(msg, ret);
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define osmux_chunk_length(msg, rem) (rem - msg->len);
|
|
||||||
|
|
||||||
int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
struct osmux_hdr *osmuxh;
|
|
||||||
struct llist_head list;
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
struct mgcp_config *cfg = ofd->data;
|
|
||||||
uint32_t rem;
|
|
||||||
|
|
||||||
msg = osmux_recv(ofd, &addr);
|
|
||||||
if (!msg)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* not any further processing dummy messages */
|
|
||||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
rem = msg->len;
|
|
||||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
|
|
||||||
/* Yes, we use MGCP_DEST_NET to locate the origin */
|
|
||||||
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
|
|
||||||
&addr.sin_addr, MGCP_DEST_NET);
|
|
||||||
if (!endp) {
|
|
||||||
LOGP(DMGCP, 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++;
|
|
||||||
rem = msg->len;
|
|
||||||
|
|
||||||
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
|
|
||||||
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
msgb_free(msg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is called from the bsc-nat */
|
|
||||||
static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr,
|
|
||||||
struct msgb *msg)
|
|
||||||
{
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
uint8_t osmux_cid;
|
|
||||||
|
|
||||||
if (msg->len < 1 + sizeof(osmux_cid)) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Discarding truncated Osmux dummy load\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n",
|
|
||||||
inet_ntoa(addr->sin_addr));
|
|
||||||
|
|
||||||
if (!cfg->osmux) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"bsc wants to use Osmux but bsc-nat did not request it\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* extract the osmux CID from the dummy message */
|
|
||||||
memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid));
|
|
||||||
|
|
||||||
endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS);
|
|
||||||
if (!endp) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Cannot find endpoint for Osmux CID %d\n", osmux_cid);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endp->osmux.state == OSMUX_STATE_ENABLED)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
if (osmux_enable_endpoint(endp, &addr->sin_addr, addr->sin_port) < 0 ) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Could not enable osmux in endpoint %d\n",
|
|
||||||
ENDPOINT_NUMBER(endp));
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n",
|
|
||||||
ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr),
|
|
||||||
ntohs(addr->sin_port));
|
|
||||||
out:
|
|
||||||
msgb_free(msg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
struct osmux_hdr *osmuxh;
|
|
||||||
struct llist_head list;
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
struct mgcp_config *cfg = ofd->data;
|
|
||||||
uint32_t rem;
|
|
||||||
|
|
||||||
msg = osmux_recv(ofd, &addr);
|
|
||||||
if (!msg)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* not any further processing dummy messages */
|
|
||||||
if (msg->data[0] == MGCP_DUMMY_LOAD)
|
|
||||||
return osmux_handle_dummy(cfg, &addr, msg);
|
|
||||||
|
|
||||||
rem = msg->len;
|
|
||||||
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) {
|
|
||||||
struct mgcp_endpoint *endp;
|
|
||||||
|
|
||||||
/* Yes, we use MGCP_DEST_BTS to locate the origin */
|
|
||||||
endp = endpoint_lookup(cfg, osmuxh->circuit_id,
|
|
||||||
&addr.sin_addr, MGCP_DEST_BTS);
|
|
||||||
if (!endp) {
|
|
||||||
LOGP(DMGCP, 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++;
|
|
||||||
rem = msg->len;
|
|
||||||
|
|
||||||
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
|
|
||||||
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
msgb_free(msg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int osmux_init(int role, struct mgcp_config *cfg)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
switch(role) {
|
|
||||||
case OSMUX_ROLE_BSC:
|
|
||||||
osmux_fd.cb = osmux_read_from_bsc_nat_cb;
|
|
||||||
break;
|
|
||||||
case OSMUX_ROLE_BSC_NAT:
|
|
||||||
osmux_fd.cb = osmux_read_from_bsc_cb;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "wrong role for OSMUX\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
osmux_fd.data = cfg;
|
|
||||||
|
|
||||||
ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp);
|
|
||||||
osmux_fd.when |= BSC_FD_READ;
|
|
||||||
|
|
||||||
ret = osmo_fd_register(&osmux_fd);
|
|
||||||
if (ret < 0) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "cannot register OSMUX socket\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
cfg->osmux_init = 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int osmux_enable_endpoint(struct mgcp_endpoint *endp, 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.
|
|
||||||
*/
|
|
||||||
static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256;
|
|
||||||
|
|
||||||
if (endp->osmux.state == OSMUX_STATE_DISABLED) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Endpoint %u didn't request Osmux\n",
|
|
||||||
ENDPOINT_NUMBER(endp));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
osmux_xfrm_output_init(&endp->osmux.out,
|
|
||||||
(endp->osmux.cid * rtp_ssrc_winlen) +
|
|
||||||
(random() % rtp_ssrc_winlen));
|
|
||||||
|
|
||||||
endp->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
|
|
||||||
if (!endp->osmux.in) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Cannot allocate input osmux handle\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (!osmux_xfrm_input_open_circuit(endp->osmux.in, endp->osmux.cid,
|
|
||||||
endp->cfg->osmux_dummy)) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Cannot open osmux circuit %u\n",
|
|
||||||
endp->osmux.cid);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (endp->cfg->role) {
|
|
||||||
case MGCP_BSC_NAT:
|
|
||||||
endp->type = MGCP_OSMUX_BSC_NAT;
|
|
||||||
break;
|
|
||||||
case MGCP_BSC:
|
|
||||||
endp->type = MGCP_OSMUX_BSC;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
endp->osmux.state = OSMUX_STATE_ENABLED;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void osmux_disable_endpoint(struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
LOGP(DMGCP, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void osmux_release_cid(struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
if (endp->osmux.allocated_cid >= 0)
|
|
||||||
osmux_put_cid(endp->osmux.allocated_cid);
|
|
||||||
endp->osmux.allocated_cid = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void osmux_allocate_cid(struct mgcp_endpoint *endp)
|
|
||||||
{
|
|
||||||
osmux_release_cid(endp);
|
|
||||||
endp->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)
|
|
||||||
{
|
|
||||||
char buf[1 + sizeof(uint8_t)];
|
|
||||||
struct in_addr addr_unset = {};
|
|
||||||
|
|
||||||
buf[0] = MGCP_DUMMY_LOAD;
|
|
||||||
memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid));
|
|
||||||
|
|
||||||
/* Wait until we have the connection information from MDCX */
|
|
||||||
if (memcmp(&endp->net_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) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Could not activate osmux in endpoint %d\n",
|
|
||||||
ENDPOINT_NUMBER(endp));
|
|
||||||
}
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Osmux CID %u for %s:%u is now enabled\n",
|
|
||||||
endp->osmux.cid, inet_ntoa(endp->net_end.addr),
|
|
||||||
endp->cfg->osmux_port);
|
|
||||||
}
|
|
||||||
LOGP(DMGCP, LOGL_DEBUG,
|
|
||||||
"sending OSMUX dummy load to %s CID %u\n",
|
|
||||||
inet_ntoa(endp->net_end.addr), endp->osmux.cid);
|
|
||||||
|
|
||||||
return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr,
|
|
||||||
htons(endp->cfg->osmux_port), buf, sizeof(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* bsc-nat allocates/releases the Osmux circuit ID */
|
|
||||||
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8];
|
|
||||||
|
|
||||||
int osmux_used_cid(void)
|
|
||||||
{
|
|
||||||
int i, j, used = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
|
|
||||||
for (j = 0; j < 8; j++) {
|
|
||||||
if (osmux_cid_bitmap[i] & (1 << j))
|
|
||||||
used += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return used;
|
|
||||||
}
|
|
||||||
|
|
||||||
int osmux_get_cid(void)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
for (i = 0; i < sizeof(osmux_cid_bitmap); i++) {
|
|
||||||
for (j = 0; j < 8; j++) {
|
|
||||||
if (osmux_cid_bitmap[i] & (1 << j))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
osmux_cid_bitmap[i] |= (1 << j);
|
|
||||||
LOGP(DMGCP, LOGL_DEBUG,
|
|
||||||
"Allocating Osmux CID %u from pool\n", (i * 8) + j);
|
|
||||||
return (i * 8) + j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void osmux_put_cid(uint8_t osmux_cid)
|
|
||||||
{
|
|
||||||
LOGP(DMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid);
|
|
||||||
osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8));
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,305 +0,0 @@
|
||||||
/*
|
|
||||||
* Some SDP file parsing...
|
|
||||||
*
|
|
||||||
* (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
||||||
* (C) 2009-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 <openbsc/mgcp.h>
|
|
||||||
#include <openbsc/mgcp_internal.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
struct sdp_rtp_map {
|
|
||||||
/* the type */
|
|
||||||
int payload_type;
|
|
||||||
/* null, static or later dynamic codec name */
|
|
||||||
char *codec_name;
|
|
||||||
/* A pointer to the original line for later parsing */
|
|
||||||
char *map_line;
|
|
||||||
|
|
||||||
int rate;
|
|
||||||
int channels;
|
|
||||||
};
|
|
||||||
|
|
||||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
|
||||||
int payload_type, const char *audio_name)
|
|
||||||
{
|
|
||||||
int rate = codec->rate;
|
|
||||||
int channels = codec->channels;
|
|
||||||
char audio_codec[64];
|
|
||||||
|
|
||||||
talloc_free(codec->subtype_name);
|
|
||||||
codec->subtype_name = NULL;
|
|
||||||
talloc_free(codec->audio_name);
|
|
||||||
codec->audio_name = NULL;
|
|
||||||
|
|
||||||
if (payload_type != PTYPE_UNDEFINED)
|
|
||||||
codec->payload_type = payload_type;
|
|
||||||
|
|
||||||
if (!audio_name) {
|
|
||||||
switch (payload_type) {
|
|
||||||
case 0: audio_name = "PCMU/8000/1"; break;
|
|
||||||
case 3: audio_name = "GSM/8000/1"; break;
|
|
||||||
case 8: audio_name = "PCMA/8000/1"; break;
|
|
||||||
case 18: audio_name = "G729/8000/1"; break;
|
|
||||||
default:
|
|
||||||
/* Payload type is unknown, don't change rate and
|
|
||||||
* channels. */
|
|
||||||
/* TODO: return value? */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
|
||||||
audio_codec, &rate, &channels) < 1)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
codec->rate = rate;
|
|
||||||
codec->channels = channels;
|
|
||||||
codec->subtype_name = talloc_strdup(ctx, audio_codec);
|
|
||||||
codec->audio_name = talloc_strdup(ctx, audio_name);
|
|
||||||
|
|
||||||
if (!strcmp(audio_codec, "G729")) {
|
|
||||||
codec->frame_duration_num = 10;
|
|
||||||
codec->frame_duration_den = 1000;
|
|
||||||
} else {
|
|
||||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
|
||||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payload_type < 0) {
|
|
||||||
payload_type = 96;
|
|
||||||
if (rate == 8000 && channels == 1) {
|
|
||||||
if (!strcmp(audio_codec, "GSM"))
|
|
||||||
payload_type = 3;
|
|
||||||
else if (!strcmp(audio_codec, "PCMA"))
|
|
||||||
payload_type = 8;
|
|
||||||
else if (!strcmp(audio_codec, "PCMU"))
|
|
||||||
payload_type = 0;
|
|
||||||
else if (!strcmp(audio_codec, "G729"))
|
|
||||||
payload_type = 18;
|
|
||||||
}
|
|
||||||
|
|
||||||
codec->payload_type = payload_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channels != 1)
|
|
||||||
LOGP(DMGCP, LOGL_NOTICE,
|
|
||||||
"Channels != 1 in SDP: '%s'\n", audio_name);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < used; ++i) {
|
|
||||||
switch (codecs[i].payload_type) {
|
|
||||||
case 0:
|
|
||||||
codecs[i].codec_name = "PCMU";
|
|
||||||
codecs[i].rate = 8000;
|
|
||||||
codecs[i].channels = 1;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
codecs[i].codec_name = "GSM";
|
|
||||||
codecs[i].rate = 8000;
|
|
||||||
codecs[i].channels = 1;
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
codecs[i].codec_name = "PCMA";
|
|
||||||
codecs[i].rate = 8000;
|
|
||||||
codecs[i].channels = 1;
|
|
||||||
break;
|
|
||||||
case 18:
|
|
||||||
codecs[i].codec_name = "G729";
|
|
||||||
codecs[i].rate = 8000;
|
|
||||||
codecs[i].channels = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, char *audio_name)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < used; ++i) {
|
|
||||||
char audio_codec[64];
|
|
||||||
int rate = -1;
|
|
||||||
int channels = -1;
|
|
||||||
if (codecs[i].payload_type != payload)
|
|
||||||
continue;
|
|
||||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
|
||||||
audio_codec, &rate, &channels) < 1) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Failed to parse '%s'\n", audio_name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
codecs[i].map_line = talloc_strdup(ctx, audio_name);
|
|
||||||
codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
|
|
||||||
codecs[i].rate = rate;
|
|
||||||
codecs[i].channels = channels;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_codec_compatible(struct mgcp_endpoint *endp, struct sdp_rtp_map *codec)
|
|
||||||
{
|
|
||||||
char *bts_codec;
|
|
||||||
char audio_codec[64];
|
|
||||||
|
|
||||||
if (!codec->codec_name)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go
|
|
||||||
* by name first.
|
|
||||||
*/
|
|
||||||
bts_codec = endp->tcfg->audio_name;
|
|
||||||
if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return strcasecmp(audio_codec, codec->codec_name) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
|
|
||||||
{
|
|
||||||
struct sdp_rtp_map codecs[10];
|
|
||||||
int codecs_used = 0;
|
|
||||||
char *line;
|
|
||||||
int maxptime = -1;
|
|
||||||
int i;
|
|
||||||
int codecs_assigned = 0;
|
|
||||||
void *tmp_ctx = talloc_new(NULL);
|
|
||||||
|
|
||||||
memset(&codecs, 0, sizeof(codecs));
|
|
||||||
|
|
||||||
for_each_line(line, p->save) {
|
|
||||||
switch (line[0]) {
|
|
||||||
case 'o':
|
|
||||||
case 's':
|
|
||||||
case 't':
|
|
||||||
case 'v':
|
|
||||||
/* skip these SDP attributes */
|
|
||||||
break;
|
|
||||||
case 'a': {
|
|
||||||
int payload;
|
|
||||||
int ptime, ptime2 = 0;
|
|
||||||
char audio_name[64];
|
|
||||||
|
|
||||||
|
|
||||||
if (sscanf(line, "a=rtpmap:%d %63s",
|
|
||||||
&payload, audio_name) == 2) {
|
|
||||||
codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
|
|
||||||
} else if (sscanf(line, "a=ptime:%d-%d",
|
|
||||||
&ptime, &ptime2) >= 1) {
|
|
||||||
if (ptime2 > 0 && ptime2 != ptime)
|
|
||||||
rtp->packet_duration_ms = 0;
|
|
||||||
else
|
|
||||||
rtp->packet_duration_ms = ptime;
|
|
||||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
|
|
||||||
maxptime = ptime2;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'm': {
|
|
||||||
int port, rc;
|
|
||||||
|
|
||||||
rc = sscanf(line, "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d",
|
|
||||||
&port,
|
|
||||||
&codecs[0].payload_type,
|
|
||||||
&codecs[1].payload_type,
|
|
||||||
&codecs[2].payload_type,
|
|
||||||
&codecs[3].payload_type,
|
|
||||||
&codecs[4].payload_type,
|
|
||||||
&codecs[5].payload_type,
|
|
||||||
&codecs[6].payload_type,
|
|
||||||
&codecs[7].payload_type,
|
|
||||||
&codecs[8].payload_type,
|
|
||||||
&codecs[9].payload_type);
|
|
||||||
if (rc >= 2) {
|
|
||||||
rtp->rtp_port = htons(port);
|
|
||||||
rtp->rtcp_port = htons(port + 1);
|
|
||||||
codecs_used = rc - 1;
|
|
||||||
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'c': {
|
|
||||||
char ipv4[16];
|
|
||||||
|
|
||||||
if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
|
|
||||||
inet_aton(ipv4, &rtp->addr);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if (p->endp)
|
|
||||||
LOGP(DMGCP, LOGL_NOTICE,
|
|
||||||
"Unhandled SDP option: '%c'/%d on 0x%x\n",
|
|
||||||
line[0], line[0], ENDPOINT_NUMBER(p->endp));
|
|
||||||
else
|
|
||||||
LOGP(DMGCP, LOGL_NOTICE,
|
|
||||||
"Unhandled SDP option: '%c'/%d\n",
|
|
||||||
line[0], line[0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now select the primary and alt_codec */
|
|
||||||
for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) {
|
|
||||||
struct mgcp_rtp_codec *codec = codecs_assigned == 0 ?
|
|
||||||
&rtp->codec : &rtp->alt_codec;
|
|
||||||
|
|
||||||
if (endp->tcfg->no_audio_transcoding &&
|
|
||||||
!is_codec_compatible(endp, &codecs[i])) {
|
|
||||||
LOGP(DMGCP, LOGL_NOTICE, "Skipping codec %s\n",
|
|
||||||
codecs[i].codec_name);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
mgcp_set_audio_info(p->cfg, codec,
|
|
||||||
codecs[i].payload_type,
|
|
||||||
codecs[i].map_line);
|
|
||||||
codecs_assigned += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codecs_assigned > 0) {
|
|
||||||
/* TODO/XXX: Store this per codec and derive it on use */
|
|
||||||
if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den >
|
|
||||||
rtp->codec.frame_duration_num * 1500) {
|
|
||||||
/* more than 1 frame */
|
|
||||||
rtp->packet_duration_ms = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DMGCP, LOGL_NOTICE,
|
|
||||||
"Got media info via SDP: port %d, payload %d (%s), "
|
|
||||||
"duration %d, addr %s\n",
|
|
||||||
ntohs(rtp->rtp_port), rtp->codec.payload_type,
|
|
||||||
rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown",
|
|
||||||
rtp->packet_duration_ms, inet_ntoa(rtp->addr));
|
|
||||||
}
|
|
||||||
|
|
||||||
talloc_free(tmp_ctx);
|
|
||||||
return codecs_assigned > 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,612 +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 <openbsc/debug.h>
|
|
||||||
#include <openbsc/mgcp.h>
|
|
||||||
#include <openbsc/mgcp_internal.h>
|
|
||||||
#include <openbsc/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(DMGCP, 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(DMGCP, 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(DMGCP, 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(DMGCP, 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(DMGCP, LOGL_ERROR,
|
|
||||||
"Failed to initialize GSM decoder.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
case AF_G729:
|
|
||||||
state->src_frame_size = 10;
|
|
||||||
state->src_samples_per_frame = 80;
|
|
||||||
state->src.g729_dec = initBcg729DecoderChannel();
|
|
||||||
if (!state->src.g729_dec) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Failed to initialize G.729 decoder.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case AF_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(DMGCP, LOGL_ERROR,
|
|
||||||
"Failed to initialize GSM encoder.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
case AF_G729:
|
|
||||||
state->dst_frame_size = 10;
|
|
||||||
state->dst_samples_per_frame = 80;
|
|
||||||
state->dst.g729_enc = initBcg729EncoderChannel();
|
|
||||||
if (!state->dst.g729_enc) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Failed to initialize G.729 decoder.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case AF_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(DMGCP, LOGL_INFO,
|
|
||||||
"Initialized RTP processing on: 0x%x "
|
|
||||||
"conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n",
|
|
||||||
ENDPOINT_NUMBER(endp),
|
|
||||||
src_fmt, src_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(DMGCP, 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(DMGCP, LOGL_ERROR,
|
|
||||||
"Failed to decode GSM.\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#ifdef HAVE_BCG729
|
|
||||||
case AF_G729:
|
|
||||||
bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case AF_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(DMGCP, 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(DMGCP, 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(DMGCP, LOGL_NOTICE,
|
|
||||||
"RTP time jumps backwards, delta = %d, "
|
|
||||||
"discarding buffered samples\n",
|
|
||||||
delta);
|
|
||||||
state->sample_cnt = 0;
|
|
||||||
state->sample_offs = 0;
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure the samples start without offset */
|
|
||||||
if (state->sample_offs && state->sample_cnt)
|
|
||||||
memmove(&state->samples[0],
|
|
||||||
&state->samples[state->sample_offs],
|
|
||||||
state->sample_cnt *
|
|
||||||
sizeof(state->samples[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
state->sample_offs = 0;
|
|
||||||
|
|
||||||
/* Append decoded audio to samples */
|
|
||||||
decode_audio(state, &src, &nbytes);
|
|
||||||
|
|
||||||
if (nbytes > 0)
|
|
||||||
LOGP(DMGCP, LOGL_NOTICE,
|
|
||||||
"Skipped audio frame in RTP packet: %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
|
@ -1,616 +0,0 @@
|
||||||
/* mgcp_utils - common functions to setup an MGCP connection
|
|
||||||
*/
|
|
||||||
/* (C) 2016 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/core/select.h>
|
|
||||||
#include <osmocom/core/write_queue.h>
|
|
||||||
#include <osmocom/core/msgb.h>
|
|
||||||
#include <osmocom/core/logging.h>
|
|
||||||
|
|
||||||
#include <openbsc/mgcpgw_client.h>
|
|
||||||
#include <openbsc/mgcp.h>
|
|
||||||
#include <openbsc/mgcp_internal.h>
|
|
||||||
#include <openbsc/debug.h>
|
|
||||||
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf)
|
|
||||||
{
|
|
||||||
/* NULL and -1 default to MGCPGW_CLIENT_*_DEFAULT values */
|
|
||||||
*conf = (struct mgcpgw_client_conf){
|
|
||||||
.local_addr = NULL,
|
|
||||||
.local_port = -1,
|
|
||||||
.remote_addr = NULL,
|
|
||||||
.remote_port = -1,
|
|
||||||
.first_endpoint = 0,
|
|
||||||
.last_endpoint = 0,
|
|
||||||
.bts_base = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Test if a given endpoint id is currently in use */
|
|
||||||
static bool endpoint_in_use(uint16_t id, struct mgcpgw_client *client)
|
|
||||||
{
|
|
||||||
struct mgcp_inuse_endpoint *endpoint;
|
|
||||||
llist_for_each_entry(endpoint, &client->inuse_endpoints, entry) {
|
|
||||||
if (endpoint->id == id)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find and seize an unsused endpoint id */
|
|
||||||
int mgcpgw_client_next_endpoint(struct mgcpgw_client *client)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
uint16_t first_endpoint = client->actual.first_endpoint;
|
|
||||||
uint16_t last_endpoint = client->actual.last_endpoint;
|
|
||||||
struct mgcp_inuse_endpoint *endpoint;
|
|
||||||
|
|
||||||
/* Use the maximum permitted range if the VTY
|
|
||||||
* configuration does not specify a range */
|
|
||||||
if (client->actual.last_endpoint == 0) {
|
|
||||||
first_endpoint = 1;
|
|
||||||
last_endpoint = 65534;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Test the permitted endpoint range for an endpoint
|
|
||||||
* number that is not in use. When a suitable endpoint
|
|
||||||
* number can be found, seize it by adding it to the
|
|
||||||
* inuse list. */
|
|
||||||
for (i=first_endpoint;i<last_endpoint;i++)
|
|
||||||
{
|
|
||||||
if (endpoint_in_use(i,client) == false) {
|
|
||||||
endpoint = talloc_zero(client, struct mgcp_inuse_endpoint);
|
|
||||||
endpoint->id = i;
|
|
||||||
llist_add_tail(&endpoint->entry, &client->inuse_endpoints);
|
|
||||||
return endpoint->id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* All endpoints are busy! */
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Release a seized endpoint id to make it available again for other calls */
|
|
||||||
void mgcpgw_client_release_endpoint(uint16_t id, struct mgcpgw_client *client)
|
|
||||||
{
|
|
||||||
struct mgcp_inuse_endpoint *endpoint;
|
|
||||||
struct mgcp_inuse_endpoint *endpoint_tmp;
|
|
||||||
llist_for_each_entry_safe(endpoint, endpoint_tmp, &client->inuse_endpoints, entry) {
|
|
||||||
if (endpoint->id == id) {
|
|
||||||
llist_del(&endpoint->entry);
|
|
||||||
talloc_free(endpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mgcpgw_client_handle_response(struct mgcpgw_client *mgcp,
|
|
||||||
struct mgcp_response_pending *pending,
|
|
||||||
struct mgcp_response *response)
|
|
||||||
{
|
|
||||||
if (!pending) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Cannot handle NULL response\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (pending->response_cb)
|
|
||||||
pending->response_cb(response, pending->priv);
|
|
||||||
else
|
|
||||||
LOGP(DMGCP, LOGL_INFO, "MGCP response ignored (NULL cb)\n");
|
|
||||||
talloc_free(pending);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
|
|
||||||
{
|
|
||||||
int comment_pos;
|
|
||||||
char *end;
|
|
||||||
|
|
||||||
if (mgcp_msg_terminate_nul(msg))
|
|
||||||
goto response_parse_failure;
|
|
||||||
|
|
||||||
r->body = (char *)msg->data;
|
|
||||||
|
|
||||||
if (sscanf(r->body, "%3d %u %n",
|
|
||||||
&r->head.response_code, &r->head.trans_id,
|
|
||||||
&comment_pos) != 2)
|
|
||||||
goto response_parse_failure;
|
|
||||||
|
|
||||||
r->head.comment = r->body + comment_pos;
|
|
||||||
end = strchr(r->head.comment, '\r');
|
|
||||||
if (!end)
|
|
||||||
goto response_parse_failure;
|
|
||||||
/* Mark the end of the comment */
|
|
||||||
*end = '\0';
|
|
||||||
r->body = end + 1;
|
|
||||||
if (r->body[0] == '\n')
|
|
||||||
r->body ++;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
response_parse_failure:
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Failed to parse MGCP response header\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO undup against mgcp_protocol.c:mgcp_check_param() */
|
|
||||||
static bool mgcp_line_is_valid(const char *line)
|
|
||||||
{
|
|
||||||
const size_t line_len = strlen(line);
|
|
||||||
if (line[0] == '\0')
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (line_len < 2
|
|
||||||
|| line[1] != '=') {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Wrong MGCP option format: '%s'\n",
|
|
||||||
line);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse a line like "m=audio 16002 RTP/AVP 98" */
|
|
||||||
static int mgcp_parse_audio(struct mgcp_response *r, const char *line)
|
|
||||||
{
|
|
||||||
if (sscanf(line, "m=audio %hu",
|
|
||||||
&r->audio_port) != 1)
|
|
||||||
goto response_parse_failure;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
response_parse_failure:
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Failed to parse MGCP response header\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcp_response_parse_params(struct mgcp_response *r)
|
|
||||||
{
|
|
||||||
char *line;
|
|
||||||
int rc;
|
|
||||||
OSMO_ASSERT(r->body);
|
|
||||||
char *data = strstr(r->body, "\n\n");
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"MGCP response: cannot find start of parameters\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Advance to after the \n\n, replace the second \n with \0. That's
|
|
||||||
* where the parameters start. */
|
|
||||||
data ++;
|
|
||||||
*data = '\0';
|
|
||||||
data ++;
|
|
||||||
|
|
||||||
for_each_line(line, data) {
|
|
||||||
if (!mgcp_line_is_valid(line))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
switch (line[0]) {
|
|
||||||
case 'm':
|
|
||||||
rc = mgcp_parse_audio(r, line);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* skip unhandled parameters */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct mgcp_response_pending *mgcpgw_client_response_pending_get(
|
|
||||||
struct mgcpgw_client *mgcp,
|
|
||||||
struct mgcp_response *r)
|
|
||||||
{
|
|
||||||
struct mgcp_response_pending *pending;
|
|
||||||
if (!r)
|
|
||||||
return NULL;
|
|
||||||
llist_for_each_entry(pending, &mgcp->responses_pending, entry) {
|
|
||||||
if (pending->trans_id == r->head.trans_id) {
|
|
||||||
llist_del(&pending->entry);
|
|
||||||
return pending;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Feed an MGCP message into the receive processing.
|
|
||||||
* Parse the head and call any callback registered for the transaction id found
|
|
||||||
* in the MGCP message. This is normally called directly from the internal
|
|
||||||
* mgcp_do_read that reads from the socket connected to the MGCP gateway. This
|
|
||||||
* function is published mainly to be able to feed data from the test suite.
|
|
||||||
*/
|
|
||||||
int mgcpgw_client_rx(struct mgcpgw_client *mgcp, struct msgb *msg)
|
|
||||||
{
|
|
||||||
struct mgcp_response r = { 0 };
|
|
||||||
struct mgcp_response_pending *pending;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
rc = mgcp_response_parse_head(&r, msg);
|
|
||||||
if (rc) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Cannot parse MGCP response\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pending = mgcpgw_client_response_pending_get(mgcp, &r);
|
|
||||||
if (!pending) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Cannot find matching MGCP transaction for trans_id %d\n",
|
|
||||||
r.head.trans_id);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
mgcpgw_client_handle_response(mgcp, pending, &r);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mgcp_do_read(struct osmo_fd *fd)
|
|
||||||
{
|
|
||||||
struct mgcpgw_client *mgcp = fd->data;
|
|
||||||
struct msgb *msg;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
|
|
||||||
if (!msg) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = read(fd->fd, msg->data, 4096 - 128);
|
|
||||||
if (ret <= 0) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno));
|
|
||||||
msgb_free(msg);
|
|
||||||
return -1;
|
|
||||||
} else if (ret > 4096 - 128) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
|
|
||||||
msgb_free(msg);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg->l2h = msgb_put(msg, ret);
|
|
||||||
ret = mgcpgw_client_rx(mgcp, msg);
|
|
||||||
talloc_free(msg);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
static char strbuf[4096];
|
|
||||||
unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf);
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
strncpy(strbuf, (const char*)msg->data, l);
|
|
||||||
for (i = 0; i < sizeof(strbuf); i++) {
|
|
||||||
if (strbuf[i] == '\n' || strbuf[i] == '\r') {
|
|
||||||
strbuf[i] = '\0';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DEBUGP(DMGCP, "Tx MGCP msg to MGCP GW: '%s'\n", strbuf);
|
|
||||||
|
|
||||||
LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len);
|
|
||||||
|
|
||||||
ret = write(fd->fd, msg->data, msg->len);
|
|
||||||
if (ret != msg->len)
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Failed to forward message to MGCP"
|
|
||||||
" GW: %s\n", strerror(errno));
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mgcpgw_client *mgcpgw_client_init(void *ctx,
|
|
||||||
struct mgcpgw_client_conf *conf)
|
|
||||||
{
|
|
||||||
struct mgcpgw_client *mgcp;
|
|
||||||
|
|
||||||
mgcp = talloc_zero(ctx, struct mgcpgw_client);
|
|
||||||
|
|
||||||
INIT_LLIST_HEAD(&mgcp->responses_pending);
|
|
||||||
INIT_LLIST_HEAD(&mgcp->inuse_endpoints);
|
|
||||||
|
|
||||||
mgcp->next_trans_id = 1;
|
|
||||||
|
|
||||||
mgcp->actual.local_addr = conf->local_addr ? conf->local_addr :
|
|
||||||
MGCPGW_CLIENT_LOCAL_ADDR_DEFAULT;
|
|
||||||
mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port :
|
|
||||||
MGCPGW_CLIENT_LOCAL_PORT_DEFAULT;
|
|
||||||
|
|
||||||
mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr :
|
|
||||||
MGCPGW_CLIENT_REMOTE_ADDR_DEFAULT;
|
|
||||||
mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
|
|
||||||
MGCPGW_CLIENT_REMOTE_PORT_DEFAULT;
|
|
||||||
|
|
||||||
mgcp->actual.first_endpoint = conf->first_endpoint > 0 ? (uint16_t)conf->first_endpoint : 0;
|
|
||||||
mgcp->actual.last_endpoint = conf->last_endpoint > 0 ? (uint16_t)conf->last_endpoint : 0;
|
|
||||||
mgcp->actual.bts_base = conf->bts_base > 0 ? (uint16_t)conf->bts_base : 4000;
|
|
||||||
|
|
||||||
return mgcp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcpgw_client_connect(struct mgcpgw_client *mgcp)
|
|
||||||
{
|
|
||||||
int on;
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
struct osmo_wqueue *wq;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (!mgcp) {
|
|
||||||
LOGP(DMGCP, LOGL_FATAL, "MGCPGW client not initialized properly\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
wq = &mgcp->wq;
|
|
||||||
|
|
||||||
wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
if (wq->bfd.fd < 0) {
|
|
||||||
LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno);
|
|
||||||
return -errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
on = 1;
|
|
||||||
if (setsockopt(wq->bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
|
|
||||||
LOGP(DMGCP, LOGL_FATAL,
|
|
||||||
"Failed to initialize socket for MGCP GW: %s\n",
|
|
||||||
strerror(errno));
|
|
||||||
rc = -errno;
|
|
||||||
goto error_close_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* bind socket */
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
inet_aton(mgcp->actual.local_addr, &addr.sin_addr);
|
|
||||||
addr.sin_port = htons(mgcp->actual.local_port);
|
|
||||||
if (bind(wq->bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
|
||||||
LOGP(DMGCP, LOGL_FATAL,
|
|
||||||
"Failed to bind for MGCP GW to %s %u\n",
|
|
||||||
mgcp->actual.local_addr, mgcp->actual.local_port);
|
|
||||||
rc = -errno;
|
|
||||||
goto error_close_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* connect to the remote */
|
|
||||||
inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
|
|
||||||
addr.sin_port = htons(mgcp->actual.remote_port);
|
|
||||||
if (connect(wq->bfd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
||||||
LOGP(DMGCP, LOGL_FATAL,
|
|
||||||
"Failed to connect to MGCP GW at %s %u: %s\n",
|
|
||||||
mgcp->actual.remote_addr, mgcp->actual.remote_port,
|
|
||||||
strerror(errno));
|
|
||||||
rc = -errno;
|
|
||||||
goto error_close_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
|
|
||||||
|
|
||||||
osmo_wqueue_init(wq, 10);
|
|
||||||
wq->bfd.when = BSC_FD_READ;
|
|
||||||
wq->bfd.data = mgcp;
|
|
||||||
wq->read_cb = mgcp_do_read;
|
|
||||||
wq->write_cb = mgcp_do_write;
|
|
||||||
|
|
||||||
if (osmo_fd_register(&wq->bfd) != 0) {
|
|
||||||
LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n");
|
|
||||||
rc = -EIO;
|
|
||||||
goto error_close_fd;
|
|
||||||
}
|
|
||||||
LOGP(DMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
|
|
||||||
mgcp->actual.local_addr, mgcp->actual.local_port,
|
|
||||||
mgcp->actual.remote_addr, mgcp->actual.remote_port);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
error_close_fd:
|
|
||||||
close(wq->bfd.fd);
|
|
||||||
wq->bfd.fd = -1;
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp)
|
|
||||||
{
|
|
||||||
return mgcp->actual.remote_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t mgcpgw_client_remote_port(struct mgcpgw_client *mgcp)
|
|
||||||
{
|
|
||||||
return mgcp->actual.remote_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the MGCP GW binary IPv4 address in network byte order. */
|
|
||||||
uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp)
|
|
||||||
{
|
|
||||||
return mgcp->remote_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mgcp_response_pending * mgcpgw_client_pending_add(
|
|
||||||
struct mgcpgw_client *mgcp,
|
|
||||||
mgcp_trans_id_t trans_id,
|
|
||||||
mgcp_response_cb_t response_cb,
|
|
||||||
void *priv)
|
|
||||||
{
|
|
||||||
struct mgcp_response_pending *pending;
|
|
||||||
|
|
||||||
pending = talloc_zero(mgcp, struct mgcp_response_pending);
|
|
||||||
pending->trans_id = trans_id;
|
|
||||||
pending->response_cb = response_cb;
|
|
||||||
pending->priv = priv;
|
|
||||||
llist_add_tail(&pending->entry, &mgcp->responses_pending);
|
|
||||||
|
|
||||||
return pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send the MGCP message in msg to the MGCP GW and handle a response with
|
|
||||||
* response_cb. NOTE: the response_cb still needs to call
|
|
||||||
* mgcp_response_parse_params(response) to get the parsed parameters -- to
|
|
||||||
* potentially save some CPU cycles, only the head line has been parsed when
|
|
||||||
* the response_cb is invoked. */
|
|
||||||
int mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg,
|
|
||||||
mgcp_response_cb_t response_cb, void *priv)
|
|
||||||
{
|
|
||||||
struct mgcp_response_pending *pending;
|
|
||||||
mgcp_trans_id_t trans_id;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID];
|
|
||||||
if (!trans_id) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Unset transaction id in mgcp send request\n");
|
|
||||||
talloc_free(msg);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
pending = mgcpgw_client_pending_add(mgcp, trans_id, response_cb, priv);
|
|
||||||
|
|
||||||
if (msgb_l2len(msg) > 4096) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Cannot send, MGCP message too large: %u\n",
|
|
||||||
msgb_l2len(msg));
|
|
||||||
msgb_free(msg);
|
|
||||||
rc = -EINVAL;
|
|
||||||
goto mgcp_tx_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
|
|
||||||
if (rc) {
|
|
||||||
LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW\n");
|
|
||||||
msgb_free(msg);
|
|
||||||
goto mgcp_tx_error;
|
|
||||||
} else
|
|
||||||
LOGP(DMGCP, LOGL_INFO, "Queued %u bytes for MGCP GW\n",
|
|
||||||
msgb_l2len(msg));
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
mgcp_tx_error:
|
|
||||||
/* Pass NULL to response cb to indicate an error */
|
|
||||||
mgcpgw_client_handle_response(mgcp, pending, NULL);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
|
|
||||||
const char *buf, int len)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
|
|
||||||
if (len > (4096 - 128)) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Cannot send to MGCP GW:"
|
|
||||||
" message too large: %d\n", len);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
|
|
||||||
OSMO_ASSERT(msg);
|
|
||||||
|
|
||||||
char *dst = (char*)msgb_put(msg, len);
|
|
||||||
memcpy(dst, buf, len);
|
|
||||||
msg->l2h = msg->data;
|
|
||||||
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id,
|
|
||||||
const char *fmt, ...)
|
|
||||||
{
|
|
||||||
static char compose[4096 - 128];
|
|
||||||
va_list ap;
|
|
||||||
int len;
|
|
||||||
OSMO_ASSERT(fmt);
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
len = vsnprintf(compose, sizeof(compose), fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
if (len >= sizeof(compose)) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Message too large: trans_id=%u len=%d\n",
|
|
||||||
trans_id, len);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (len < 1) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR,
|
|
||||||
"Failed to compose message: trans_id=%u len=%d\n",
|
|
||||||
trans_id, len);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return mgcp_msg_from_buf(trans_id, compose, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static mgcp_trans_id_t mgcpgw_client_next_trans_id(struct mgcpgw_client *mgcp)
|
|
||||||
{
|
|
||||||
/* avoid zero trans_id to distinguish from unset trans_id */
|
|
||||||
if (!mgcp->next_trans_id)
|
|
||||||
mgcp->next_trans_id ++;
|
|
||||||
return mgcp->next_trans_id ++;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct msgb *mgcp_msg_crcx(struct mgcpgw_client *mgcp,
|
|
||||||
uint16_t rtp_endpoint, unsigned int call_id,
|
|
||||||
enum mgcp_connection_mode mode)
|
|
||||||
{
|
|
||||||
mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
|
|
||||||
return mgcp_msg_from_str(trans_id,
|
|
||||||
"CRCX %u %x@mgw MGCP 1.0\r\n"
|
|
||||||
"C: %x\r\n"
|
|
||||||
"L: p:20, a:AMR, nt:IN\r\n"
|
|
||||||
"M: %s\r\n"
|
|
||||||
,
|
|
||||||
trans_id,
|
|
||||||
rtp_endpoint,
|
|
||||||
call_id,
|
|
||||||
mgcp_cmode_name(mode));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct msgb *mgcp_msg_mdcx(struct mgcpgw_client *mgcp,
|
|
||||||
uint16_t rtp_endpoint, const char *rtp_conn_addr,
|
|
||||||
uint16_t rtp_port, enum mgcp_connection_mode mode)
|
|
||||||
|
|
||||||
{
|
|
||||||
mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
|
|
||||||
return mgcp_msg_from_str(trans_id,
|
|
||||||
"MDCX %u %x@mgw MGCP 1.0\r\n"
|
|
||||||
"M: %s\r\n"
|
|
||||||
"\r\n"
|
|
||||||
"c=IN IP4 %s\r\n"
|
|
||||||
"m=audio %u RTP/AVP 255\r\n"
|
|
||||||
,
|
|
||||||
trans_id,
|
|
||||||
rtp_endpoint,
|
|
||||||
mgcp_cmode_name(mode),
|
|
||||||
rtp_conn_addr,
|
|
||||||
rtp_port);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct msgb *mgcp_msg_dlcx(struct mgcpgw_client *mgcp, uint16_t rtp_endpoint,
|
|
||||||
unsigned int call_id)
|
|
||||||
{
|
|
||||||
mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
|
|
||||||
return mgcp_msg_from_str(trans_id,
|
|
||||||
"DLCX %u %x@mgw MGCP 1.0\r\n"
|
|
||||||
"C: %x\r\n", trans_id, rtp_endpoint, call_id);
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
/* MGCPGW client interface to quagga VTY */
|
|
||||||
/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de>
|
|
||||||
* Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
|
|
||||||
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
|
||||||
* (C) 2009-2011 by Holger Hans Peter Freyther
|
|
||||||
* 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 <inttypes.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <talloc.h>
|
|
||||||
|
|
||||||
#include <osmocom/vty/command.h>
|
|
||||||
|
|
||||||
#include <openbsc/vty.h>
|
|
||||||
#include <openbsc/mgcpgw_client.h>
|
|
||||||
|
|
||||||
#define MGCPGW_STR "MGCP gateway configuration for RTP streams\n"
|
|
||||||
|
|
||||||
struct mgcpgw_client_conf *global_mgcpgw_client_conf = NULL;
|
|
||||||
|
|
||||||
DEFUN(cfg_mgcpgw_local_ip, cfg_mgcpgw_local_ip_cmd,
|
|
||||||
"mgcpgw local-ip A.B.C.D",
|
|
||||||
MGCPGW_STR "local bind to connect to MGCP gateway with\n"
|
|
||||||
"local bind IP address\n")
|
|
||||||
{
|
|
||||||
if (!global_mgcpgw_client_conf)
|
|
||||||
return CMD_ERR_NOTHING_TODO;
|
|
||||||
global_mgcpgw_client_conf->local_addr =
|
|
||||||
talloc_strdup(gsmnet_from_vty(vty), argv[0]);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd,
|
|
||||||
"mgcpgw local-port <0-65535>",
|
|
||||||
MGCPGW_STR "local bind to connect to MGCP gateway with\n"
|
|
||||||
"local bind port\n")
|
|
||||||
{
|
|
||||||
if (!global_mgcpgw_client_conf)
|
|
||||||
return CMD_ERR_NOTHING_TODO;
|
|
||||||
global_mgcpgw_client_conf->local_port = atoi(argv[0]);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_mgcpgw_remote_ip, cfg_mgcpgw_remote_ip_cmd,
|
|
||||||
"mgcpgw remote-ip A.B.C.D",
|
|
||||||
MGCPGW_STR "remote bind to connect to MGCP gateway with\n"
|
|
||||||
"remote bind IP address\n")
|
|
||||||
{
|
|
||||||
if (!global_mgcpgw_client_conf)
|
|
||||||
return CMD_ERR_NOTHING_TODO;
|
|
||||||
global_mgcpgw_client_conf->remote_addr =
|
|
||||||
talloc_strdup(gsmnet_from_vty(vty), argv[0]);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_mgcpgw_remote_port, cfg_mgcpgw_remote_port_cmd,
|
|
||||||
"mgcpgw remote-port <0-65535>",
|
|
||||||
MGCPGW_STR "remote bind to connect to MGCP gateway with\n"
|
|
||||||
"remote bind port\n")
|
|
||||||
{
|
|
||||||
if (!global_mgcpgw_client_conf)
|
|
||||||
return CMD_ERR_NOTHING_TODO;
|
|
||||||
global_mgcpgw_client_conf->remote_port = atoi(argv[0]);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_mgcpgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
|
|
||||||
"mgcpgw endpoint-range <1-65534> <1-65534>",
|
|
||||||
MGCPGW_STR "usable range of endpoint identifiers\n"
|
|
||||||
"set first useable endpoint identifier\n"
|
|
||||||
"set the last useable endpoint identifier\n")
|
|
||||||
{
|
|
||||||
uint16_t first_endpoint = atoi(argv[0]);
|
|
||||||
uint16_t last_endpoint = atoi(argv[1]);
|
|
||||||
|
|
||||||
if (last_endpoint < first_endpoint) {
|
|
||||||
vty_out(vty, "last endpoint must be greater than first endpoint!%s",
|
|
||||||
VTY_NEWLINE);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
global_mgcpgw_client_conf->first_endpoint = first_endpoint;
|
|
||||||
global_mgcpgw_client_conf->last_endpoint = last_endpoint;
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BTS_START_STR "First UDP port allocated for the BTS side\n"
|
|
||||||
#define UDP_PORT_STR "UDP Port number\n"
|
|
||||||
DEFUN(cfg_mgcp_rtp_bts_base_port,
|
|
||||||
cfg_mgcp_rtp_bts_base_port_cmd,
|
|
||||||
"mgcpgw bts-base <0-65534>",
|
|
||||||
MGCPGW_STR
|
|
||||||
BTS_START_STR
|
|
||||||
UDP_PORT_STR)
|
|
||||||
{
|
|
||||||
global_mgcpgw_client_conf->bts_base = atoi(argv[0]);
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgcpgw_client_config_write(struct vty *vty, const char *indent)
|
|
||||||
{
|
|
||||||
const char *addr;
|
|
||||||
int port;
|
|
||||||
uint16_t first_endpoint;
|
|
||||||
uint16_t last_endpoint;
|
|
||||||
uint16_t bts_base;
|
|
||||||
|
|
||||||
addr = global_mgcpgw_client_conf->local_addr;
|
|
||||||
if (addr)
|
|
||||||
vty_out(vty, "%smgcpgw local-ip %s%s", indent, addr,
|
|
||||||
VTY_NEWLINE);
|
|
||||||
port = global_mgcpgw_client_conf->local_port;
|
|
||||||
if (port >= 0)
|
|
||||||
vty_out(vty, "%smgcpgw local-port %u%s", indent,
|
|
||||||
(uint16_t)port, VTY_NEWLINE);
|
|
||||||
|
|
||||||
addr = global_mgcpgw_client_conf->remote_addr;
|
|
||||||
if (addr)
|
|
||||||
vty_out(vty, "%smgcpgw remote-ip %s%s", indent, addr,
|
|
||||||
VTY_NEWLINE);
|
|
||||||
port = global_mgcpgw_client_conf->remote_port;
|
|
||||||
if (port >= 0)
|
|
||||||
vty_out(vty, "%smgcpgw remote-port %u%s", indent,
|
|
||||||
(uint16_t)port, VTY_NEWLINE);
|
|
||||||
|
|
||||||
first_endpoint = global_mgcpgw_client_conf->first_endpoint;
|
|
||||||
last_endpoint = global_mgcpgw_client_conf->last_endpoint;
|
|
||||||
if (last_endpoint != 0) {
|
|
||||||
vty_out(vty, "%smgcpgw endpoint-range %u %u%s", indent,
|
|
||||||
first_endpoint, last_endpoint, VTY_NEWLINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
bts_base = global_mgcpgw_client_conf->bts_base;
|
|
||||||
if (bts_base) {
|
|
||||||
vty_out(vty, "%smgcpgw bts-base %u%s", indent,
|
|
||||||
bts_base, VTY_NEWLINE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mgcpgw_client_vty_init(int node, struct mgcpgw_client_conf *conf)
|
|
||||||
{
|
|
||||||
global_mgcpgw_client_conf = conf;
|
|
||||||
|
|
||||||
install_element(node, &cfg_mgcpgw_local_ip_cmd);
|
|
||||||
install_element(node, &cfg_mgcpgw_local_port_cmd);
|
|
||||||
install_element(node, &cfg_mgcpgw_remote_ip_cmd);
|
|
||||||
install_element(node, &cfg_mgcpgw_remote_port_cmd);
|
|
||||||
install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
|
|
||||||
install_element(node, &cfg_mgcp_rtp_bts_base_port_cmd);
|
|
||||||
}
|
|
|
@ -34,7 +34,7 @@
|
||||||
#include <openbsc/a_iface.h>
|
#include <openbsc/a_iface.h>
|
||||||
#include <openbsc/a_iface_bssap.h>
|
#include <openbsc/a_iface_bssap.h>
|
||||||
#include <openbsc/transaction.h>
|
#include <openbsc/transaction.h>
|
||||||
#include <openbsc/mgcpgw_client.h>
|
#include <osmocom/legacy_mgcp/mgcpgw_client.h>
|
||||||
#include <osmocom/core/byteswap.h>
|
#include <osmocom/core/byteswap.h>
|
||||||
#include <osmocom/sccp/sccp_types.h>
|
#include <osmocom/sccp/sccp_types.h>
|
||||||
#include <openbsc/a_reset.h>
|
#include <openbsc/a_reset.h>
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
#include <openbsc/msc_ifaces.h>
|
#include <openbsc/msc_ifaces.h>
|
||||||
#include <openbsc/gsm_subscriber.h>
|
#include <openbsc/gsm_subscriber.h>
|
||||||
#include <openbsc/transaction.h>
|
#include <openbsc/transaction.h>
|
||||||
#include <openbsc/mgcp.h>
|
#include <osmocom/legacy_mgcp/mgcp.h>
|
||||||
#include <openbsc/mgcpgw_client.h>
|
#include <osmocom/legacy_mgcp/mgcpgw_client.h>
|
||||||
#include <openbsc/vlr.h>
|
#include <openbsc/vlr.h>
|
||||||
#include <openbsc/a_iface.h>
|
#include <openbsc/a_iface.h>
|
||||||
|
|
||||||
|
@ -249,7 +249,7 @@ int msc_call_assignment(struct gsm_trans *trans)
|
||||||
* its RTP traffic to that port on the MGCPGW side. The MGCPGW only
|
* its RTP traffic to that port on the MGCPGW side. The MGCPGW only
|
||||||
* gets the endpoint ID via the CRCX. It will do the same calculation
|
* gets the endpoint ID via the CRCX. It will do the same calculation
|
||||||
* on his side too to get knowledge of the rtp port. */
|
* on his side too to get knowledge of the rtp port. */
|
||||||
bts_base = mgcp->actual.bts_base;
|
bts_base = mgcpgw_client_conf_actual(mgcp)->bts_base;
|
||||||
conn->rtp.port_subscr = bts_base + 2 * conn->rtp.mgcp_rtp_endpoint;
|
conn->rtp.port_subscr = bts_base + 2 * conn->rtp.mgcp_rtp_endpoint;
|
||||||
|
|
||||||
/* Establish the RTP stream first as looping back to the originator.
|
/* Establish the RTP stream first as looping back to the originator.
|
||||||
|
|
|
@ -155,7 +155,7 @@ void msc_vty_init(struct gsm_network *msc_network)
|
||||||
install_element(MSC_NODE, &cfg_msc_cs7_instance_a_cmd);
|
install_element(MSC_NODE, &cfg_msc_cs7_instance_a_cmd);
|
||||||
install_element(MSC_NODE, &cfg_msc_cs7_instance_iu_cmd);
|
install_element(MSC_NODE, &cfg_msc_cs7_instance_iu_cmd);
|
||||||
|
|
||||||
mgcpgw_client_vty_init(MSC_NODE, &msc_network->mgcpgw.conf);
|
mgcpgw_client_vty_init(msc_network, MSC_NODE, &msc_network->mgcpgw.conf);
|
||||||
#ifdef BUILD_IU
|
#ifdef BUILD_IU
|
||||||
ranap_iu_vty_init(MSC_NODE, &msc_network->iu.rab_assign_addr_enc);
|
ranap_iu_vty_init(MSC_NODE, &msc_network->iu.rab_assign_addr_enc);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
#include <openbsc/bsc_msc_data.h>
|
#include <openbsc/bsc_msc_data.h>
|
||||||
#include <openbsc/debug.h>
|
#include <openbsc/debug.h>
|
||||||
#include <openbsc/bsc_subscriber.h>
|
#include <openbsc/bsc_subscriber.h>
|
||||||
#include <openbsc/mgcp.h>
|
#include <osmocom/legacy_mgcp/mgcp.h>
|
||||||
#include <openbsc/paging.h>
|
#include <openbsc/paging.h>
|
||||||
|
|
||||||
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
AM_CPPFLAGS = \
|
|
||||||
$(all_includes) \
|
|
||||||
-I$(top_srcdir)/include \
|
|
||||||
-I$(top_builddir) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_CFLAGS = \
|
|
||||||
-Wall \
|
|
||||||
$(LIBOSMOCORE_CFLAGS) \
|
|
||||||
$(LIBOSMOGSM_CFLAGS) \
|
|
||||||
$(LIBOSMOVTY_CFLAGS) \
|
|
||||||
$(LIBOSMOABIS_CFLAGS) \
|
|
||||||
$(LIBOSMONETIF_CFLAGS) \
|
|
||||||
$(COVERAGE_CFLAGS) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
bin_PROGRAMS = \
|
|
||||||
osmo-bsc_mgcp \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
osmo_bsc_mgcp_SOURCES = \
|
|
||||||
mgcp_main.c \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
osmo_bsc_mgcp_LDADD = \
|
|
||||||
$(top_builddir)/src/libcommon/libcommon.a \
|
|
||||||
$(top_builddir)/src/libmgcp/libmgcp.a \
|
|
||||||
$(LIBOSMOVTY_LIBS) \
|
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(LIBOSMOGSM_LIBS) \
|
|
||||||
$(LIBOSMONETIF_LIBS) \
|
|
||||||
$(LIBBCG729_LIBS) \
|
|
||||||
$(LIBRARY_GSM) \
|
|
||||||
$(LIBOSMOSIGTRAN_LIBS) \
|
|
||||||
-lrt \
|
|
||||||
$(NULL)
|
|
|
@ -1,313 +0,0 @@
|
||||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
|
|
||||||
/* The main method to drive it as a standalone process */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
|
|
||||||
* (C) 2009-2011 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 <ctype.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
#include <openbsc/debug.h>
|
|
||||||
#include <openbsc/gsm_data.h>
|
|
||||||
#include <openbsc/mgcp.h>
|
|
||||||
#include <openbsc/mgcp_internal.h>
|
|
||||||
#include <openbsc/vty.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/application.h>
|
|
||||||
#include <osmocom/core/msgb.h>
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
#include <osmocom/core/select.h>
|
|
||||||
#include <osmocom/core/stats.h>
|
|
||||||
#include <osmocom/core/rate_ctr.h>
|
|
||||||
|
|
||||||
#include <osmocom/vty/telnet_interface.h>
|
|
||||||
#include <osmocom/vty/logging.h>
|
|
||||||
#include <osmocom/vty/ports.h>
|
|
||||||
#include <osmocom/vty/command.h>
|
|
||||||
#include <osmocom/vty/stats.h>
|
|
||||||
|
|
||||||
#include "../../bscconfig.h"
|
|
||||||
|
|
||||||
#ifdef BUILD_MGCP_TRANSCODING
|
|
||||||
#include "openbsc/mgcp_transcode.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
#warning "Make use of the rtp proxy code"
|
|
||||||
|
|
||||||
static struct mgcp_config *cfg;
|
|
||||||
static struct mgcp_trunk_config *reset_trunk;
|
|
||||||
static int reset_endpoints = 0;
|
|
||||||
static int daemonize = 0;
|
|
||||||
|
|
||||||
const char *openbsc_copyright =
|
|
||||||
"Copyright (C) 2009-2010 Holger Freyther and On-Waves\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"
|
|
||||||
"This is free software: you are free to change and redistribute it.\r\n"
|
|
||||||
"There is NO WARRANTY, to the extent permitted by law.\r\n";
|
|
||||||
|
|
||||||
static char *config_file = "mgcp.cfg";
|
|
||||||
|
|
||||||
/* used by msgb and mgcp */
|
|
||||||
void *tall_bsc_ctx = NULL;
|
|
||||||
|
|
||||||
static void print_help()
|
|
||||||
{
|
|
||||||
printf("Some useful help...\n");
|
|
||||||
printf(" -h --help is printing this text.\n");
|
|
||||||
printf(" -c --config-file filename The config file to use.\n");
|
|
||||||
printf(" -s --disable-color\n");
|
|
||||||
printf(" -D --daemonize Fork the process into a background daemon\n");
|
|
||||||
printf(" -V --version Print the version number\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_options(int argc, char **argv)
|
|
||||||
{
|
|
||||||
while (1) {
|
|
||||||
int option_index = 0, c;
|
|
||||||
static struct option long_options[] = {
|
|
||||||
{"help", 0, 0, 'h'},
|
|
||||||
{"config-file", 1, 0, 'c'},
|
|
||||||
{"daemonize", 0, 0, 'D'},
|
|
||||||
{"version", 0, 0, 'V'},
|
|
||||||
{"disable-color", 0, 0, 's'},
|
|
||||||
{0, 0, 0, 0},
|
|
||||||
};
|
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
|
|
||||||
|
|
||||||
if (c == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch(c) {
|
|
||||||
case 'h':
|
|
||||||
print_help();
|
|
||||||
exit(0);
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
config_file = talloc_strdup(tall_bsc_ctx, optarg);
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
log_set_use_color(osmo_stderr_target, 0);
|
|
||||||
break;
|
|
||||||
case 'V':
|
|
||||||
print_version(1);
|
|
||||||
exit(0);
|
|
||||||
break;
|
|
||||||
case 'D':
|
|
||||||
daemonize = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* ignore */
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* simply remember this */
|
|
||||||
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
|
|
||||||
{
|
|
||||||
reset_endpoints = 1;
|
|
||||||
reset_trunk = tcfg;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_call_agent(struct osmo_fd *fd, unsigned int what)
|
|
||||||
{
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
socklen_t slen = sizeof(addr);
|
|
||||||
struct msgb *msg;
|
|
||||||
struct msgb *resp;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
msg = (struct msgb *) fd->data;
|
|
||||||
|
|
||||||
/* read one less so we can use it as a \0 */
|
|
||||||
int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0,
|
|
||||||
(struct sockaddr *) &addr, &slen);
|
|
||||||
if (rc < 0) {
|
|
||||||
perror("Gateway failed to read");
|
|
||||||
return -1;
|
|
||||||
} else if (slen > sizeof(addr)) {
|
|
||||||
fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n",
|
|
||||||
(size_t) slen, sizeof(addr));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* handle message now */
|
|
||||||
msg->l2h = msgb_put(msg, rc);
|
|
||||||
resp = mgcp_handle_message(cfg, msg);
|
|
||||||
msgb_reset(msg);
|
|
||||||
|
|
||||||
if (resp) {
|
|
||||||
sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
|
|
||||||
msgb_free(resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reset_endpoints) {
|
|
||||||
LOGP(DMGCP, LOGL_NOTICE,
|
|
||||||
"Asked to reset endpoints: %d/%d\n",
|
|
||||||
reset_trunk->trunk_nr, reset_trunk->trunk_type);
|
|
||||||
reset_endpoints = 0;
|
|
||||||
|
|
||||||
/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
|
|
||||||
for (i = 1; i < reset_trunk->number_endpoints; ++i)
|
|
||||||
mgcp_release_endp(&reset_trunk->endpoints[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern int bsc_vty_go_parent(struct vty *vty);
|
|
||||||
|
|
||||||
static struct vty_app_info vty_info = {
|
|
||||||
.name = "OpenBSC MGCP",
|
|
||||||
.version = PACKAGE_VERSION,
|
|
||||||
.go_parent_cb = bsc_vty_go_parent,
|
|
||||||
.is_config_node = bsc_vty_is_config_node,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
struct gsm_network dummy_network;
|
|
||||||
struct sockaddr_in addr;
|
|
||||||
int on = 1, rc;
|
|
||||||
|
|
||||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
|
|
||||||
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
|
|
||||||
|
|
||||||
osmo_init_ignore_signals();
|
|
||||||
osmo_init_logging(&log_info);
|
|
||||||
|
|
||||||
cfg = mgcp_config_alloc();
|
|
||||||
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);
|
|
||||||
osmo_stats_vty_add_cmds(&log_info);
|
|
||||||
mgcp_vty_init();
|
|
||||||
|
|
||||||
handle_options(argc, argv);
|
|
||||||
|
|
||||||
rate_ctr_init(tall_bsc_ctx);
|
|
||||||
osmo_stats_init(tall_bsc_ctx);
|
|
||||||
|
|
||||||
rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* start telnet after reading config for vty_get_bind_addr() */
|
|
||||||
rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network,
|
|
||||||
vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* set some callbacks */
|
|
||||||
cfg->reset_cb = mgcp_rsip_cb;
|
|
||||||
|
|
||||||
/* we need to bind a socket */
|
|
||||||
if (rc == 0) {
|
|
||||||
cfg->gw_fd.bfd.when = BSC_FD_READ;
|
|
||||||
cfg->gw_fd.bfd.cb = read_call_agent;
|
|
||||||
cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
if (cfg->gw_fd.bfd.fd < 0) {
|
|
||||||
perror("Gateway failed to listen");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
|
||||||
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(cfg->source_port);
|
|
||||||
inet_aton(cfg->source_addr, &addr.sin_addr);
|
|
||||||
|
|
||||||
if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
|
||||||
perror("Gateway failed to bind");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
|
|
||||||
if (!cfg->gw_fd.bfd.data) {
|
|
||||||
fprintf(stderr, "Gateway memory error.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cfg->call_agent_addr) {
|
|
||||||
addr.sin_port = htons(2727);
|
|
||||||
inet_aton(cfg->call_agent_addr, &addr.sin_addr);
|
|
||||||
if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
|
|
||||||
cfg->call_agent_addr, errno);
|
|
||||||
close(cfg->gw_fd.bfd.fd);
|
|
||||||
cfg->gw_fd.bfd.fd = -1;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) {
|
|
||||||
LOGP(DMGCP, LOGL_FATAL, "Failed to register the fd\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGP(DMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialisation */
|
|
||||||
srand(time(NULL));
|
|
||||||
|
|
||||||
if (daemonize) {
|
|
||||||
rc = osmo_daemonize();
|
|
||||||
if (rc < 0) {
|
|
||||||
perror("Error during daemonize");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* main loop */
|
|
||||||
while (1) {
|
|
||||||
osmo_select_main(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -13,6 +13,7 @@ AM_CFLAGS = \
|
||||||
$(LIBOSMOSCCP_CFLAGS) \
|
$(LIBOSMOSCCP_CFLAGS) \
|
||||||
$(LIBOSMOABIS_CFLAGS) \
|
$(LIBOSMOABIS_CFLAGS) \
|
||||||
$(LIBOSMONETIF_CFLAGS) \
|
$(LIBOSMONETIF_CFLAGS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_CFLAGS) \
|
||||||
$(LIBCRYPTO_CFLAGS) \
|
$(LIBCRYPTO_CFLAGS) \
|
||||||
$(COVERAGE_CFLAGS) \
|
$(COVERAGE_CFLAGS) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
@ -40,7 +41,6 @@ osmo_bsc_nat_SOURCES = \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
osmo_bsc_nat_LDADD = \
|
osmo_bsc_nat_LDADD = \
|
||||||
$(top_builddir)/src/libmgcp/libmgcp.a \
|
|
||||||
$(top_builddir)/src/libfilter/libfilter.a \
|
$(top_builddir)/src/libfilter/libfilter.a \
|
||||||
$(top_builddir)/src/libbsc/libbsc.a \
|
$(top_builddir)/src/libbsc/libbsc.a \
|
||||||
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
|
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
|
||||||
|
@ -54,6 +54,8 @@ osmo_bsc_nat_LDADD = \
|
||||||
$(LIBOSMOABIS_LIBS) \
|
$(LIBOSMOABIS_LIBS) \
|
||||||
$(LIBOSMONETIF_LIBS) \
|
$(LIBOSMONETIF_LIBS) \
|
||||||
$(LIBOSMOSIGTRAN_LIBS) \
|
$(LIBOSMOSIGTRAN_LIBS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_LIBS) \
|
||||||
$(LIBCRYPTO_LIBS) \
|
$(LIBCRYPTO_LIBS) \
|
||||||
|
$(LIBRARY_GSM) \
|
||||||
-lrt \
|
-lrt \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -48,8 +48,8 @@
|
||||||
#include <openbsc/gsm_data.h>
|
#include <openbsc/gsm_data.h>
|
||||||
#include <openbsc/debug.h>
|
#include <openbsc/debug.h>
|
||||||
#include <openbsc/ipaccess.h>
|
#include <openbsc/ipaccess.h>
|
||||||
#include <openbsc/mgcp.h>
|
#include <osmocom/legacy_mgcp/mgcp.h>
|
||||||
#include <openbsc/mgcp_internal.h>
|
#include <osmocom/legacy_mgcp/mgcp_internal.h>
|
||||||
#include <openbsc/osmux.h>
|
#include <openbsc/osmux.h>
|
||||||
|
|
||||||
#include <osmocom/ctrl/control_cmd.h>
|
#include <osmocom/ctrl/control_cmd.h>
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <openbsc/bsc_msg_filter.h>
|
#include <openbsc/bsc_msg_filter.h>
|
||||||
#include <openbsc/vty.h>
|
#include <openbsc/vty.h>
|
||||||
#include <openbsc/gsm_data.h>
|
#include <openbsc/gsm_data.h>
|
||||||
|
#include <openbsc/debug.h>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include <openbsc/bsc_msg_filter.h>
|
#include <openbsc/bsc_msg_filter.h>
|
||||||
#include <openbsc/bsc_msc.h>
|
#include <openbsc/bsc_msc.h>
|
||||||
#include <openbsc/gsm_04_08.h>
|
#include <openbsc/gsm_04_08.h>
|
||||||
#include <openbsc/mgcp.h>
|
#include <osmocom/legacy_mgcp/mgcp.h>
|
||||||
#include <openbsc/vty.h>
|
#include <openbsc/vty.h>
|
||||||
#include <openbsc/nat_rewrite_trie.h>
|
#include <openbsc/nat_rewrite_trie.h>
|
||||||
|
|
||||||
|
@ -1213,15 +1213,15 @@ DEFUN(cfg_bsc_osmux,
|
||||||
conf->osmux = OSMUX_USAGE_ONLY;
|
conf->osmux = OSMUX_USAGE_ONLY;
|
||||||
|
|
||||||
if (old == 0 && conf->osmux > 0 && !conf->nat->mgcp_cfg->osmux_init) {
|
if (old == 0 && conf->osmux > 0 && !conf->nat->mgcp_cfg->osmux_init) {
|
||||||
LOGP(DMGCP, LOGL_NOTICE, "Setting up OSMUX socket\n");
|
LOGP(DLMGCP, LOGL_NOTICE, "Setting up OSMUX socket\n");
|
||||||
if (osmux_init(OSMUX_ROLE_BSC_NAT, conf->nat->mgcp_cfg) < 0) {
|
if (osmux_init(OSMUX_ROLE_BSC_NAT, conf->nat->mgcp_cfg) < 0) {
|
||||||
LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
|
LOGP(DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
|
||||||
vty_out(vty, "%% failed to create Osmux socket%s",
|
vty_out(vty, "%% failed to create Osmux socket%s",
|
||||||
VTY_NEWLINE);
|
VTY_NEWLINE);
|
||||||
return CMD_WARNING;
|
return CMD_WARNING;
|
||||||
}
|
}
|
||||||
} else if (old > 0 && conf->osmux == 0) {
|
} else if (old > 0 && conf->osmux == 0) {
|
||||||
LOGP(DMGCP, LOGL_NOTICE, "Disabling OSMUX socket\n");
|
LOGP(DLMGCP, LOGL_NOTICE, "Disabling OSMUX socket\n");
|
||||||
/* Don't stop the socket, we may already have ongoing voice
|
/* Don't stop the socket, we may already have ongoing voice
|
||||||
* flows already using Osmux. This just switch indicates that
|
* flows already using Osmux. This just switch indicates that
|
||||||
* new upcoming flows should use RTP.
|
* new upcoming flows should use RTP.
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <openbsc/bsc_msg_filter.h>
|
#include <openbsc/bsc_msg_filter.h>
|
||||||
#include <openbsc/ipaccess.h>
|
#include <openbsc/ipaccess.h>
|
||||||
#include <openbsc/socket.h>
|
#include <openbsc/socket.h>
|
||||||
|
#include <openbsc/debug.h>
|
||||||
|
|
||||||
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
||||||
#include <osmocom/gsm/gsm0480.h>
|
#include <osmocom/gsm/gsm0480.h>
|
||||||
|
|
|
@ -17,6 +17,7 @@ AM_CFLAGS = \
|
||||||
$(LIBOSMORANAP_CFLAGS) \
|
$(LIBOSMORANAP_CFLAGS) \
|
||||||
$(LIBASN1C_CFLAGS) \
|
$(LIBASN1C_CFLAGS) \
|
||||||
$(LIBOSMOSIGTRAN_CFLAGS) \
|
$(LIBOSMOSIGTRAN_CFLAGS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_CFLAGS) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
AM_LDFLAGS = \
|
AM_LDFLAGS = \
|
||||||
|
@ -37,7 +38,6 @@ osmo_msc_LDADD = \
|
||||||
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
|
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
|
||||||
$(top_builddir)/src/libtrau/libtrau.a \
|
$(top_builddir)/src/libtrau/libtrau.a \
|
||||||
$(top_builddir)/src/libcommon/libcommon.a \
|
$(top_builddir)/src/libcommon/libcommon.a \
|
||||||
$(top_builddir)/src/libmgcp/libmgcp.a \
|
|
||||||
$(LIBOSMOGSM_LIBS) \
|
$(LIBOSMOGSM_LIBS) \
|
||||||
$(LIBOSMOVTY_LIBS) \
|
$(LIBOSMOVTY_LIBS) \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
@ -46,6 +46,8 @@ osmo_msc_LDADD = \
|
||||||
$(LIBSMPP34_LIBS) \
|
$(LIBSMPP34_LIBS) \
|
||||||
$(LIBCRYPTO_LIBS) \
|
$(LIBCRYPTO_LIBS) \
|
||||||
$(LIBOSMOSIGTRAN_LIBS) \
|
$(LIBOSMOSIGTRAN_LIBS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_LIBS) \
|
||||||
|
$(LIBRARY_GSM) \
|
||||||
-ldbi \
|
-ldbi \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
if BUILD_IU
|
if BUILD_IU
|
||||||
|
|
|
@ -64,7 +64,7 @@
|
||||||
#include <openbsc/osmo_bsc_rf.h>
|
#include <openbsc/osmo_bsc_rf.h>
|
||||||
#include <openbsc/smpp.h>
|
#include <openbsc/smpp.h>
|
||||||
#include <osmocom/sigtran/osmo_ss7.h>
|
#include <osmocom/sigtran/osmo_ss7.h>
|
||||||
#include <openbsc/mgcpgw_client.h>
|
#include <osmocom/legacy_mgcp/mgcpgw_client.h>
|
||||||
|
|
||||||
#ifdef BUILD_IU
|
#ifdef BUILD_IU
|
||||||
#include <osmocom/ranap/iu_client.h>
|
#include <osmocom/ranap/iu_client.h>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
SUBDIRS = \
|
SUBDIRS = \
|
||||||
gsm0408 \
|
gsm0408 \
|
||||||
channel \
|
channel \
|
||||||
mgcp \
|
|
||||||
gprs \
|
gprs \
|
||||||
abis \
|
abis \
|
||||||
gbproxy \
|
gbproxy \
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
|
||||||
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS)
|
AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOSCCP_CFLAGS) $(LIBOSMOABIS_CFLAGS) $(COVERAGE_CFLAGS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_CFLAGS) \
|
||||||
|
$(NULL)
|
||||||
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
|
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
|
||||||
|
|
||||||
EXTRA_DIST = bsc_nat_trie_test.ok prefixes.csv
|
EXTRA_DIST = bsc_nat_trie_test.ok prefixes.csv
|
||||||
|
@ -9,9 +11,11 @@ noinst_PROGRAMS = bsc_nat_trie_test
|
||||||
bsc_nat_trie_test_SOURCES = bsc_nat_trie_test.c \
|
bsc_nat_trie_test_SOURCES = bsc_nat_trie_test.c \
|
||||||
$(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c
|
$(top_srcdir)/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c
|
||||||
bsc_nat_trie_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
|
bsc_nat_trie_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \
|
||||||
$(top_builddir)/src/libmgcp/libmgcp.a \
|
|
||||||
$(top_builddir)/src/libtrau/libtrau.a \
|
$(top_builddir)/src/libtrau/libtrau.a \
|
||||||
$(top_builddir)/src/libcommon/libcommon.a \
|
$(top_builddir)/src/libcommon/libcommon.a \
|
||||||
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \
|
$(LIBOSMOCORE_LIBS) $(LIBOSMOGSM_LIBS) -lrt \
|
||||||
$(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \
|
$(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) \
|
||||||
$(LIBOSMOABIS_LIBS)
|
$(LIBOSMOABIS_LIBS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_LIBS) \
|
||||||
|
$(LIBRARY_GSM) \
|
||||||
|
$(NULL)
|
||||||
|
|
|
@ -12,6 +12,7 @@ AM_CFLAGS = \
|
||||||
$(LIBOSMOSCCP_CFLAGS) \
|
$(LIBOSMOSCCP_CFLAGS) \
|
||||||
$(LIBOSMOABIS_CFLAGS) \
|
$(LIBOSMOABIS_CFLAGS) \
|
||||||
$(LIBOSMONETIF_CFLAGS) \
|
$(LIBOSMONETIF_CFLAGS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_CFLAGS) \
|
||||||
$(COVERAGE_CFLAGS) \
|
$(COVERAGE_CFLAGS) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
@ -45,7 +46,6 @@ bsc_nat_test_LDADD = \
|
||||||
$(top_builddir)/src/libfilter/libfilter.a \
|
$(top_builddir)/src/libfilter/libfilter.a \
|
||||||
$(top_builddir)/src/libbsc/libbsc.a \
|
$(top_builddir)/src/libbsc/libbsc.a \
|
||||||
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
|
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
|
||||||
$(top_builddir)/src/libmgcp/libmgcp.a \
|
|
||||||
$(top_builddir)/src/libtrau/libtrau.a \
|
$(top_builddir)/src/libtrau/libtrau.a \
|
||||||
$(top_builddir)/src/libcommon/libcommon.a \
|
$(top_builddir)/src/libcommon/libcommon.a \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
@ -55,5 +55,7 @@ bsc_nat_test_LDADD = \
|
||||||
$(LIBOSMOABIS_LIBS) \
|
$(LIBOSMOABIS_LIBS) \
|
||||||
$(LIBOSMONETIF_LIBS) \
|
$(LIBOSMONETIF_LIBS) \
|
||||||
$(LIBOSMOCTRL_LIBS) \
|
$(LIBOSMOCTRL_LIBS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_LIBS) \
|
||||||
|
$(LIBRARY_GSM) \
|
||||||
-lrt \
|
-lrt \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -10,6 +10,7 @@ AM_CFLAGS = \
|
||||||
$(LIBOSMOGSM_CFLAGS) \
|
$(LIBOSMOGSM_CFLAGS) \
|
||||||
$(LIBOSMOSCCP_CFLAGS) \
|
$(LIBOSMOSCCP_CFLAGS) \
|
||||||
$(LIBOSMOABIS_CFLAGS) \
|
$(LIBOSMOABIS_CFLAGS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_CFLAGS) \
|
||||||
$(COVERAGE_CFLAGS) \
|
$(COVERAGE_CFLAGS) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
@ -33,7 +34,6 @@ bsc_test_SOURCES = \
|
||||||
bsc_test_LDADD = \
|
bsc_test_LDADD = \
|
||||||
$(top_builddir)/src/libbsc/libbsc.a \
|
$(top_builddir)/src/libbsc/libbsc.a \
|
||||||
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
|
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
|
||||||
$(top_builddir)/src/libmgcp/libmgcp.a \
|
|
||||||
$(top_builddir)/src/libtrau/libtrau.a \
|
$(top_builddir)/src/libtrau/libtrau.a \
|
||||||
$(top_builddir)/src/libcommon/libcommon.a \
|
$(top_builddir)/src/libcommon/libcommon.a \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
@ -41,5 +41,7 @@ bsc_test_LDADD = \
|
||||||
$(LIBOSMOSCCP_LIBS) \
|
$(LIBOSMOSCCP_LIBS) \
|
||||||
$(LIBOSMOVTY_LIBS) \
|
$(LIBOSMOVTY_LIBS) \
|
||||||
$(LIBOSMOABIS_LIBS) \
|
$(LIBOSMOABIS_LIBS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_LIBS) \
|
||||||
|
$(LIBRARY_GSM) \
|
||||||
-lrt \
|
-lrt \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
AM_CPPFLAGS = \
|
|
||||||
$(all_includes) \
|
|
||||||
-I$(top_srcdir)/include \
|
|
||||||
-I$(top_srcdir) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_CFLAGS = \
|
|
||||||
-Wall \
|
|
||||||
-ggdb3 \
|
|
||||||
$(LIBOSMOCORE_CFLAGS) \
|
|
||||||
$(LIBOSMOGSM_FLAGS) \
|
|
||||||
$(LIBOSMONETIF_CFLAGS) \
|
|
||||||
$(LIBOSMOSCCP_CFLAGS) \
|
|
||||||
$(COVERAGE_CFLAGS) \
|
|
||||||
$(LIBBCG729_CFLAGS) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_LDFLAGS = \
|
|
||||||
$(COVERAGE_LDFLAGS) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
EXTRA_DIST = \
|
|
||||||
mgcp_test.ok \
|
|
||||||
mgcp_transcoding_test.ok \
|
|
||||||
mgcpgw_client_test.ok \
|
|
||||||
mgcpgw_client_test.err \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
noinst_PROGRAMS = \
|
|
||||||
mgcp_test \
|
|
||||||
mgcpgw_client_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/libbsc/libbsc.a \
|
|
||||||
$(top_builddir)/src/libmgcp/libmgcp.a \
|
|
||||||
$(top_builddir)/src/libcommon/libcommon.a \
|
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(LIBOSMOGSM_LIBS) \
|
|
||||||
$(LIBOSMOSCCP_LIBS) \
|
|
||||||
$(LIBOSMOVTY_LIBS) \
|
|
||||||
$(LIBRARY_DL) \
|
|
||||||
$(LIBOSMONETIF_LIBS) \
|
|
||||||
-lrt \
|
|
||||||
-lm \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
mgcp_transcoding_test_SOURCES = \
|
|
||||||
mgcp_transcoding_test.c \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
mgcp_transcoding_test_LDADD = \
|
|
||||||
$(top_builddir)/src/libbsc/libbsc.a \
|
|
||||||
$(top_builddir)/src/libmgcp/libmgcp.a \
|
|
||||||
$(top_builddir)/src/libcommon/libcommon.a \
|
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(LIBOSMOGSM_LIBS) \
|
|
||||||
$(LIBBCG729_LIBS) \
|
|
||||||
$(LIBOSMOSCCP_LIBS) \
|
|
||||||
$(LIBOSMOVTY_LIBS) \
|
|
||||||
$(LIBRARY_DL) \
|
|
||||||
$(LIBOSMONETIF_LIBS) \
|
|
||||||
$(LIBRARY_GSM) \
|
|
||||||
-lrt \
|
|
||||||
-lm \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
mgcpgw_client_test_SOURCES = \
|
|
||||||
mgcpgw_client_test.c \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
mgcpgw_client_test_LDADD = \
|
|
||||||
$(top_builddir)/src/libbsc/libbsc.a \
|
|
||||||
$(top_builddir)/src/libmgcp/libmgcp.a \
|
|
||||||
$(top_builddir)/src/libcommon/libcommon.a \
|
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(LIBOSMOGSM_LIBS) \
|
|
||||||
$(LIBOSMOSCCP_LIBS) \
|
|
||||||
$(LIBOSMOVTY_LIBS) \
|
|
||||||
$(LIBRARY_DL) \
|
|
||||||
$(LIBOSMONETIF_LIBS) \
|
|
||||||
-lrt \
|
|
||||||
-lm \
|
|
||||||
$(NULL)
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,480 +0,0 @@
|
||||||
line: 'one CR'
|
|
||||||
line: 'two CR'
|
|
||||||
line: ''
|
|
||||||
line: 'one CRLF'
|
|
||||||
line: 'two CRLF'
|
|
||||||
line: ''
|
|
||||||
line: 'one LF'
|
|
||||||
line: 'two LF'
|
|
||||||
line: ''
|
|
||||||
line: 'mixed (4 lines)'
|
|
||||||
line: ''
|
|
||||||
line: ''
|
|
||||||
line: ''
|
|
||||||
Testing AUEP1
|
|
||||||
Testing AUEP2
|
|
||||||
Testing MDCX1
|
|
||||||
Testing MDCX2
|
|
||||||
Testing CRCX
|
|
||||||
Dummy packets: 1
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 1: RECV
|
|
||||||
Testing MDCX3
|
|
||||||
Dummy packets: 1
|
|
||||||
Packet duration not set
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode not set
|
|
||||||
Testing MDCX4
|
|
||||||
Dummy packets: 1
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 3: SEND RECV
|
|
||||||
Testing MDCX4_PT1
|
|
||||||
Dummy packets: 1
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-40
|
|
||||||
Connection mode: 3: SEND RECV
|
|
||||||
Testing MDCX4_PT2
|
|
||||||
Dummy packets: 1
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 3: SEND RECV
|
|
||||||
Testing MDCX4_PT3
|
|
||||||
Dummy packets: 1
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode: 3: SEND RECV
|
|
||||||
Testing MDCX4_SO
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 2: SEND
|
|
||||||
Testing MDCX4_RO
|
|
||||||
Dummy packets: 1
|
|
||||||
Packet duration not set
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 1: RECV
|
|
||||||
Testing DLCX
|
|
||||||
Detected packet duration: 20
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode: 0: NONE
|
|
||||||
Testing CRCX_ZYN
|
|
||||||
Dummy packets: 1
|
|
||||||
Packet duration not set
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode: 1: RECV
|
|
||||||
Testing EMPTY
|
|
||||||
Testing SHORT1
|
|
||||||
Testing SHORT2
|
|
||||||
Testing SHORT3
|
|
||||||
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
|
|
||||||
Detected packet duration: 40
|
|
||||||
Requested packetetization period: 20-20
|
|
||||||
Connection mode: 1: RECV
|
|
||||||
Testing MDCX3
|
|
||||||
Dummy packets: 1
|
|
||||||
Packet duration not set
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode not set
|
|
||||||
Testing DLCX
|
|
||||||
Detected packet duration: 20
|
|
||||||
Requested packetization period not set
|
|
||||||
Connection mode: 0: NONE
|
|
||||||
Testing CRCX
|
|
||||||
Re-transmitting CRCX
|
|
||||||
Testing RQNT1
|
|
||||||
Re-transmitting RQNT1
|
|
||||||
Testing RQNT2
|
|
||||||
Re-transmitting RQNT2
|
|
||||||
Testing MDCX3
|
|
||||||
Re-transmitting MDCX3
|
|
||||||
Testing DLCX
|
|
||||||
Re-transmitting DLCX
|
|
||||||
Testing packet loss calculation.
|
|
||||||
Testing stat parsing
|
|
||||||
Parsing result: 0
|
|
||||||
Parsing result: 0
|
|
||||||
Testing packet error detection, patch SSRC.
|
|
||||||
Output SSRC changed to 11223344
|
|
||||||
In TS: 0, dTS: 0, Seq: 0
|
|
||||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 160, dTS: 160, Seq: 1
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 2
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 3
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 10, Transit = 160
|
|
||||||
In TS: 480, dTS: 160, Seq: 4
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 9, Transit = 160
|
|
||||||
In TS: 640, dTS: 160, Seq: 5
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 8, Transit = 160
|
|
||||||
In TS: 960, dTS: 320, Seq: 6
|
|
||||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 18, Transit = 0
|
|
||||||
In TS: 1120, dTS: 160, Seq: 7
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 0
|
|
||||||
In TS: 1280, dTS: 160, Seq: 8
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 0
|
|
||||||
In TS: 1400, dTS: 120, Seq: 9
|
|
||||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 17, Transit = 40
|
|
||||||
In TS: 1560, dTS: 160, Seq: 10
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 40
|
|
||||||
In TS: 1720, dTS: 160, Seq: 11
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 15, Transit = 40
|
|
||||||
In TS: 34688, dTS: 0, Seq: 12
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 34848, dTS: 160, Seq: 13
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35008, dTS: 160, Seq: 14
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35128, dTS: 120, Seq: 15
|
|
||||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35288, dTS: 160, Seq: 16
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35448, dTS: 160, Seq: 17
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35768, dTS: 160, Seq: 19
|
|
||||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 12, Transit = -32888
|
|
||||||
In TS: 35928, dTS: 160, Seq: 20
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 11, Transit = -32888
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 20, Transit = -33048
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 29, Transit = -32888
|
|
||||||
In TS: 36248, dTS: 160, Seq: 22
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 27, Transit = -32888
|
|
||||||
In TS: 36408, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 25, Transit = -32888
|
|
||||||
In TS: 36568, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 24, Transit = -32888
|
|
||||||
In TS: 36728, dTS: 160, Seq: 24
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 22, Transit = -32888
|
|
||||||
In TS: 36888, dTS: 160, Seq: 25
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 21, Transit = -32888
|
|
||||||
In TS: 160000, dTS: 0, Seq: 1000
|
|
||||||
Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160160, dTS: 160, Seq: 1001
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160320, dTS: 160, Seq: 1002
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
Testing packet error detection.
|
|
||||||
Output SSRC changed to 11223344
|
|
||||||
In TS: 0, dTS: 0, Seq: 0
|
|
||||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 160, dTS: 160, Seq: 1
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 2
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 3
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 10, Transit = 160
|
|
||||||
In TS: 480, dTS: 160, Seq: 4
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 9, Transit = 160
|
|
||||||
In TS: 640, dTS: 160, Seq: 5
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 8, Transit = 160
|
|
||||||
In TS: 960, dTS: 320, Seq: 6
|
|
||||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 18, Transit = 0
|
|
||||||
In TS: 1120, dTS: 160, Seq: 7
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 0
|
|
||||||
In TS: 1280, dTS: 160, Seq: 8
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 0
|
|
||||||
In TS: 1400, dTS: 120, Seq: 9
|
|
||||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 17, Transit = 40
|
|
||||||
In TS: 1560, dTS: 160, Seq: 10
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 40
|
|
||||||
In TS: 1720, dTS: 160, Seq: 11
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 15, Transit = 40
|
|
||||||
Output SSRC changed to 10203040
|
|
||||||
In TS: 34688, dTS: 0, Seq: 12
|
|
||||||
Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 34848, dTS: 160, Seq: 13
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35008, dTS: 160, Seq: 14
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35128, dTS: 120, Seq: 15
|
|
||||||
Out TS change: 120, dTS: 120, Seq change: 1, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35288, dTS: 160, Seq: 16
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35448, dTS: 160, Seq: 17
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35768, dTS: 160, Seq: 19
|
|
||||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 12, Transit = -32888
|
|
||||||
In TS: 35928, dTS: 160, Seq: 20
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 11, Transit = -32888
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 20, Transit = -33048
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 29, Transit = -32888
|
|
||||||
In TS: 36248, dTS: 160, Seq: 22
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 27, Transit = -32888
|
|
||||||
In TS: 36408, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 25, Transit = -32888
|
|
||||||
In TS: 36568, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 24, Transit = -32888
|
|
||||||
In TS: 36728, dTS: 160, Seq: 24
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 22, Transit = -32888
|
|
||||||
In TS: 36888, dTS: 160, Seq: 25
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 21, Transit = -32888
|
|
||||||
Output SSRC changed to 50607080
|
|
||||||
In TS: 160000, dTS: 0, Seq: 1000
|
|
||||||
Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160160, dTS: 160, Seq: 1001
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160320, dTS: 160, Seq: 1002
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
Testing packet error detection, patch timestamps.
|
|
||||||
Output SSRC changed to 11223344
|
|
||||||
In TS: 0, dTS: 0, Seq: 0
|
|
||||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 160, dTS: 160, Seq: 1
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 2
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 3
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 10, Transit = 160
|
|
||||||
In TS: 480, dTS: 160, Seq: 4
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 9, Transit = 160
|
|
||||||
In TS: 640, dTS: 160, Seq: 5
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 8, Transit = 160
|
|
||||||
In TS: 960, dTS: 320, Seq: 6
|
|
||||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 18, Transit = 0
|
|
||||||
In TS: 1120, dTS: 160, Seq: 7
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 0
|
|
||||||
In TS: 1280, dTS: 160, Seq: 8
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 0
|
|
||||||
In TS: 1400, dTS: 120, Seq: 9
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 40
|
|
||||||
In TS: 1560, dTS: 160, Seq: 10
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 40
|
|
||||||
In TS: 1720, dTS: 160, Seq: 11
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 15, Transit = 40
|
|
||||||
Output SSRC changed to 10203040
|
|
||||||
In TS: 34688, dTS: 0, Seq: 12
|
|
||||||
Out TS change: 32968, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 34848, dTS: 160, Seq: 13
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35008, dTS: 160, Seq: 14
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35128, dTS: 120, Seq: 15
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35288, dTS: 160, Seq: 16
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35448, dTS: 160, Seq: 17
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35768, dTS: 160, Seq: 19
|
|
||||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 12, Transit = -32888
|
|
||||||
In TS: 35928, dTS: 160, Seq: 20
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 11, Transit = -32888
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 20, Transit = -33048
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 29, Transit = -32888
|
|
||||||
In TS: 36248, dTS: 160, Seq: 22
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 27, Transit = -32888
|
|
||||||
In TS: 36408, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 25, Transit = -32888
|
|
||||||
In TS: 36568, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 24, Transit = -32888
|
|
||||||
In TS: 36728, dTS: 160, Seq: 24
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 22, Transit = -32888
|
|
||||||
In TS: 36888, dTS: 160, Seq: 25
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 21, Transit = -32888
|
|
||||||
Output SSRC changed to 50607080
|
|
||||||
In TS: 160000, dTS: 0, Seq: 1000
|
|
||||||
Out TS change: 123112, dTS: 160, Seq change: 975, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160160, dTS: 160, Seq: 1001
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160320, dTS: 160, Seq: 1002
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
Testing packet error detection, patch SSRC, patch timestamps.
|
|
||||||
Output SSRC changed to 11223344
|
|
||||||
In TS: 0, dTS: 0, Seq: 0
|
|
||||||
Out TS change: 0, dTS: 0, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 160, dTS: 160, Seq: 1
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 2
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = 0
|
|
||||||
In TS: 320, dTS: 160, Seq: 3
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 10, Transit = 160
|
|
||||||
In TS: 480, dTS: 160, Seq: 4
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 9, Transit = 160
|
|
||||||
In TS: 640, dTS: 160, Seq: 5
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 8, Transit = 160
|
|
||||||
In TS: 960, dTS: 320, Seq: 6
|
|
||||||
Out TS change: 320, dTS: 320, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 18, Transit = 0
|
|
||||||
In TS: 1120, dTS: 160, Seq: 7
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 0
|
|
||||||
In TS: 1280, dTS: 160, Seq: 8
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 0
|
|
||||||
In TS: 1400, dTS: 120, Seq: 9
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
|
|
||||||
Stats: Jitter = 17, Transit = 40
|
|
||||||
In TS: 1560, dTS: 160, Seq: 10
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 16, Transit = 40
|
|
||||||
In TS: 1720, dTS: 160, Seq: 11
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 15, Transit = 40
|
|
||||||
In TS: 34688, dTS: 0, Seq: 12
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 34848, dTS: 160, Seq: 13
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35008, dTS: 160, Seq: 14
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -32768
|
|
||||||
In TS: 35128, dTS: 120, Seq: 15
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +1, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35288, dTS: 160, Seq: 16
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35448, dTS: 160, Seq: 17
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 2, Transit = -32728
|
|
||||||
In TS: 35768, dTS: 160, Seq: 19
|
|
||||||
Out TS change: 320, dTS: 160, Seq change: 2, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 12, Transit = -32888
|
|
||||||
In TS: 35928, dTS: 160, Seq: 20
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 11, Transit = -32888
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 20, Transit = -33048
|
|
||||||
In TS: 36088, dTS: 160, Seq: 21
|
|
||||||
Out TS change: 0, dTS: 160, Seq change: 0, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 29, Transit = -32888
|
|
||||||
In TS: 36248, dTS: 160, Seq: 22
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 27, Transit = -32888
|
|
||||||
In TS: 36408, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 25, Transit = -32888
|
|
||||||
In TS: 36568, dTS: 160, Seq: 23
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 0, TS Err change: in +1, out +1
|
|
||||||
Stats: Jitter = 24, Transit = -32888
|
|
||||||
In TS: 36728, dTS: 160, Seq: 24
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 22, Transit = -32888
|
|
||||||
In TS: 36888, dTS: 160, Seq: 25
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 21, Transit = -32888
|
|
||||||
In TS: 160000, dTS: 0, Seq: 1000
|
|
||||||
Out TS change: 12000, dTS: 12000, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160160, dTS: 160, Seq: 1001
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
In TS: 160320, dTS: 160, Seq: 1002
|
|
||||||
Out TS change: 160, dTS: 160, Seq change: 1, TS Err change: in +0, out +0
|
|
||||||
Stats: Jitter = 0, Transit = -144000
|
|
||||||
Testing multiple payload types
|
|
||||||
Testing no sequence flow on initial packet
|
|
||||||
Testing no rtpmap name
|
|
||||||
Done
|
|
|
@ -1,654 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <err.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/talloc.h>
|
|
||||||
#include <osmocom/core/application.h>
|
|
||||||
|
|
||||||
#include <osmocom/netif/rtp.h>
|
|
||||||
|
|
||||||
#include <openbsc/debug.h>
|
|
||||||
#include <openbsc/gsm_data.h>
|
|
||||||
#include <openbsc/mgcp.h>
|
|
||||||
#include <openbsc/mgcp_internal.h>
|
|
||||||
|
|
||||||
#include "bscconfig.h"
|
|
||||||
#ifndef BUILD_MGCP_TRANSCODING
|
|
||||||
#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "openbsc/mgcp_transcode.h"
|
|
||||||
|
|
||||||
uint8_t *audio_frame_l16[] = {
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets {
|
|
||||||
float t;
|
|
||||||
int len;
|
|
||||||
char *data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_l16[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 332,
|
|
||||||
"\x80\x0B\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
"\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_gsm[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 45,
|
|
||||||
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
|
|
||||||
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
|
|
||||||
"\xDE"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_gsm_invalid_size[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 41,
|
|
||||||
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
|
|
||||||
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
|
|
||||||
"\xDE"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_gsm_invalid_data[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 45,
|
|
||||||
"\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
|
|
||||||
"\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
|
|
||||||
"\xEE"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_gsm_invalid_ptype[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 45,
|
|
||||||
"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
|
|
||||||
"\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
|
|
||||||
"\xDE"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_g729[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 32,
|
|
||||||
"\x80\x12\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xAF\xC2\x81\x40\x00\xFA\xCE\xA4\x21\x7C\xC5\xC3\x4F\xA5\x98\xF5"
|
|
||||||
"\xB2\x95\xC4\xAD"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rtp_packets audio_packets_pcma[] = {
|
|
||||||
/* RTP: SeqNo=1, TS=160 */
|
|
||||||
{0.020000, 172,
|
|
||||||
"\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
|
|
||||||
},
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
|
|
@ -1,165 +0,0 @@
|
||||||
/*
|
|
||||||
* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* Author: Neels Hofmeyr
|
|
||||||
*
|
|
||||||
* 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 <string.h>
|
|
||||||
|
|
||||||
#include <osmocom/core/msgb.h>
|
|
||||||
#include <osmocom/core/application.h>
|
|
||||||
#include <openbsc/mgcp.h>
|
|
||||||
#include <openbsc/mgcpgw_client.h>
|
|
||||||
|
|
||||||
void *ctx;
|
|
||||||
|
|
||||||
#define buf_len 4096
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
static struct msgb *from_hex(const char *hex)
|
|
||||||
{
|
|
||||||
struct msgb *msg = msgb_alloc(buf_len, "mgcpgw_test_from_hex");
|
|
||||||
unsigned int l = osmo_hexparse(hex, msg->data, buf_len);
|
|
||||||
msg->l2h = msgb_put(msg, l);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct msgb *mgcp_from_str(const char *head, const char *params)
|
|
||||||
{
|
|
||||||
struct msgb *msg = msgb_alloc(buf_len, "mgcp_from_str");
|
|
||||||
unsigned int l;
|
|
||||||
char *data;
|
|
||||||
l = strlen(head);
|
|
||||||
msg->l2h = msgb_put(msg, l);
|
|
||||||
data = (char*)msgb_l2(msg);
|
|
||||||
strncpy(data, head, l);
|
|
||||||
|
|
||||||
data = (char*)msgb_put(msg, 1);
|
|
||||||
*data = '\n';
|
|
||||||
|
|
||||||
l = strlen(params);
|
|
||||||
data = (char*)msgb_put(msg, l);
|
|
||||||
strncpy(data, params, l);
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct msgb *from_str(const char *str)
|
|
||||||
{
|
|
||||||
struct msgb *msg = msgb_alloc(buf_len, "from_str");
|
|
||||||
unsigned int l = strlen(str);
|
|
||||||
char *data;
|
|
||||||
msg->l2h = msgb_put(msg, l);
|
|
||||||
data = (char*)msgb_l2(msg);
|
|
||||||
strncpy(data, str, l);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct mgcpgw_client_conf conf;
|
|
||||||
struct mgcpgw_client *mgcp = NULL;
|
|
||||||
|
|
||||||
static void reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
|
|
||||||
int conn_id, const char *params)
|
|
||||||
{
|
|
||||||
static char compose[4096 - 128];
|
|
||||||
int len;
|
|
||||||
|
|
||||||
len = snprintf(compose, sizeof(compose),
|
|
||||||
"%d %u %s\r\nI: %d\n\n%s",
|
|
||||||
code, trans_id, comment, conn_id, params);
|
|
||||||
OSMO_ASSERT(len < sizeof(compose));
|
|
||||||
OSMO_ASSERT(len > 0);
|
|
||||||
|
|
||||||
printf("composed response:\n-----\n%s\n-----\n",
|
|
||||||
compose);
|
|
||||||
mgcpgw_client_rx(mgcp, from_str(compose));
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_response_cb(struct mgcp_response *response, void *priv)
|
|
||||||
{
|
|
||||||
OSMO_ASSERT(priv == mgcp);
|
|
||||||
mgcp_response_parse_params(response);
|
|
||||||
|
|
||||||
printf("response cb received:\n"
|
|
||||||
" head.response_code = %d\n"
|
|
||||||
" head.trans_id = %u\n"
|
|
||||||
" head.comment = %s\n"
|
|
||||||
" audio_port = %u\n",
|
|
||||||
response->head.response_code,
|
|
||||||
response->head.trans_id,
|
|
||||||
response->head.comment,
|
|
||||||
response->audio_port
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
|
|
||||||
{
|
|
||||||
mgcp_trans_id_t trans_id;
|
|
||||||
trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID];
|
|
||||||
char *end;
|
|
||||||
|
|
||||||
OSMO_ASSERT(mgcpgw_client_pending_add(mgcp, trans_id, test_response_cb, mgcp));
|
|
||||||
|
|
||||||
end = (char*)msgb_put(msg, 1);
|
|
||||||
*end = '\0';
|
|
||||||
printf("composed:\n-----\n%s\n-----\n",
|
|
||||||
(char*)msgb_l2(msg));
|
|
||||||
|
|
||||||
talloc_free(msg);
|
|
||||||
return trans_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_crcx(void)
|
|
||||||
{
|
|
||||||
struct msgb *msg;
|
|
||||||
mgcp_trans_id_t trans_id;
|
|
||||||
|
|
||||||
printf("\n===== %s =====\n", __func__);
|
|
||||||
|
|
||||||
if (mgcp)
|
|
||||||
talloc_free(mgcp);
|
|
||||||
mgcp = mgcpgw_client_init(ctx, &conf);
|
|
||||||
|
|
||||||
msg = mgcp_msg_crcx(mgcp, 23, 42, MGCP_CONN_LOOPBACK);
|
|
||||||
trans_id = dummy_mgcp_send(msg);
|
|
||||||
|
|
||||||
reply_to(trans_id, 200, "OK", 1,
|
|
||||||
"v=0\r\n"
|
|
||||||
"o=- 1 23 IN IP4 10.9.1.120\r\n"
|
|
||||||
"s=-\r\n"
|
|
||||||
"c=IN IP4 10.9.1.120\r\n"
|
|
||||||
"t=0 0\r\n"
|
|
||||||
"m=audio 16002 RTP/AVP 98\r\n"
|
|
||||||
"a=rtpmap:98 AMR/8000\r\n"
|
|
||||||
"a=ptime:20\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
ctx = talloc_named_const(NULL, 1, "mgcpgw_client_test");
|
|
||||||
msgb_talloc_ctx_init(ctx, 0);
|
|
||||||
osmo_init_logging(&log_info);
|
|
||||||
|
|
||||||
mgcpgw_client_conf_init(&conf);
|
|
||||||
|
|
||||||
test_crcx();
|
|
||||||
|
|
||||||
printf("Done\n");
|
|
||||||
fprintf(stderr, "Done\n");
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
Done
|
|
|
@ -1,31 +0,0 @@
|
||||||
|
|
||||||
===== test_crcx =====
|
|
||||||
composed:
|
|
||||||
-----
|
|
||||||
CRCX 1 17@mgw MGCP 1.0
|
|
||||||
C: 2a
|
|
||||||
L: p:20, a:AMR, nt:IN
|
|
||||||
M: loopback
|
|
||||||
|
|
||||||
-----
|
|
||||||
composed response:
|
|
||||||
-----
|
|
||||||
200 1 OK
|
|
||||||
I: 1
|
|
||||||
|
|
||||||
v=0
|
|
||||||
o=- 1 23 IN IP4 10.9.1.120
|
|
||||||
s=-
|
|
||||||
c=IN IP4 10.9.1.120
|
|
||||||
t=0 0
|
|
||||||
m=audio 16002 RTP/AVP 98
|
|
||||||
a=rtpmap:98 AMR/8000
|
|
||||||
a=ptime:20
|
|
||||||
|
|
||||||
-----
|
|
||||||
response cb received:
|
|
||||||
head.response_code = 200
|
|
||||||
head.trans_id = 1
|
|
||||||
head.comment = OK
|
|
||||||
audio_port = 16002
|
|
||||||
Done
|
|
|
@ -15,6 +15,7 @@ AM_CFLAGS = \
|
||||||
$(LIBOSMOSIGTRAN_CFLAGS) \
|
$(LIBOSMOSIGTRAN_CFLAGS) \
|
||||||
$(LIBOSMORANAP_CFLAGS) \
|
$(LIBOSMORANAP_CFLAGS) \
|
||||||
$(LIBASN1C_CFLAGS) \
|
$(LIBASN1C_CFLAGS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_CFLAGS) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
|
@ -48,7 +49,6 @@ COMMON_LDADD = \
|
||||||
$(top_builddir)/src/libvlr/libvlr.a \
|
$(top_builddir)/src/libvlr/libvlr.a \
|
||||||
$(top_builddir)/src/libbsc/libbsc.a \
|
$(top_builddir)/src/libbsc/libbsc.a \
|
||||||
$(top_builddir)/src/libtrau/libtrau.a \
|
$(top_builddir)/src/libtrau/libtrau.a \
|
||||||
$(top_builddir)/src/libmgcp/libmgcp.a \
|
|
||||||
$(top_builddir)/src/libcommon/libcommon.a \
|
$(top_builddir)/src/libcommon/libcommon.a \
|
||||||
$(LIBSMPP34_LIBS) \
|
$(LIBSMPP34_LIBS) \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
@ -59,6 +59,8 @@ COMMON_LDADD = \
|
||||||
$(LIBOSMOSIGTRAN_LIBS) \
|
$(LIBOSMOSIGTRAN_LIBS) \
|
||||||
$(LIBOSMORANAP_LIBS) \
|
$(LIBOSMORANAP_LIBS) \
|
||||||
$(LIBASN1C_LIBS) \
|
$(LIBASN1C_LIBS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_LIBS) \
|
||||||
|
$(LIBRARY_GSM) \
|
||||||
-ldbi \
|
-ldbi \
|
||||||
-lrt \
|
-lrt \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -14,6 +14,7 @@ AM_CFLAGS = \
|
||||||
$(LIBOSMOSIGTRAN_CFLAGS) \
|
$(LIBOSMOSIGTRAN_CFLAGS) \
|
||||||
$(LIBOSMORANAP_CFLAGS) \
|
$(LIBOSMORANAP_CFLAGS) \
|
||||||
$(LIBASN1C_CFLAGS) \
|
$(LIBASN1C_CFLAGS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_CFLAGS) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
|
@ -34,7 +35,6 @@ sms_queue_test_LDADD = \
|
||||||
$(top_builddir)/src/libvlr/libvlr.a \
|
$(top_builddir)/src/libvlr/libvlr.a \
|
||||||
$(top_builddir)/src/libbsc/libbsc.a \
|
$(top_builddir)/src/libbsc/libbsc.a \
|
||||||
$(top_builddir)/src/libtrau/libtrau.a \
|
$(top_builddir)/src/libtrau/libtrau.a \
|
||||||
$(top_builddir)/src/libmgcp/libmgcp.a \
|
|
||||||
$(top_builddir)/src/libcommon/libcommon.a \
|
$(top_builddir)/src/libcommon/libcommon.a \
|
||||||
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
|
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
|
||||||
$(LIBSMPP34_LIBS) \
|
$(LIBSMPP34_LIBS) \
|
||||||
|
@ -46,6 +46,8 @@ sms_queue_test_LDADD = \
|
||||||
$(LIBOSMOSIGTRAN_LIBS) \
|
$(LIBOSMOSIGTRAN_LIBS) \
|
||||||
$(LIBOSMORANAP_LIBS) \
|
$(LIBOSMORANAP_LIBS) \
|
||||||
$(LIBASN1C_LIBS) \
|
$(LIBASN1C_LIBS) \
|
||||||
|
$(LIBOSMOLEGACYMGCP_LIBS) \
|
||||||
|
$(LIBRARY_GSM) \
|
||||||
-ldbi \
|
-ldbi \
|
||||||
-lrt \
|
-lrt \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -20,26 +20,6 @@ cat $abs_srcdir/channel/channel_test.ok > expout
|
||||||
AT_CHECK([$abs_top_builddir/tests/channel/channel_test], [], [expout], [ignore])
|
AT_CHECK([$abs_top_builddir/tests/channel/channel_test], [], [expout], [ignore])
|
||||||
AT_CLEANUP
|
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
|
|
||||||
|
|
||||||
AT_SETUP([mgcp-trans])
|
|
||||||
AT_KEYWORDS([mgcp-trans])
|
|
||||||
AT_CHECK([test "$enable_mgcp_transcoding_test" == yes || exit 77])
|
|
||||||
cat $abs_srcdir/mgcp/mgcp_transcoding_test.ok > expout
|
|
||||||
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcp_transcoding_test], [], [expout], [ignore])
|
|
||||||
AT_CLEANUP
|
|
||||||
|
|
||||||
AT_SETUP([mgcpgw_client])
|
|
||||||
AT_KEYWORDS([mgcpgw_client])
|
|
||||||
cat $abs_srcdir/mgcp/mgcpgw_client_test.ok > expout
|
|
||||||
cat $abs_srcdir/mgcp/mgcpgw_client_test.err > experr
|
|
||||||
AT_CHECK([$abs_top_builddir/tests/mgcp/mgcpgw_client_test], [], [expout], [experr])
|
|
||||||
AT_CLEANUP
|
|
||||||
|
|
||||||
AT_SETUP([gprs])
|
AT_SETUP([gprs])
|
||||||
AT_KEYWORDS([gprs])
|
AT_KEYWORDS([gprs])
|
||||||
cat $abs_srcdir/gprs/gprs_test.ok > expout
|
cat $abs_srcdir/gprs/gprs_test.ok > expout
|
||||||
|
|
|
@ -71,64 +71,6 @@ class TestVTYBase(unittest.TestCase):
|
||||||
self.vty = None
|
self.vty = None
|
||||||
osmoutil.end_proc(self.proc)
|
osmoutil.end_proc(self.proc)
|
||||||
|
|
||||||
class TestVTYMGCP(TestVTYBase):
|
|
||||||
def vty_command(self):
|
|
||||||
return ["./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "-c",
|
|
||||||
"doc/examples/osmo-bsc_mgcp/mgcp.cfg"]
|
|
||||||
|
|
||||||
def vty_app(self):
|
|
||||||
return (4243, "./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp")
|
|
||||||
|
|
||||||
def testForcePtime(self):
|
|
||||||
self.vty.enable()
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assert_(res.find(' rtp force-ptime 20\r') > 0)
|
|
||||||
self.assertEquals(res.find(' no rtp force-ptime\r'), -1)
|
|
||||||
|
|
||||||
self.vty.command("configure terminal")
|
|
||||||
self.vty.command("mgcp")
|
|
||||||
self.vty.command("no rtp force-ptime")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assertEquals(res.find(' rtp force-ptime 20\r'), -1)
|
|
||||||
self.assertEquals(res.find(' no rtp force-ptime\r'), -1)
|
|
||||||
|
|
||||||
def testOmitAudio(self):
|
|
||||||
self.vty.enable()
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assert_(res.find(' sdp audio-payload send-name\r') > 0)
|
|
||||||
self.assertEquals(res.find(' no sdp audio-payload send-name\r'), -1)
|
|
||||||
|
|
||||||
self.vty.command("configure terminal")
|
|
||||||
self.vty.command("mgcp")
|
|
||||||
self.vty.command("no sdp audio-payload send-name")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assertEquals(res.find(' rtp sdp audio-payload send-name\r'), -1)
|
|
||||||
self.assert_(res.find(' no sdp audio-payload send-name\r') > 0)
|
|
||||||
|
|
||||||
# TODO: test it for the trunk!
|
|
||||||
|
|
||||||
def testBindAddr(self):
|
|
||||||
self.vty.enable()
|
|
||||||
|
|
||||||
self.vty.command("configure terminal")
|
|
||||||
self.vty.command("mgcp")
|
|
||||||
|
|
||||||
# enable.. disable bts-bind-ip
|
|
||||||
self.vty.command("rtp bts-bind-ip 254.253.252.250")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assert_(res.find('rtp bts-bind-ip 254.253.252.250') > 0)
|
|
||||||
self.vty.command("no rtp bts-bind-ip")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assertEquals(res.find(' rtp bts-bind-ip'), -1)
|
|
||||||
|
|
||||||
# enable.. disable net-bind-ip
|
|
||||||
self.vty.command("rtp net-bind-ip 254.253.252.250")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assert_(res.find('rtp net-bind-ip 254.253.252.250') > 0)
|
|
||||||
self.vty.command("no rtp net-bind-ip")
|
|
||||||
res = self.vty.command("show running-config")
|
|
||||||
self.assertEquals(res.find(' rtp net-bind-ip'), -1)
|
|
||||||
|
|
||||||
|
|
||||||
class TestVTYGenericBSC(TestVTYBase):
|
class TestVTYGenericBSC(TestVTYBase):
|
||||||
|
|
||||||
|
@ -1152,7 +1094,6 @@ if __name__ == '__main__':
|
||||||
os.chdir(workdir)
|
os.chdir(workdir)
|
||||||
print "Running tests for specific VTY commands"
|
print "Running tests for specific VTY commands"
|
||||||
suite = unittest.TestSuite()
|
suite = unittest.TestSuite()
|
||||||
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestVTYMGCP))
|
|
||||||
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestVTYMSC))
|
suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestVTYMSC))
|
||||||
add_bsc_test(suite, workdir)
|
add_bsc_test(suite, workdir)
|
||||||
add_nat_test(suite, workdir)
|
add_nat_test(suite, workdir)
|
||||||
|
|
Loading…
Reference in New Issue