]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_pjsip_session: Always produce offer on re-INVITE without SDP.
authorJoshua C. Colp <jcolp@sangoma.com>
Tue, 16 Feb 2021 18:33:07 +0000 (14:33 -0400)
committerGeorge Joseph <gjoseph@digium.com>
Thu, 25 Feb 2021 14:42:07 +0000 (08:42 -0600)
When PJSIP receives a re-INVITE without an SDP offer the INVITE
session library will first call the on_create_offer callback and
if unavailable then use the active negotiated SDP as the offer.

In some cases this would result in a different SDP then was
previously used without an incremented SDP version number. The two
known cases are:

1. Sending an initial INVITE with a set of codecs and having the
remote side answer with a subset. The active negotiated SDP would
have the pruned list but would not have an incremented SDP version
number.

2. Using re-INVITE for unhold. We would modify the active negotiated
SDP but would not increment the SDP version.

To solve these, and potential other unknown cases, the on_create_offer
callback has now been implemented which produces a fresh offer with
incremented SDP version number. This better fits within the model
provided by the INVITE session library.

ASTERISK-28452

Change-Id: I2d81048d54edcb80fe38fdbb954a86f0a58281a1

res/res_pjsip_session.c

index 48741f8bb50cbcb9aec3c7140d9d37f3dccd4455..22b0f41c9a25061c80b152c03dfea03629b55fc4 100644 (file)
@@ -2789,56 +2789,6 @@ static pj_bool_t session_reinvite_on_rx_request(pjsip_rx_data *rdata)
        }
 
        if (!sdp_info->sdp) {
-               const pjmedia_sdp_session *local;
-               int i;
-
-               ast_queue_unhold(session->channel);
-
-               pjmedia_sdp_neg_get_active_local(session->inv_session->neg, &local);
-               if (!local) {
-                       return PJ_FALSE;
-               }
-
-               /*
-                * Some devices indicate hold with deferred SDP reinvites (i.e. no SDP in the reinvite).
-                * When hold is initially indicated, we
-                * - Receive an INVITE with no SDP
-                * - Send a 200 OK with SDP, indicating sendrecv in the media streams
-                * - Receive an ACK with SDP, indicating sendonly in the media streams
-                *
-                * At this point, the pjmedia negotiator saves the state of the media direction so that
-                * if we are to send any offers, we'll offer recvonly in the media streams. This is
-                * problematic if the device is attempting to unhold, though. If the device unholds
-                * by sending a reinvite with no SDP, then we will respond with a 200 OK with recvonly.
-                * According to RFC 3264, if an offerer offers recvonly, then the answerer MUST respond
-                * with sendonly or inactive. The result of this is that the stream is not off hold.
-                *
-                * Therefore, in this case, when we receive a reinvite while the stream is on hold, we
-                * need to be sure to offer sendrecv. This way, the answerer can respond with sendrecv
-                * in order to get the stream off hold. If this is actually a different purpose reinvite
-                * (like a session timer refresh), then the answerer can respond to our sendrecv with
-                * sendonly, keeping the stream on hold.
-                */
-               for (i = 0; i < local->media_count; ++i) {
-                       pjmedia_sdp_media *m = local->media[i];
-                       pjmedia_sdp_attr *recvonly;
-                       pjmedia_sdp_attr *inactive;
-                       pjmedia_sdp_attr *sendonly;
-
-                       recvonly = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "recvonly", NULL);
-                       inactive = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "inactive", NULL);
-                       sendonly = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "sendonly", NULL);
-                       if (recvonly || inactive || sendonly) {
-                               pjmedia_sdp_attr *to_remove = recvonly ?: inactive ?: sendonly;
-                               pjmedia_sdp_attr *sendrecv;
-
-                               pjmedia_sdp_attr_remove(&m->attr_count, m->attr, to_remove);
-
-                               sendrecv = pjmedia_sdp_attr_create(session->inv_session->pool, "sendrecv", NULL);
-                               pjmedia_sdp_media_add_attr(m, sendrecv);
-                       }
-               }
-
                return PJ_FALSE;
        }
 
@@ -5288,12 +5238,70 @@ static void session_inv_on_rx_offer(pjsip_inv_session *inv, const pjmedia_sdp_se
        SCOPE_EXIT_RTN("%s: create_local_sdp failed\n", ast_sip_session_get_name(session));
 }
 
-#if 0
 static void session_inv_on_create_offer(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer)
 {
-       /* XXX STUB */
+       struct ast_sip_session *session = inv->mod_data[session_module.id];
+       const pjmedia_sdp_session *previous_sdp = NULL;
+       pjmedia_sdp_session *offer;
+       int i;
+
+       if (inv->neg) {
+               if (pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
+                       pjmedia_sdp_neg_get_active_remote(inv->neg, &previous_sdp);
+               } else {
+                       pjmedia_sdp_neg_get_active_local(inv->neg, &previous_sdp);
+               }
+       }
+
+       offer = create_local_sdp(inv, session, previous_sdp);
+       if (!offer) {
+               return;
+       }
+
+       ast_queue_unhold(session->channel);
+
+       /*
+        * Some devices indicate hold with deferred SDP reinvites (i.e. no SDP in the reinvite).
+        * When hold is initially indicated, we
+        * - Receive an INVITE with no SDP
+        * - Send a 200 OK with SDP, indicating sendrecv in the media streams
+        * - Receive an ACK with SDP, indicating sendonly in the media streams
+        *
+        * At this point, the pjmedia negotiator saves the state of the media direction so that
+        * if we are to send any offers, we'll offer recvonly in the media streams. This is
+        * problematic if the device is attempting to unhold, though. If the device unholds
+        * by sending a reinvite with no SDP, then we will respond with a 200 OK with recvonly.
+        * According to RFC 3264, if an offerer offers recvonly, then the answerer MUST respond
+        * with sendonly or inactive. The result of this is that the stream is not off hold.
+        *
+        * Therefore, in this case, when we receive a reinvite while the stream is on hold, we
+        * need to be sure to offer sendrecv. This way, the answerer can respond with sendrecv
+        * in order to get the stream off hold. If this is actually a different purpose reinvite
+        * (like a session timer refresh), then the answerer can respond to our sendrecv with
+        * sendonly, keeping the stream on hold.
+        */
+       for (i = 0; i < offer->media_count; ++i) {
+               pjmedia_sdp_media *m = offer->media[i];
+               pjmedia_sdp_attr *recvonly;
+               pjmedia_sdp_attr *inactive;
+               pjmedia_sdp_attr *sendonly;
+
+               recvonly = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "recvonly", NULL);
+               inactive = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "inactive", NULL);
+               sendonly = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "sendonly", NULL);
+               if (recvonly || inactive || sendonly) {
+                       pjmedia_sdp_attr *to_remove = recvonly ?: inactive ?: sendonly;
+                       pjmedia_sdp_attr *sendrecv;
+
+                       pjmedia_sdp_attr_remove(&m->attr_count, m->attr, to_remove);
+
+                       sendrecv = pjmedia_sdp_attr_create(session->inv_session->pool, "sendrecv", NULL);
+                       pjmedia_sdp_media_add_attr(m, sendrecv);
+               }
+       }
+
+       *p_offer = offer;
 }
-#endif
 
 static void session_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
 {
@@ -5422,6 +5430,7 @@ static pjsip_inv_callback inv_callback = {
        .on_new_session = session_inv_on_new_session,
        .on_tsx_state_changed = session_inv_on_tsx_state_changed,
        .on_rx_offer = session_inv_on_rx_offer,
+       .on_create_offer = session_inv_on_create_offer,
        .on_media_update = session_inv_on_media_update,
        .on_redirected = session_inv_on_redirected,
 };