Module: sems
Branch: master
Commit: 45cdf0231290229d08bac1ef4c35506bc604ed12
URL:    
http://git.sip-router.org/cgi-bin/gitweb.cgi/sems/?a=commit;h=45cdf0231290229d08bac1ef4c35506bc604ed12

Author: Václav Kubart <[email protected]>
Committer: Václav Kubart <[email protected]>
Date:   Fri Oct 25 15:26:09 2013 +0200

sbc b/f: correctly handle ReconnectLegEvent if there is a pending INVITE

... allows BLF call pickup

---

 apps/sbc/CallLeg.cpp |  153 +++++++++++++++++++++++++++++++++++++-------------
 apps/sbc/CallLeg.h   |   20 +++++++
 2 files changed, 133 insertions(+), 40 deletions(-)

diff --git a/apps/sbc/CallLeg.cpp b/apps/sbc/CallLeg.cpp
index 0ccfd93..b0eb388 100644
--- a/apps/sbc/CallLeg.cpp
+++ b/apps/sbc/CallLeg.cpp
@@ -524,6 +524,10 @@ void CallLeg::onB2BReconnect(ReconnectLegEvent* ev)
   resumeHeld(false);
   clearRtpReceiverRelay();
 
+  // check if we aren't processing INVITE now (BLF ringing call pickup)
+  AmSipRequest *invite = dlg->getUASPendingInv();
+  if (invite) acceptPendingInvite(invite);
+
   setOtherId(ev->session_tag);
   if (ev->role == ReconnectLegEvent::A) a_leg = true;
   else a_leg = false;
@@ -546,46 +550,18 @@ void CallLeg::onB2BReconnect(ReconnectLegEvent* ev)
                  "to", dlg->getRemoteParty().c_str(),
                  "ruri", dlg->getRemoteUri().c_str());
 
-  AmMimeBody r_body(ev->body);
-  const AmMimeBody* body = &ev->body;
-  if (rtp_relay_mode != RTP_Direct) {
-    try {
-      body = ev->body.hasContentType(SIP_APPLICATION_SDP);
-      if (body && updateLocalBody(*body, 
*r_body.hasContentType(SIP_APPLICATION_SDP))) {
-        body = &r_body;
-      }
-      else {
-        body = &ev->body;
-      }
-    } catch (const string& s) {
-      relayError(SIP_METH_INVITE, ev->r_cseq, true, 500, 
SIP_REPLY_SERVER_INTERNAL_ERROR);
-      throw;
-    }
-  }
-
-  // generate re-INVITE
-  int res = dlg->sendRequest(SIP_METH_INVITE, body, ev->hdrs, 
SIP_FLAGS_VERBATIM);
-  if (res < 0) {
-    DBG("sending re-INVITE failed, relaying back error reply\n");
-    relayError(SIP_METH_INVITE, ev->r_cseq, true, res);
-
-    stopCall(StatusChangeCause::InternalError);
-    return;
-  }
-
-  if (ev->relayed_invite) {
-    AmSipRequest fake_req;
-    fake_req.method = SIP_METH_INVITE;
-    fake_req.cseq = ev->r_cseq;
-    relayed_req[dlg->cseq - 1] = fake_req;
-    est_invite_other_cseq = ev->r_cseq;
-  }
-  else est_invite_other_cseq = 0;
-
-  saveSessionDescription(ev->body);
-
-  // save CSeq of establising INVITE
-  est_invite_cseq = dlg->cseq - 1;
+  if (invite) {
+    // there is pending INVITE, replied just above but we need to wait for ACK
+    // before sending re-INVITE with real body
+    PendingReinvite p;
+    p.hdrs = ev->hdrs;
+    p.body = ev->body;
+    p.relayed_invite = ev->relayed_invite;
+    p.r_cseq = ev->r_cseq;
+    p.establishing = true;
+    pending_reinvites.push(p);
+  }
+  else reinvite(ev->hdrs, ev->body, ev->relayed_invite, ev->r_cseq, true);
 }
 
 void CallLeg::onB2BReplace(ReplaceLegEvent *e)
@@ -841,6 +817,13 @@ void CallLeg::onSipRequest(const AmSipRequest& req)
     else
       AmB2BSession::onSipRequest(req);
   }
+
+  if (req.method == SIP_METH_ACK && !pending_reinvites.empty()) {
+    TRACE("ACK received, we can send a queued re-INVITE\n");
+    PendingReinvite p = pending_reinvites.front();
+    pending_reinvites.pop();
+    reinvite(p.hdrs, p.body, p.relayed_invite, p.r_cseq, p.establishing);
+  }
 }
 
 void CallLeg::onSipReply(const AmSipRequest& req, const AmSipReply& reply, 
AmSipDialog::Status old_dlg_status)
@@ -1259,4 +1242,94 @@ void CallLeg::changeOtherLegsRtpMode(RTPRelayMode 
new_mode)
   }
 }
 
+void CallLeg::acceptPendingInvite(AmSipRequest *invite)
+{
+  // reply the INVITE with fake 200 reply
+
+  AmMimeBody *sdp = invite->body.hasContentType(SIP_APPLICATION_SDP);
+  AmSdp s;
+  if (!sdp || s.parse((const char*)sdp->getPayload())) {
+    // no offer in the INVITE (or can't be parsed), we have to append fake 
offer
+    // into the reply
+    s.version = 0;
+    s.origin.user = "sems";
+    s.sessionName = "sems";
+    s.conn.network = NT_IN;
+    s.conn.addrType = AT_V4;
+    s.conn.address = "0.0.0.0";
+
+    s.media.push_back(SdpMedia());
+    SdpMedia &m = s.media.back();
+    m.type = MT_AUDIO;
+    m.transport = TP_RTPAVP;
+    m.send = false;
+    m.recv = false;
+    m.payloads.push_back(SdpPayload(0));
+  }
+
+  if (!s.conn.address.empty()) s.conn.address = "0.0.0.0";
+  for (vector<SdpMedia>::iterator i = s.media.begin(); i != s.media.end(); 
++i) {
+    //i->port = 0;
+    if (!i->conn.address.empty()) i->conn.address = "0.0.0.0";
+  }
+
+  AmMimeBody body;
+  string body_str;
+  s.print(body_str);
+  body.parse(SIP_APPLICATION_SDP, (const unsigned char*)body_str.c_str(), 
body_str.length());
+  if (rtp_relay_mode != RTP_Direct) {
+    try {
+      if (!updateLocalBody(body, body)) ERROR("failed to update local body 
with fake SDP\n");
+    } catch (...) { /* throw ? */  }
+  }
+
+  TRACE("replying pending INVITE with body: %s\n", body_str.c_str());
+  dlg->reply(*invite, 200, "OK", &body);
+
+  if (getCallStatus() != Connected) updateCallStatus(Connected);
+}
+
+void CallLeg::reinvite(const string &hdrs, const AmMimeBody &body, bool 
relayed, unsigned r_cseq, bool establishing)
+{
+  int res;
+  if (rtp_relay_mode != RTP_Direct && 
body.hasContentType(SIP_APPLICATION_SDP)) {
+    try {
+      AmMimeBody r_body(body);
+      AmMimeBody *sdp = r_body.hasContentType(SIP_APPLICATION_SDP);
+      if (!updateLocalBody(*sdp, *sdp)) {
+        ERROR("can't update local body\n");
+        res = -500;
+      }
+      else res = dlg->sendRequest(SIP_METH_INVITE, &r_body, hdrs, 
SIP_FLAGS_VERBATIM);
+    } catch (const string& s) { res = -500; }
+  }
+  else res = dlg->sendRequest(SIP_METH_INVITE, &body, hdrs, 
SIP_FLAGS_VERBATIM);
+
+  if (res < 0) {
+    if (relayed) {
+      DBG("sending re-INVITE failed, relaying back error reply\n");
+      relayError(SIP_METH_INVITE, r_cseq, true, res);
+    }
+
+    DBG("sending re-INVITE failed, terminating the call\n");
+    stopCall(StatusChangeCause::InternalError);
+    return;
+  }
+
+  if (relayed) {
+    AmSipRequest fake_req;
+    fake_req.method = SIP_METH_INVITE;
+    fake_req.cseq = r_cseq;
+    relayed_req[dlg->cseq - 1] = fake_req;
+    est_invite_other_cseq = r_cseq;
+  }
+  else est_invite_other_cseq = 0;
+
+  saveSessionDescription(body);
+
+  if (establishing) {
+    // save CSeq of establishing INVITE
+    est_invite_cseq = dlg->cseq - 1;
+  }
+}
 
diff --git a/apps/sbc/CallLeg.h b/apps/sbc/CallLeg.h
index 7b90713..2d42405 100644
--- a/apps/sbc/CallLeg.h
+++ b/apps/sbc/CallLeg.h
@@ -5,6 +5,17 @@
 #include "AmSessionContainer.h"
 #include "CallLegEvents.h"
 
+#include <queue>
+
+struct PendingReinvite
+{
+  string hdrs;
+  AmMimeBody body;
+  unsigned r_cseq;
+  bool relayed_invite;
+  bool establishing;
+};
+
 /** composed AmB2BCalleeSession & AmB2BCallerSession
  * represents indepenedently A or B leg of a call,
  * old clases left for compatibility
@@ -118,6 +129,15 @@ class CallLeg: public AmB2BSession
     unsigned hold_request_cseq; // CSeq of a request generated by us to hold 
the call
     enum { NotHeld, OnHold, HoldRequested, ResumeRequested } hold_status; // 
remote is put on hold by us
 
+    std::queue<PendingReinvite> pending_reinvites;
+
+    // generate re-INVITE with given parameters (establishing means that the
+    // INVITE is establishing a connection between two legs)
+    void reinvite(const string &hdrs, const AmMimeBody &body, bool relayed, 
unsigned r_cseq, bool establishing);
+
+    // generate 200 reply on a pending INVITE (uses fake body)
+    void acceptPendingInvite(AmSipRequest *invite);
+
     // methods just for make this stuff more readable, not intended to be
     // overriden, override onB2BEvent instead!
     void onB2BReply(B2BSipReplyEvent *e);

_______________________________________________
Semsdev mailing list
[email protected]
http://lists.iptel.org/mailman/listinfo/semsdev

Reply via email to