This patch parses the MGCP 'p' value of the local connection options, the 'ptime' and 'maxptime' SDP attributes, and the SDP rate information and sets up packet_duration_ms accordingly. The MGCP 'p' has priority over the values derived from the SDP data. If the packet duration is unknown or allows for different values (e.g. because 'p' uses a range or maxptime allows for more than one frame) the duration is set to 0.
Sponsored-by: On-Waves ehf --- openbsc/src/libmgcp/mgcp_network.c | 9 ++++ openbsc/src/libmgcp/mgcp_protocol.c | 89 +++++++++++++++++++++++++++++++---- openbsc/tests/mgcp/mgcp_test.c | 4 ++ openbsc/tests/mgcp/mgcp_test.ok | 14 +++--- 4 files changed, 100 insertions(+), 16 deletions(-)
diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index 8bd8afb..40227af 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -300,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 " diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 3e26ad6..ba53df1 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -75,7 +75,7 @@ char *strline_r(char *str, char **saveptr) /* Assume audio frame length of 20ms */ #define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20 #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 -#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 +#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 0 /* we don't know */ #define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end); @@ -562,25 +562,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 +647,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; }
+static void parse_local_cx_options(struct mgcp_rtp_end *rtp, const char *options) +{ + int ptime, ptime2 = 0; + char *p_opt = strstr(options, "p:"); + if (p_opt && sscanf(p_opt, "p:%d-%d", &ptime, &ptime2) >= 1) { + if (ptime2 > 0 && ptime2 != ptime) + ptime = 0; + + if (rtp->packet_duration_ms && + rtp->packet_duration_ms != ptime) + LOGP(DMGCP, LOGL_NOTICE, + "CRCX inconsistent ptime: SDP: %d, L: %d " + "(will use %d)\n", + rtp->packet_duration_ms, ptime, ptime); + + rtp->packet_duration_ms = ptime; + } +} + void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, struct mgcp_rtp_end *rtp) { @@ -707,7 +767,8 @@ mgcp_header_done: endp->callid = talloc_strdup(tcfg->endpoints, callid);
if (local_options) - endp->local_options = talloc_strdup(tcfg->endpoints, local_options); + endp->local_options = talloc_strdup(tcfg->endpoints, + local_options);
if (parse_conn_mode(mode, &endp->conn_mode) != 0) { error_code = 517; @@ -740,6 +801,9 @@ mgcp_header_done: if (have_sdp) parse_sdp_data(&endp->net_end, p);
+ if (local_options) + parse_local_cx_options(&endp->net_end, local_options); + /* policy CB */ if (p->cfg->policy_cb) { int rc; @@ -783,6 +847,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); @@ -806,7 +871,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) { @@ -832,6 +897,12 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) } }
+ if (local_options) { + endp->local_options = talloc_strdup(endp->tcfg->endpoints, + local_options); + parse_local_cx_options(&endp->net_end, local_options); + } + /* policy CB */ if (p->cfg->policy_cb) { int rc; diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c index 11e4b56..fb056e6 100644 --- a/openbsc/tests/mgcp/mgcp_test.c +++ b/openbsc/tests/mgcp/mgcp_test.c @@ -628,6 +628,10 @@ static void test_packet_error_detection(int patch_ssrc, int patch_ts) * trunk.endpoints are set up properly. */ mgcp_free_endp(&endp);
+ /* There is no MGCP/SDP around, so set this by hand to make timestamp + * patching possible */ + rtp->packet_duration_ms = 20; + rtp->payload_type = 98;
for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) { diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok index 5c6fe37..32af3b5 100644 --- a/openbsc/tests/mgcp/mgcp_test.ok +++ b/openbsc/tests/mgcp/mgcp_test.ok @@ -16,19 +16,19 @@ Testing AUEP2 Testing MDCX1 Testing MDCX2 Testing CRCX -Packet duration not set +Detected packet duration: 20 Testing MDCX3 Packet duration not set Testing MDCX4 -Packet duration not set +Detected packet duration: 20 Testing MDCX4_PT1 -Packet duration not set +Detected packet duration: 0 Testing MDCX4_PT2 -Packet duration not set +Detected packet duration: 20 Testing MDCX4_PT3 -Packet duration not set +Detected packet duration: 40 Testing DLCX -Detected packet duration: 20 +Detected packet duration: 0 Testing CRCX_ZYN Packet duration not set Testing EMPTY @@ -39,7 +39,7 @@ Testing SHORT4 Testing RQNT1 Testing RQNT2 Testing DLCX -Detected packet duration: 20 +Detected packet duration: 0 Testing CRCX Re-transmitting CRCX Testing RQNT1