aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <holger@moiji-mobile.com>2013-12-13 13:51:50 +0100
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2013-12-13 13:51:50 +0100
commitdaf2a38eb6ac143ade53e115f09ba1b8beb63ba8 (patch)
treecf2ee13ad6bff930d5243814db15c256b54e331e /openbsc/src
parente8ae1ac76ade2ee490e482871817d406f17ab0b4 (diff)
parent0a1bc56e5a55393ed5294a0671cd8feab78f454b (diff)
Merge branch 'jerlbeck/features/mgcp-get-info-from-sdp'
Diffstat (limited to 'openbsc/src')
-rw-r--r--openbsc/src/libmgcp/mgcp_network.c107
-rw-r--r--openbsc/src/libmgcp/mgcp_protocol.c109
-rw-r--r--openbsc/src/libmgcp/mgcp_vty.c50
3 files changed, 222 insertions, 44 deletions
diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c
index bf205d1c7..40227af01 100644
--- a/openbsc/src/libmgcp/mgcp_network.c
+++ b/openbsc/src/libmgcp/mgcp_network.c
@@ -200,6 +200,58 @@ static int check_rtp_timestamp(struct mgcp_endpoint *endp,
return 1;
}
+/* Set the timestamp offset according to the packet duration. */
+static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp,
+ struct mgcp_rtp_state *state,
+ struct mgcp_rtp_end *rtp_end,
+ struct sockaddr_in *addr,
+ int16_t delta_seq, uint32_t timestamp)
+{
+ int32_t tsdelta = state->packet_duration;
+ int timestamp_offset;
+
+ if (tsdelta == 0) {
+ tsdelta = state->out_stream.last_tsdelta;
+ if (tsdelta != 0) {
+ LOGP(DMGCP, LOGL_NOTICE,
+ "A fixed packet duration is not available on 0x%x, "
+ "using last output timestamp delta instead: %d "
+ "from %s:%d in %d\n",
+ ENDPOINT_NUMBER(endp), tsdelta,
+ inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+ endp->conn_mode);
+ } else {
+ tsdelta = rtp_end->rate * 20 / 1000;
+ LOGP(DMGCP, LOGL_NOTICE,
+ "Fixed packet duration and last timestamp delta "
+ "are not available on 0x%x, "
+ "using fixed 20ms instead: %d "
+ "from %s:%d in %d\n",
+ ENDPOINT_NUMBER(endp), tsdelta,
+ inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+ endp->conn_mode);
+ }
+ }
+
+ timestamp_offset =
+ state->out_stream.last_timestamp - timestamp +
+ delta_seq * tsdelta;
+
+ if (state->timestamp_offset != timestamp_offset) {
+ state->timestamp_offset = timestamp_offset;
+
+ LOGP(DMGCP, LOGL_NOTICE,
+ "Timestamp offset change on 0x%x SSRC: %u "
+ "SeqNo delta: %d, TS offset: %d, "
+ "from %s:%d in %d\n",
+ ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
+ delta_seq, state->timestamp_offset,
+ inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+ endp->conn_mode);
+ }
+
+ return timestamp_offset;
+}
/**
* The RFC 3550 Appendix A assumes there are multiple sources but
@@ -248,6 +300,15 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta
state->seq_offset, state->packet_duration,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
endp->conn_mode);
+ if (state->packet_duration == 0 && rtp_end->force_constant_timing)
+ LOGP(DMGCP, LOGL_ERROR,
+ "Cannot patch timestamps on 0x%x: "
+ "RTP packet duration is unknown, SSRC: %u, "
+ "from %s:%d in %d\n",
+ ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
+ inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+ endp->conn_mode);
+
} else if (state->in_stream.ssrc != ssrc) {
LOGP(DMGCP, LOGL_NOTICE,
"The SSRC changed on 0x%x: %u -> %u "
@@ -259,22 +320,14 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta
state->in_stream.ssrc = ssrc;
if (rtp_end->force_constant_ssrc) {
- int32_t tsdelta = state->out_stream.last_tsdelta;
- if (tsdelta == 0) {
- tsdelta = state->packet_duration;
- LOGP(DMGCP, LOGL_NOTICE,
- "Timestamp delta is not available on 0x%x, "
- "using packet duration instead: %d "
- "from %s:%d in %d\n",
- ENDPOINT_NUMBER(endp), tsdelta,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
- }
+ const int16_t delta_seq = 1;
+
state->seq_offset =
- (state->out_stream.last_seq + 1) - seq;
- state->timestamp_offset =
- (state->out_stream.last_timestamp + tsdelta) -
- timestamp;
+ (state->out_stream.last_seq + delta_seq) - seq;
+
+ adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
+ delta_seq, timestamp);
+
state->patch_ssrc = 1;
ssrc = state->orig_ssrc;
if (rtp_end->force_constant_ssrc != -1)
@@ -282,10 +335,10 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta
LOGP(DMGCP, LOGL_NOTICE,
"SSRC patching enabled on 0x%x SSRC: %u "
- "offset: %d tsdelta: %d "
+ "SeqNo offset: %d, TS offset: %d "
"from %s:%d in %d\n",
ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
- state->seq_offset, tsdelta,
+ state->seq_offset, state->timestamp_offset,
inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
endp->conn_mode);
}
@@ -307,22 +360,12 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta
if (rtp_end->force_constant_timing &&
state->out_stream.ssrc == ssrc && state->packet_duration) {
- int delta_seq = seq + state->seq_offset - state->out_stream.last_seq;
- int timestamp_offset =
- state->out_stream.last_timestamp - timestamp +
- delta_seq * state->packet_duration;
- if (state->timestamp_offset != timestamp_offset) {
- state->timestamp_offset = timestamp_offset;
+ /* Update the timestamp offset */
+ const int delta_seq =
+ seq + state->seq_offset - state->out_stream.last_seq;
- LOGP(DMGCP, LOGL_NOTICE,
- "Timestamp patching enabled on 0x%x SSRC: %u "
- "SeqNo delta: %d, TS offset: %d, "
- "from %s:%d in %d\n",
- ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
- delta_seq, state->timestamp_offset,
- inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
- endp->conn_mode);
- }
+ adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
+ delta_seq, timestamp);
}
/* Store the updated SSRC back to the packet */
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c
index ab941645e..7d3ad74df 100644
--- a/openbsc/src/libmgcp/mgcp_protocol.c
+++ b/openbsc/src/libmgcp/mgcp_protocol.c
@@ -230,11 +230,12 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
const char *addr = endp->cfg->local_ip;
const char *fmtp_extra = endp->bts_end.fmtp_extra;
char sdp_record[4096];
+ int len;
if (!addr)
addr = endp->cfg->source_addr;
- snprintf(sdp_record, sizeof(sdp_record) - 1,
+ len = snprintf(sdp_record, sizeof(sdp_record) - 1,
"I: %u\n\n"
"v=0\r\n"
"o=- %u 23 IN IP4 %s\r\n"
@@ -247,7 +248,25 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
endp->net_end.local_port, endp->bts_end.payload_type,
endp->bts_end.payload_type, endp->tcfg->audio_name,
fmtp_extra ? fmtp_extra : "", fmtp_extra ? "\r\n" : "");
+
+ if (len < 0 || len >= sizeof(sdp_record))
+ goto buffer_too_small;
+
+ if (endp->bts_end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
+ int nchars = snprintf(sdp_record + len, sizeof(sdp_record) - len,
+ "a=ptime:%d\r\n",
+ endp->bts_end.packet_duration_ms);
+ if (nchars < 0 || nchars >= sizeof(sdp_record) - len)
+ goto buffer_too_small;
+
+ len += nchars;
+ }
return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record);
+
+buffer_too_small:
+ LOGP(DMGCP, LOGL_ERROR, "SDP buffer too small: %d (needed %d)\n",
+ sizeof(sdp_record), len);
+ return NULL;
}
/*
@@ -562,25 +581,64 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
{
char *line;
int found_media = 0;
+ int audio_payload = -1;
for_each_line(line, p->save) {
switch (line[0]) {
- case 'a':
case 'o':
case 's':
case 't':
case 'v':
/* skip these SDP attributes */
break;
+ case 'a': {
+ int payload;
+ int rate;
+ int channels = 1;
+ int ptime, ptime2 = 0;
+ char audio_name[64];
+ char audio_codec[64];
+
+ if (audio_payload == -1)
+ break;
+
+ if (sscanf(line, "a=rtpmap:%d %64s",
+ &payload, audio_name) == 2) {
+ if (payload != audio_payload)
+ break;
+
+ if (sscanf(audio_name, "%[^/]/%d/%d",
+ audio_codec, &rate, &channels) < 2)
+ break;
+
+ rtp->rate = rate;
+ if (channels != 1)
+ LOGP(DMGCP, LOGL_NOTICE,
+ "Channels != 1 in SDP: '%s' on 0x%x\n",
+ line, ENDPOINT_NUMBER(p->endp));
+ } else if (sscanf(line, "a=ptime:%d-%d",
+ &ptime, &ptime2) >= 1) {
+ if (ptime2 > 0 && ptime2 != ptime)
+ rtp->packet_duration_ms = 0;
+ else
+ rtp->packet_duration_ms = ptime;
+ } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
+ if (ptime2 * rtp->frame_duration_den >
+ rtp->frame_duration_num * 1500)
+ /* more than 1 frame */
+ rtp->packet_duration_ms = 0;
+ }
+ break;
+ }
case 'm': {
int port;
- int payload;
+ audio_payload = -1;
if (sscanf(line, "m=audio %d RTP/AVP %d",
- &port, &payload) == 2) {
+ &port, &audio_payload) == 2) {
rtp->rtp_port = htons(port);
rtp->rtcp_port = htons(port + 1);
- rtp->payload_type = payload;
+ rtp->payload_type = audio_payload;
found_media = 1;
}
break;
@@ -608,12 +666,33 @@ static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
if (found_media)
LOGP(DMGCP, LOGL_NOTICE,
- "Got media info via SDP: port %d, payload %d, addr %s\n",
- ntohs(rtp->rtp_port), rtp->payload_type, inet_ntoa(rtp->addr));
+ "Got media info via SDP: port %d, payload %d, "
+ "duration %d, addr %s\n",
+ ntohs(rtp->rtp_port), rtp->payload_type,
+ rtp->packet_duration_ms, inet_ntoa(rtp->addr));
return found_media;
}
+/* Set the LCO from a string (see RFC 3435).
+ * The string is stored in the 'string' field. A NULL string is handled excatly
+ * like an empty string, the 'string' field is never NULL after this function
+ * has been called. */
+static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
+ const char *options)
+{
+ char *p_opt;
+
+ talloc_free(lco->string);
+ lco->pkt_period_min = lco->pkt_period_max = 0;
+ lco->string = talloc_strdup(ctx, options ? 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;
+}
+
void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
struct mgcp_rtp_end *rtp)
{
@@ -712,8 +791,8 @@ mgcp_header_done:
/* copy some parameters */
endp->callid = talloc_strdup(tcfg->endpoints, callid);
- if (local_options)
- endp->local_options = talloc_strdup(tcfg->endpoints, local_options);
+ set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
+ local_options);
if (parse_conn_mode(mode, &endp->conn_mode) != 0) {
error_code = 517;
@@ -789,6 +868,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
int error_code = 500;
int silent = 0;
char *line;
+ const char *local_options = NULL;
if (p->found != 0)
return create_err_response(NULL, 510, "MDCX", p->trans);
@@ -812,7 +892,7 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
break;
}
case 'L':
- /* skip */
+ local_options = (const char *) line + 3;
break;
case 'M':
if (parse_conn_mode(line + 3, &endp->conn_mode) != 0) {
@@ -838,6 +918,9 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
}
}
+ set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
+ local_options);
+
/* policy CB */
if (p->cfg->policy_cb) {
int rc;
@@ -1045,6 +1128,7 @@ struct mgcp_config *mgcp_config_alloc(void)
cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL;
cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000");
cfg->trunk.audio_payload = 126;
+ cfg->trunk.audio_send_ptime = 1;
cfg->trunk.omit_rtcp = 0;
INIT_LLIST_HEAD(&cfg->trunks);
@@ -1067,6 +1151,7 @@ struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr)
trunk->trunk_nr = nr;
trunk->audio_name = talloc_strdup(cfg, "AMR/8000");
trunk->audio_payload = 126;
+ trunk->audio_send_ptime = 1;
trunk->number_endpoints = 33;
trunk->omit_rtcp = 0;
llist_add_tail(&trunk->entry, &cfg->trunks);
@@ -1148,8 +1233,8 @@ void mgcp_free_endp(struct mgcp_endpoint *endp)
talloc_free(endp->callid);
endp->callid = NULL;
- talloc_free(endp->local_options);
- endp->local_options = NULL;
+ talloc_free(endp->local_options.string);
+ endp->local_options.string = NULL;
mgcp_rtp_end_reset(&endp->bts_end);
mgcp_rtp_end_reset(&endp->net_end);
diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c
index e48b0503e..235b8bd6a 100644
--- a/openbsc/src/libmgcp/mgcp_vty.c
+++ b/openbsc/src/libmgcp/mgcp_vty.c
@@ -105,6 +105,8 @@ static int config_write_mgcp(struct vty *vty)
if (g_cfg->trunk.audio_fmtp_extra)
vty_out(vty, " sdp audio fmtp-extra %s%s",
g_cfg->trunk.audio_fmtp_extra, VTY_NEWLINE);
+ vty_out(vty, " %ssdp audio-payload send-ptime%s",
+ g_cfg->trunk.audio_send_ptime ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " loop %u%s", !!g_cfg->trunk.audio_loop, VTY_NEWLINE);
vty_out(vty, " number endpoints %u%s", g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE);
if (g_cfg->call_agent_addr)
@@ -391,6 +393,26 @@ ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_name, cfg_mgcp_sdp_payload_name_cmd_old,
"sdp audio payload name NAME",
SDP_STR AUDIO_STR AUDIO_STR "Name\n" "Payload name\n")
+DEFUN(cfg_mgcp_sdp_payload_send_ptime,
+ cfg_mgcp_sdp_payload_send_ptime_cmd,
+ "sdp audio-payload send-ptime",
+ SDP_STR AUDIO_STR
+ "Send SDP ptime (packet duration) attribute\n")
+{
+ g_cfg->trunk.audio_send_ptime = 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_sdp_payload_send_ptime,
+ cfg_mgcp_no_sdp_payload_send_ptime_cmd,
+ "no sdp audio-payload send-ptime",
+ NO_STR SDP_STR AUDIO_STR
+ "Send SDP ptime (packet duration) attribute\n")
+{
+ g_cfg->trunk.audio_send_ptime = 0;
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_mgcp_loop,
cfg_mgcp_loop_cmd,
"loop (0|1)",
@@ -568,6 +590,8 @@ static int config_write_trunk(struct vty *vty)
trunk->audio_payload, VTY_NEWLINE);
vty_out(vty, " sdp audio-payload name %s%s",
trunk->audio_name, VTY_NEWLINE);
+ vty_out(vty, " %ssdp audio-payload send-ptime%s",
+ trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE);
vty_out(vty, " loop %d%s",
trunk->audio_loop, VTY_NEWLINE);
if (trunk->omit_rtcp)
@@ -649,6 +673,28 @@ DEFUN(cfg_trunk_loop,
return CMD_SUCCESS;
}
+DEFUN(cfg_trunk_sdp_payload_send_ptime,
+ cfg_trunk_sdp_payload_send_ptime_cmd,
+ "sdp audio-payload send-ptime",
+ SDP_STR AUDIO_STR
+ "Send SDP ptime (packet duration) attribute\n")
+{
+ struct mgcp_trunk_config *trunk = vty->index;
+ trunk->audio_send_ptime = 1;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_no_sdp_payload_send_ptime,
+ cfg_trunk_no_sdp_payload_send_ptime_cmd,
+ "no sdp audio-payload send-ptime",
+ NO_STR SDP_STR AUDIO_STR
+ "Send SDP ptime (packet duration) attribute\n")
+{
+ struct mgcp_trunk_config *trunk = vty->index;
+ trunk->audio_send_ptime = 0;
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_trunk_omit_rtcp,
cfg_trunk_omit_rtcp_cmd,
"rtcp-omit",
@@ -966,6 +1012,8 @@ int mgcp_vty_init(void)
install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ts_cmd);
install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_cmd);
install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_ptime_cmd);
+ install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_ptime_cmd);
install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
install_node(&trunk_node, config_write_trunk);
@@ -983,6 +1031,8 @@ int mgcp_vty_init(void)
install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ts_cmd);
install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd);
install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd);
+ install_element(TRUNK_NODE, &cfg_trunk_sdp_payload_send_ptime_cmd);
+ install_element(TRUNK_NODE, &cfg_trunk_no_sdp_payload_send_ptime_cmd);
return 0;
}