diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 76740c145546140e2d7e0a8a45c95e2a1b3d53d1..0eafb9c364655a140b41d7b3a2b88058c686ee8c 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -69,6 +69,13 @@ static int handle_incoming(struct ast_sip_session *session, pjsip_rx_data *rdata enum ast_sip_session_response_priority response_priority); static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata); static void handle_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata); +static int sip_session_refresh(struct ast_sip_session *session, + ast_sip_session_request_creation_cb on_request_creation, + ast_sip_session_sdp_creation_cb on_sdp_creation, + ast_sip_session_response_cb on_response, + enum ast_sip_session_refresh_method method, int generate_new_sdp, + struct ast_sip_session_media_state *media_state, + int queued); /*! \brief NAT hook for modifying outgoing messages with SDP */ static struct ast_sip_nat_hook *nat_hook; @@ -1244,8 +1251,18 @@ static void delayed_request_free(struct ast_sip_session_delayed_request *delay) ast_free(delay); } +/*! + * \internal + * \brief Send a delayed request + * + * \retval -1 failure + * \retval 0 success + * \retval 1 refresh request not sent as no change would occur + */ static int send_delayed_request(struct ast_sip_session *session, struct ast_sip_session_delayed_request *delay) { + int res; + ast_debug(3, "Endpoint '%s(%s)' sending delayed %s request.\n", ast_sorcery_object_get_id(session->endpoint), session->channel ? ast_channel_name(session->channel) : "", @@ -1253,19 +1270,19 @@ static int send_delayed_request(struct ast_sip_session *session, struct ast_sip_ switch (delay->method) { case DELAYED_METHOD_INVITE: - ast_sip_session_refresh(session, delay->on_request_creation, + res = sip_session_refresh(session, delay->on_request_creation, delay->on_sdp_creation, delay->on_response, - AST_SIP_SESSION_REFRESH_METHOD_INVITE, delay->generate_new_sdp, delay->media_state); + AST_SIP_SESSION_REFRESH_METHOD_INVITE, delay->generate_new_sdp, delay->media_state, 1); /* Ownership of media state transitions to ast_sip_session_refresh */ delay->media_state = NULL; - return 0; + return res; case DELAYED_METHOD_UPDATE: - ast_sip_session_refresh(session, delay->on_request_creation, + res = sip_session_refresh(session, delay->on_request_creation, delay->on_sdp_creation, delay->on_response, - AST_SIP_SESSION_REFRESH_METHOD_UPDATE, delay->generate_new_sdp, delay->media_state); + AST_SIP_SESSION_REFRESH_METHOD_UPDATE, delay->generate_new_sdp, delay->media_state, 1); /* Ownership of media state transitions to ast_sip_session_refresh */ delay->media_state = NULL; - return 0; + return res; case DELAYED_METHOD_BYE: ast_sip_session_terminate(session, 0); return 0; @@ -1300,7 +1317,9 @@ static int invite_proceeding(void *vsession) AST_LIST_REMOVE_CURRENT(next); res = send_delayed_request(session, delay); delayed_request_free(delay); - found = 1; + if (!res) { + found = 1; + } break; case DELAYED_METHOD_BYE: /* A BYE is pending so don't bother anymore. */ @@ -1354,7 +1373,9 @@ static int invite_terminated(void *vsession) AST_LIST_REMOVE_CURRENT(next); res = send_delayed_request(session, delay); delayed_request_free(delay); - break; + if (!res) { + break; + } } } AST_LIST_TRAVERSE_SAFE_END; @@ -1558,12 +1579,13 @@ static void set_from_header(struct ast_sip_session *session) } } -int ast_sip_session_refresh(struct ast_sip_session *session, +static int sip_session_refresh(struct ast_sip_session *session, ast_sip_session_request_creation_cb on_request_creation, ast_sip_session_sdp_creation_cb on_sdp_creation, ast_sip_session_response_cb on_response, enum ast_sip_session_refresh_method method, int generate_new_sdp, - struct ast_sip_session_media_state *media_state) + struct ast_sip_session_media_state *media_state, + int queued) { pjsip_inv_session *inv_session = session->inv_session; pjmedia_sdp_session *new_sdp = NULL; @@ -1630,6 +1652,20 @@ int ast_sip_session_refresh(struct ast_sip_session *session, int type_streams[AST_MEDIA_TYPE_END] = {0}; struct ast_stream *stream; + /* Media state conveys a desired media state, so if there are outstanding + * delayed requests we need to ensure we go into the queue and not jump + * ahead. If we sent this media state now then updates could go out of + * order. + */ + if (!queued && !AST_LIST_EMPTY(&session->delayed_requests)) { + ast_debug(3, "Delay sending reinvite to %s because of outstanding requests...\n", + ast_sorcery_object_get_id(session->endpoint)); + return delay_request(session, on_request_creation, on_sdp_creation, + on_response, generate_new_sdp, + method == AST_SIP_SESSION_REFRESH_METHOD_INVITE + ? DELAYED_METHOD_INVITE : DELAYED_METHOD_UPDATE, media_state); + } + /* Prune the media state so the number of streams fit within the configured limits - we do it here * so that the index of the resulting streams in the SDP match. If we simply left the streams out * of the SDP when producing it we'd be in trouble. We also enforce formats here for media types that @@ -1742,7 +1778,11 @@ int ast_sip_session_refresh(struct ast_sip_session *session, /* If the resulting media state matches the existing active state don't bother doing a session refresh */ if (ast_stream_topology_equal(session->active_media_state->topology, media_state->topology)) { ast_sip_session_media_state_free(media_state); - return 0; + /* For external consumers we return 0 to say success, but internally for + * send_delayed_request we return a separate value to indicate that this + * session refresh would be redundant so we didn't send it + */ + return queued ? 1 : 0; } } @@ -1794,6 +1834,17 @@ int ast_sip_session_refresh(struct ast_sip_session *session, return 0; } +int ast_sip_session_refresh(struct ast_sip_session *session, + ast_sip_session_request_creation_cb on_request_creation, + ast_sip_session_sdp_creation_cb on_sdp_creation, + ast_sip_session_response_cb on_response, + enum ast_sip_session_refresh_method method, int generate_new_sdp, + struct ast_sip_session_media_state *media_state) +{ + return sip_session_refresh(session, on_request_creation, on_sdp_creation, + on_response, method, generate_new_sdp, media_state, 0); +} + int ast_sip_session_regenerate_answer(struct ast_sip_session *session, ast_sip_session_sdp_creation_cb on_sdp_creation) {