From 33a648d4c694b8951db2b664ca8d8edb0cb9da26 Mon Sep 17 00:00:00 2001 From: Torrey Searle <torrey@voxbone.com> Date: Mon, 21 Aug 2017 11:28:52 +0200 Subject: [PATCH] res/res_pjsip_session: allow SDP answer to be regenerated If an SDP answer hasn't been sent yet, it's legal to change it. This is required for PJSIP_DTMF_MODE to work correctly, and can also have use in the future for updating codecs too. ASTERISK-27209 #close Change-Id: Idbbfb7cb3f72fbd96c94d10d93540f69bd51e7a1 --- channels/pjsip/dialplan_functions.c | 5 +++- include/asterisk/res_pjsip_session.h | 17 ++++++++++++ res/res_pjsip_session.c | 40 ++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c index c89d9ca262..93875b35c1 100644 --- a/channels/pjsip/dialplan_functions.c +++ b/channels/pjsip/dialplan_functions.c @@ -1241,10 +1241,13 @@ static int dtmf_mode_refresh_cb(void *obj) struct refresh_data *data = obj; if (data->session->inv_session->state == PJSIP_INV_STATE_CONFIRMED) { - ast_debug(3, "Changing DTMF mode on channel %s after OFFER/ANSER completion. Sending session refresh\n", ast_channel_name(data->session->channel)); + ast_debug(3, "Changing DTMF mode on channel %s after OFFER/ANSWER completion. Sending session refresh\n", ast_channel_name(data->session->channel)); ast_sip_session_refresh(data->session, NULL, NULL, sip_session_response_cb, data->method, 1, NULL); + } else if (data->session->inv_session->state == PJSIP_INV_STATE_INCOMING) { + ast_debug(3, "Changing DTMF mode on channel %s during OFFER/ANSWER exchange. Updating SDP answer\n", ast_channel_name(data->session->channel)); + ast_sip_session_regenerate_answer(data->session, NULL); } return 0; diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h index 5f49c82371..d5b6fa194e 100644 --- a/include/asterisk/res_pjsip_session.h +++ b/include/asterisk/res_pjsip_session.h @@ -672,6 +672,23 @@ int ast_sip_session_refresh(struct ast_sip_session *session, int generate_new_sdp, struct ast_sip_session_media_state *media_state); +/*! + * \brief Regenerate SDP Answer + * + * This method is used when an SDP offer has been received but an SDP answer + * has not been sent yet. It requests that a new local SDP be created and + * set as the SDP answer. As with any outgoing request in res_pjsip_session, + * this will call into registered supplements in case they wish to add anything. + * + * \param session The session on which the answer will be updated + * \param on_sdp_creation Callback called when SDP is created + * \param generate_new_sdp Boolean to indicate if a new SDP should be created + * \retval 0 Successfully updated the SDP answer + * \retval -1 Failure to updated the SDP answer + */ +int ast_sip_session_regenerate_answer(struct ast_sip_session *session, + ast_sip_session_sdp_creation_cb on_sdp_creation); + /*! * \brief Send a SIP response * diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index b6b8a21ac3..f6b3b937a1 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -1550,6 +1550,46 @@ int ast_sip_session_refresh(struct ast_sip_session *session, return 0; } +int ast_sip_session_regenerate_answer(struct ast_sip_session *session, + ast_sip_session_sdp_creation_cb on_sdp_creation) +{ + pjsip_inv_session *inv_session = session->inv_session; + pjmedia_sdp_session *new_answer = NULL; + const pjmedia_sdp_session *previous_offer = NULL; + + /* The SDP answer can only be regenerated if it is still pending to be sent */ + if (!inv_session->neg || (pjmedia_sdp_neg_get_state(inv_session->neg) != PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER && + pjmedia_sdp_neg_get_state(inv_session->neg) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)) { + ast_log(LOG_WARNING, "Requested to regenerate local SDP answer for channel '%s' but negotiation in state '%s'\n", + ast_channel_name(session->channel), pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state(inv_session->neg))); + return -1; + } + + pjmedia_sdp_neg_get_neg_remote(inv_session->neg, &previous_offer); + if (pjmedia_sdp_neg_get_state(inv_session->neg) == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { + /* Transition the SDP negotiator back to when it received the remote offer */ + pjmedia_sdp_neg_negotiate(inv_session->pool, inv_session->neg, 0); + pjmedia_sdp_neg_set_remote_offer(inv_session->pool, inv_session->neg, previous_offer); + } + + new_answer = create_local_sdp(inv_session, session, previous_offer); + if (!new_answer) { + ast_log(LOG_WARNING, "Could not create a new local SDP answer for channel '%s'\n", + ast_channel_name(session->channel)); + return -1; + } + + if (on_sdp_creation) { + if (on_sdp_creation(session, new_answer)) { + return -1; + } + } + + pjsip_inv_set_sdp_answer(inv_session, new_answer); + + return 0; +} + void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata) { handle_outgoing_response(session, tdata); -- GitLab