diff --git a/src/call.c b/src/call.c index 9f593ea..cd3f508 100644 --- a/src/call.c +++ b/src/call.c @@ -172,3 +172,12 @@ const char *call_leg_state(struct call_leg *leg) return "Unknown call type"; } } + +void call_leg_update_sdp(struct call_leg *leg, const char *sdp) +{ + /* If no SDP was received, keep whatever SDP was previously seen. */ + if (!sdp || !*sdp || !strncmp(leg->sdp, sdp, sizeof(leg->sdp))) + return; + OSMO_STRLCPY_ARRAY(leg->sdp, sdp); + LOGP(DAPP, LOGL_DEBUG, "call(%u) leg(0x%p) received new SDP\n", leg->call->id, leg); +} diff --git a/src/call.h b/src/call.h index a835f71..af1964b 100644 --- a/src/call.h +++ b/src/call.h @@ -167,6 +167,8 @@ void calls_init(void); struct call_leg *call_leg_other(struct call_leg *leg); +void call_leg_update_sdp(struct call_leg *cl, const char *sdp); + void call_leg_release(struct call_leg *leg); diff --git a/src/mncc.c b/src/mncc.c index 02ba2cc..24c702d 100644 --- a/src/mncc.c +++ b/src/mncc.c @@ -195,6 +195,8 @@ static bool send_rtp_connect(struct mncc_call_leg *leg, struct call_leg *other) mncc.callref = leg->callref; mncc.addr = other->addr; mncc.payload_type = other->payload_type; + OSMO_STRLCPY_ARRAY(mncc.sdp, other->sdp); + /* * FIXME: mncc.payload_msg_type should already be compatible.. but * payload_type should be different.. @@ -408,6 +410,8 @@ static void check_rtp_connect(struct mncc_connection *conn, const char *buf, int return; } + call_leg_update_sdp(&leg->base, rtp->sdp); + /* extract information about where the RTP is */ if (rtp->addr.ss_family != AF_UNSPEC || osmo_sockaddr_port((const struct sockaddr *)&rtp->addr) != 0 || @@ -446,6 +450,7 @@ static void check_rtp_create(struct mncc_connection *conn, const char *buf, int leg->base.addr = rtp->addr; leg->base.payload_type = rtp->payload_type; leg->base.payload_msg_type = rtp->payload_msg_type; + call_leg_update_sdp(&leg->base, rtp->sdp); /* TODO.. now we can continue with the call */ LOGP(DMNCC, LOGL_INFO, @@ -557,6 +562,7 @@ static void check_setup(struct mncc_connection *conn, const char *buf, int rc) memcpy(&leg->called, called, sizeof(leg->called)); memcpy(&leg->calling, &data->calling, sizeof(leg->calling)); memcpy(&leg->imsi, data->imsi, sizeof(leg->imsi)); + call_leg_update_sdp(&leg->base, data->sdp); if (data->fields & MNCC_F_GCR) { leg->base.call->gcr_present = true; @@ -672,6 +678,8 @@ static void check_stp_cmpl_ind(struct mncc_connection *conn, const char *buf, in if (!leg) return; + call_leg_update_sdp(&leg->base, data->sdp); + LOGP(DMNCC, LOGL_INFO, "leg(%u) is now connected.\n", leg->callref); stop_cmd_timer(leg, MNCC_SETUP_COMPL_IND); leg->state = MNCC_CC_CONNECTED; @@ -706,6 +714,8 @@ static void check_cnf_ind(struct mncc_connection *conn, const char *buf, int rc) if (!leg) return; + call_leg_update_sdp(&leg->base, data->sdp); + LOGP(DMNCC, LOGL_DEBUG, "leg(%u) confirmed. creating RTP socket.\n", leg->callref); @@ -724,6 +734,8 @@ static void check_alrt_ind(struct mncc_connection *conn, const char *buf, int rc if (!leg) return; + call_leg_update_sdp(&leg->base, data->sdp); + LOGP(DMNCC, LOGL_DEBUG, "leg(%u) is alerting.\n", leg->callref); @@ -802,6 +814,8 @@ static void check_stp_cnf(struct mncc_connection *conn, const char *buf, int rc) if (!leg) return; + call_leg_update_sdp(&leg->base, data->sdp); + LOGP(DMNCC, LOGL_DEBUG, "leg(%u) setup completed\n", leg->callref); other_leg = call_leg_other(&leg->base); @@ -831,6 +845,8 @@ static void check_dtmf_start(struct mncc_connection *conn, const char *buf, int if (!leg) return; + call_leg_update_sdp(&leg->base, data->sdp); + LOGP(DMNCC, LOGL_DEBUG, "leg(%u) DTMF key=%c\n", leg->callref, data->keypad); other_leg = call_leg_other(&leg->base); @@ -853,6 +869,8 @@ static void check_dtmf_stop(struct mncc_connection *conn, const char *buf, int r if (!leg) return; + call_leg_update_sdp(&leg->base, data->sdp); + LOGP(DMNCC, LOGL_DEBUG, "leg(%u) DTMF key=%c\n", leg->callref, data->keypad); mncc_fill_header(&out_mncc, MNCC_STOP_DTMF_RSP, leg->callref); @@ -945,11 +963,15 @@ int mncc_create_remote_leg(struct mncc_connection *conn, struct call *call) msgb_free(msg); } + /* The call->initial leg is a SIP call leg that starts an MT call. There was SDP received in the SIP INVITE that + * started this call. This here will be the call->remote, always forwarding the SDP that came in on + * call->initial. */ + if (call->initial) + OSMO_STRLCPY_ARRAY(mncc.sdp, call->initial->sdp); + /* * TODO/FIXME: - * - Determine/request channel based on offered audio codecs * - Screening, redirect? - * - Synth. the bearer caps based on codecs? */ rc = mncc_write(conn, &mncc); if (rc != sizeof(mncc)) { diff --git a/src/sdp.c b/src/sdp.c index 7bfcff5..47a5d26 100644 --- a/src/sdp.c +++ b/src/sdp.c @@ -191,7 +191,9 @@ bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec) continue; for (map = media->m_rtpmaps; map; map = map->rm_next) { - if (!any_codec && strcasecmp(map->rm_encoding, leg->wanted_codec) != 0) + if (!any_codec + && leg->wanted_codec + && strcasecmp(map->rm_encoding, leg->wanted_codec) != 0) continue; port = media->m_port; @@ -226,55 +228,59 @@ bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec) return true; } +/* One leg has sent a SIP or MNCC message, which is now translated/forwarded to the counterpart MNCC or SIP. + * Take as much from the source's SDP as possible, but make sure the connection mode reflects the 'mode' arg (sendrecv, + * recvonly, sendonly, inactive). + * For example, if the MSC sent an MNCC_SETUP_IND, the SDP from the MNCC is found in 'other', while 'leg' reflects the + * SIP side that should receive this SDP in the SIP Invite that is being composed by the caller of this function. + * \param leg The target for which the returned SDP is intended. + * \param other The source of which we are to reflect the SDP. + * \return SDP string, using 'leg' as talloc ctx. + */ char *sdp_create_file(struct sip_call_leg *leg, struct call_leg *other, sdp_mode_t mode) { - char *fmtp_str = NULL, *sdp; - char *mode_attribute; - char ip_addr[INET6_ADDRSTRLEN]; - char ipv; + sdp_parser_t *parser; + sdp_session_t *sdp; + sdp_media_t *media; + const char *sdp_data; + sdp_printer_t *printer; + char buf[1024]; + const char *sdp_str; + char *ret; - osmo_sockaddr_ntop((const struct sockaddr*)&other->addr, ip_addr); - ipv = other->addr.ss_family == AF_INET6 ? '6' : '4'; - leg->wanted_codec = app_media_name(other->payload_msg_type); - - if (strcmp(leg->wanted_codec, "AMR") == 0) - fmtp_str = talloc_asprintf(leg, "a=fmtp:%d octet-align=1\r\n", other->payload_type); - - switch (mode) { - case sdp_inactive: - mode_attribute = "a=inactive\r\n"; - break; - case sdp_sendrecv: - mode_attribute = "a=sendrecv\r\n"; - break; - case sdp_sendonly: - mode_attribute = "a=sendonly\r\n"; - break; - case sdp_recvonly: - mode_attribute = "a=recvonly\r\n"; - break; - default: - OSMO_ASSERT(false); - break; + sdp_data = other->sdp; + parser = sdp_parse(NULL, sdp_data, strlen(sdp_data), 0); + if (!parser) { + LOGP(DSIP, LOGL_ERROR, "leg(%p) failed to parse SDP\n", other); + return talloc_strdup(leg, sdp_data); } - sdp = talloc_asprintf(leg, - "v=0\r\n" - "o=Osmocom 0 0 IN IP%c %s\r\n" - "s=GSM Call\r\n" - "c=IN IP%c %s\r\n" - "t=0 0\r\n" - "m=audio %d RTP/AVP %d\r\n" - "%s" - "a=rtpmap:%d %s/8000\r\n" - "%s", - ipv, ip_addr, ipv, ip_addr, - osmo_sockaddr_port((const struct sockaddr *)&other->addr), - other->payload_type, - fmtp_str ? fmtp_str : "", - other->payload_type, - leg->wanted_codec, - mode_attribute); - talloc_free(fmtp_str); - return sdp; + sdp = sdp_session(parser); + if (!sdp) { + LOGP(DSIP, LOGL_ERROR, "leg(%p) no sdp session\n", other); + sdp_parser_free(parser); + return talloc_strdup(leg, sdp_data); + } + + for (media = sdp->sdp_media; media; media = media->m_next) + media->m_mode = mode; + + printer = sdp_print(NULL, sdp, buf, sizeof(buf), sdp_f_mode_always); + if (!printer) { + LOGP(DSIP, LOGL_ERROR, "leg(%p) failed to print SDP\n", other); + sdp_parser_free(parser); + return talloc_strdup(leg, sdp_data); + } + + sdp_str = sdp_message(printer); + if (!sdp_str) { + LOGP(DSIP, LOGL_ERROR, "leg(%p) failed to print SDP: %s\n", other, sdp_printing_error(printer)); + sdp_str = sdp_data; + } + + ret = talloc_strdup(leg, sdp_str); + + sdp_parser_free(parser); + sdp_printer_free(printer); + return ret; } diff --git a/src/sdp.h b/src/sdp.h index 8e4e314..7e67365 100644 --- a/src/sdp.h +++ b/src/sdp.h @@ -8,8 +8,15 @@ struct sip_call_leg; struct call_leg; +enum sdp_mode_e { + SDP_MODE_INVALID = -1, + SDP_MODE_INACTIVE = 0, + SDP_MODE_SENDONLY = 1, + SDP_MODE_RECVONLY = 2, + SDP_MODE_SENDRECV = 3, +}; + bool sdp_get_sdp_mode(const sip_t *sip, sdp_mode_t *mode); bool sdp_screen_sdp(const sip_t *sip); bool sdp_extract_sdp(struct sip_call_leg *leg, const sip_t *sip, bool any_codec); - -char *sdp_create_file(struct sip_call_leg *, struct call_leg *, sdp_mode_t mode); +char *sdp_create_file(struct sip_call_leg *leg, struct call_leg *other, sdp_mode_t mode); diff --git a/src/sip.c b/src/sip.c index 9124752..97e11b7 100644 --- a/src/sip.c +++ b/src/sip.c @@ -46,6 +46,12 @@ static void sip_dtmf_call(struct call_leg *_leg, int keypad); static void sip_hold_call(struct call_leg *_leg); static void sip_retrieve_call(struct call_leg *_leg); +static const char *sip_get_sdp(const sip_t *sip) +{ + if (!sip || !sip->sip_payload) + return NULL; + return sip->sip_payload->pl_data; +} /* Find a SIP Call leg by given nua_handle */ static struct sip_call_leg *sip_find_leg(nua_handle_t *nh) @@ -201,6 +207,8 @@ static void new_call(struct sip_agent *agent, nua_handle_t *nh, nua_handle_bind(nh, leg); leg->sdp_payload = talloc_strdup(leg, sip->sip_payload->pl_data); + call_leg_update_sdp(&leg->base, sip_get_sdp(sip)); + app_route_call(call, talloc_strdup(leg, from), talloc_strdup(leg, to)); @@ -241,6 +249,8 @@ static void sip_handle_reinvite(struct sip_call_leg *leg, nua_handle_t *nh, cons osmo_sockaddr_ntop((struct sockaddr*)&prev_addr, ip_addr), osmo_sockaddr_port((struct sockaddr*)&prev_addr)); + call_leg_update_sdp(&leg->base, sip_get_sdp(sip)); + if (mode == sdp_sendonly) { /* SIP side places call on HOLD */ sdp = sdp_create_file(leg, other, sdp_recvonly); @@ -360,6 +370,8 @@ void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua, struct sip_call_leg *leg; leg = (struct sip_call_leg *) hmagic; + call_leg_update_sdp(&leg->base, sip_get_sdp(sip)); + /* MT call is moving forward */ /* The dialogue is now confirmed */ @@ -396,8 +408,10 @@ void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua, * respond to the re-INVITE query. */ if (sip->sip_payload && sip->sip_payload->pl_data) { struct sip_call_leg *leg = sip_find_leg(nh); - if (leg) + if (leg) { + call_leg_update_sdp(&leg->base, sip_get_sdp(sip)); sip_handle_reinvite(leg, nh, sip); + } } } else if (event == nua_r_bye || event == nua_r_cancel) { /* our bye or hang up is answered */ @@ -423,10 +437,12 @@ void nua_callback(nua_event_t event, int status, char const *phrase, nua_t *nua, if (status == 100) { struct sip_call_leg *leg = sip_find_leg(nh); - if (leg) + if (leg) { + call_leg_update_sdp(&leg->base, sip_get_sdp(sip)); sip_handle_reinvite(leg, nh, sip); - else + } else { new_call((struct sip_agent *) magic, nh, sip); + } } } else if (event == nua_i_cancel) { struct sip_call_leg *leg;