<p>Joshua Colp has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/15433">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip_session: Always produce offer on re-INVITE without SDP.<br><br>When PJSIP receives a re-INVITE without an SDP offer the INVITE<br>session library will first call the on_create_offer callback and<br>if unavailable then use the active negotiated SDP as the offer.<br><br>In some cases this would result in a different SDP then was<br>previously used without an incremented SDP version number. The two<br>known cases are:<br><br>1. Sending an initial INVITE with a set of codecs and having the<br>remote side answer with a subset. The active negotiated SDP would<br>have the pruned list but would not have an incremented SDP version<br>number.<br><br>2. Using re-INVITE for unhold. We would modify the active negotiated<br>SDP but would not increment the SDP version.<br><br>To solve these, and potential other unknown cases, the on_create_offer<br>callback has now been implemented which produces a fresh offer with<br>incremented SDP version number. This better fits within the model<br>provided by the INVITE session library.<br><br>ASTERISK-28452<br><br>Change-Id: I2d81048d54edcb80fe38fdbb954a86f0a58281a1<br>---<br>M res/res_pjsip_session.c<br>1 file changed, 62 insertions(+), 53 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/33/15433/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c</span><br><span>index 1d26309..7a80567 100644</span><br><span>--- a/res/res_pjsip_session.c</span><br><span>+++ b/res/res_pjsip_session.c</span><br><span>@@ -2765,56 +2765,6 @@</span><br><span> }</span><br><span> </span><br><span> if (!sdp_info->sdp) {</span><br><span style="color: hsl(0, 100%, 40%);">- const pjmedia_sdp_session *local;</span><br><span style="color: hsl(0, 100%, 40%);">- int i;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- ast_queue_unhold(session->channel);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- pjmedia_sdp_neg_get_active_local(session->inv_session->neg, &local);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!local) {</span><br><span style="color: hsl(0, 100%, 40%);">- return PJ_FALSE;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- /*</span><br><span style="color: hsl(0, 100%, 40%);">- * Some devices indicate hold with deferred SDP reinvites (i.e. no SDP in the reinvite).</span><br><span style="color: hsl(0, 100%, 40%);">- * When hold is initially indicated, we</span><br><span style="color: hsl(0, 100%, 40%);">- * - Receive an INVITE with no SDP</span><br><span style="color: hsl(0, 100%, 40%);">- * - Send a 200 OK with SDP, indicating sendrecv in the media streams</span><br><span style="color: hsl(0, 100%, 40%);">- * - Receive an ACK with SDP, indicating sendonly in the media streams</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * At this point, the pjmedia negotiator saves the state of the media direction so that</span><br><span style="color: hsl(0, 100%, 40%);">- * if we are to send any offers, we'll offer recvonly in the media streams. This is</span><br><span style="color: hsl(0, 100%, 40%);">- * problematic if the device is attempting to unhold, though. If the device unholds</span><br><span style="color: hsl(0, 100%, 40%);">- * by sending a reinvite with no SDP, then we will respond with a 200 OK with recvonly.</span><br><span style="color: hsl(0, 100%, 40%);">- * According to RFC 3264, if an offerer offers recvonly, then the answerer MUST respond</span><br><span style="color: hsl(0, 100%, 40%);">- * with sendonly or inactive. The result of this is that the stream is not off hold.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * Therefore, in this case, when we receive a reinvite while the stream is on hold, we</span><br><span style="color: hsl(0, 100%, 40%);">- * need to be sure to offer sendrecv. This way, the answerer can respond with sendrecv</span><br><span style="color: hsl(0, 100%, 40%);">- * in order to get the stream off hold. If this is actually a different purpose reinvite</span><br><span style="color: hsl(0, 100%, 40%);">- * (like a session timer refresh), then the answerer can respond to our sendrecv with</span><br><span style="color: hsl(0, 100%, 40%);">- * sendonly, keeping the stream on hold.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">- for (i = 0; i < local->media_count; ++i) {</span><br><span style="color: hsl(0, 100%, 40%);">- pjmedia_sdp_media *m = local->media[i];</span><br><span style="color: hsl(0, 100%, 40%);">- pjmedia_sdp_attr *recvonly;</span><br><span style="color: hsl(0, 100%, 40%);">- pjmedia_sdp_attr *inactive;</span><br><span style="color: hsl(0, 100%, 40%);">- pjmedia_sdp_attr *sendonly;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- recvonly = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "recvonly", NULL);</span><br><span style="color: hsl(0, 100%, 40%);">- inactive = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "inactive", NULL);</span><br><span style="color: hsl(0, 100%, 40%);">- sendonly = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "sendonly", NULL);</span><br><span style="color: hsl(0, 100%, 40%);">- if (recvonly || inactive || sendonly) {</span><br><span style="color: hsl(0, 100%, 40%);">- pjmedia_sdp_attr *to_remove = recvonly ?: inactive ?: sendonly;</span><br><span style="color: hsl(0, 100%, 40%);">- pjmedia_sdp_attr *sendrecv;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- pjmedia_sdp_attr_remove(&m->attr_count, m->attr, to_remove);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- sendrecv = pjmedia_sdp_attr_create(session->inv_session->pool, "sendrecv", NULL);</span><br><span style="color: hsl(0, 100%, 40%);">- pjmedia_sdp_media_add_attr(m, sendrecv);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> return PJ_FALSE;</span><br><span> }</span><br><span> </span><br><span>@@ -5219,12 +5169,70 @@</span><br><span> SCOPE_EXIT_RTN("%s: create_local_sdp failed\n", ast_sip_session_get_name(session));</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-#if 0</span><br><span> static void session_inv_on_create_offer(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- /* XXX STUB */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session *session = inv->mod_data[session_module.id];</span><br><span style="color: hsl(120, 100%, 40%);">+ const pjmedia_sdp_session *previous_sdp = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_session *offer;</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (inv->neg) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pjmedia_sdp_neg_was_answer_remote(inv->neg)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_neg_get_active_remote(inv->neg, &previous_sdp);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_neg_get_active_local(inv->neg, &previous_sdp);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ offer = create_local_sdp(inv, session, previous_sdp);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!offer) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_queue_unhold(session->channel);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Some devices indicate hold with deferred SDP reinvites (i.e. no SDP in the reinvite).</span><br><span style="color: hsl(120, 100%, 40%);">+ * When hold is initially indicated, we</span><br><span style="color: hsl(120, 100%, 40%);">+ * - Receive an INVITE with no SDP</span><br><span style="color: hsl(120, 100%, 40%);">+ * - Send a 200 OK with SDP, indicating sendrecv in the media streams</span><br><span style="color: hsl(120, 100%, 40%);">+ * - Receive an ACK with SDP, indicating sendonly in the media streams</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * At this point, the pjmedia negotiator saves the state of the media direction so that</span><br><span style="color: hsl(120, 100%, 40%);">+ * if we are to send any offers, we'll offer recvonly in the media streams. This is</span><br><span style="color: hsl(120, 100%, 40%);">+ * problematic if the device is attempting to unhold, though. If the device unholds</span><br><span style="color: hsl(120, 100%, 40%);">+ * by sending a reinvite with no SDP, then we will respond with a 200 OK with recvonly.</span><br><span style="color: hsl(120, 100%, 40%);">+ * According to RFC 3264, if an offerer offers recvonly, then the answerer MUST respond</span><br><span style="color: hsl(120, 100%, 40%);">+ * with sendonly or inactive. The result of this is that the stream is not off hold.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Therefore, in this case, when we receive a reinvite while the stream is on hold, we</span><br><span style="color: hsl(120, 100%, 40%);">+ * need to be sure to offer sendrecv. This way, the answerer can respond with sendrecv</span><br><span style="color: hsl(120, 100%, 40%);">+ * in order to get the stream off hold. If this is actually a different purpose reinvite</span><br><span style="color: hsl(120, 100%, 40%);">+ * (like a session timer refresh), then the answerer can respond to our sendrecv with</span><br><span style="color: hsl(120, 100%, 40%);">+ * sendonly, keeping the stream on hold.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < offer->media_count; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_media *m = offer->media[i];</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_attr *recvonly;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_attr *inactive;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_attr *sendonly;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ recvonly = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "recvonly", NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ inactive = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "inactive", NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ sendonly = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "sendonly", NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (recvonly || inactive || sendonly) {</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_attr *to_remove = recvonly ?: inactive ?: sendonly;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_attr *sendrecv;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_attr_remove(&m->attr_count, m->attr, to_remove);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ sendrecv = pjmedia_sdp_attr_create(session->inv_session->pool, "sendrecv", NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_media_add_attr(m, sendrecv);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ *p_offer = offer;</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">-#endif</span><br><span> </span><br><span> static void session_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)</span><br><span> {</span><br><span>@@ -5353,6 +5361,7 @@</span><br><span> .on_new_session = session_inv_on_new_session,</span><br><span> .on_tsx_state_changed = session_inv_on_tsx_state_changed,</span><br><span> .on_rx_offer = session_inv_on_rx_offer,</span><br><span style="color: hsl(120, 100%, 40%);">+ .on_create_offer = session_inv_on_create_offer,</span><br><span> .on_media_update = session_inv_on_media_update,</span><br><span> .on_redirected = session_inv_on_redirected,</span><br><span> };</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/15433">change 15433</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/15433"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 16 </div>
<div style="display:none"> Gerrit-Change-Id: I2d81048d54edcb80fe38fdbb954a86f0a58281a1 </div>
<div style="display:none"> Gerrit-Change-Number: 15433 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>