mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-11-02 13:03:33 +00:00
Compare commits
25 Commits
1.3.0
...
stsp/show_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3c2eaeec0 | ||
|
|
704c4f0adf | ||
|
|
bc0346e080 | ||
|
|
5928dc9345 | ||
|
|
04da5e5e98 | ||
|
|
54b4f82f91 | ||
|
|
3d7b58d77a | ||
|
|
604410cd13 | ||
|
|
b340f90c9e | ||
|
|
3c8ccb6724 | ||
|
|
d4e6aa42ca | ||
|
|
e6df0e47e7 | ||
|
|
54eb0e1204 | ||
|
|
b38fb8911f | ||
|
|
1b3a385b9d | ||
|
|
dbd70c7b68 | ||
|
|
a19547b7a1 | ||
|
|
06823731d8 | ||
|
|
ed1cff5ab9 | ||
|
|
f2321b7a72 | ||
|
|
4219904cb2 | ||
|
|
b2753f2044 | ||
|
|
ba61f68137 | ||
|
|
9e1d164469 | ||
|
|
0ec1d4e17c |
@@ -243,6 +243,12 @@ struct mgcp_config {
|
||||
* message.
|
||||
*/
|
||||
uint16_t osmux_dummy;
|
||||
|
||||
/* Use a jitterbuffer on the bts-side receiver */
|
||||
bool bts_use_jibuf;
|
||||
/* Minimum and maximum buffer size for the jitter buffer, in ms */
|
||||
uint32_t bts_jitter_delay_min;
|
||||
uint32_t bts_jitter_delay_max;
|
||||
};
|
||||
|
||||
/* config management */
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/netif/jibuf.h>
|
||||
|
||||
#define CI_UNUSED 0
|
||||
|
||||
@@ -198,6 +199,14 @@ struct mgcp_endpoint {
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
} osmux;
|
||||
|
||||
/* Jitter buffer */
|
||||
struct osmo_jibuf* bts_jb;
|
||||
/* Use a jitterbuffer on the bts-side receiver */
|
||||
bool bts_use_jibuf;
|
||||
/* Minimum and maximum buffer size for the jitter buffer, in ms */
|
||||
uint32_t bts_jitter_delay_min;
|
||||
uint32_t bts_jitter_delay_max;
|
||||
};
|
||||
|
||||
#define for_each_line(line, save) \
|
||||
@@ -335,3 +344,8 @@ static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp)
|
||||
}
|
||||
|
||||
int mgcp_msg_terminate_nul(struct msgb *msg);
|
||||
|
||||
/**
|
||||
* Internal jitter buffer related
|
||||
*/
|
||||
void mgcp_dejitter_udp_send(struct msgb *msg, void *data);
|
||||
|
||||
@@ -5,5 +5,6 @@ noinst_HEADERS = \
|
||||
mgcp_stat.h \
|
||||
mgcp_endp.h \
|
||||
mgcp_sdp.h \
|
||||
mgcp_codec.h \
|
||||
debug.h \
|
||||
$(NULL)
|
||||
|
||||
6
include/osmocom/mgcp/mgcp_codec.h
Normal file
6
include/osmocom/mgcp/mgcp_codec.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
void mgcp_codec_summary(struct mgcp_conn_rtp *conn);
|
||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn);
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name);
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn);
|
||||
@@ -82,4 +82,8 @@ static inline int mgcp_msg_terminate_nul(struct msgb *msg)
|
||||
/* A prefix to denote the virtual trunk (RTP on both ends) */
|
||||
#define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/"
|
||||
|
||||
/* Maximal number of payload types / codecs that can be negotiated via SDP at
|
||||
* at once. */
|
||||
#define MGCP_MAX_CODECS 10
|
||||
|
||||
#endif
|
||||
|
||||
@@ -66,7 +66,7 @@ struct mgcp_endpoint {
|
||||
/*!< Call identifier string (as supplied by the call agant) */
|
||||
char *callid;
|
||||
|
||||
/*!< Local connection options (see mgcp_intermal.h) */
|
||||
/*!< Local connection options (see mgcp_internal.h) */
|
||||
struct mgcp_lco local_options;
|
||||
|
||||
/*!< List with connections active on this endpoint */
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <osmocom/mgcp/mgcp.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/counter.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#define CI_UNUSED 0
|
||||
|
||||
@@ -45,7 +46,7 @@ struct mgcp_rtp_stream_state {
|
||||
uint32_t ssrc;
|
||||
uint16_t last_seq;
|
||||
uint32_t last_timestamp;
|
||||
uint32_t err_ts_counter;
|
||||
struct rate_ctr *err_ts_ctr;
|
||||
int32_t last_tsdelta;
|
||||
uint32_t last_arrival_time;
|
||||
};
|
||||
@@ -113,13 +114,19 @@ struct mgcp_rtp_end {
|
||||
/* 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 */
|
||||
/* currently selected audio codec */
|
||||
struct mgcp_rtp_codec *codec;
|
||||
|
||||
/* array with assigned audio codecs to choose from (SDP) */
|
||||
struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS];
|
||||
|
||||
/* number of assigned audio codecs (SDP) */
|
||||
unsigned int codecs_assigned;
|
||||
|
||||
/* per endpoint data */
|
||||
int frames_per_packet;
|
||||
uint32_t packet_duration_ms;
|
||||
int maximum_packet_time; /* -1: not set */
|
||||
char *fmtp_extra;
|
||||
/* are we transmitting packets (1) or dropping (0) outbound packets */
|
||||
int output_enabled;
|
||||
@@ -202,6 +209,8 @@ struct mgcp_conn_rtp {
|
||||
uint32_t octets;
|
||||
} stats;
|
||||
} osmux;
|
||||
|
||||
struct rate_ctr_group *rate_ctr_group;
|
||||
};
|
||||
|
||||
/*! Connection type, specifies which member of the union "u" in mgcp_conn
|
||||
@@ -280,6 +289,8 @@ static inline int endp_back_channel(int endpoint)
|
||||
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);
|
||||
|
||||
char *get_lco_identifier(const char *options);
|
||||
int check_local_cx_options(void *ctx, const char *options);
|
||||
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,
|
||||
|
||||
@@ -21,15 +21,11 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
|
||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn,
|
||||
struct mgcp_parse_data *p);
|
||||
|
||||
int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
int payload_type, const char *audio_name);
|
||||
|
||||
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
const struct mgcp_conn_rtp *conn, struct msgb *sdp,
|
||||
const char *addr);
|
||||
|
||||
@@ -26,6 +26,33 @@ struct mgcp_client_conf {
|
||||
|
||||
typedef unsigned int mgcp_trans_id_t;
|
||||
|
||||
/*! Enumeration of the codec types that mgcp_client is able to handle. */
|
||||
enum mgcp_codecs {
|
||||
CODEC_PCMU_8000_1 = 0,
|
||||
CODEC_GSM_8000_1 = 3,
|
||||
CODEC_PCMA_8000_1 = 8,
|
||||
CODEC_G729_8000_1 = 18,
|
||||
CODEC_GSMEFR_8000_1 = 110,
|
||||
CODEC_GSMHR_8000_1 = 111,
|
||||
CODEC_AMR_8000_1 = 112,
|
||||
CODEC_AMRWB_16000_1 = 113,
|
||||
};
|
||||
/* Note: when new codec types are added, the corresponding value strings
|
||||
* in mgcp_client.c (codec_table) must be updated as well. Enumerations
|
||||
* in enum mgcp_codecs must correspond to a valid payload type. However,
|
||||
* this is an internal assumption that is made to avoid lookup tables.
|
||||
* The API-User should not rely on this coincidence! */
|
||||
|
||||
/*! Structure to build a payload type map to allow the defiition custom payload
|
||||
* types. */
|
||||
struct ptmap {
|
||||
/*!< codec for which a payload type number should be defined */
|
||||
enum mgcp_codecs codec;
|
||||
|
||||
/*!< payload type number (96-127) */
|
||||
unsigned int pt;
|
||||
};
|
||||
|
||||
struct mgcp_response_head {
|
||||
int response_code;
|
||||
mgcp_trans_id_t trans_id;
|
||||
@@ -39,6 +66,11 @@ struct mgcp_response {
|
||||
struct mgcp_response_head head;
|
||||
uint16_t audio_port;
|
||||
char audio_ip[INET_ADDRSTRLEN];
|
||||
unsigned int ptime;
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_len;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
};
|
||||
|
||||
enum mgcp_verb {
|
||||
@@ -66,6 +98,11 @@ struct mgcp_msg {
|
||||
uint16_t audio_port;
|
||||
char *audio_ip;
|
||||
enum mgcp_connection_mode conn_mode;
|
||||
unsigned int ptime;
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_len;
|
||||
struct ptmap ptmap[MGCP_MAX_CODECS];
|
||||
unsigned int ptmap_len;
|
||||
};
|
||||
|
||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf);
|
||||
@@ -117,3 +154,9 @@ static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode)
|
||||
{
|
||||
return get_value_string(mgcp_client_connection_mode_strs, mode);
|
||||
}
|
||||
|
||||
enum mgcp_codecs map_str_to_codec(const char *str);
|
||||
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
enum mgcp_codecs codec);
|
||||
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
unsigned int pt);
|
||||
|
||||
@@ -25,9 +25,20 @@ struct mgcp_conn_peer {
|
||||
|
||||
/*!< CALL ID (unique per connection) */
|
||||
unsigned int call_id;
|
||||
|
||||
/*!< RTP packetization interval (optional) */
|
||||
unsigned int ptime;
|
||||
|
||||
/*!< RTP codec list (optional) */
|
||||
enum mgcp_codecs codecs[MGCP_MAX_CODECS];
|
||||
|
||||
/*!< Number of codecs in RTP codec list (optional) */
|
||||
unsigned int codecs_len;
|
||||
};
|
||||
|
||||
struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt,
|
||||
uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
|
||||
int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
|
||||
void mgcp_conn_delete(struct osmo_fsm_inst *fi);
|
||||
|
||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi);
|
||||
|
||||
@@ -584,6 +584,36 @@ static int mgcp_send_transcoder(struct mgcp_rtp_end *end,
|
||||
return rc;
|
||||
}
|
||||
|
||||
void mgcp_dejitter_udp_send(struct msgb *msg, void *data)
|
||||
{
|
||||
struct mgcp_rtp_end *rtp_end = (struct mgcp_rtp_end *) data;
|
||||
|
||||
int rc = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr,
|
||||
rtp_end->rtp_port, (char*) msg->data, msg->len);
|
||||
if (rc != msg->len)
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to send data after jitter buffer: %d\n", rc);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static int enqueue_dejitter(struct osmo_jibuf *jb, struct mgcp_rtp_end *rtp_end, char *buf, int len)
|
||||
{
|
||||
struct msgb *msg;
|
||||
msg = msgb_alloc(len, "mgcp-jibuf");
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
memcpy(msg->data, buf, len);
|
||||
msgb_put(msg, len);
|
||||
|
||||
if (osmo_jibuf_enqueue(jb, msg) < 0) {
|
||||
rtp_end->dropped_packets += 1;
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
|
||||
struct sockaddr_in *addr, char *buf, int rc)
|
||||
{
|
||||
@@ -591,6 +621,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
|
||||
struct mgcp_rtp_end *rtp_end;
|
||||
struct mgcp_rtp_state *rtp_state;
|
||||
int tap_idx;
|
||||
struct osmo_jibuf *jb;
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint %x dest %s tcfg->audio_loop %d endp->conn_mode %d (== loopback: %d)\n",
|
||||
@@ -612,10 +643,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
|
||||
rtp_end = &endp->net_end;
|
||||
rtp_state = &endp->bts_state;
|
||||
tap_idx = MGCP_TAP_NET_OUT;
|
||||
jb = endp->bts_jb;
|
||||
} else {
|
||||
rtp_end = &endp->bts_end;
|
||||
rtp_state = &endp->net_state;
|
||||
tap_idx = MGCP_TAP_BTS_OUT;
|
||||
jb = NULL;
|
||||
}
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"endpoint %x dest %s net_end %s %d %d bts_end %s %d %d rtp_end %s %d %d\n",
|
||||
@@ -680,9 +713,12 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp,
|
||||
rtp_state->patched_first_rtp_payload = true;
|
||||
}
|
||||
|
||||
rc = mgcp_udp_send(rtp_end->rtp.fd,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtp_port, buf, len);
|
||||
if (jb)
|
||||
rc = enqueue_dejitter(jb, rtp_end, buf, len);
|
||||
else
|
||||
rc = mgcp_udp_send(rtp_end->rtp.fd,
|
||||
&rtp_end->addr,
|
||||
rtp_end->rtp_port, buf, len);
|
||||
|
||||
if (rc <= 0)
|
||||
return rc;
|
||||
|
||||
@@ -267,7 +267,6 @@ 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;
|
||||
@@ -297,8 +296,7 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
endp->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
|
||||
osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
@@ -359,7 +357,6 @@ 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;
|
||||
@@ -389,8 +386,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
endp->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &endp->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
|
||||
osmux_xfrm_output_sched(&endp->osmux.out, osmuxh);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
@@ -470,9 +466,13 @@ int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint
|
||||
switch (endp->cfg->role) {
|
||||
case MGCP_BSC_NAT:
|
||||
endp->type = MGCP_OSMUX_BSC_NAT;
|
||||
osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
|
||||
scheduled_tx_net_cb, endp);
|
||||
break;
|
||||
case MGCP_BSC:
|
||||
endp->type = MGCP_OSMUX_BSC;
|
||||
osmux_xfrm_output_set_tx_cb(&endp->osmux.out,
|
||||
scheduled_tx_bts_cb, endp);
|
||||
break;
|
||||
}
|
||||
endp->osmux.state = OSMUX_STATE_ENABLED;
|
||||
@@ -484,6 +484,11 @@ void osmux_disable_endpoint(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n",
|
||||
ENDPOINT_NUMBER(endp), endp->osmux.cid);
|
||||
|
||||
/* We are closing, we don't need pending RTP packets to be transmitted */
|
||||
osmux_xfrm_output_set_tx_cb(&endp->osmux.out, NULL, NULL);
|
||||
osmux_xfrm_output_flush(&endp->osmux.out);
|
||||
|
||||
osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid);
|
||||
endp->osmux.state = OSMUX_STATE_DISABLED;
|
||||
endp->osmux.cid = -1;
|
||||
|
||||
@@ -863,6 +863,11 @@ mgcp_header_done:
|
||||
goto error2;
|
||||
}
|
||||
|
||||
/* Apply Jiter buffer settings for this endpoint, they can be overriden by CRCX policy later */
|
||||
endp->bts_use_jibuf = endp->cfg->bts_use_jibuf;
|
||||
endp->bts_jitter_delay_min = endp->cfg->bts_jitter_delay_min;
|
||||
endp->bts_jitter_delay_max = endp->cfg->bts_jitter_delay_max;
|
||||
|
||||
endp->allocated = 1;
|
||||
|
||||
/* set up RTP media parameters */
|
||||
@@ -898,6 +903,13 @@ mgcp_header_done:
|
||||
case MGCP_POLICY_DEFER:
|
||||
/* stop processing */
|
||||
create_transcoder(endp);
|
||||
/* Set up jitter buffer if required after policy has updated jibuf endp values */
|
||||
if (endp->bts_use_jibuf) {
|
||||
endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints);
|
||||
osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min);
|
||||
osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max);
|
||||
osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end);
|
||||
}
|
||||
return NULL;
|
||||
break;
|
||||
case MGCP_POLICY_CONT:
|
||||
@@ -906,6 +918,14 @@ mgcp_header_done:
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up jitter buffer if required after policy has updated jibuf endp values */
|
||||
if (endp->bts_use_jibuf) {
|
||||
endp->bts_jb = osmo_jibuf_alloc(tcfg->endpoints);
|
||||
osmo_jibuf_set_min_delay(endp->bts_jb, endp->bts_jitter_delay_min);
|
||||
osmo_jibuf_set_max_delay(endp->bts_jb, endp->bts_jitter_delay_max);
|
||||
osmo_jibuf_set_dequeue_cb(endp->bts_jb, mgcp_dejitter_udp_send, &endp->net_end);
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n",
|
||||
ENDPOINT_NUMBER(endp), endp->ci,
|
||||
endp->net_end.local_port, endp->bts_end.local_port);
|
||||
@@ -1373,6 +1393,9 @@ int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
|
||||
void mgcp_release_endp(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
|
||||
if (endp->bts_jb)
|
||||
osmo_jibuf_delete(endp->bts_jb);
|
||||
endp->bts_jb = NULL;
|
||||
endp->ci = CI_UNUSED;
|
||||
endp->allocated = 0;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <osmocom/legacy_mgcp/vty.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
|
||||
#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
|
||||
@@ -164,6 +165,13 @@ static int config_write_mgcp(struct vty *vty)
|
||||
vty_out(vty, " osmux dummy %s%s",
|
||||
g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE);
|
||||
}
|
||||
if (g_cfg->bts_use_jibuf)
|
||||
vty_out(vty, " bts-jitter-buffer%s", VTY_NEWLINE);
|
||||
if (g_cfg->bts_jitter_delay_min)
|
||||
vty_out(vty, " bts-jitter-delay-min %"PRIu32"%s", g_cfg->bts_jitter_delay_min, VTY_NEWLINE);
|
||||
if (g_cfg->bts_jitter_delay_max)
|
||||
vty_out(vty, " bts-jitter-delay-max %"PRIu32"%s", g_cfg->bts_jitter_delay_max, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -241,6 +249,11 @@ DEFUN(show_mcgp, show_mgcp_cmd,
|
||||
|
||||
if (g_cfg->osmux)
|
||||
vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(), VTY_NEWLINE);
|
||||
vty_out(vty, "Jitter Buffer by default on Uplink : %s%s",
|
||||
g_cfg->bts_use_jibuf ? "on" : "off", VTY_NEWLINE);
|
||||
if (g_cfg->bts_use_jibuf)
|
||||
vty_out(vty, "Jitter Buffer delays: min=%"PRIu32" max=%"PRIu32"%s",
|
||||
g_cfg->bts_jitter_delay_min, g_cfg->bts_jitter_delay_max, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -1344,6 +1357,63 @@ DEFUN(cfg_mgcp_osmux_dummy,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define DEJITTER_STR "Uplink Jitter Buffer"
|
||||
DEFUN(cfg_mgcp_bts_use_jibuf,
|
||||
cfg_mgcp_bts_use_jibuf_cmd,
|
||||
"bts-jitter-buffer",
|
||||
DEJITTER_STR "\n")
|
||||
{
|
||||
g_cfg->bts_use_jibuf = true;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_no_bts_use_jibuf,
|
||||
cfg_mgcp_no_bts_use_jibuf_cmd,
|
||||
"no bts-jitter-buffer",
|
||||
NO_STR DEJITTER_STR "\n")
|
||||
{
|
||||
g_cfg->bts_use_jibuf = false;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_bts_jitter_delay_min,
|
||||
cfg_mgcp_bts_jitter_delay_min_cmd,
|
||||
"bts-jitter-buffer-delay-min <1-65535>",
|
||||
DEJITTER_STR " Minimum Delay in ms\n" "Minimum Delay in ms\n")
|
||||
{
|
||||
g_cfg->bts_jitter_delay_min = atoi(argv[0]);
|
||||
if (!g_cfg->bts_jitter_delay_min) {
|
||||
vty_out(vty, "bts-jitter-buffer-delay-min cannot be zero.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max &&
|
||||
g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) {
|
||||
vty_out(vty, "bts-jitter-buffer-delay-min cannot be bigger than " \
|
||||
"bts-jitter-buffer-delay-max.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_bts_jitter_delay_max,
|
||||
cfg_mgcp_bts_jitter_delay_max_cmd,
|
||||
"bts-jitter-buffer-delay-max <1-65535>",
|
||||
DEJITTER_STR " Maximum Delay in ms\n" "Maximum Delay in ms\n")
|
||||
{
|
||||
g_cfg->bts_jitter_delay_max = atoi(argv[0]);
|
||||
if (!g_cfg->bts_jitter_delay_max) {
|
||||
vty_out(vty, "bts-jitter-buffer-delay-max cannot be zero.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
if (g_cfg->bts_jitter_delay_min && g_cfg->bts_jitter_delay_max &&
|
||||
g_cfg->bts_jitter_delay_min > g_cfg->bts_jitter_delay_max) {
|
||||
vty_out(vty, "bts-jitter-buffer-delay-max cannot be smaller than " \
|
||||
"bts-jitter-buffer-delay-min.%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int mgcp_vty_init(void)
|
||||
{
|
||||
install_element_ve(&show_mgcp_cmd);
|
||||
@@ -1411,6 +1481,10 @@ int mgcp_vty_init(void)
|
||||
install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_bts_use_jibuf_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_no_bts_use_jibuf_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_min_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_bts_jitter_delay_max_cmd);
|
||||
|
||||
|
||||
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
|
||||
|
||||
@@ -36,6 +36,150 @@
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Codec descripton for dynamic payload types (SDP) */
|
||||
static const struct value_string codec_table[] = {
|
||||
{ CODEC_PCMU_8000_1, "PCMU/8000/1" },
|
||||
{ CODEC_GSM_8000_1, "GSM/8000/1" },
|
||||
{ CODEC_PCMA_8000_1, "PCMA/8000/1" },
|
||||
{ CODEC_G729_8000_1, "G729/8000/1" },
|
||||
{ CODEC_GSMEFR_8000_1, "GSM-EFR/8000/1" },
|
||||
{ CODEC_GSMHR_8000_1, "GSM-HR-08/8000/1" },
|
||||
{ CODEC_AMR_8000_1, "AMR/8000/1" },
|
||||
{ CODEC_AMRWB_16000_1, "AMR-WB/16000/1" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
/* Get encoding name from a full codec string e,g.
|
||||
* ("CODEC/8000/2" => returns "CODEC") */
|
||||
static char *extract_codec_name(const char *str)
|
||||
{
|
||||
static char buf[64];
|
||||
unsigned int i;
|
||||
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
||||
/* FIXME osmo_strlcpy */
|
||||
osmo_strlcpy(buf, str, sizeof(buf));
|
||||
|
||||
for (i = 0; i < strlen(buf); i++) {
|
||||
if (buf[i] == '/')
|
||||
buf[i] = '\0';
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*! Map a string to a codec.
|
||||
* \ptmap[in] str input string (e.g "GSM/8000/1", "GSM/8000" or "GSM")
|
||||
* \returns codec that corresponds to the given string representation. */
|
||||
enum mgcp_codecs map_str_to_codec(const char *str)
|
||||
{
|
||||
unsigned int i;
|
||||
char *codec_name;
|
||||
char str_buf[64];
|
||||
|
||||
osmo_strlcpy(str_buf, extract_codec_name(str), sizeof(str_buf));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(codec_table); i++) {
|
||||
codec_name = extract_codec_name(codec_table[i].str);
|
||||
if (!codec_name)
|
||||
continue;
|
||||
if (strcmp(codec_name, str_buf) == 0)
|
||||
return codec_table[i].value;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check the ptmap for illegal mappings */
|
||||
static int check_ptmap(struct ptmap *ptmap)
|
||||
{
|
||||
/* Check if there are mappings that leave the IANA assigned dynamic
|
||||
* payload type range. Under normal conditions such mappings should
|
||||
* not occur */
|
||||
|
||||
/* Its ok to have a 1:1 mapping in the statically defined
|
||||
* range, this won't hurt */
|
||||
if (ptmap->codec == ptmap->pt)
|
||||
return 0;
|
||||
|
||||
if (ptmap->codec < 96 || ptmap->codec > 127)
|
||||
goto error;
|
||||
if (ptmap->pt < 96 || ptmap->pt > 127)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"ptmap contains illegal mapping: codec=%u maps to pt=%u\n",
|
||||
ptmap->codec, ptmap->pt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Map a codec to a payload type.
|
||||
* \ptmap[in] payload pointer to payload type map with specified payload types.
|
||||
* \ptmap[in] ptmap_len length of the payload type map.
|
||||
* \ptmap[in] codec the codec for which the payload type should be looked up.
|
||||
* \returns assigned payload type */
|
||||
unsigned int map_codec_to_pt(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
enum mgcp_codecs codec)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/*! Note: If the payload type map is empty or the codec is not found
|
||||
* in the map, then a 1:1 mapping is performed. If the codec falls
|
||||
* into the statically defined range or if the mapping table isself
|
||||
* tries to map to the statically defined range, then the mapping
|
||||
* is also ignored and a 1:1 mapping is performed instead. */
|
||||
|
||||
/* we may return the codec directly since enum mgcp_codecs directly
|
||||
* corresponds to the statićally assigned payload types */
|
||||
if (codec < 96 || codec > 127)
|
||||
return codec;
|
||||
|
||||
for (i = 0; i < ptmap_len; i++) {
|
||||
/* Skip illegal map entries */
|
||||
if (check_ptmap(ptmap) == 0 && ptmap->codec == codec)
|
||||
return ptmap->pt;
|
||||
ptmap++;
|
||||
}
|
||||
|
||||
/* If nothing is found, do not perform any mapping */
|
||||
return codec;
|
||||
}
|
||||
|
||||
/*! Map a payload type to a codec.
|
||||
* \ptmap[in] payload pointer to payload type map with specified payload types.
|
||||
* \ptmap[in] ptmap_len length of the payload type map.
|
||||
* \ptmap[in] payload type for which the codec should be looked up.
|
||||
* \returns codec that corresponds to the specified payload type */
|
||||
enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len,
|
||||
unsigned int pt)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/*! Note: If the payload type map is empty or the payload type is not
|
||||
* found in the map, then a 1:1 mapping is performed. If the payload
|
||||
* type falls into the statically defined range or if the mapping
|
||||
* table isself tries to map to the statically defined range, then
|
||||
* the mapping is also ignored and a 1:1 mapping is performed
|
||||
* instead. */
|
||||
|
||||
/* See also note in map_codec_to_pt() */
|
||||
if (pt < 96 || pt > 127)
|
||||
return pt;
|
||||
|
||||
for (i = 0; i < ptmap_len; i++) {
|
||||
if (check_ptmap(ptmap) == 0 && ptmap->pt == pt)
|
||||
return ptmap->codec;
|
||||
ptmap++;
|
||||
}
|
||||
|
||||
/* If nothing is found, do not perform any mapping */
|
||||
return pt;
|
||||
}
|
||||
|
||||
/*! Initalize MGCP client configuration struct with default values.
|
||||
* \param[out] conf Client configuration.*/
|
||||
void mgcp_client_conf_init(struct mgcp_client_conf *conf)
|
||||
@@ -178,22 +322,114 @@ static bool mgcp_line_is_valid(const char *line)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Parse a line like "m=audio 16002 RTP/AVP 98" */
|
||||
static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
|
||||
/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
|
||||
static int mgcp_parse_audio_port_pt(struct mgcp_response *r, char *line)
|
||||
{
|
||||
if (sscanf(line, "m=audio %hu",
|
||||
&r->audio_port) != 1)
|
||||
goto response_parse_failure;
|
||||
char *pt_str;
|
||||
unsigned int pt;
|
||||
unsigned int count = 0;
|
||||
unsigned int i;
|
||||
|
||||
/* Extract port information */
|
||||
if (sscanf(line, "m=audio %hu", &r->audio_port) != 1)
|
||||
goto response_parse_failure_port;
|
||||
if (r->audio_port == 0)
|
||||
goto response_parse_failure;
|
||||
goto response_parse_failure_port;
|
||||
|
||||
/* Extract payload types */
|
||||
line = strstr(line, "RTP/AVP ");
|
||||
if (!line)
|
||||
goto exit;
|
||||
|
||||
pt_str = strtok(line, " ");
|
||||
while (1) {
|
||||
/* Do not allow excessive payload types */
|
||||
if (count > ARRAY_SIZE(r->codecs))
|
||||
goto response_parse_failure_pt;
|
||||
|
||||
pt_str = strtok(NULL, " ");
|
||||
if (!pt_str)
|
||||
break;
|
||||
pt = atoi(pt_str);
|
||||
|
||||
/* Do not allow duplicate payload types */
|
||||
for (i = 0; i < count; i++)
|
||||
if (r->codecs[i] == pt)
|
||||
goto response_parse_failure_pt;
|
||||
|
||||
/* Note: The payload type we store may not necessarly match
|
||||
* the codec types we have defined in enum mgcp_codecs. To
|
||||
* ensure that the end result only contains codec types which
|
||||
* match enum mgcp_codecs, we will go through afterwards and
|
||||
* remap the affected entries with the inrofmation we learn
|
||||
* from rtpmap */
|
||||
r->codecs[count] = pt;
|
||||
count++;
|
||||
}
|
||||
|
||||
r->codecs_len = count;
|
||||
|
||||
exit:
|
||||
return 0;
|
||||
|
||||
response_parse_failure:
|
||||
response_parse_failure_port:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse MGCP response header (audio port)\n");
|
||||
"Failed to parse SDP parameter port (%s)\n", line);
|
||||
return -EINVAL;
|
||||
|
||||
response_parse_failure_pt:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter payload types (%s)\n", line);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse a line like "m=audio 16002 RTP/AVP 98", extract port and payload types */
|
||||
static int mgcp_parse_audio_ptime_rtpmap(struct mgcp_response *r, const char *line)
|
||||
{
|
||||
unsigned int pt;
|
||||
char codec_resp[64];
|
||||
unsigned int codec;
|
||||
|
||||
|
||||
if (strstr(line, "ptime")) {
|
||||
if (sscanf(line, "a=ptime:%u", &r->ptime) != 1)
|
||||
goto response_parse_failure_ptime;
|
||||
} else if (strstr(line, "rtpmap")) {
|
||||
if (sscanf(line, "a=rtpmap:%d %63s", &pt, codec_resp) == 2) {
|
||||
/* The MGW may assign an own payload type in the
|
||||
* response if the choosen codec falls into the IANA
|
||||
* assigned dynamic payload type range (96-127).
|
||||
* Normally the MGW should obey the 3gpp payload type
|
||||
* assignments, which are fixed, so we likely wont see
|
||||
* anything unexpected here. In order to be sure that
|
||||
* we will now check the codec string and if the result
|
||||
* does not match to what is IANA / 3gpp assigned, we
|
||||
* will create an entry in the ptmap table so we can
|
||||
* lookup later what has been assigned. */
|
||||
codec = map_str_to_codec(codec_resp);
|
||||
if (codec != pt) {
|
||||
if (r->ptmap_len < ARRAY_SIZE(r->ptmap)) {
|
||||
r->ptmap[r->ptmap_len].pt = pt;
|
||||
r->ptmap[r->ptmap_len].codec = codec;
|
||||
r->ptmap_len++;
|
||||
} else
|
||||
goto response_parse_failure_rtpmap;
|
||||
}
|
||||
|
||||
} else
|
||||
goto response_parse_failure_rtpmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
response_parse_failure_ptime:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, invalid ptime (%s)\n", line);
|
||||
return -EINVAL;
|
||||
response_parse_failure_rtpmap:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Failed to parse SDP parameter, invalid rtpmap (%s)\n", line);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse a line like "c=IN IP4 10.11.12.13" */
|
||||
@@ -253,6 +489,7 @@ int mgcp_response_parse_params(struct mgcp_response *r)
|
||||
int rc;
|
||||
char *data;
|
||||
char *data_ptr;
|
||||
int i;
|
||||
|
||||
/* Since this functions performs a destructive parsing, we create a
|
||||
* local copy of the body data */
|
||||
@@ -277,8 +514,13 @@ int mgcp_response_parse_params(struct mgcp_response *r)
|
||||
return -EINVAL;
|
||||
|
||||
switch (line[0]) {
|
||||
case 'a':
|
||||
rc = mgcp_parse_audio_ptime_rtpmap(r, line);
|
||||
if (rc)
|
||||
goto exit;
|
||||
break;
|
||||
case 'm':
|
||||
rc = mgcp_parse_audio_port(r, line);
|
||||
rc = mgcp_parse_audio_port_pt(r, line);
|
||||
if (rc)
|
||||
goto exit;
|
||||
break;
|
||||
@@ -293,6 +535,10 @@ int mgcp_response_parse_params(struct mgcp_response *r)
|
||||
}
|
||||
}
|
||||
|
||||
/* See also note in mgcp_parse_audio_port_pt() */
|
||||
for (i = 0; i < r->codecs_len; i++)
|
||||
r->codecs[i] = map_pt_to_codec(r->ptmap, r->ptmap_len, r->codecs[i]);
|
||||
|
||||
rc = 0;
|
||||
exit:
|
||||
talloc_free(data);
|
||||
@@ -813,6 +1059,119 @@ struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
|
||||
#define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
|
||||
#define MGCP_RSIP_MANDATORY 0 /* none */
|
||||
|
||||
/* Helper function for mgcp_msg_gen(): Add LCO information to MGCP message */
|
||||
static int add_lco(struct msgb *msg, struct mgcp_msg *mgcp_msg)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc = 0;
|
||||
const char *codec;
|
||||
unsigned int pt;
|
||||
|
||||
rc += msgb_printf(msg, "L:");
|
||||
|
||||
if (mgcp_msg->ptime)
|
||||
rc += msgb_printf(msg, " p:%u,", mgcp_msg->ptime);
|
||||
|
||||
if (mgcp_msg->codecs_len) {
|
||||
rc += msgb_printf(msg, " a:");
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = mgcp_msg->codecs[i];
|
||||
codec = get_value_string_or_null(codec_table, pt);
|
||||
|
||||
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||
* in mgcp_client only! */
|
||||
OSMO_ASSERT(codec);
|
||||
rc += msgb_printf(msg, "%s", extract_codec_name(codec));
|
||||
if (i < mgcp_msg->codecs_len - 1)
|
||||
rc += msgb_printf(msg, ";");
|
||||
}
|
||||
rc += msgb_printf(msg, ",");
|
||||
}
|
||||
|
||||
rc += msgb_printf(msg, " nt:IN\r\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Helper function for mgcp_msg_gen(): Add SDP information to MGCP message */
|
||||
static int add_sdp(struct msgb *msg, struct mgcp_msg *mgcp_msg, struct mgcp_client *mgcp)
|
||||
{
|
||||
unsigned int i;
|
||||
int rc = 0;
|
||||
char local_ip[INET_ADDRSTRLEN];
|
||||
const char *codec;
|
||||
unsigned int pt;
|
||||
|
||||
/* Add separator to mark the beginning of the SDP block */
|
||||
rc += msgb_printf(msg, "\r\n");
|
||||
|
||||
/* Add SDP protocol version */
|
||||
rc += msgb_printf(msg, "v=0\r\n");
|
||||
|
||||
/* Determine local IP-Address */
|
||||
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Could not determine local IP-Address!\n");
|
||||
msgb_free(msg);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Add owner/creator (SDP) */
|
||||
rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
|
||||
mgcp_msg->call_id, local_ip);
|
||||
|
||||
/* Add session name (none) */
|
||||
rc += msgb_printf(msg, "s=-\r\n");
|
||||
|
||||
/* Add RTP address and port */
|
||||
if (mgcp_msg->audio_port == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Invalid port number, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return -2;
|
||||
}
|
||||
if (strlen(mgcp_msg->audio_ip) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Empty ip address, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return -2;
|
||||
}
|
||||
rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
|
||||
|
||||
/* Add time description, active time (SDP) */
|
||||
rc += msgb_printf(msg, "t=0 0\r\n");
|
||||
|
||||
rc += msgb_printf(msg, "m=audio %u RTP/AVP", mgcp_msg->audio_port);
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||
rc += msgb_printf(msg, " %u", pt);
|
||||
|
||||
}
|
||||
rc += msgb_printf(msg, "\r\n");
|
||||
|
||||
for (i = 0; i < mgcp_msg->codecs_len; i++) {
|
||||
pt = map_codec_to_pt(mgcp_msg->ptmap, mgcp_msg->ptmap_len, mgcp_msg->codecs[i]);
|
||||
|
||||
/* Note: Only dynamic payload type from the range 96-127
|
||||
* require to be explained further via rtpmap. All others
|
||||
* are implcitly definedby the number in m=audio */
|
||||
if (pt >= 96 && pt <= 127) {
|
||||
codec = get_value_string_or_null(codec_table, mgcp_msg->codecs[i]);
|
||||
|
||||
/* Note: Use codec descriptors from enum mgcp_codecs
|
||||
* in mgcp_client only! */
|
||||
OSMO_ASSERT(codec);
|
||||
|
||||
rc += msgb_printf(msg, "a=rtpmap:%u %s\r\n", pt, codec);
|
||||
}
|
||||
}
|
||||
|
||||
if (mgcp_msg->ptime)
|
||||
rc += msgb_printf(msg, "a=ptime:%u\r\n", mgcp_msg->ptime);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Generate an MGCP message
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \param[in] mgcp_msg Message description
|
||||
@@ -823,7 +1182,8 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
uint32_t mandatory_mask;
|
||||
struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
|
||||
int rc = 0;
|
||||
char local_ip[INET_ADDRSTRLEN];
|
||||
int rc_sdp;
|
||||
bool use_sdp = false;
|
||||
|
||||
msg->l2h = msg->data;
|
||||
msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
|
||||
@@ -902,9 +1262,17 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
rc += msgb_printf(msg, "I: %s\r\n", mgcp_msg->conn_id);
|
||||
}
|
||||
|
||||
/* Add local connection options */
|
||||
if (mgcp_msg->verb == MGCP_VERB_CRCX)
|
||||
rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n");
|
||||
/* Using SDP makes sense when a valid IP/Port combination is specifiec,
|
||||
* if we do not know this information yet, we fall back to LCO */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
|
||||
&& mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT)
|
||||
use_sdp = true;
|
||||
|
||||
/* Add local connection options (LCO) */
|
||||
if (!use_sdp
|
||||
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|
||||
|| mgcp_msg->verb == MGCP_VERB_MDCX))
|
||||
rc += add_lco(msg, mgcp_msg);
|
||||
|
||||
/* Add mode */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
|
||||
@@ -912,52 +1280,15 @@ struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
|
||||
msgb_printf(msg, "M: %s\r\n",
|
||||
mgcp_client_cmode_name(mgcp_msg->conn_mode));
|
||||
|
||||
/* Add SDP body */
|
||||
if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
|
||||
&& mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
|
||||
|
||||
/* Add separator to mark the beginning of the SDP block */
|
||||
rc += msgb_printf(msg, "\r\n");
|
||||
|
||||
/* Add SDP protocol version */
|
||||
rc += msgb_printf(msg, "v=0\r\n");
|
||||
|
||||
/* Determine local IP-Address */
|
||||
if (osmo_sock_local_ip(local_ip, mgcp->actual.remote_addr) < 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Could not determine local IP-Address!\n");
|
||||
msgb_free(msg);
|
||||
/* Add session description protocol (SDP) */
|
||||
if (use_sdp
|
||||
&& (mgcp_msg->verb == MGCP_VERB_CRCX
|
||||
|| mgcp_msg->verb == MGCP_VERB_MDCX)) {
|
||||
rc_sdp = add_sdp(msg, mgcp_msg, mgcp);
|
||||
if (rc_sdp == -2)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add owner/creator (SDP) */
|
||||
rc += msgb_printf(msg, "o=- %x 23 IN IP4 %s\r\n",
|
||||
mgcp_msg->call_id, local_ip);
|
||||
|
||||
/* Add session name (none) */
|
||||
rc += msgb_printf(msg, "s=-\r\n");
|
||||
|
||||
/* Add RTP address and port */
|
||||
if (mgcp_msg->audio_port == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Invalid port number, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
if (strlen(mgcp_msg->audio_ip) <= 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Empty ip address, can not generate MGCP message\n");
|
||||
msgb_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
|
||||
|
||||
/* Add time description, active time (SDP) */
|
||||
rc += msgb_printf(msg, "t=0 0\r\n");
|
||||
|
||||
rc +=
|
||||
msgb_printf(msg, "m=audio %u RTP/AVP 255\r\n",
|
||||
mgcp_msg->audio_port);
|
||||
else
|
||||
rc += rc_sdp;
|
||||
}
|
||||
|
||||
if (rc != 0) {
|
||||
|
||||
@@ -112,9 +112,12 @@ static struct msgb *make_crcx_msg_bind(struct mgcp_ctx *mgcp_ctx)
|
||||
.verb = MGCP_VERB_CRCX,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
|
||||
.call_id = mgcp_ctx->conn_peer_local.call_id,
|
||||
.conn_mode = MGCP_CONN_LOOPBACK,
|
||||
.conn_mode = MGCP_CONN_RECV_ONLY,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
|
||||
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
}
|
||||
@@ -124,15 +127,19 @@ static struct msgb *make_crcx_msg_bind_connect(struct mgcp_ctx *mgcp_ctx)
|
||||
struct mgcp_msg mgcp_msg;
|
||||
|
||||
mgcp_msg = (struct mgcp_msg) {
|
||||
.verb = MGCP_VERB_CRCX,.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
|
||||
MGCP_MSG_PRESENCE_AUDIO_PORT),
|
||||
.verb = MGCP_VERB_CRCX,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
|
||||
MGCP_MSG_PRESENCE_AUDIO_PORT),
|
||||
.call_id = mgcp_ctx->conn_peer_local.call_id,
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
.audio_ip = mgcp_ctx->conn_peer_local.addr,
|
||||
.audio_port = mgcp_ctx->conn_peer_local.port,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_local.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
|
||||
|
||||
return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg);
|
||||
}
|
||||
@@ -150,8 +157,11 @@ static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx)
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
.audio_ip = mgcp_ctx->conn_peer_local.addr,
|
||||
.audio_port = mgcp_ctx->conn_peer_local.port,
|
||||
.ptime = mgcp_ctx->conn_peer_local.ptime,
|
||||
.codecs_len = mgcp_ctx->conn_peer_local.codecs_len
|
||||
};
|
||||
osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN);
|
||||
memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs));
|
||||
|
||||
/* Note: We take the endpoint and the call_id from the remote
|
||||
* connection info, because we can be confident that the
|
||||
@@ -215,6 +225,14 @@ static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the CI that the MGW allocated during CRCX response. This is purely informational for logging
|
||||
* and identity tracking; the mgcp_conn_*() functions take care of using the right CI internally. */
|
||||
const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct mgcp_ctx *mgcp_ctx = fi->priv;
|
||||
return mgcp_ctx->conn_id;
|
||||
}
|
||||
|
||||
static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = priv;
|
||||
@@ -476,7 +494,7 @@ static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca
|
||||
* mgcp_conn_delete() to instruct the FSM to perform a graceful exit */
|
||||
if (strlen(mgcp_ctx->conn_id)) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"MGW/DLCX: aprupt FSM termination with connections still present, sending unconditional DLCX...\n");
|
||||
"MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n");
|
||||
msg = make_dlcx_msg(mgcp_ctx);
|
||||
OSMO_ASSERT(msg);
|
||||
mgcp_client_tx(mgcp, msg, NULL, NULL);
|
||||
@@ -548,7 +566,7 @@ static struct osmo_fsm fsm_mgcp_client = {
|
||||
|
||||
/*! allocate FSM, and create a new connection on the MGW.
|
||||
* \param[in] mgcp MGCP client descriptor.
|
||||
* \param[in] mgcpparent_fi Parent FSM instance.
|
||||
* \param[in] parent_fi Parent FSM instance.
|
||||
* \param[in] parent_term_evt Event to be sent to parent when terminating.
|
||||
* \param[in] parent_evt Event to be sent to parent when operation is done.
|
||||
* \param[in] conn_peer Connection parameters (ip, port...).
|
||||
@@ -565,7 +583,7 @@ struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm
|
||||
OSMO_ASSERT(mgcp);
|
||||
OSMO_ASSERT(conn_peer);
|
||||
|
||||
/* Check if IP/Port informstaion in conn info makes sense */
|
||||
/* Check if IP/Port information in conn info makes sense */
|
||||
if (conn_peer->port && inet_aton(conn_peer->addr, &ip_test) == 0)
|
||||
return NULL;
|
||||
|
||||
@@ -622,17 +640,24 @@ int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_
|
||||
OSMO_ASSERT(fi->state != ST_DLCX_RESP);
|
||||
|
||||
/* Check if IP/Port parameters make sense */
|
||||
if (conn_peer->port == 0)
|
||||
if (conn_peer->port == 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n");
|
||||
return -EINVAL;
|
||||
if (inet_aton(conn_peer->addr, &ip_test) == 0)
|
||||
}
|
||||
if (inet_aton(conn_peer->addr, &ip_test) == 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! The user may supply an endpoint identifier in conn_peer. The
|
||||
* identifier is then checked. This check is optional. Later steps do
|
||||
* not depend on the endpoint identifier supplied here because it is
|
||||
* already implicitly known from the CRCX phase. */
|
||||
if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint))
|
||||
if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint)) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, endpoint mismatches: requested %s, should be %s\n",
|
||||
conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! Note: The call-id is implicitly known from the previous CRCX and
|
||||
* will not be checked even when it is set in conn_peer. */
|
||||
|
||||
@@ -35,6 +35,7 @@ libosmo_mgcp_a_SOURCES = \
|
||||
mgcp_vty.c \
|
||||
mgcp_osmux.c \
|
||||
mgcp_sdp.c \
|
||||
mgcp_codec.c \
|
||||
mgcp_msg.c \
|
||||
mgcp_conn.c \
|
||||
mgcp_stat.c \
|
||||
|
||||
343
src/libosmo-mgcp/mgcp_codec.c
Normal file
343
src/libosmo-mgcp/mgcp_codec.c
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* (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 <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Helper function to dump codec information of a specified codec to a printable
|
||||
* string, used by dump_codec_summary() */
|
||||
static char *dump_codec(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
static char str[256];
|
||||
char *pt_str;
|
||||
|
||||
if (codec->payload_type > 76)
|
||||
pt_str = "DYNAMIC";
|
||||
else if (codec->payload_type > 72)
|
||||
pt_str = "RESERVED <!>";
|
||||
else if (codec->payload_type != PTYPE_UNDEFINED)
|
||||
pt_str = codec->subtype_name;
|
||||
else
|
||||
pt_str = "INVALID <!>";
|
||||
|
||||
snprintf(str, sizeof(str), "(pt:%i=%s, audio:%s subt=%s, rate=%u, ch=%i, t=%u/%u)", codec->payload_type, pt_str,
|
||||
codec->audio_name, codec->subtype_name, codec->rate, codec->channels, codec->frame_duration_num,
|
||||
codec->frame_duration_den);
|
||||
return str;
|
||||
}
|
||||
|
||||
/*! Dump a summary of all negotiated codecs to debug log
|
||||
* \param[in] conn related rtp-connection. */
|
||||
void mgcp_codec_summary(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
struct mgcp_rtp_end *rtp;
|
||||
unsigned int i;
|
||||
struct mgcp_rtp_codec *codec;
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
rtp = &conn->end;
|
||||
endp = conn->conn->endp;
|
||||
|
||||
if (rtp->codecs_assigned == 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "endpoint:0x%x conn:%s no codecs available\n", ENDPOINT_NUMBER(endp),
|
||||
mgcp_conn_dump(conn->conn));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store parsed codec information */
|
||||
for (i = 0; i < rtp->codecs_assigned; i++) {
|
||||
codec = &rtp->codecs[i];
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG, "endpoint:0x%x conn:%s codecs[%u]:%s", ENDPOINT_NUMBER(endp),
|
||||
mgcp_conn_dump(conn->conn), i, dump_codec(codec));
|
||||
|
||||
if (codec == rtp->codec)
|
||||
LOGPC(DLMGCP, LOGL_DEBUG, " [selected]");
|
||||
|
||||
LOGPC(DLMGCP, LOGL_DEBUG, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Initalize or reset codec information with default data. */
|
||||
void codec_init(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
if (codec->subtype_name)
|
||||
talloc_free(codec->subtype_name);
|
||||
if (codec->audio_name)
|
||||
talloc_free(codec->audio_name);
|
||||
memset(codec, 0, sizeof(*codec));
|
||||
codec->payload_type = -1;
|
||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||
}
|
||||
|
||||
/*! Initalize or reset codec information with default data.
|
||||
* \param[out] conn related rtp-connection. */
|
||||
void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
memset(conn->end.codecs, 0, sizeof(conn->end.codecs));
|
||||
conn->end.codecs_assigned = 0;
|
||||
conn->end.codec = NULL;
|
||||
}
|
||||
|
||||
/* Set members of struct mgcp_rtp_codec, extrapolate in missing information */
|
||||
static int codec_set(void *ctx, struct mgcp_rtp_codec *codec,
|
||||
int payload_type, const char *audio_name, unsigned int pt_offset)
|
||||
{
|
||||
int rate;
|
||||
int channels;
|
||||
char audio_codec[64];
|
||||
|
||||
/* Initalize the codec struct with some default data to begin with */
|
||||
codec_init(codec);
|
||||
|
||||
if (payload_type != PTYPE_UNDEFINED) {
|
||||
/* Make sure we do not get any reserved or undefined type numbers */
|
||||
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||
if (payload_type == 1 || payload_type == 2 || payload_type == 19)
|
||||
goto error;
|
||||
if (payload_type >= 72 && payload_type <= 76)
|
||||
goto error;
|
||||
if (payload_type >= 127)
|
||||
goto error;
|
||||
|
||||
codec->payload_type = payload_type;
|
||||
}
|
||||
|
||||
/* When no audio name is given, we are forced to use the payload
|
||||
* type to generate the audio name. This is only possible for
|
||||
* non dynamic payload types, which are statically defined */
|
||||
if (!audio_name) {
|
||||
switch (payload_type) {
|
||||
case 0:
|
||||
audio_name = talloc_strdup(ctx, "PCMU/8000/1");
|
||||
break;
|
||||
case 3:
|
||||
audio_name = talloc_strdup(ctx, "GSM/8000/1");
|
||||
break;
|
||||
case 8:
|
||||
audio_name = talloc_strdup(ctx, "PCMA/8000/1");
|
||||
break;
|
||||
case 18:
|
||||
audio_name = talloc_strdup(ctx, "G729/8000/1");
|
||||
break;
|
||||
default:
|
||||
/* The given payload type is not known to us, or it
|
||||
* it is a dynamic payload type for which we do not
|
||||
* know the audio name. We must give up here */
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we extract the codec subtype name, rate and channels. The latter
|
||||
* two are optional. If they are not present we use the safe defaults
|
||||
* above. */
|
||||
if (strlen(audio_name) > sizeof(audio_codec))
|
||||
goto error;
|
||||
channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||
rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1)
|
||||
goto error;
|
||||
|
||||
/* Note: We only accept configurations with one audio channel! */
|
||||
if (channels != 1)
|
||||
goto error;
|
||||
|
||||
codec->rate = rate;
|
||||
codec->channels = channels;
|
||||
codec->subtype_name = talloc_strdup(ctx, audio_codec);
|
||||
codec->audio_name = talloc_strdup(ctx, audio_name);
|
||||
codec->payload_type = payload_type;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Derive the payload type if it is unknown */
|
||||
if (codec->payload_type == PTYPE_UNDEFINED) {
|
||||
|
||||
/* For the known codecs from the static range we restore
|
||||
* the IANA or 3GPP assigned payload type number */
|
||||
if (codec->rate == 8000 && codec->channels == 1) {
|
||||
/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */
|
||||
if (!strcmp(codec->subtype_name, "GSM"))
|
||||
codec->payload_type = 3;
|
||||
else if (!strcmp(codec->subtype_name, "PCMA"))
|
||||
codec->payload_type = 8;
|
||||
else if (!strcmp(codec->subtype_name, "PCMU"))
|
||||
codec->payload_type = 0;
|
||||
else if (!strcmp(codec->subtype_name, "G729"))
|
||||
codec->payload_type = 18;
|
||||
|
||||
/* See also: 3GPP TS 48.103, chapter 5.4.2.2 RTP Payload
|
||||
* Note: These are not fixed payload types as the IANA
|
||||
* defined once, they still remain dymanic payload
|
||||
* types, but with a payload type number preference. */
|
||||
else if (!strcmp(codec->subtype_name, "GSM-EFR"))
|
||||
codec->payload_type = 110;
|
||||
else if (!strcmp(codec->subtype_name, "GSM-HR-08"))
|
||||
codec->payload_type = 111;
|
||||
else if (!strcmp(codec->subtype_name, "AMR"))
|
||||
codec->payload_type = 112;
|
||||
else if (!strcmp(codec->subtype_name, "AMR-WB"))
|
||||
codec->payload_type = 113;
|
||||
}
|
||||
|
||||
/* If we could not determine a payload type we assume that
|
||||
* we are dealing with a codec from the dynamic range. We
|
||||
* choose a fixed identifier from 96-109. (Note: normally,
|
||||
* the dynamic payload type rante is from 96-127, but from
|
||||
* 110 onwards 3gpp defines prefered codec types, which are
|
||||
* also fixed, see above) */
|
||||
if (codec->payload_type < 0) {
|
||||
codec->payload_type = 96 + pt_offset;
|
||||
if (codec->payload_type > 109)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
/* Make sure we leave a clean codec entry on error. */
|
||||
codec_init(codec);
|
||||
memset(codec, 0, sizeof(*codec));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! Add codec configuration depending on payload type and/or codec name. This
|
||||
* function uses the input parameters to extrapolate the full codec information.
|
||||
* \param[out] codec configuration (caller provided memory).
|
||||
* \param[out] conn related rtp-connection.
|
||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* The amount of codecs we can store is limited, make sure we do not
|
||||
* overrun this limit. */
|
||||
if (conn->end.codecs_assigned >= MGCP_MAX_CODECS)
|
||||
return -EINVAL;
|
||||
|
||||
rc = codec_set(conn->conn, &conn->end.codecs[conn->end.codecs_assigned], payload_type, audio_name,
|
||||
conn->end.codecs_assigned);
|
||||
if (rc != 0)
|
||||
return -EINVAL;
|
||||
|
||||
conn->end.codecs_assigned++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if the given codec is applicable on the specified endpoint
|
||||
* Helper function for mgcp_codec_decide() */
|
||||
static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
char codec_name[64];
|
||||
|
||||
/* A codec name must be set, if not, this might mean that the codec
|
||||
* (payload type) that was assigned is unknown to us so we must stop
|
||||
* here. */
|
||||
if (!codec->subtype_name)
|
||||
return false;
|
||||
|
||||
/* We now extract the codec_name (letters before the /, e.g. "GSM"
|
||||
* from the audio name that is stored in the trunk configuration.
|
||||
* We do not compare to the full audio_name because we expect that
|
||||
* "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
|
||||
* audio name of the codec is set to "GSM" */
|
||||
if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
|
||||
return false;
|
||||
|
||||
/* Finally we check if the subtype_name we have generated from the
|
||||
* audio_name in the trunc struct patches the codec_name of the
|
||||
* given codec */
|
||||
if (strcasecmp(codec_name, codec->subtype_name) == 0)
|
||||
return true;
|
||||
|
||||
/* FIXME: It is questinable that the method to pick a compatible
|
||||
* codec can work properly. Since this useses tcfg->audio_name, as
|
||||
* a reference, which is set to "AMR/8000" permanently.
|
||||
* tcfg->audio_name must be updated by the first connection that
|
||||
* has been made on an endpoint, so that the second connection
|
||||
* can make a meaningful decision here */
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! Decide for one suitable codec
|
||||
* \param[in] conn related rtp-connection.
|
||||
* \returns 0 on success, -EINVAL on failure. */
|
||||
int mgcp_codec_decide(struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
struct mgcp_rtp_end *rtp;
|
||||
unsigned int i;
|
||||
struct mgcp_endpoint *endp;
|
||||
bool codec_assigned = false;
|
||||
|
||||
endp = conn->conn->endp;
|
||||
rtp = &conn->end;
|
||||
|
||||
/* This function works on the results the SDP/LCO parser has extracted
|
||||
* from the MGCP message. The goal is to select a suitable codec for
|
||||
* the given connection. When transcoding is available, the first codec
|
||||
* from the codec list is taken without further checking. When
|
||||
* transcoding is not available, then the choice must be made more
|
||||
* carefully. Each codec in the list is checked until one is found that
|
||||
* is rated compatible. The rating is done by the helper function
|
||||
* is_codec_compatible(), which does the actual checking. */
|
||||
for (i = 0; i < rtp->codecs_assigned; i++) {
|
||||
/* When no transcoding is available, avoid codecs that would
|
||||
* require transcoding. */
|
||||
if (endp->tcfg->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) {
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n",
|
||||
rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
rtp->codec = &rtp->codecs[i];
|
||||
codec_assigned = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* FIXME: To the reviewes: This is problematic. I do not get why we
|
||||
* need to reset the packet_duration_ms depending on the codec
|
||||
* selection. I thought it were all 20ms? Is this to address some
|
||||
* cornercase. (This piece of code was in the code path before,
|
||||
* together with the note: "TODO/XXX: Store this per codec and derive
|
||||
* it on use" */
|
||||
if (codec_assigned) {
|
||||
if (rtp->maximum_packet_time >= 0
|
||||
&& rtp->maximum_packet_time * rtp->codec->frame_duration_den >
|
||||
rtp->codec->frame_duration_num * 1500)
|
||||
rtp->packet_duration_ms = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -25,9 +25,32 @@
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_common.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <ctype.h>
|
||||
|
||||
enum {
|
||||
IN_STREAM_ERR_TSTMP_CTR,
|
||||
OUT_STREAM_ERR_TSTMP_CTR,
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc rate_ctr_desc[] = {
|
||||
[IN_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:in", "Inbound rtp-stream timestamp errors."},
|
||||
[OUT_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:out", "Outbound rtp-stream timestamp errors."},
|
||||
};
|
||||
|
||||
|
||||
const static struct rate_ctr_group_desc rate_ctr_group_desc = {
|
||||
.group_name_prefix = "conn_rtp",
|
||||
.group_description = "rtp connection statistics",
|
||||
.class_id = 1,
|
||||
.num_ctr = 2,
|
||||
.ctr_desc = rate_ctr_desc
|
||||
};
|
||||
|
||||
|
||||
/* Allocate a new connection identifier. According to RFC3435, they must
|
||||
* be unique only within the scope of the endpoint. (Caller must provide
|
||||
* memory for id) */
|
||||
@@ -67,26 +90,14 @@ static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Reset codec state and free memory */
|
||||
static void mgcp_rtp_codec_init(struct mgcp_rtp_codec *codec)
|
||||
{
|
||||
codec->payload_type = -1;
|
||||
codec->subtype_name = NULL;
|
||||
codec->audio_name = NULL;
|
||||
codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
|
||||
codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
|
||||
codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
|
||||
codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
|
||||
|
||||
/* see also mgcp_sdp.c, mgcp_set_audio_info() */
|
||||
talloc_free(codec->subtype_name);
|
||||
talloc_free(codec->audio_name);
|
||||
}
|
||||
|
||||
/* Initialize rtp connection struct with default values */
|
||||
static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn)
|
||||
{
|
||||
struct mgcp_rtp_end *end = &conn_rtp->end;
|
||||
/* FIXME: Each new rate counter group requires an unique index. At the
|
||||
* moment we generate this index using this counter, but perhaps there
|
||||
* is a more concious way to assign the indexes. */
|
||||
static unsigned int rate_ctr_index = 0;
|
||||
|
||||
conn_rtp->type = MGCP_RTP_DEFAULT;
|
||||
conn_rtp->osmux.allocated_cid = -1;
|
||||
@@ -105,9 +116,15 @@ static void mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn
|
||||
end->frames_per_packet = 0; /* unknown */
|
||||
end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
|
||||
end->output_enabled = 0;
|
||||
end->maximum_packet_time = -1;
|
||||
|
||||
mgcp_rtp_codec_init(&end->codec);
|
||||
mgcp_rtp_codec_init(&end->alt_codec);
|
||||
conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index);
|
||||
conn_rtp->state.in_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
|
||||
conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
|
||||
rate_ctr_index++;
|
||||
|
||||
/* Make sure codec table is reset */
|
||||
mgcp_codec_reset_all(conn_rtp);
|
||||
}
|
||||
|
||||
/* Cleanup rtp connection struct */
|
||||
@@ -116,6 +133,7 @@ static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp)
|
||||
osmux_disable_conn(conn_rtp);
|
||||
osmux_release_cid(conn_rtp);
|
||||
mgcp_free_rtp_port(&conn_rtp->end);
|
||||
rate_ctr_group_free(conn_rtp->rate_ctr_group);
|
||||
}
|
||||
|
||||
/*! allocate a new connection list entry.
|
||||
|
||||
@@ -222,7 +222,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
||||
|
||||
if (seq == sstate->last_seq) {
|
||||
if (timestamp != sstate->last_timestamp) {
|
||||
sstate->err_ts_counter += 1;
|
||||
rate_ctr_inc(sstate->err_ts_ctr);
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"The %s timestamp delta is != 0 but the sequence "
|
||||
"number %d is the same, "
|
||||
@@ -272,7 +272,7 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
|
||||
ts_alignment_error(sstate, state->packet_duration, timestamp);
|
||||
|
||||
if (timestamp_error) {
|
||||
sstate->err_ts_counter += 1;
|
||||
rate_ctr_inc(sstate->err_ts_ctr);
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"The %s timestamp has an alignment error of %d "
|
||||
"on 0x%x SSRC: %u "
|
||||
@@ -310,7 +310,7 @@ static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
|
||||
ENDPOINT_NUMBER(endp), tsdelta,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
} else {
|
||||
tsdelta = rtp_end->codec.rate * 20 / 1000;
|
||||
tsdelta = rtp_end->codec->rate * 20 / 1000;
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"Fixed packet duration and last timestamp delta "
|
||||
"are not available on 0x%x, "
|
||||
@@ -421,8 +421,8 @@ void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp,
|
||||
"endpoint:0x%x conn:%s using format defaults\n",
|
||||
ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
|
||||
|
||||
*payload_type = conn->end.codec.payload_type;
|
||||
*audio_name = conn->end.codec.audio_name;
|
||||
*payload_type = conn->end.codec->payload_type;
|
||||
*audio_name = conn->end.codec->audio_name;
|
||||
*fmtp_extra = conn->end.fmtp_extra;
|
||||
}
|
||||
|
||||
@@ -490,7 +490,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
uint16_t seq;
|
||||
uint32_t timestamp, ssrc;
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
int payload = rtp_end->codec.payload_type;
|
||||
int payload = rtp_end->codec->payload_type;
|
||||
|
||||
if (len < sizeof(*rtp_hdr))
|
||||
return;
|
||||
@@ -498,7 +498,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
rtp_hdr = (struct rtp_hdr *)data;
|
||||
seq = ntohs(rtp_hdr->sequence);
|
||||
timestamp = ntohl(rtp_hdr->timestamp);
|
||||
arrival_time = get_current_ts(rtp_end->codec.rate);
|
||||
arrival_time = get_current_ts(rtp_end->codec->rate);
|
||||
ssrc = ntohl(rtp_hdr->ssrc);
|
||||
transit = arrival_time - timestamp;
|
||||
|
||||
@@ -511,7 +511,9 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
state->in_stream.last_tsdelta = 0;
|
||||
state->packet_duration =
|
||||
mgcp_rtp_packet_duration(endp, rtp_end);
|
||||
state->out_stream = state->in_stream;
|
||||
state->out_stream.last_seq = seq - 1;
|
||||
state->out_stream.ssrc = state->patch.orig_ssrc = ssrc;
|
||||
state->out_stream.last_tsdelta = 0;
|
||||
state->out_stream.last_timestamp = timestamp;
|
||||
state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
|
||||
LOGP(DRTP, LOGL_INFO,
|
||||
@@ -522,7 +524,7 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
|
||||
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
|
||||
if (state->packet_duration == 0) {
|
||||
state->packet_duration =
|
||||
rtp_end->codec.rate * 20 / 1000;
|
||||
rtp_end->codec->rate * 20 / 1000;
|
||||
LOGP(DRTP, LOGL_NOTICE,
|
||||
"endpoint:0x%x fixed packet duration is not available, "
|
||||
"using fixed 20ms instead: %d from %s:%d\n",
|
||||
@@ -865,6 +867,15 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
|
||||
struct mgcp_endpoint *endp;
|
||||
endp = conn->conn->endp;
|
||||
|
||||
/* Note: it is legal to create a connection but never setting a port
|
||||
* and IP-address for outgoing data. */
|
||||
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) {
|
||||
LOGP(DRTP, LOGL_DEBUG,
|
||||
"endpoint:0x%x destination IP-address and rtp port is (not yet) known\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0) {
|
||||
LOGP(DRTP, LOGL_ERROR,
|
||||
"endpoint:0x%x destination IP-address is invalid\n",
|
||||
|
||||
@@ -322,11 +322,10 @@ 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;
|
||||
struct mgcp_conn_rtp *conn_net = NULL;
|
||||
struct mgcp_conn_rtp *conn_bts = NULL;
|
||||
|
||||
msg = osmux_recv(ofd, &addr);
|
||||
if (!msg)
|
||||
@@ -345,8 +344,8 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
&addr.sin_addr, MGCP_DEST_NET);
|
||||
|
||||
/* FIXME: Get rid of CONN_ID_XXX! */
|
||||
conn_net = mgcp_conn_get_rtp(endp, CONN_ID_NET);
|
||||
if (!conn_net)
|
||||
conn_bts = mgcp_conn_get_rtp(endp, CONN_ID_BTS);
|
||||
if (!conn_bts)
|
||||
goto out;
|
||||
|
||||
if (!endp) {
|
||||
@@ -355,12 +354,11 @@ int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
osmuxh->circuit_id);
|
||||
goto out;
|
||||
}
|
||||
conn_net->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
conn_net->osmux.stats.chunks++;
|
||||
conn_bts->osmux.stats.octets += osmux_chunk_length(msg, rem);
|
||||
conn_bts->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_bts_cb, endp);
|
||||
osmux_xfrm_output_sched(&conn_bts->osmux.out, osmuxh);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
@@ -426,7 +424,6 @@ 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;
|
||||
@@ -463,8 +460,7 @@ int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
conn_net->osmux.stats.chunks++;
|
||||
rem = msg->len;
|
||||
|
||||
osmux_xfrm_output(osmuxh, &conn_net->osmux.out, &list);
|
||||
osmux_tx_sched(&list, scheduled_tx_net_cb, endp);
|
||||
osmux_xfrm_output_sched(&conn_net->osmux.out, osmuxh);
|
||||
}
|
||||
out:
|
||||
msgb_free(msg);
|
||||
@@ -553,9 +549,13 @@ int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
|
||||
switch (endp->cfg->role) {
|
||||
case MGCP_BSC_NAT:
|
||||
conn->type = MGCP_OSMUX_BSC_NAT;
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
|
||||
scheduled_tx_net_cb, endp);
|
||||
break;
|
||||
case MGCP_BSC:
|
||||
conn->type = MGCP_OSMUX_BSC;
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
|
||||
scheduled_tx_bts_cb, endp);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -576,6 +576,11 @@ void osmux_disable_conn(struct mgcp_conn_rtp *conn)
|
||||
|
||||
LOGP(DLMGCP, LOGL_INFO, "Releasing connection %s using Osmux CID %u\n",
|
||||
conn->conn->id, conn->osmux.cid);
|
||||
|
||||
/* We are closing, we don't need pending RTP packets to be transmitted */
|
||||
osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
|
||||
osmux_xfrm_output_flush(&conn->osmux.out);
|
||||
|
||||
osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
|
||||
conn->osmux.state = OSMUX_STATE_DISABLED;
|
||||
conn->osmux.cid = -1;
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
|
||||
struct mgcp_request {
|
||||
char *name;
|
||||
@@ -356,13 +357,15 @@ static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
|
||||
|
||||
/* Try to find a free port by attempting to bind on it. Also handle the
|
||||
* counter that points on the next free port. Since we have a pointer
|
||||
* to the next free port, binding should work on the first attempt,
|
||||
* nevertheless, try at least the next 200 ports before giving up */
|
||||
* to the next free port, binding should in work on the first attempt in
|
||||
* general. In case of failure the next port is tryed until the whole port
|
||||
* range is tryed once. */
|
||||
static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
{
|
||||
int i;
|
||||
struct mgcp_rtp_end *end;
|
||||
struct mgcp_port_range *range;
|
||||
unsigned int tries;
|
||||
|
||||
OSMO_ASSERT(conn);
|
||||
end = &conn->end;
|
||||
@@ -371,7 +374,8 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
range = &endp->cfg->net_ports;
|
||||
|
||||
/* attempt to find a port */
|
||||
for (i = 0; i < 200; ++i) {
|
||||
tries = (range->range_end - range->range_start) / 2;
|
||||
for (i = 0; i < tries; ++i) {
|
||||
int rc;
|
||||
|
||||
if (range->last_port >= range->range_end)
|
||||
@@ -387,8 +391,123 @@ static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
"Allocating a RTP/RTCP port failed %u times 0x%x.\n",
|
||||
tries, ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! Helper function for check_local_cx_options() to get a pointer of the next
|
||||
* lco option identifier
|
||||
* \param[in] lco string
|
||||
* \returns pointer to the beginning of the LCO identifier, NULL on failure */
|
||||
char *get_lco_identifier(const char *options)
|
||||
{
|
||||
char *ptr;
|
||||
unsigned int count = 0;
|
||||
|
||||
/* Jump to the end of the lco identifier */
|
||||
ptr = strstr(options, ":");
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
/* Walk backwards until the pointer points to the beginning of the
|
||||
* lco identifier. We know that we stand at the beginning when we
|
||||
* are either at the beginning of the memory or see a space or
|
||||
* comma. (this is tolerant, it will accept a:10, b:11 as well as
|
||||
* a:10,b:11) */
|
||||
while (1) {
|
||||
/* Endless loop protection */
|
||||
if (count > 10000)
|
||||
return NULL;
|
||||
else if (ptr < options || *ptr == ' ' || *ptr == ',') {
|
||||
ptr++;
|
||||
break;
|
||||
}
|
||||
ptr--;
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Check if we got any result */
|
||||
if (*ptr == ':')
|
||||
return NULL;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/*! Check the LCO option. This function checks for multiple appearence of LCO
|
||||
* options, which is illegal
|
||||
* \param[in] ctx talloc context
|
||||
* \param[in] lco string
|
||||
* \returns 0 on success, -1 on failure */
|
||||
int check_local_cx_options(void *ctx, const char *options)
|
||||
{
|
||||
int i;
|
||||
char *options_copy;
|
||||
char *lco_identifier;
|
||||
char *lco_identifier_end;
|
||||
char *next_lco_identifier;
|
||||
|
||||
char **lco_seen;
|
||||
unsigned int lco_seen_n = 0;
|
||||
|
||||
if (!options)
|
||||
return -1;
|
||||
|
||||
lco_seen =
|
||||
(char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
|
||||
options_copy = talloc_strdup(ctx, options);
|
||||
lco_identifier = options_copy;
|
||||
|
||||
do {
|
||||
/* Move the lco_identifier pointer to the beginning of the
|
||||
* current lco option identifier */
|
||||
lco_identifier = get_lco_identifier(lco_identifier);
|
||||
if (!lco_identifier)
|
||||
goto error;
|
||||
|
||||
/* Look ahead to the next LCO option early, since we
|
||||
* will parse destructively */
|
||||
next_lco_identifier = strstr(lco_identifier + 1, ",");
|
||||
|
||||
/* Pinch off the end of the lco field identifier name
|
||||
* and see if we still got something, also check if
|
||||
* there is some value after the colon. */
|
||||
lco_identifier_end = strstr(lco_identifier, ":");
|
||||
if (!lco_identifier_end)
|
||||
goto error;
|
||||
if (*(lco_identifier_end + 1) == ' '
|
||||
|| *(lco_identifier_end + 1) == ','
|
||||
|| *(lco_identifier_end + 1) == '\0')
|
||||
goto error;
|
||||
*lco_identifier_end = '\0';
|
||||
if (strlen(lco_identifier) == 0)
|
||||
goto error;
|
||||
|
||||
/* Check if we have already seen the current field identifier
|
||||
* before. If yes, we must bail, an LCO must only appear once
|
||||
* in the LCO string */
|
||||
for (i = 0; i < lco_seen_n; i++) {
|
||||
if (strcmp(lco_seen[i], lco_identifier) == 0)
|
||||
goto error;
|
||||
}
|
||||
lco_seen[lco_seen_n] = lco_identifier;
|
||||
lco_seen_n++;
|
||||
|
||||
/* The first identifier must always be found at the beginnning
|
||||
* of the LCO string */
|
||||
if (lco_seen[0] != options_copy)
|
||||
goto error;
|
||||
|
||||
/* Go to the next lco option */
|
||||
lco_identifier = next_lco_identifier;
|
||||
} while (lco_identifier);
|
||||
|
||||
talloc_free(lco_seen);
|
||||
talloc_free(options_copy);
|
||||
return 0;
|
||||
error:
|
||||
talloc_free(lco_seen);
|
||||
talloc_free(options_copy);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -402,20 +521,34 @@ static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
|
||||
char *p_opt, *a_opt;
|
||||
char codec[9];
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
if (strlen(options) == 0)
|
||||
return 0;
|
||||
|
||||
/* Make sure the encoding of the LCO is consistant before we proceed */
|
||||
if (check_local_cx_options(ctx, options) != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"local CX options: Internal inconsistency in Local Connection Options!\n");
|
||||
return 524;
|
||||
}
|
||||
|
||||
talloc_free(lco->string);
|
||||
talloc_free(lco->codec);
|
||||
lco->codec = NULL;
|
||||
lco->pkt_period_min = lco->pkt_period_max = 0;
|
||||
lco->string = talloc_strdup(ctx, options ? options : "");
|
||||
lco->string = talloc_strdup(ctx, options);
|
||||
|
||||
p_opt = strstr(lco->string, "p:");
|
||||
if (p_opt && sscanf(p_opt, "p:%d-%d",
|
||||
&lco->pkt_period_min, &lco->pkt_period_max) == 1)
|
||||
lco->pkt_period_max = lco->pkt_period_min;
|
||||
|
||||
/* FIXME: LCO also supports the negotiation of more then one codec.
|
||||
* (e.g. a:PCMU;G726-32) But this implementation only supports a single
|
||||
* codec only. */
|
||||
a_opt = strstr(lco->string, "a:");
|
||||
if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
|
||||
if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1) {
|
||||
talloc_free(lco->codec);
|
||||
lco->codec = talloc_strdup(ctx, codec);
|
||||
}
|
||||
|
||||
LOGP(DLMGCP, LOGL_DEBUG,
|
||||
"local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
|
||||
@@ -456,15 +589,15 @@ uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
|
||||
/* Get the number of frames per channel and packet */
|
||||
if (rtp->frames_per_packet)
|
||||
f = rtp->frames_per_packet;
|
||||
else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) {
|
||||
int den = 1000 * rtp->codec.frame_duration_num;
|
||||
f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den +
|
||||
else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
|
||||
int den = 1000 * rtp->codec->frame_duration_num;
|
||||
f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
|
||||
den / 2)
|
||||
/ den;
|
||||
}
|
||||
|
||||
return rtp->codec.rate * f * rtp->codec.frame_duration_num /
|
||||
rtp->codec.frame_duration_den;
|
||||
return rtp->codec->rate * f * rtp->codec->frame_duration_num /
|
||||
rtp->codec->frame_duration_den;
|
||||
}
|
||||
|
||||
static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
|
||||
@@ -480,6 +613,68 @@ static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
|
||||
return mgcp_parse_osmux_cid(line);
|
||||
}
|
||||
|
||||
/* Process codec information contained in CRCX/MDCX */
|
||||
static int handle_codec_info(struct mgcp_conn_rtp *conn,
|
||||
struct mgcp_parse_data *p, int have_sdp, bool crcx)
|
||||
{
|
||||
struct mgcp_endpoint *endp = p->endp;
|
||||
int rc;
|
||||
char *cmd;
|
||||
|
||||
if (crcx)
|
||||
cmd = "CRCX";
|
||||
else
|
||||
cmd = "MDCX";
|
||||
|
||||
/* Collect codec information */
|
||||
if (have_sdp) {
|
||||
/* If we have SDP, we ignore the local connection options and
|
||||
* use only the SDP information. */
|
||||
mgcp_codec_reset_all(conn);
|
||||
rc = mgcp_parse_sdp_data(endp, conn, p);
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"%s: endpoint:%x sdp not parseable\n", cmd,
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
/* See also RFC 3661: Protocol error */
|
||||
return 510;
|
||||
}
|
||||
} else if (endp->local_options.codec) {
|
||||
/* When no SDP is available, we use the codec information from
|
||||
* the local connection options (if present) */
|
||||
mgcp_codec_reset_all(conn);
|
||||
rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec);
|
||||
if (rc != 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Make sure we always set a sane default codec */
|
||||
if (conn->end.codecs_assigned == 0) {
|
||||
/* When SDP and/or LCO did not supply any codec information,
|
||||
* than it makes sense to pick a sane default: (payload-type 0,
|
||||
* PCMU), see also: OS#2658 */
|
||||
mgcp_codec_reset_all(conn);
|
||||
rc = mgcp_codec_add(conn, 0, NULL);
|
||||
if (rc != 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Make codec decision */
|
||||
if (mgcp_codec_decide(conn) != 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"%s: endpoint:0x%x codec negotiation failure\n", cmd,
|
||||
ENDPOINT_NUMBER(endp));
|
||||
|
||||
/* See also RFC 3661: Codec negotiation failure */
|
||||
return 534;
|
||||
}
|
||||
|
||||
/* CRCX command handler, processes the received command */
|
||||
static struct msgb *handle_create_con(struct mgcp_parse_data *p)
|
||||
{
|
||||
@@ -597,17 +792,6 @@ mgcp_header_done:
|
||||
* connection ids) */
|
||||
endp->callid = talloc_strdup(tcfg->endpoints, callid);
|
||||
|
||||
/* Extract audio codec information */
|
||||
rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
|
||||
local_options);
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:%x inavlid local connection options!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
error_code = rc;
|
||||
goto error2;
|
||||
}
|
||||
|
||||
snprintf(conn_name, sizeof(conn_name), "%s", callid);
|
||||
_conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP, conn_name);
|
||||
if (!_conn) {
|
||||
@@ -638,12 +822,27 @@ mgcp_header_done:
|
||||
goto error2;
|
||||
}
|
||||
|
||||
/* set up RTP media parameters */
|
||||
if (have_sdp)
|
||||
mgcp_parse_sdp_data(endp, conn, p);
|
||||
else if (endp->local_options.codec)
|
||||
mgcp_set_audio_info(p->cfg, &conn->end.codec,
|
||||
PTYPE_UNDEFINED, endp->local_options.codec);
|
||||
/* Set local connection options, if present */
|
||||
if (local_options) {
|
||||
rc = set_local_cx_options(endp->tcfg->endpoints,
|
||||
&endp->local_options, local_options);
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"CRCX: endpoint:%x inavlid local connection options!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
error_code = rc;
|
||||
goto error2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle codec information and decide for a suitable codec */
|
||||
rc = handle_codec_info(conn, p, have_sdp, true);
|
||||
mgcp_codec_summary(conn);
|
||||
if (rc) {
|
||||
error_code = rc;
|
||||
goto error2;
|
||||
}
|
||||
|
||||
conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
|
||||
tcfg->audio_fmtp_extra);
|
||||
|
||||
@@ -718,11 +917,15 @@ mgcp_header_done:
|
||||
error2:
|
||||
mgcp_endp_release(endp);
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"CRCX: endpoint:0x%x unable to create connection resource error\n",
|
||||
"CRCX: endpoint:0x%x unable to create connection\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return create_err_response(endp, error_code, "CRCX", p->trans);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* MDCX command handler, processes the received command */
|
||||
static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
|
||||
{
|
||||
@@ -814,23 +1017,27 @@ mgcp_header_done:
|
||||
} else
|
||||
conn->conn->mode = conn->conn->mode_orig;
|
||||
|
||||
if (have_sdp)
|
||||
mgcp_parse_sdp_data(endp, conn, p);
|
||||
/* Set local connection options, if present */
|
||||
if (local_options) {
|
||||
rc = set_local_cx_options(endp->tcfg->endpoints,
|
||||
&endp->local_options, local_options);
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint:%x inavlid local connection options!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
error_code = rc;
|
||||
goto error3;
|
||||
}
|
||||
}
|
||||
|
||||
rc = set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
|
||||
local_options);
|
||||
if (rc != 0) {
|
||||
LOGP(DLMGCP, LOGL_ERROR,
|
||||
"MDCX: endpoint:%x inavlid local connection options!\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
/* Handle codec information and decide for a suitable codec */
|
||||
rc = handle_codec_info(conn, p, have_sdp, false);
|
||||
mgcp_codec_summary(conn);
|
||||
if (rc) {
|
||||
error_code = rc;
|
||||
goto error3;
|
||||
}
|
||||
|
||||
if (!have_sdp && endp->local_options.codec)
|
||||
mgcp_set_audio_info(p->cfg, &conn->end.codec,
|
||||
PTYPE_UNDEFINED, endp->local_options.codec);
|
||||
|
||||
/* check connection mode setting */
|
||||
if (conn->conn->mode != MGCP_CONN_LOOPBACK
|
||||
&& conn->conn->mode != MGCP_CONN_RECV_ONLY
|
||||
@@ -842,6 +1049,7 @@ mgcp_header_done:
|
||||
goto error3;
|
||||
}
|
||||
|
||||
|
||||
if (setup_rtp_processing(endp, conn) != 0)
|
||||
goto error3;
|
||||
|
||||
|
||||
@@ -25,9 +25,13 @@
|
||||
#include <osmocom/mgcp/mgcp_internal.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
/* A struct to store intermediate parsing results. The function
|
||||
* mgcp_parse_sdp_data() is using it as temporary storage for parsing the SDP
|
||||
* codec information. */
|
||||
struct sdp_rtp_map {
|
||||
/* the type */
|
||||
int payload_type;
|
||||
@@ -40,89 +44,8 @@ struct sdp_rtp_map {
|
||||
int channels;
|
||||
};
|
||||
|
||||
/*! Set codec configuration depending on payload type and codec name.
|
||||
* \param[in] ctx talloc context.
|
||||
* \param[out] codec configuration (caller provided memory).
|
||||
* \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined).
|
||||
* \param[in] audio_name audio codec name (e.g. "GSM/8000/1").
|
||||
* \returns 0 on success, -1 on failure. */
|
||||
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(DLMGCP, LOGL_NOTICE,
|
||||
"Channels != 1 in SDP: '%s'\n", audio_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function to extrapolate missing codec parameters in a codec mao from
|
||||
* an already filled in payload_type, called from: mgcp_parse_sdp_data() */
|
||||
static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
||||
{
|
||||
int i;
|
||||
@@ -149,10 +72,16 @@ static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
|
||||
codecs[i].rate = 8000;
|
||||
codecs[i].channels = 1;
|
||||
break;
|
||||
default:
|
||||
codecs[i].codec_name = NULL;
|
||||
codecs[i].rate = 0;
|
||||
codecs[i].channels = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to update codec map information with additional data from
|
||||
* SDP, called from: mgcp_parse_sdp_data() */
|
||||
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
int payload, const char *audio_name)
|
||||
{
|
||||
@@ -162,8 +91,13 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
char audio_codec[64];
|
||||
int rate = -1;
|
||||
int channels = -1;
|
||||
|
||||
/* Note: We can only update payload codecs that already exist
|
||||
* in our codec list. If we get an unexpected payload type,
|
||||
* we just drop it */
|
||||
if (codecs[i].payload_type != payload)
|
||||
continue;
|
||||
|
||||
if (sscanf(audio_name, "%63[^/]/%d/%d",
|
||||
audio_codec, &rate, &channels) < 1) {
|
||||
LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
|
||||
@@ -182,43 +116,72 @@ static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
|
||||
audio_name);
|
||||
}
|
||||
|
||||
/* Check if the codec matches what is set up in the trunk config */
|
||||
static int is_codec_compatible(const struct mgcp_endpoint *endp,
|
||||
const struct sdp_rtp_map *codec)
|
||||
/* Extract payload types from SDP, also check for duplicates */
|
||||
static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
|
||||
unsigned int codecs_len, char *sdp)
|
||||
{
|
||||
char *codec_str;
|
||||
char audio_codec[64];
|
||||
char *str;
|
||||
char *str_ptr;
|
||||
char *pt_str;
|
||||
unsigned int pt;
|
||||
unsigned int count = 0;
|
||||
unsigned int i;
|
||||
|
||||
if (!codec->codec_name)
|
||||
return 0;
|
||||
str = talloc_zero_size(ctx, strlen(sdp) + 1);
|
||||
str_ptr = str;
|
||||
strcpy(str_ptr, sdp);
|
||||
|
||||
/* GSM, GSM/8000 and GSM/8000/1 should all be compatible...
|
||||
* let's go by name first. */
|
||||
codec_str = endp->tcfg->audio_name;
|
||||
if (sscanf(codec_str, "%63[^/]/%*d/%*d", audio_codec) < 1)
|
||||
return 0;
|
||||
str_ptr = strstr(str_ptr, "RTP/AVP ");
|
||||
if (!str_ptr)
|
||||
goto exit;
|
||||
|
||||
return strcasecmp(audio_codec, codec->codec_name) == 0;
|
||||
pt_str = strtok(str_ptr, " ");
|
||||
if (!pt_str)
|
||||
goto exit;
|
||||
|
||||
while (1) {
|
||||
/* Do not allow excessive payload types */
|
||||
if (count > codecs_len)
|
||||
goto error;
|
||||
|
||||
pt_str = strtok(NULL, " ");
|
||||
if (!pt_str)
|
||||
break;
|
||||
|
||||
pt = atoi(pt_str);
|
||||
|
||||
/* Do not allow duplicate payload types */
|
||||
for (i = 0; i < count; i++)
|
||||
if (codecs[i].payload_type == pt)
|
||||
goto error;
|
||||
|
||||
codecs[count].payload_type = pt;
|
||||
count++;
|
||||
}
|
||||
|
||||
exit:
|
||||
talloc_free(str);
|
||||
return count;
|
||||
error:
|
||||
talloc_free(str);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*! Analyze SDP input string.
|
||||
* \param[in] endp trunk endpoint.
|
||||
* \param[out] conn associated rtp connection.
|
||||
* \param[out] caller provided memory to store the parsing results.
|
||||
* \returns 0 on success, -1 on failure.
|
||||
*
|
||||
* Note: In conn (conn->end) the function returns the packet duration,
|
||||
* the rtp port and the rtcp port */
|
||||
* rtp port, rtcp port and the codec information.
|
||||
* \returns 0 on success, -1 on failure. */
|
||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
struct mgcp_conn_rtp *conn,
|
||||
struct mgcp_parse_data *p)
|
||||
struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
|
||||
{
|
||||
struct sdp_rtp_map codecs[10];
|
||||
int codecs_used = 0;
|
||||
struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
|
||||
unsigned int codecs_used = 0;
|
||||
char *line;
|
||||
int maxptime = -1;
|
||||
int i;
|
||||
int codecs_assigned = 0;
|
||||
unsigned int i;
|
||||
void *tmp_ctx = talloc_new(NULL);
|
||||
struct mgcp_rtp_end *rtp;
|
||||
|
||||
@@ -255,30 +218,21 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
rtp->packet_duration_ms = 0;
|
||||
else
|
||||
rtp->packet_duration_ms = ptime;
|
||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2)
|
||||
== 1) {
|
||||
maxptime = ptime2;
|
||||
} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
|
||||
rtp->maximum_packet_time = ptime2;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
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) {
|
||||
rc = sscanf(line, "m=audio %d RTP/AVP", &port);
|
||||
if (rc == 1) {
|
||||
rtp->rtp_port = htons(port);
|
||||
rtp->rtcp_port = htons(port + 1);
|
||||
codecs_used = rc - 1;
|
||||
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
||||
}
|
||||
|
||||
rc = pt_from_sdp(conn->conn, codecs,
|
||||
ARRAY_SIZE(codecs), line);
|
||||
if (rc > 0)
|
||||
codecs_used = rc;
|
||||
break;
|
||||
case 'c':
|
||||
|
||||
@@ -299,43 +253,37 @@ int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
|
||||
break;
|
||||
}
|
||||
}
|
||||
OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
|
||||
|
||||
/* 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;
|
||||
/* So far we have only set the payload type in the codec struct. Now we
|
||||
* fill up the remaining fields of the codec description with some default
|
||||
* information */
|
||||
codecs_initialize(tmp_ctx, codecs, codecs_used);
|
||||
|
||||
if (endp->tcfg->no_audio_transcoding &&
|
||||
!is_codec_compatible(endp, &codecs[i])) {
|
||||
LOGP(DLMGCP, 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(DLMGCP, 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));
|
||||
/* Store parsed codec information */
|
||||
for (i = 0; i < codecs_used; i++) {
|
||||
rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line);
|
||||
if (rc < 0)
|
||||
LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
return codecs_assigned > 0;
|
||||
|
||||
LOGP(DLMGCP, LOGL_NOTICE,
|
||||
"Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
|
||||
ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
|
||||
rtp->packet_duration_ms);
|
||||
if (codecs_used == 0)
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "none");
|
||||
for (i = 0; i < codecs_used; i++) {
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
|
||||
rtp->codecs[i].payload_type,
|
||||
rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, " ");
|
||||
}
|
||||
LOGPC(DLMGCP, LOGL_NOTICE, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Generate SDP response string.
|
||||
@@ -380,7 +328,9 @@ int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
|
||||
if (rc < 0)
|
||||
goto buffer_too_small;
|
||||
|
||||
if (audio_name && endp->tcfg->audio_send_name) {
|
||||
/* FIXME: Check if the payload type is from the static range,
|
||||
* if yes, omitthe a=rtpmap since it is unnecessary */
|
||||
if (audio_name && endp->tcfg->audio_send_name && (payload_type >= 96 && payload_type <= 127)) {
|
||||
rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n",
|
||||
payload_type, audio_name);
|
||||
|
||||
|
||||
@@ -87,9 +87,9 @@ static void mgcp_format_stats_rtp(char *str, size_t str_len,
|
||||
if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
|
||||
/* Error Counter */
|
||||
nchars = snprintf(str, str_len,
|
||||
"\r\nX-Osmo-CP: EC TI=%u, TO=%u",
|
||||
conn->state.in_stream.err_ts_counter,
|
||||
conn->state.out_stream.err_ts_counter);
|
||||
"\r\nX-Osmo-CP: EC TI=%lu, TO=%lu",
|
||||
conn->state.in_stream.err_ts_ctr->current,
|
||||
conn->state.out_stream.err_ts_ctr->current);
|
||||
if (nchars < 0 || nchars >= str_len)
|
||||
goto truncate;
|
||||
|
||||
|
||||
@@ -157,18 +157,19 @@ static int config_write_mgcp(struct vty *vty)
|
||||
static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
|
||||
struct mgcp_rtp_end *end)
|
||||
{
|
||||
struct mgcp_rtp_codec *codec = &end->codec;
|
||||
struct mgcp_rtp_codec *codec = end->codec;
|
||||
|
||||
vty_out(vty,
|
||||
" Timestamp Errs: %d->%d%s"
|
||||
" Timestamp Errs: %lu->%lu%s"
|
||||
" Dropped Packets: %d%s"
|
||||
" Payload Type: %d Rate: %u Channels: %d %s"
|
||||
" Frame Duration: %u Frame Denominator: %u%s"
|
||||
" FPP: %d Packet Duration: %u%s"
|
||||
" FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s"
|
||||
" Output-Enabled: %d Force-PTIME: %d%s",
|
||||
state->in_stream.err_ts_counter,
|
||||
state->out_stream.err_ts_counter, VTY_NEWLINE,
|
||||
state->in_stream.err_ts_ctr->current,
|
||||
state->out_stream.err_ts_ctr->current,
|
||||
VTY_NEWLINE,
|
||||
end->stats.dropped_packets, VTY_NEWLINE,
|
||||
codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE,
|
||||
codec->frame_duration_num, codec->frame_duration_den,
|
||||
@@ -178,15 +179,31 @@ static void dump_rtp_end(struct vty *vty, struct mgcp_rtp_state *state,
|
||||
end->force_output_ptime, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
|
||||
int verbose)
|
||||
static void dump_endpoint(struct vty *vty, struct mgcp_endpoint *endp, int epidx, int verbose)
|
||||
{
|
||||
int i;
|
||||
struct mgcp_conn *conn;
|
||||
|
||||
vty_out(vty, "%s trunk nr %d with %d endpoints:%s",
|
||||
cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
|
||||
cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
|
||||
vty_out(vty, "Endpoint %s%d:%s", MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, epidx, VTY_NEWLINE);
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
vty_out(vty, " CONN: %s%s",
|
||||
mgcp_conn_dump(conn), VTY_NEWLINE);
|
||||
|
||||
if (verbose) {
|
||||
/* FIXME: Also add verbosity for other
|
||||
* connection types (E1) as soon as
|
||||
* the implementation is available */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
dump_rtp_end(vty, &conn->u.rtp.state,
|
||||
&conn->u.rtp.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_endpoints(struct vty *vty, struct mgcp_trunk_config *cfg, int verbose)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!cfg->endpoints) {
|
||||
vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE);
|
||||
@@ -195,26 +212,22 @@ static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
|
||||
|
||||
for (i = 1; i < cfg->number_endpoints; ++i) {
|
||||
struct mgcp_endpoint *endp = &cfg->endpoints[i];
|
||||
|
||||
vty_out(vty, "Endpoint 0x%.2x:%s", i, VTY_NEWLINE);
|
||||
|
||||
llist_for_each_entry(conn, &endp->conns, entry) {
|
||||
vty_out(vty, " CONN: %s%s",
|
||||
mgcp_conn_dump(conn), VTY_NEWLINE);
|
||||
|
||||
if (verbose) {
|
||||
/* FIXME: Also add verbosity for other
|
||||
* connection types (E1) as soon as
|
||||
* the implementation is available */
|
||||
if (conn->type == MGCP_CONN_TYPE_RTP) {
|
||||
dump_rtp_end(vty, &conn->u.rtp.state,
|
||||
&conn->u.rtp.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
dump_endpoint(vty, endp, i, verbose);
|
||||
if (i < cfg->number_endpoints - 1)
|
||||
vty_out(vty, "%s", VTY_NEWLINE);
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg,
|
||||
int verbose)
|
||||
{
|
||||
vty_out(vty, "%s trunk nr %d with %d endpoints:%s",
|
||||
cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1",
|
||||
cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE);
|
||||
|
||||
dump_endpoints(vty, cfg, verbose);
|
||||
}
|
||||
|
||||
DEFUN(show_mcgp, show_mgcp_cmd,
|
||||
"show mgcp [stats]",
|
||||
SHOW_STR
|
||||
@@ -236,6 +249,33 @@ DEFUN(show_mcgp, show_mgcp_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_mcgp_endpoint, show_mgcp_endpoint_cmd,
|
||||
"show mgcp trunk <0-255> endpoint rtpbridge/<1-65534>",
|
||||
SHOW_STR
|
||||
"Display information about MGCP Media Gateway endpoint\n"
|
||||
"Include Statistics\n")
|
||||
{
|
||||
struct mgcp_trunk_config *trunk;
|
||||
int trunkidx = atoi(argv[1]);
|
||||
int epidx = atoi(argv[2]);
|
||||
int tidx = 0, i;
|
||||
|
||||
llist_for_each_entry(trunk, &g_cfg->trunks, entry) {
|
||||
if (tidx++ == trunkidx) {
|
||||
for (i = 1; i < trunk->number_endpoints; ++i) {
|
||||
struct mgcp_endpoint *endp = &trunk->endpoints[i];
|
||||
if (i == epidx) {
|
||||
dump_endpoint(vty, endp, i, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp, cfg_mgcp_cmd, "mgcp", "Configure the MGCP")
|
||||
{
|
||||
vty->node = MGCP_NODE;
|
||||
@@ -281,13 +321,6 @@ DEFUN(cfg_mgcp_bind_early,
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
static void parse_range(struct mgcp_port_range *range, const char **argv)
|
||||
{
|
||||
range->range_start = atoi(argv[0]);
|
||||
range->range_end = atoi(argv[1]);
|
||||
range->last_port = g_cfg->net_ports.range_start;
|
||||
}
|
||||
|
||||
#define RTP_STR "RTP configuration\n"
|
||||
#define UDP_PORT_STR "UDP Port number\n"
|
||||
#define NET_START_STR "First UDP port allocated\n"
|
||||
@@ -296,11 +329,38 @@ static void parse_range(struct mgcp_port_range *range, const char **argv)
|
||||
|
||||
DEFUN(cfg_mgcp_rtp_port_range,
|
||||
cfg_mgcp_rtp_port_range_cmd,
|
||||
"rtp port-range <0-65534> <0-65534>",
|
||||
"rtp port-range <1024-65534> <1025-65535>",
|
||||
RTP_STR "Range of ports to use for the NET side\n"
|
||||
RANGE_START_STR RANGE_END_STR)
|
||||
{
|
||||
parse_range(&g_cfg->net_ports, argv);
|
||||
int start;
|
||||
int end;
|
||||
|
||||
start = atoi(argv[0]);
|
||||
end = atoi(argv[1]);
|
||||
|
||||
if (end < start) {
|
||||
vty_out(vty, "range end port (%i) must be greater than the range start port (%i)!%s",
|
||||
end, start, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (start & 1) {
|
||||
vty_out(vty, "range must begin at an even port number, autocorrecting port (%i) to: %i%s",
|
||||
start, start & 0xFFFE, VTY_NEWLINE);
|
||||
start &= 0xFFFE;
|
||||
}
|
||||
|
||||
if ((end & 1) == 0) {
|
||||
vty_out(vty, "range must end at an odd port number, autocorrecting port (%i) to: %i%s",
|
||||
end, end | 1, VTY_NEWLINE);
|
||||
end |= 1;
|
||||
}
|
||||
|
||||
g_cfg->net_ports.range_start = start;
|
||||
g_cfg->net_ports.range_end = end;
|
||||
g_cfg->net_ports.last_port = g_cfg->net_ports.range_start;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
ALIAS_DEPRECATED(cfg_mgcp_rtp_port_range,
|
||||
@@ -1192,6 +1252,7 @@ DEFUN(cfg_mgcp_domain,
|
||||
int mgcp_vty_init(void)
|
||||
{
|
||||
install_element_ve(&show_mgcp_cmd);
|
||||
install_element_ve(&show_mgcp_endpoint_cmd);
|
||||
install_element(ENABLE_NODE, &loop_conn_cmd);
|
||||
install_element(ENABLE_NODE, &tap_rtp_cmd);
|
||||
install_element(ENABLE_NODE, &free_endp_cmd);
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <osmocom/mgcp/mgcp_stat.h>
|
||||
#include <osmocom/mgcp/mgcp_msg.h>
|
||||
#include <osmocom/mgcp/mgcp_endp.h>
|
||||
#include <osmocom/mgcp/mgcp_sdp.h>
|
||||
#include <osmocom/mgcp/mgcp_codec.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
@@ -156,8 +158,8 @@ static void test_strline(void)
|
||||
"s=-\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16002 RTP/AVP 96\r\n" \
|
||||
"a=rtpmap:96 AMR\r\n" \
|
||||
"m=audio 16002 RTP/AVP 112\r\n" \
|
||||
"a=rtpmap:112 AMR\r\n" \
|
||||
"a=ptime:40\r\n"
|
||||
|
||||
#define MDCX4_PT1 \
|
||||
@@ -404,7 +406,7 @@ static void test_strline(void)
|
||||
"v=0\r\n" \
|
||||
"o=- 1439038275 1439038275 IN IP4 192.168.181.247\r\n" \
|
||||
"s=-\r\nc=IN IP4 192.168.181.247\r\n" \
|
||||
"t=0 0\r\nm=audio 29084 RTP/AVP 255 0 8 3 18 4 96 97 101\r\n" \
|
||||
"t=0 0\r\nm=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101\r\n" \
|
||||
"a=rtpmap:0 PCMU/8000\r\n" \
|
||||
"a=rtpmap:8 PCMA/8000\r\n" \
|
||||
"a=rtpmap:3 gsm/8000\r\n" \
|
||||
@@ -425,7 +427,24 @@ static void test_strline(void)
|
||||
"I: %s\r\n" \
|
||||
"\r\n" \
|
||||
"c=IN IP4 8.8.8.8\r\n" \
|
||||
"m=audio 16434 RTP/AVP 255\r\n"
|
||||
"m=audio 16434 RTP/AVP 3\r\n"
|
||||
|
||||
#define CRCX_NO_LCO_NO_SDP \
|
||||
"CRCX 2 6@mgw MGCP 1.0\r\n" \
|
||||
"M: recvonly\r\n" \
|
||||
"C: 2\r\n"
|
||||
|
||||
#define CRCX_NO_LCO_NO_SDP_RET \
|
||||
"200 2 OK\r\n" \
|
||||
"I: %s\r\n" \
|
||||
"\r\n" \
|
||||
"v=0\r\n" \
|
||||
"o=- %s 23 IN IP4 0.0.0.0\r\n" \
|
||||
"s=-\r\n" \
|
||||
"c=IN IP4 0.0.0.0\r\n" \
|
||||
"t=0 0\r\n" \
|
||||
"m=audio 16008 RTP/AVP 0\r\n" \
|
||||
"a=ptime:20\r\n"
|
||||
|
||||
struct mgcp_test {
|
||||
const char *name;
|
||||
@@ -462,6 +481,7 @@ static const struct mgcp_test tests[] = {
|
||||
{"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
|
||||
"a=fmtp:126 0/1/2"},
|
||||
{"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
|
||||
{"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
|
||||
};
|
||||
|
||||
static const struct mgcp_test retransmit[] = {
|
||||
@@ -764,14 +784,14 @@ static void test_messages(void)
|
||||
fprintf(stderr, "endpoint %d: "
|
||||
"payload type %d (expected %d)\n",
|
||||
last_endpoint,
|
||||
conn->end.codec.payload_type, t->ptype);
|
||||
conn->end.codec->payload_type, t->ptype);
|
||||
|
||||
if (t->ptype != PTYPE_IGNORE)
|
||||
OSMO_ASSERT(conn->end.codec.payload_type ==
|
||||
OSMO_ASSERT(conn->end.codec->payload_type ==
|
||||
t->ptype);
|
||||
|
||||
/* Reset them again for next test */
|
||||
conn->end.codec.payload_type = PTYPE_NONE;
|
||||
conn->end.codec->payload_type = PTYPE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1129,10 +1149,12 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
uint32_t last_ssrc = 0;
|
||||
uint32_t last_timestamp = 0;
|
||||
uint32_t last_seqno = 0;
|
||||
int last_in_ts_err_cnt = 0;
|
||||
int last_out_ts_err_cnt = 0;
|
||||
uint64_t last_in_ts_err_cnt = 0;
|
||||
uint64_t last_out_ts_err_cnt = 0;
|
||||
struct mgcp_conn_rtp *conn = NULL;
|
||||
struct mgcp_conn *_conn = NULL;
|
||||
struct rate_ctr test_ctr_in;
|
||||
struct rate_ctr test_ctr_out;
|
||||
|
||||
printf("Testing packet error detection%s%s.\n",
|
||||
patch_ssrc ? ", patch SSRC" : "",
|
||||
@@ -1142,6 +1164,11 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
memset(&endp, 0, sizeof(endp));
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
memset(&test_ctr_in, 0, sizeof(test_ctr_in));
|
||||
memset(&test_ctr_out, 0, sizeof(test_ctr_out));
|
||||
state.in_stream.err_ts_ctr = &test_ctr_in;
|
||||
state.out_stream.err_ts_ctr = &test_ctr_out;
|
||||
|
||||
endp.type = &ep_typeset.rtp;
|
||||
|
||||
trunk.vty_number_endpoints = 1;
|
||||
@@ -1160,7 +1187,8 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
|
||||
rtp = &conn->end;
|
||||
|
||||
rtp->codec.payload_type = 98;
|
||||
OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1") == 0);
|
||||
rtp->codec = &rtp->codecs[0];
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
|
||||
struct rtp_packet_info *info = test_rtp_packets1 + i;
|
||||
@@ -1186,18 +1214,18 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts)
|
||||
state.in_stream.last_tsdelta, state.in_stream.last_seq);
|
||||
|
||||
printf("Out TS change: %d, dTS: %d, Seq change: %d, "
|
||||
"TS Err change: in %+d, out %+d\n",
|
||||
"TS Err change: in +%u, out +%u\n",
|
||||
state.out_stream.last_timestamp - last_timestamp,
|
||||
state.out_stream.last_tsdelta,
|
||||
state.out_stream.last_seq - last_seqno,
|
||||
state.in_stream.err_ts_counter - last_in_ts_err_cnt,
|
||||
state.out_stream.err_ts_counter - last_out_ts_err_cnt);
|
||||
(unsigned int) (state.in_stream.err_ts_ctr->current - last_in_ts_err_cnt),
|
||||
(unsigned int) (state.out_stream.err_ts_ctr->current - last_out_ts_err_cnt));
|
||||
|
||||
printf("Stats: Jitter = %u, Transit = %d\n",
|
||||
calc_jitter(&state), state.stats.transit);
|
||||
|
||||
last_in_ts_err_cnt = state.in_stream.err_ts_counter;
|
||||
last_out_ts_err_cnt = state.out_stream.err_ts_counter;
|
||||
last_in_ts_err_cnt = state.in_stream.err_ts_ctr->current;
|
||||
last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
|
||||
last_timestamp = state.out_stream.last_timestamp;
|
||||
last_seqno = state.out_stream.last_seq;
|
||||
}
|
||||
@@ -1236,8 +1264,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 18);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
|
||||
/* Allocate 2@mgw with three codecs, last one ignored */
|
||||
last_endpoint = -1;
|
||||
@@ -1252,10 +1279,14 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 18);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == 97);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
|
||||
/* Allocate 3@mgw with no codecs, check for PT == -1 */
|
||||
/* Allocate 3@mgw with no codecs, check for PT == 0 */
|
||||
/* Note: It usually makes no sense to leave the payload type list
|
||||
* out. However RFC 2327 does not clearly forbid this case and
|
||||
* it makes and since we already decided in OS#2658 that a missing
|
||||
* LCO should pick a sane default codec, it makes sense to expect
|
||||
* the same behaviour if SDP lacks proper payload type information */
|
||||
last_endpoint = -1;
|
||||
inp = create_msg(CRCX_MULT_3, NULL);
|
||||
resp = mgcp_handle_message(cfg, inp);
|
||||
@@ -1268,8 +1299,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == -1);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
|
||||
/* Allocate 4@mgw with a single codec */
|
||||
last_endpoint = -1;
|
||||
@@ -1284,8 +1314,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 18);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 18);
|
||||
|
||||
/* Allocate 5@mgw at select GSM.. */
|
||||
last_endpoint = -1;
|
||||
@@ -1303,8 +1332,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 3);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 3);
|
||||
|
||||
inp = create_msg(MDCX_NAT_DUMMY, conn_id);
|
||||
last_endpoint = -1;
|
||||
@@ -1315,8 +1343,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 3);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == -1);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 3);
|
||||
OSMO_ASSERT(conn->end.rtp_port == htons(16434));
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
inet_aton("8.8.8.8", &addr);
|
||||
@@ -1346,8 +1373,7 @@ static void test_multilple_codec(void)
|
||||
endp = &cfg->trunk.endpoints[last_endpoint];
|
||||
conn = mgcp_conn_get_rtp(endp, conn_id);
|
||||
OSMO_ASSERT(conn);
|
||||
OSMO_ASSERT(conn->end.codec.payload_type == 255);
|
||||
OSMO_ASSERT(conn->end.alt_codec.payload_type == 0);
|
||||
OSMO_ASSERT(conn->end.codec->payload_type == 0);
|
||||
|
||||
talloc_free(cfg);
|
||||
}
|
||||
@@ -1465,6 +1491,108 @@ const struct log_info log_info = {
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
||||
|
||||
static void test_get_lco_identifier(void)
|
||||
{
|
||||
char *test;
|
||||
printf("Testing get_lco_identifier()\n");
|
||||
|
||||
/* Normal case at the beginning */
|
||||
test = "p:10, a:PCMU";
|
||||
printf("%s -> %s\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "p:10, a:PCMU";
|
||||
printf("%s -> %s\n", test, get_lco_identifier(test));
|
||||
|
||||
/* Begin parsing in the middle of the value part of
|
||||
* the previous LCO option value */
|
||||
test = "XXXX, p:10, a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "XXXX,p:10,a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "10,a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "10, a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "10,a: PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = "10 ,a: PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
/* Begin parsing right at the end of the previous LCO
|
||||
* option value */
|
||||
test = ", a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
test = " a:PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
/* Empty string, result should be (null) */
|
||||
test = "";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
|
||||
/* Missing colons, result should be (null) */
|
||||
test = "p10, aPCMU";
|
||||
printf("%s -> %s\n", test, get_lco_identifier(test));
|
||||
|
||||
/* Colon separated from the identifier, result should be (null) */
|
||||
test = "10,a :PCMU";
|
||||
printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
|
||||
}
|
||||
|
||||
static void test_check_local_cx_options(void *ctx)
|
||||
{
|
||||
/* Legal cases */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p:10, IN:10") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "one:AAA, two:BB, tree:C") ==
|
||||
0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:G726-32") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10-20, b:64") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "b:32-64, e:off") == 0);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU;G726-32") == 0);
|
||||
|
||||
/* Illegal spaces before and after colon */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p :10") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a :PCMU, p:10") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p: 10, a:PCMU") == -1);
|
||||
|
||||
/* Check if multiple appearances of LCOs are rejected */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, p:10") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, a:PCMU, p:10") ==
|
||||
-1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, p:10") == -1);
|
||||
|
||||
/* Check if empty LCO are rejected */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p: , a:PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p: , a: PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a: PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:, a:PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:") == -1);
|
||||
|
||||
/* Garbeled beginning and ends */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, ":10, a:10") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "10, a:PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, ", a:PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, " a:PCMU") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU,") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, ") == -1);
|
||||
|
||||
/* Illegal strings */
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, " ") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, "AAA") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, ":,") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, ",:") == -1);
|
||||
OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
|
||||
@@ -1486,6 +1614,8 @@ int main(int argc, char **argv)
|
||||
test_no_cycle();
|
||||
test_no_name();
|
||||
test_osmux_cid();
|
||||
test_get_lco_identifier();
|
||||
test_check_local_cx_options(ctx);
|
||||
|
||||
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
|
||||
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
|
||||
|
||||
@@ -404,6 +404,21 @@ using message as statically defined for comparison
|
||||
================================================
|
||||
Testing DLCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
DLCX 7 1@mgw MGCP 1.0
|
||||
I: %s
|
||||
C: 2
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message as statically defined for comparison
|
||||
Response matches our expectations.
|
||||
(response contains a connection id)
|
||||
|
||||
================================================
|
||||
Testing CRCX
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
CRCX 2 6@mgw MGCP 1.0
|
||||
M: recvonly
|
||||
C: 2
|
||||
@@ -1031,7 +1046,7 @@ o=- 1439038275 1439038275 IN IP4 192.168.181.247
|
||||
|
||||
---------8<---------
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
---------8<---------
|
||||
CRCX 259260421 5@mgw MGCP 1.0
|
||||
C: 1355c6041e
|
||||
L: p:20, a:GSM, nt:IN
|
||||
@@ -1054,7 +1069,7 @@ C: 1355c6041e
|
||||
a=rtpmap:97 iLBC/8000
|
||||
a=fmtp:97 mode=30
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-15
|
||||
a=fmtp:101 0-15
|
||||
a=recvonly
|
||||
|
||||
---------8<---------
|
||||
@@ -1069,7 +1084,7 @@ o=- 1439038275 1439038275 IN IP4 192.168.181.247
|
||||
|
||||
---------8<---------
|
||||
creating message from statically defined input:
|
||||
---------8<---------
|
||||
---------8<---------
|
||||
CRCX 259260421 5@mgw MGCP 1.0
|
||||
C: 1355c6041e
|
||||
L: p:20, a:GSM, nt:IN
|
||||
@@ -1104,4 +1119,18 @@ a=ptime:40
|
||||
M: recvonly
|
||||
C: 2
|
||||
L: p:20
|
||||
|
||||
v=0
|
||||
c=IN IP4 123.12.12.123
|
||||
m=audio 5904 RTP/AVP 97
|
||||
a=rtpmap:97 GSM-EFR/8000
|
||||
a=ptime:40
|
||||
|
||||
---------8<---------
|
||||
checking response:
|
||||
using message with patched conn_id for comparison
|
||||
Response matches our expectations.
|
||||
Testing get_lco_identifier()
|
||||
p:10, a:PCMU -> p:10, a:PCMU
|
||||
p:10, a:PCMU -> p:10, a:PCMU
|
||||
'XXXX, p:10, a:PCMU' -> 'p:10, a:PCMU'
|
||||
|
||||
@@ -95,21 +95,26 @@ static int reply_to(mgcp_trans_id_t trans_id, int code, const char *comment,
|
||||
|
||||
void test_response_cb(struct mgcp_response *response, void *priv)
|
||||
{
|
||||
unsigned int i;
|
||||
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"
|
||||
" audio_ip = %s\n",
|
||||
response->head.response_code,
|
||||
response->head.trans_id,
|
||||
response->head.comment,
|
||||
response->audio_port,
|
||||
response->audio_ip
|
||||
);
|
||||
printf("response cb received:\n");
|
||||
printf(" head.response_code = %d\n", response->head.response_code);
|
||||
printf(" head.trans_id = %u\n", response->head.trans_id);
|
||||
printf(" head.comment = %s\n", response->head.comment);
|
||||
printf(" audio_port = %u\n", response->audio_port);
|
||||
printf(" audio_ip = %s\n", response->audio_ip);
|
||||
printf(" ptime = %u\n", response->ptime);
|
||||
printf(" codecs_len = %u\n", response->codecs_len);
|
||||
for(i=0;i<response->codecs_len;i++)
|
||||
printf(" codecs[%u] = %u\n", i, response->codecs[i]);
|
||||
printf(" ptmap_len = %u\n", response->ptmap_len);
|
||||
for(i=0;i<response->ptmap_len;i++) {
|
||||
printf(" ptmap[%u].codec = %u\n", i, response->ptmap[i].codec);
|
||||
printf(" ptmap[%u].pt = %u\n", i, response->ptmap[i].pt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mgcp_trans_id_t dummy_mgcp_send(struct msgb *msg)
|
||||
@@ -149,8 +154,9 @@ void test_crcx(void)
|
||||
"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"
|
||||
"m=audio 16002 RTP/AVP 110 96\r\n"
|
||||
"a=rtpmap:110 AMR/8000\r\n"
|
||||
"a=rtpmap:96 GSM-EFR/8000\r\n"
|
||||
"a=ptime:20\r\n");
|
||||
}
|
||||
|
||||
@@ -166,7 +172,15 @@ void test_mgcp_msg(void)
|
||||
.audio_port = 1234,
|
||||
.call_id = 47,
|
||||
.conn_id = "11",
|
||||
.conn_mode = MGCP_CONN_RECV_SEND
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
.ptime = 20,
|
||||
.codecs[0] = CODEC_GSM_8000_1,
|
||||
.codecs[1] = CODEC_AMR_8000_1,
|
||||
.codecs[2] = CODEC_GSMEFR_8000_1,
|
||||
.codecs_len = 1,
|
||||
.ptmap[0].codec = CODEC_GSMEFR_8000_1,
|
||||
.ptmap[0].pt = 96,
|
||||
.ptmap_len = 1
|
||||
};
|
||||
|
||||
if (mgcp)
|
||||
@@ -183,6 +197,26 @@ void test_mgcp_msg(void)
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated CRCX message (two codecs):\n");
|
||||
mgcp_msg.verb = MGCP_VERB_CRCX;
|
||||
mgcp_msg.presence =
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
|
||||
mgcp_msg.codecs_len = 2;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated CRCX message (three codecs, one with custom pt):\n");
|
||||
mgcp_msg.verb = MGCP_VERB_CRCX;
|
||||
mgcp_msg.presence =
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE);
|
||||
mgcp_msg.codecs_len = 3;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated MDCX message:\n");
|
||||
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||
mgcp_msg.presence =
|
||||
@@ -192,6 +226,28 @@ void test_mgcp_msg(void)
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated MDCX message (two codecs):\n");
|
||||
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||
mgcp_msg.presence =
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
|
||||
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
|
||||
mgcp_msg.codecs_len = 2;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated MDCX message (three codecs, one with custom pt):\n");
|
||||
mgcp_msg.verb = MGCP_VERB_MDCX;
|
||||
mgcp_msg.presence =
|
||||
(MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID |
|
||||
MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE |
|
||||
MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT);
|
||||
mgcp_msg.codecs_len = 3;
|
||||
msg = mgcp_msg_gen(mgcp, &mgcp_msg);
|
||||
mgcp_msg.codecs_len = 1;
|
||||
printf("%s\n", (char *)msg->data);
|
||||
|
||||
printf("Generated DLCX message:\n");
|
||||
mgcp_msg.verb = MGCP_VERB_DLCX;
|
||||
mgcp_msg.presence =
|
||||
@@ -242,6 +298,9 @@ void test_mgcp_client_cancel()
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID
|
||||
| MGCP_MSG_PRESENCE_CONN_ID | MGCP_MSG_PRESENCE_CONN_MODE),
|
||||
.ptime = 20,
|
||||
.codecs[0] = CODEC_AMR_8000_1,
|
||||
.codecs_len = 1
|
||||
};
|
||||
|
||||
printf("\n%s():\n", __func__);
|
||||
@@ -376,6 +435,99 @@ void test_sdp_section_start()
|
||||
OSMO_ASSERT(!failures);
|
||||
}
|
||||
|
||||
static void test_map_pt_to_codec(void)
|
||||
{
|
||||
/* Full form */
|
||||
OSMO_ASSERT(map_str_to_codec("PCMU/8000/1") == CODEC_PCMU_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM/8000/1") == CODEC_GSM_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("PCMA/8000/1") == CODEC_PCMA_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("G729/8000/1") == CODEC_G729_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM-EFR/8000/1") == CODEC_GSMEFR_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM-HR-08/8000/1") == CODEC_GSMHR_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("AMR/8000/1") == CODEC_AMR_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1") == CODEC_AMRWB_16000_1);
|
||||
|
||||
/* Short form */
|
||||
OSMO_ASSERT(map_str_to_codec("GSM-EFR") == CODEC_GSMEFR_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("G729") == CODEC_G729_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM-HR-08") == CODEC_GSMHR_8000_1);
|
||||
|
||||
/* We do not care about what is after the first delimiter */
|
||||
OSMO_ASSERT(map_str_to_codec("AMR-WB/123///456") == CODEC_AMRWB_16000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("PCMA/asdf") == CODEC_PCMA_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM/qwertz") == CODEC_GSM_8000_1);
|
||||
|
||||
/* A trailing delimiter should not hurt */
|
||||
OSMO_ASSERT(map_str_to_codec("AMR/") == CODEC_AMR_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("G729/") == CODEC_G729_8000_1);
|
||||
OSMO_ASSERT(map_str_to_codec("GSM/") == CODEC_GSM_8000_1);
|
||||
|
||||
/* This is expected to fail */
|
||||
OSMO_ASSERT(map_str_to_codec("INVALID/1234/7") == -1);
|
||||
OSMO_ASSERT(map_str_to_codec(NULL) == -1);
|
||||
OSMO_ASSERT(map_str_to_codec("") == -1);
|
||||
OSMO_ASSERT(map_str_to_codec("/////") == -1);
|
||||
|
||||
/* The buffers are 64 bytes long, check what happens with overlong
|
||||
* strings as input (This schould still work.) */
|
||||
OSMO_ASSERT(map_str_to_codec("AMR-WB/16000/1############################################################################################################") == CODEC_AMRWB_16000_1);
|
||||
|
||||
/* This should not work, as there is no delimiter after the codec
|
||||
* name */
|
||||
OSMO_ASSERT(map_str_to_codec("AMR-WB####################################################################################################################") == -1);
|
||||
}
|
||||
|
||||
static void test_map_codec_to_pt_and_map_pt_to_codec(void)
|
||||
{
|
||||
struct ptmap ptmap[10];
|
||||
unsigned int ptmap_len;
|
||||
unsigned int i;
|
||||
|
||||
ptmap[0].codec = CODEC_GSMEFR_8000_1;
|
||||
ptmap[0].pt = 96;
|
||||
ptmap[1].codec = CODEC_GSMHR_8000_1;
|
||||
ptmap[1].pt = 97;
|
||||
ptmap[2].codec = CODEC_AMR_8000_1;
|
||||
ptmap[2].pt = 98;
|
||||
ptmap[3].codec = CODEC_AMRWB_16000_1;
|
||||
ptmap[3].pt = 99;
|
||||
ptmap_len = 4;
|
||||
|
||||
/* Mappings that are covered by the table */
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
|
||||
printf("\n");
|
||||
|
||||
/* Map some codecs/payload types from the static range, result must
|
||||
* always be a 1:1 mapping */
|
||||
printf(" %u => %u\n", CODEC_PCMU_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMU_8000_1));
|
||||
printf(" %u => %u\n", CODEC_GSM_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_GSM_8000_1));
|
||||
printf(" %u => %u\n", CODEC_PCMA_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_PCMA_8000_1));
|
||||
printf(" %u => %u\n", CODEC_G729_8000_1, map_codec_to_pt(ptmap, ptmap_len, CODEC_G729_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_PCMU_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMU_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_GSM_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_GSM_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_PCMA_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_PCMA_8000_1));
|
||||
printf(" %u <= %u\n", CODEC_G729_8000_1, map_pt_to_codec(ptmap, ptmap_len, CODEC_G729_8000_1));
|
||||
printf("\n");
|
||||
|
||||
/* Try to do mappings from statically defined range to danymic range and vice versa. This
|
||||
* is illegal and should result into a 1:1 mapping */
|
||||
ptmap[3].codec = CODEC_AMRWB_16000_1;
|
||||
ptmap[3].pt = 2;
|
||||
ptmap[4].codec = CODEC_PCMU_8000_1;
|
||||
ptmap[4].pt = 100;
|
||||
ptmap_len = 5;
|
||||
|
||||
/* Apply all mappings again, the illegal ones we defined should result into 1:1 mappings */
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u => %u\n", ptmap[i].codec, map_codec_to_pt(ptmap, ptmap_len, ptmap[i].codec));
|
||||
for (i = 0; i < ptmap_len; i++)
|
||||
printf(" %u <= %u\n", ptmap[i].pt, map_pt_to_codec(ptmap, ptmap_len, ptmap[i].pt));
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
};
|
||||
|
||||
@@ -403,6 +555,8 @@ int main(int argc, char **argv)
|
||||
test_mgcp_msg();
|
||||
test_mgcp_client_cancel();
|
||||
test_sdp_section_start();
|
||||
test_map_codec_to_pt_and_map_pt_to_codec();
|
||||
test_map_pt_to_codec();
|
||||
|
||||
printf("Done\n");
|
||||
fprintf(stderr, "Done\n");
|
||||
|
||||
@@ -62,4 +62,8 @@ test_sdp_section_start() test [9]:
|
||||
body: "some mgcp header data\r\nand header params\n\r\rm=audio 23\r\n"
|
||||
DLMGCP MGCP response: cannot find start of SDP parameters
|
||||
got rc=-22
|
||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
DLMGCP ptmap contains illegal mapping: codec=113 maps to pt=2
|
||||
DLMGCP ptmap contains illegal mapping: codec=0 maps to pt=100
|
||||
Done
|
||||
|
||||
@@ -18,8 +18,9 @@ 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
|
||||
m=audio 16002 RTP/AVP 110 96
|
||||
a=rtpmap:110 AMR/8000
|
||||
a=rtpmap:96 GSM-EFR/8000
|
||||
a=ptime:20
|
||||
|
||||
-----
|
||||
@@ -29,16 +30,39 @@ response cb received:
|
||||
head.comment = OK
|
||||
audio_port = 16002
|
||||
audio_ip = 10.9.1.120
|
||||
ptime = 20
|
||||
codecs_len = 2
|
||||
codecs[0] = 112
|
||||
codecs[1] = 110
|
||||
ptmap_len = 2
|
||||
ptmap[0].codec = 112
|
||||
ptmap[0].pt = 110
|
||||
ptmap[1].codec = 110
|
||||
ptmap[1].pt = 96
|
||||
|
||||
Generated CRCX message:
|
||||
CRCX 1 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
L: p:20, a:AMR, nt:IN
|
||||
L: p:20, a:GSM, nt:IN
|
||||
M: sendrecv
|
||||
|
||||
Generated CRCX message (two codecs):
|
||||
CRCX 2 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
L: p:20, a:GSM;AMR, nt:IN
|
||||
M: sendrecv
|
||||
|
||||
Generated CRCX message (three codecs, one with custom pt):
|
||||
CRCX 3 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
L: p:20, a:GSM;AMR;GSM-EFR, nt:IN
|
||||
M: sendrecv
|
||||
|
||||
Generated MDCX message:
|
||||
MDCX 2 23@mgw MGCP 1.0
|
||||
MDCX 4 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
M: sendrecv
|
||||
@@ -48,18 +72,50 @@ o=- 2f 23 IN IP4 127.0.0.1
|
||||
s=-
|
||||
c=IN IP4 192.168.100.23
|
||||
t=0 0
|
||||
m=audio 1234 RTP/AVP 255
|
||||
m=audio 1234 RTP/AVP 3
|
||||
a=ptime:20
|
||||
|
||||
Generated MDCX message (two codecs):
|
||||
MDCX 5 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
M: sendrecv
|
||||
|
||||
v=0
|
||||
o=- 2f 23 IN IP4 127.0.0.1
|
||||
s=-
|
||||
c=IN IP4 192.168.100.23
|
||||
t=0 0
|
||||
m=audio 1234 RTP/AVP 3 112
|
||||
a=rtpmap:112 AMR/8000/1
|
||||
a=ptime:20
|
||||
|
||||
Generated MDCX message (three codecs, one with custom pt):
|
||||
MDCX 6 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
M: sendrecv
|
||||
|
||||
v=0
|
||||
o=- 2f 23 IN IP4 127.0.0.1
|
||||
s=-
|
||||
c=IN IP4 192.168.100.23
|
||||
t=0 0
|
||||
m=audio 1234 RTP/AVP 3 112 96
|
||||
a=rtpmap:112 AMR/8000/1
|
||||
a=rtpmap:96 GSM-EFR/8000/1
|
||||
a=ptime:20
|
||||
|
||||
Generated DLCX message:
|
||||
DLCX 3 23@mgw MGCP 1.0
|
||||
DLCX 7 23@mgw MGCP 1.0
|
||||
C: 2f
|
||||
I: 11
|
||||
|
||||
Generated AUEP message:
|
||||
AUEP 4 23@mgw MGCP 1.0
|
||||
AUEP 8 23@mgw MGCP 1.0
|
||||
|
||||
Generated RSIP message:
|
||||
RSIP 5 23@mgw MGCP 1.0
|
||||
RSIP 9 23@mgw MGCP 1.0
|
||||
|
||||
Overfolow test:
|
||||
|
||||
@@ -102,4 +158,33 @@ test_sdp_section_start() test [7]:
|
||||
test_sdp_section_start() test [8]:
|
||||
|
||||
test_sdp_section_start() test [9]:
|
||||
110 => 96
|
||||
111 => 97
|
||||
112 => 98
|
||||
113 => 99
|
||||
96 <= 110
|
||||
97 <= 111
|
||||
98 <= 112
|
||||
99 <= 113
|
||||
|
||||
0 => 0
|
||||
3 => 3
|
||||
8 => 8
|
||||
18 => 18
|
||||
0 <= 0
|
||||
3 <= 3
|
||||
8 <= 8
|
||||
18 <= 18
|
||||
|
||||
110 => 96
|
||||
111 => 97
|
||||
112 => 98
|
||||
113 => 113
|
||||
0 => 0
|
||||
96 <= 110
|
||||
97 <= 111
|
||||
98 <= 112
|
||||
2 <= 2
|
||||
100 <= 100
|
||||
|
||||
Done
|
||||
|
||||
Reference in New Issue
Block a user