summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pjmedia/include/pjmedia/sdp_neg.h12
-rw-r--r--pjsip/include/pjsip-ua/sip_inv.h47
-rw-r--r--pjsip/src/pjsip-ua/sip_inv.c132
3 files changed, 168 insertions, 23 deletions
diff --git a/pjmedia/include/pjmedia/sdp_neg.h b/pjmedia/include/pjmedia/sdp_neg.h
index 0be9e528..3f6eb319 100644
--- a/pjmedia/include/pjmedia/sdp_neg.h
+++ b/pjmedia/include/pjmedia/sdp_neg.h
@@ -52,15 +52,15 @@
* create_w_local_offer() +-------------+ send_local_offer()
* ----------------------->| LOCAL_OFFER |<-----------------------
* | +-------------+______ |
- * | | \______ cancel() |
- * | set_remote_answer() | \______ |
- * | V \ |
+ * | | \_____________ |
+ * | set_remote_answer() | cancel_offer() \ |
+ * | V v |
* +--+---+ +-----------+ negotiate() +-~----+
* | NULL | | WAIT_NEGO |-------------------->| DONE |
* +------+ +-----------+ +------+
- * | A |
- * | set_local_answer() | |
- * | | |
+ * | A ______________________^ |
+ * | set_local_answer() | / cancel_offer() |
+ * | | / |
* | +--------------+ set_remote_offer() |
* ----------------------->| REMOTE_OFFER |<----------------------
* create_w_remote_offer() +--------------+
diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h
index 3017b8ce..4e5f820e 100644
--- a/pjsip/include/pjsip-ua/sip_inv.h
+++ b/pjsip/include/pjsip-ua/sip_inv.h
@@ -161,6 +161,31 @@ typedef struct pjsip_inv_callback
const pjmedia_sdp_session *offer);
/**
+ * This callback is optional, and is called when the invite session has
+ * received a re-INVITE from the peer. It will be called after
+ * on_rx_offer() callback and works only for re-INVITEs. It allows more
+ * fine-grained control over the response to a re-INVITE, e.g. sending
+ * a provisional response first. Application can return PJ_SUCCESS and
+ * send a reply using the function #pjsip_inv_initial_answer() or
+ * #pjsip_inv_answer(), as with the initial INVITE. If application
+ * returns non-PJ_SUCCESS, it needs to set the SDP answer with
+ * #pjsip_inv_set_sdp_answer() and the re-INVITE will be answered
+ * automatically.
+ *
+ * @param inv The invite session.
+ * @param offer Remote offer.
+ * @param rdata The received re-INVITE request.
+ *
+ * @return - PJ_SUCCESS: application will answer the re-INVITE
+ * manually
+ * - non-PJ_SUCCESS: answer the re-INVITE automatically
+ * using the SDP set via #pjsip_inv_set_sdp_answer()
+ */
+ pj_status_t (*on_rx_reinvite)(pjsip_inv_session *inv,
+ const pjmedia_sdp_session *offer,
+ pjsip_rx_data *rdata);
+
+ /**
* This callback is optional, and it is used to ask the application
* to create a fresh offer, when the invite session has received
* re-INVITE without offer. This offer then will be sent in the
@@ -698,9 +723,7 @@ PJ_DECL(pj_status_t) pjsip_inv_initial_answer( pjsip_inv_session *inv,
pjsip_tx_data **p_tdata);
/**
- * Create a response message to the initial INVITE request. This function
- * can only be called for the initial INVITE request, as subsequent
- * re-INVITE request will be answered automatically.
+ * Create a response message to an INVITE request.
*
* @param inv The UAS invite session.
* @param st_code The st_code contains the status code to be sent,
@@ -797,6 +820,24 @@ PJ_DECL(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv,
pjsip_tx_data **p_tdata );
+/**
+ * Create a CANCEL request for an ongoing re-INVITE transaction. If no
+ * provisional response has been received, the function will not create
+ * CANCEL request (the function will return PJ_SUCCESS but the \a p_tdata
+ * will contain NULL) because we cannot send CANCEL before receiving
+ * provisional response. If then a provisional response is received,
+ * the invite session will send CANCEL automatically.
+ *
+ * @param inv The invite session.
+ * @param p_tdata Pointer to receive the message to be created. Note
+ * that it's possible to receive NULL here while the
+ * function returns PJ_SUCCESS, see the description.
+ *
+ * @return PJ_SUCCESS if termination is initiated.
+ */
+PJ_DECL(pj_status_t) pjsip_inv_cancel_reinvite( pjsip_inv_session *inv,
+ pjsip_tx_data **p_tdata );
+
/**
* Create a re-INVITE request.
diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c
index 4f490f92..87aca6fe 100644
--- a/pjsip/src/pjsip-ua/sip_inv.c
+++ b/pjsip/src/pjsip-ua/sip_inv.c
@@ -1835,6 +1835,15 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
if (pjmedia_sdp_neg_get_state(inv->neg) !=
PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)
{
+ if (mod_inv.cb.on_rx_reinvite && inv->notify &&
+ msg->type == PJSIP_REQUEST_MSG &&
+ msg->line.req.method.id == PJSIP_INVITE_METHOD)
+ {
+ /* Do not return failure first, allow the application
+ * to set the answer in the on_rx_reinvite() callback.
+ */
+ return PJ_SUCCESS;
+ }
return PJ_EINVALIDOP;
}
@@ -1972,6 +1981,16 @@ static pj_status_t process_answer( pjsip_inv_session *inv,
}
}
+ /* Cancel SDP negotiation if this is a negative reply to a re-INVITE */
+ if (st_code >= 300 && inv->neg != NULL &&
+ inv->state == PJSIP_INV_STATE_CONFIRMED)
+ {
+ pjmedia_sdp_neg_state neg_state;
+ neg_state = pjmedia_sdp_neg_get_state(inv->neg);
+ if (neg_state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER) {
+ pjmedia_sdp_neg_cancel_offer(inv->neg);
+ }
+ }
return PJ_SUCCESS;
}
@@ -2052,8 +2071,7 @@ on_return:
/*
- * Answer initial INVITE
- * Re-INVITE will be answered automatically, and will not use this function.
+ * Answer INVITE request.
*/
PJ_DEF(pj_status_t) pjsip_inv_answer( pjsip_inv_session *inv,
int st_code,
@@ -2279,6 +2297,62 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv,
return PJ_SUCCESS;
}
+/*
+ * Cancel re-INVITE transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_inv_cancel_reinvite( pjsip_inv_session *inv,
+ pjsip_tx_data **p_tdata )
+{
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ /* Verify arguments. */
+ PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
+
+ pj_log_push_indent();
+
+ /* Create appropriate message. */
+ switch (inv->state) {
+ case PJSIP_INV_STATE_CONFIRMED:
+ /* MUST have the original UAC INVITE transaction */
+ PJ_ASSERT_RETURN(inv->invite_tsx != NULL, PJ_EBUG);
+
+ /* CANCEL should only be called when we have received a
+ * provisional response.
+ */
+ if (inv->invite_tsx->status_code < 100) {
+ inv->cancelling = PJ_TRUE;
+ inv->pending_cancel = PJ_TRUE;
+ *p_tdata = NULL;
+ PJ_LOG(4, (inv->obj_name, "Delaying CANCEL since no "
+ "provisional response is received yet"));
+ pj_log_pop_indent();
+ return PJ_SUCCESS;
+ }
+
+ status = pjsip_endpt_create_cancel(inv->dlg->endpt,
+ inv->invite_tsx->last_tx,
+ &tdata);
+ if (status != PJ_SUCCESS) {
+ pj_log_pop_indent();
+ return status;
+ }
+ break;
+
+ default:
+ /* We cannot send CANCEL to a re-INVITE if the INVITE session is
+ * not confirmed.
+ */
+ pj_log_pop_indent();
+ return PJ_EINVALIDOP;
+ }
+
+ pj_log_pop_indent();
+
+ *p_tdata = tdata;
+ return PJ_SUCCESS;
+}
+
/* Following redirection recursion, get next target from the target set and
* notify user.
*
@@ -4082,17 +4156,9 @@ static void inv_on_state_connecting( pjsip_inv_session *inv, pjsip_event *e)
{
/*
- * Handle strandled incoming CANCEL.
+ * Handle strandled incoming CANCEL or CANCEL for re-INVITE
*/
- pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
- pjsip_tx_data *tdata;
- pj_status_t status;
-
- status = pjsip_dlg_create_response(dlg, rdata, 200, NULL, &tdata);
- if (status != PJ_SUCCESS) return;
-
- status = pjsip_dlg_send_response(dlg, tsx, tdata);
- if (status != PJ_SUCCESS) return;
+ inv_respond_incoming_cancel(inv, tsx, e);
} else if (tsx->role == PJSIP_ROLE_UAS &&
tsx->state == PJSIP_TSX_STATE_TRYING &&
@@ -4228,7 +4294,7 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e)
pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
pjsip_tx_data *tdata;
pj_status_t status;
- pjsip_rdata_sdp_info *sdp_info;
+ pjsip_rdata_sdp_info *sdp_info = NULL;
pjsip_status_code st_code;
/* Check if we have INVITE pending. */
@@ -4301,6 +4367,29 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e)
/* Process SDP in incoming message. */
status = inv_check_sdp_in_incoming_msg(inv, tsx, rdata);
+ if (status == PJ_SUCCESS && mod_inv.cb.on_rx_reinvite &&
+ inv->notify)
+ {
+ sdp_info = pjsip_rdata_get_sdp_info(rdata);
+ if ((*mod_inv.cb.on_rx_reinvite)
+ (inv, sdp_info->sdp, rdata) == PJ_SUCCESS)
+ {
+ /* Application will send its own response.
+ * Our job is done. */
+ return;
+ }
+
+ /* If application lets us answer the re-INVITE,
+ * application must set the SDP answer with
+ * #pjsip_inv_set_sdp_answer().
+ */
+ if (pjmedia_sdp_neg_get_state(inv->neg) !=
+ PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)
+ {
+ status = PJ_EINVALIDOP;
+ }
+ }
+
if (status != PJ_SUCCESS) {
/* Not Acceptable */
@@ -4342,7 +4431,8 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e)
/* If the INVITE request has SDP body, send answer.
* Otherwise generate offer from local active SDP.
*/
- sdp_info = pjsip_rdata_get_sdp_info(rdata);
+ if (!sdp_info)
+ sdp_info = pjsip_rdata_get_sdp_info(rdata);
if (sdp_info->sdp != NULL) {
status = process_answer(inv, 200, tdata, NULL);
} else {
@@ -4459,6 +4549,20 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e)
/* Save pending invite transaction */
inv->invite_tsx = tsx;
+ } else if (tsx->state == PJSIP_TSX_STATE_PROCEEDING) {
+
+ /* CANCEL the re-INVITE if necessary */
+ if (inv->pending_cancel) {
+ pj_status_t status;
+ pjsip_tx_data *cancel;
+
+ inv->pending_cancel = PJ_FALSE;
+
+ status = pjsip_inv_cancel_reinvite(inv, &cancel);
+ if (status == PJ_SUCCESS && cancel)
+ status = pjsip_inv_send_msg(inv, cancel);
+ }
+
} else if (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
tsx->status_code/100 == 2)
{