summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiong Sauw Ming <ming@teluu.com>2011-10-03 02:04:36 +0000
committerLiong Sauw Ming <ming@teluu.com>2011-10-03 02:04:36 +0000
commitdd10c1af79c2a0d4d4720245b7f5d28a63a5292c (patch)
tree89efde612812bee65dd3541f5f6c9c364d1761f1
parent21bee233619f1e2187345efd4eaed85e49facc5b (diff)
Closed #1266:
Handle incoming calls when media transport is created asynchronously. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3777 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip/include/pjsip-ua/sip_inv.h34
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h13
-rw-r--r--pjsip/src/pjsip-ua/sip_inv.c149
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c206
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c8
5 files changed, 332 insertions, 78 deletions
diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h
index e455a20f..4fe44758 100644
--- a/pjsip/include/pjsip-ua/sip_inv.h
+++ b/pjsip/include/pjsip-ua/sip_inv.h
@@ -545,6 +545,25 @@ PJ_DECL(pj_status_t) pjsip_inv_verify_request2( pjsip_rx_data *rdata,
pjsip_tx_data **tdata);
/**
+ * Variant of #pjsip_inv_verify_request() which allows application not to
+ * specify the rdata (i.e. pass NULL as the rdata parameter) and specify
+ * the parsed SDP in the \a offer argument and a temporary pool in the
+ * \a tmp_pool argument.
+ * This is useful if application no longer has access to the rdata.
+ *
+ * @see pjsip_inv_verify_request()
+ */
+PJ_DECL(pj_status_t) pjsip_inv_verify_request3( pjsip_rx_data *rdata,
+ pj_pool_t *tmp_pool,
+ unsigned *options,
+ const pjmedia_sdp_session *offer,
+ const pjmedia_sdp_session *answer,
+ pjsip_dialog *dlg,
+ pjsip_endpoint *endpt,
+ pjsip_tx_data **tdata);
+
+
+/**
* Create UAS invite session for the specified dialog in dlg. Application
* SHOULD call the verification function before calling this function,
* to ensure that it can create the session successfully.
@@ -711,6 +730,21 @@ PJ_DECL(pj_status_t) pjsip_inv_answer( pjsip_inv_session *inv,
/**
+ * Set local offer or answer depending on negotiator state (it may also
+ * create a negotiator if it doesn't exist yet).
+ *
+ * @param inv The invite session.
+ * @param sdp The SDP description which will be set as
+ * an offer/answer to remote.
+ *
+ * @return PJ_SUCCESS if local offer/answer can be accepted by
+ * SDP negotiator.
+ */
+PJ_DECL(pj_status_t) pjsip_inv_set_local_sdp(pjsip_inv_session *inv,
+ const pjmedia_sdp_session *sdp );
+
+
+/**
* Set local answer to respond to remote SDP offer, to be carried by
* subsequent response (or request).
*
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index 162758ea..0b65a7f0 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -100,6 +100,16 @@ struct pjsua_call_media
*/
#define PJSUA_MAX_CALL_MEDIA PJMEDIA_MAX_SDP_MEDIA
+/* Call answer's list. */
+typedef struct call_answer
+{
+ PJ_DECL_LIST_MEMBER(struct call_answer);
+ pjsua_msg_data *msg_data; /**< Answer's headers list. */
+ pj_str_t *reason; /**< Answer's reason phrase. */
+ unsigned code; /**< Answer's status code. */
+} call_answer;
+
+
/**
* Structure to be attached to invite dialog.
* Given a dialog "dlg", application can retrieve this structure
@@ -151,6 +161,9 @@ struct pjsua_call
unsigned options; /**< Outgoing call options. */
pjsua_msg_data *msg_data;/**< Headers for outgoing INVITE. */
} out_call;
+ struct {
+ call_answer answers;/**< A list of call answers. */
+ } inc_call;
} call_var;
} async_call; /**< Temporary storage for async
outgoing/incoming call. */
diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c
index 5444b1af..eca737eb 100644
--- a/pjsip/src/pjsip-ua/sip_inv.c
+++ b/pjsip/src/pjsip-ua/sip_inv.c
@@ -811,7 +811,8 @@ PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata)
/*
* Verify incoming INVITE request.
*/
-PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
+PJ_DEF(pj_status_t) pjsip_inv_verify_request3(pjsip_rx_data *rdata,
+ pj_pool_t *tmp_pool,
unsigned *options,
const pjmedia_sdp_session *r_sdp,
const pjmedia_sdp_session *l_sdp,
@@ -819,11 +820,11 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
pjsip_endpoint *endpt,
pjsip_tx_data **p_tdata)
{
- pjsip_msg *msg;
- pjsip_allow_hdr *allow;
- pjsip_supported_hdr *sup_hdr;
- pjsip_require_hdr *req_hdr;
- pjsip_contact_hdr *c_hdr;
+ pjsip_msg *msg = NULL;
+ pjsip_allow_hdr *allow = NULL;
+ pjsip_supported_hdr *sup_hdr = NULL;
+ pjsip_require_hdr *req_hdr = NULL;
+ pjsip_contact_hdr *c_hdr = NULL;
int code = 200;
unsigned rem_option = 0;
pj_status_t status = PJ_SUCCESS;
@@ -834,7 +835,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
if (p_tdata) *p_tdata = NULL;
/* Verify arguments. */
- PJ_ASSERT_RETURN(rdata != NULL && options != NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(tmp_pool != NULL && options != NULL, PJ_EINVAL);
/* Normalize options */
if (*options & PJSIP_INV_REQUIRE_100REL)
@@ -844,13 +845,15 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
if (*options & PJSIP_INV_REQUIRE_ICE)
*options |= PJSIP_INV_SUPPORT_ICE;
- /* Get the message in rdata */
- msg = rdata->msg_info.msg;
-
- /* Must be INVITE request. */
- PJ_ASSERT_RETURN(msg->type == PJSIP_REQUEST_MSG &&
- msg->line.req.method.id == PJSIP_INVITE_METHOD,
- PJ_EINVAL);
+ if (rdata) {
+ /* Get the message in rdata */
+ msg = rdata->msg_info.msg;
+
+ /* Must be INVITE request. */
+ PJ_ASSERT_RETURN(msg && msg->type == PJSIP_REQUEST_MSG &&
+ msg->line.req.method.id == PJSIP_INVITE_METHOD,
+ PJ_EINVAL);
+ }
/* If tdata is specified, then either dlg or endpt must be specified */
PJ_ASSERT_RETURN((!p_tdata) || (endpt || dlg), PJ_EINVAL);
@@ -862,15 +865,17 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
pj_list_init(&res_hdr_list);
/* Check the Contact header */
- c_hdr = (pjsip_contact_hdr*)
- pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL);
- if (!c_hdr || !c_hdr->uri) {
- /* Missing Contact header or Contact contains "*" */
+ if (msg) {
+ c_hdr = (pjsip_contact_hdr*)
+ pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL);
+ }
+ if (msg && (!c_hdr || !c_hdr->uri)) {
+ /* Missing Contact header or Contact contains "*" */
pjsip_warning_hdr *w;
pj_str_t warn_text;
warn_text = pj_str("Bad/missing Contact header");
- w = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
+ w = pjsip_warning_hdr_create(tmp_pool, 399,
pjsip_endpt_name(endpt),
&warn_text);
if (w) {
@@ -885,13 +890,13 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
/* Check the request body, see if it's something that we support,
* only when the body hasn't been parsed before.
*/
- if (r_sdp == NULL) {
+ if (r_sdp == NULL && rdata) {
sdp_info = pjsip_rdata_get_sdp_info(rdata);
} else {
sdp_info = NULL;
}
- if (r_sdp==NULL && msg->body) {
+ if (r_sdp==NULL && msg && msg->body) {
/* Check if body really contains SDP. */
if (sdp_info->body.ptr == NULL) {
@@ -903,7 +908,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
/* Add Accept header to response */
pjsip_accept_hdr *acc;
- acc = pjsip_accept_hdr_create(rdata->tp_info.pool);
+ acc = pjsip_accept_hdr_create(tmp_pool);
PJ_ASSERT_RETURN(acc, PJ_ENOMEM);
acc->values[acc->count++] = pj_str("application/sdp");
pj_list_push_back(&res_hdr_list, acc);
@@ -920,7 +925,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
/* Add Warning header. */
pjsip_warning_hdr *w;
- w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
+ w = pjsip_warning_hdr_create_from_status(tmp_pool,
pjsip_endpt_name(endpt),
sdp_info->sdp_err);
PJ_ASSERT_RETURN(w, PJ_ENOMEM);
@@ -945,11 +950,11 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
/* Create SDP negotiator */
status = pjmedia_sdp_neg_create_w_remote_offer(
- rdata->tp_info.pool, l_sdp, r_sdp, &neg);
+ tmp_pool, l_sdp, r_sdp, &neg);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Negotiate SDP */
- status = pjmedia_sdp_neg_negotiate(rdata->tp_info.pool, neg, 0);
+ status = pjmedia_sdp_neg_negotiate(tmp_pool, neg, 0);
if (status != PJ_SUCCESS) {
/* Incompatible media */
@@ -961,14 +966,14 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
/* Add Warning header. */
w = pjsip_warning_hdr_create_from_status(
- rdata->tp_info.pool,
+ tmp_pool,
pjsip_endpt_name(endpt), status);
PJ_ASSERT_RETURN(w, PJ_ENOMEM);
pj_list_push_back(&res_hdr_list, w);
/* Add Accept header to response */
- acc = pjsip_accept_hdr_create(rdata->tp_info.pool);
+ acc = pjsip_accept_hdr_create(tmp_pool);
PJ_ASSERT_RETURN(acc, PJ_ENOMEM);
acc->values[acc->count++] = pj_str("application/sdp");
pj_list_push_back(&res_hdr_list, acc);
@@ -984,7 +989,10 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
* We just assume that peer supports standard INVITE, ACK, CANCEL, and BYE
* implicitly by sending this INVITE.
*/
- allow = (pjsip_allow_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_ALLOW, NULL);
+ if (msg) {
+ allow = (pjsip_allow_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_ALLOW,
+ NULL);
+ }
if (allow) {
unsigned i;
const pj_str_t STR_UPDATE = { "UPDATE", 6 };
@@ -1002,8 +1010,10 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
}
/* Check Supported header */
- sup_hdr = (pjsip_supported_hdr*)
- pjsip_msg_find_hdr(msg, PJSIP_H_SUPPORTED, NULL);
+ if (msg) {
+ sup_hdr = (pjsip_supported_hdr*)
+ pjsip_msg_find_hdr(msg, PJSIP_H_SUPPORTED, NULL);
+ }
if (sup_hdr) {
unsigned i;
const pj_str_t STR_100REL = { "100rel", 6};
@@ -1021,8 +1031,10 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
}
/* Check Require header */
- req_hdr = (pjsip_require_hdr*)
- pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL);
+ if (msg) {
+ req_hdr = (pjsip_require_hdr*)
+ pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL);
+ }
if (req_hdr) {
unsigned i;
const pj_str_t STR_100REL = { "100rel", 6};
@@ -1074,7 +1086,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
const pjsip_hdr *h;
/* Add Unsupported header. */
- unsupp_hdr = pjsip_unsupported_hdr_create(rdata->tp_info.pool);
+ unsupp_hdr = pjsip_unsupported_hdr_create(tmp_pool);
PJ_ASSERT_RETURN(unsupp_hdr != NULL, PJ_ENOMEM);
unsupp_hdr->count = unsupp_cnt;
@@ -1089,7 +1101,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
pj_assert(h);
if (h) {
sup_hdr = (pjsip_supported_hdr*)
- pjsip_hdr_clone(rdata->tp_info.pool, h);
+ pjsip_hdr_clone(tmp_pool, h);
pj_list_push_back(&res_hdr_list, sup_hdr);
}
}
@@ -1101,10 +1113,10 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
/* Check if there are local requirements that are not supported
* by peer.
*/
- if ( ((*options & PJSIP_INV_REQUIRE_100REL)!=0 &&
+ if ( msg && (((*options & PJSIP_INV_REQUIRE_100REL)!=0 &&
(rem_option & PJSIP_INV_SUPPORT_100REL)==0) ||
((*options & PJSIP_INV_REQUIRE_TIMER)!=0 &&
- (rem_option & PJSIP_INV_SUPPORT_TIMER)==0))
+ (rem_option & PJSIP_INV_SUPPORT_TIMER)==0)))
{
code = PJSIP_SC_EXTENSION_REQUIRED;
status = PJSIP_ERRNO_FROM_SIP_STATUS(code);
@@ -1113,7 +1125,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
const pjsip_hdr *h;
/* Add Require header. */
- req_hdr = pjsip_require_hdr_create(rdata->tp_info.pool);
+ req_hdr = pjsip_require_hdr_create(tmp_pool);
PJ_ASSERT_RETURN(req_hdr != NULL, PJ_ENOMEM);
if (*options & PJSIP_INV_REQUIRE_100REL)
@@ -1129,7 +1141,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
pj_assert(h);
if (h) {
sup_hdr = (pjsip_supported_hdr*)
- pjsip_hdr_clone(rdata->tp_info.pool, h);
+ pjsip_hdr_clone(tmp_pool, h);
pj_list_push_back(&res_hdr_list, sup_hdr);
}
@@ -1157,6 +1169,10 @@ on_return:
pjsip_tx_data *tdata;
const pjsip_hdr *h;
+ if (!rdata) {
+ return PJSIP_ERRNO_FROM_SIP_STATUS(code);
+ }
+
if (dlg) {
status = pjsip_dlg_create_response(dlg, rdata, code, NULL,
&tdata);
@@ -1198,6 +1214,23 @@ on_return:
/*
* Verify incoming INVITE request.
*/
+PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
+ unsigned *options,
+ const pjmedia_sdp_session *r_sdp,
+ const pjmedia_sdp_session *l_sdp,
+ pjsip_dialog *dlg,
+ pjsip_endpoint *endpt,
+ pjsip_tx_data **p_tdata)
+{
+ return pjsip_inv_verify_request3(rdata, rdata->tp_info.pool,
+ options, r_sdp, l_sdp, dlg,
+ endpt, p_tdata);
+}
+
+
+/*
+ * Verify incoming INVITE request.
+ */
PJ_DEF(pj_status_t) pjsip_inv_verify_request( pjsip_rx_data *rdata,
unsigned *options,
const pjmedia_sdp_session *l_sdp,
@@ -1205,7 +1238,8 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request( pjsip_rx_data *rdata,
pjsip_endpoint *endpt,
pjsip_tx_data **p_tdata)
{
- return pjsip_inv_verify_request2(rdata, options, NULL, l_sdp, dlg,
+ return pjsip_inv_verify_request3(rdata, rdata->tp_info.pool,
+ options, NULL, l_sdp, dlg,
endpt, p_tdata);
}
@@ -2027,6 +2061,43 @@ on_return:
/*
+ * Set local SDP offer/answer.
+ */
+PJ_DEF(pj_status_t) pjsip_inv_set_local_sdp(pjsip_inv_session *inv,
+ const pjmedia_sdp_session *sdp )
+{
+ const pjmedia_sdp_session *offer;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(inv && sdp, PJ_EINVAL);
+
+ /* If we have remote SDP offer, set local answer to respond to the offer,
+ * otherwise we set/modify our local offer (and create an SDP negotiator
+ * if we don't have one yet).
+ */
+ if (inv->neg) {
+ pjmedia_sdp_neg_state neg_state = pjmedia_sdp_neg_get_state(inv->neg);
+
+ if ((neg_state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER ||
+ neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) &&
+ pjmedia_sdp_neg_get_neg_remote(inv->neg, &offer) == PJ_SUCCESS)
+ {
+ status = pjsip_inv_set_sdp_answer(inv, sdp);
+ } else if (neg_state == PJMEDIA_SDP_NEG_STATE_DONE) {
+ status = pjmedia_sdp_neg_modify_local_offer(inv->pool,
+ inv->neg, sdp);
+ } else
+ return PJMEDIA_SDPNEG_EINSTATE;
+ } else {
+ status = pjmedia_sdp_neg_create_w_local_offer(inv->pool,
+ sdp, &inv->neg);
+ }
+
+ return status;
+}
+
+
+/*
* Set SDP answer.
*/
PJ_DEF(pj_status_t) pjsip_inv_set_sdp_answer( pjsip_inv_session *inv,
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index 02f3f6a3..d5ff2408 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -331,6 +331,7 @@ static int call_get_secure_level(pjsua_call *call)
}
*/
+/* Outgoing call callback when media transport creation is completed. */
static pj_status_t
on_make_call_med_tp_complete(pjsua_call_id call_id,
const pjsua_med_tp_state_info *info)
@@ -611,6 +612,9 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
/* Attach user data */
call->user_data = user_data;
+ /* Store variables required for the callback after the async
+ * media transport creation is completed.
+ */
call->async_call.call_var.out_call.options = options;
if (msg_data) {
call->async_call.call_var.out_call.msg_data = pjsua_msg_data_clone(
@@ -685,6 +689,93 @@ static void update_remote_nat_type(pjsua_call *call,
}
+/* Incoming call callback when media transport creation is completed. */
+static pj_status_t
+on_incoming_call_med_tp_complete(pjsua_call_id call_id,
+ const pjsua_med_tp_state_info *info)
+{
+ pjsua_call *call = &pjsua_var.calls[call_id];
+ const pjmedia_sdp_session *offer=NULL;
+ pjmedia_sdp_session *answer;
+ pjsip_tx_data *response = NULL;
+ unsigned options = 0;
+ int sip_err_code = (info? info->sip_err_code: 0);
+ pj_status_t status = (info? info->status: PJ_SUCCESS);
+
+ PJSUA_LOCK();
+
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error initializing media channel", status);
+ goto on_return;
+ }
+
+ /* Get remote SDP offer (if any). */
+ if (call->inv->neg)
+ pjmedia_sdp_neg_get_neg_remote(call->inv->neg, &offer);
+
+ status = pjsua_media_channel_create_sdp(call_id,
+ call->async_call.dlg->pool,
+ offer, &answer, &sip_err_code);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
+ goto on_return;
+ }
+
+ status = pjsip_inv_set_local_sdp(call->inv, answer);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error setting local SDP", status);
+ sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
+ goto on_return;
+ }
+
+ /* Verify that we can handle the request. */
+ status = pjsip_inv_verify_request3(NULL,
+ call->inv->pool_prov, &options, offer,
+ answer, NULL, pjsua_var.endpt, &response);
+ if (status != PJ_SUCCESS) {
+ /*
+ * No we can't handle the incoming INVITE request.
+ */
+ goto on_return;
+ }
+
+on_return:
+ if (status != PJ_SUCCESS) {
+ pjsip_tx_data *tdata;
+ pj_status_t status_;
+
+ status_ = pjsip_inv_end_session(call->inv, sip_err_code, NULL, &tdata);
+ if (status_ == PJ_SUCCESS && tdata)
+ status_ = pjsip_inv_send_msg(call->inv, tdata);
+
+ pjsua_media_channel_deinit(call->index);
+ }
+
+ /* Set the callback to NULL to indicate that the async operation
+ * has completed.
+ */
+ call->med_ch_cb = NULL;
+
+ if (status == PJ_SUCCESS &&
+ !pj_list_empty(&call->async_call.call_var.inc_call.answers))
+ {
+ struct call_answer *answer, *next;
+
+ answer = call->async_call.call_var.inc_call.answers.next;
+ while (answer != &call->async_call.call_var.inc_call.answers) {
+ next = answer->next;
+ pjsua_call_answer(call_id, answer->code, answer->reason,
+ answer->msg_data);
+ pj_list_erase(answer);
+ answer = next;
+ }
+ }
+
+ PJSUA_UNLOCK();
+ return status;
+}
+
+
/**
* Handle incoming INVITE request.
* Called by pjsua_core.c
@@ -703,7 +794,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
pjsua_call *call;
int call_id = -1;
int sip_err_code;
- pjmedia_sdp_session *offer=NULL, *answer;
+ pjmedia_sdp_session *offer=NULL;
pj_status_t status;
/* Don't want to handle anything but INVITE */
@@ -866,30 +957,6 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
offer = NULL;
}
- /* Init media channel */
- status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
- call->secure_level,
- rdata->tp_info.pool,
- offer,
- &sip_err_code, PJ_FALSE,
- NULL);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error initializing media channel", status);
- pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
- sip_err_code, NULL, NULL, NULL, NULL);
- goto on_return;
- }
-
- /* Create answer */
- status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
- offer, &answer, &sip_err_code);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
- pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
- sip_err_code, NULL, NULL, NULL, NULL);
- goto on_return;
- }
-
/* Verify that we can handle the request. */
options |= PJSIP_INV_SUPPORT_100REL;
options |= PJSIP_INV_SUPPORT_TIMER;
@@ -902,7 +969,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
else if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
options |= PJSIP_INV_ALWAYS_USE_TIMER;
- status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
+ status = pjsip_inv_verify_request2(rdata, &options, offer, NULL, NULL,
pjsua_var.endpt, &response);
if (status != PJ_SUCCESS) {
@@ -922,11 +989,9 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
NULL, NULL, NULL);
}
- pjsua_media_channel_deinit(call->index);
goto on_return;
}
-
/* Get suitable Contact header */
if (pjsua_var.acc[acc_id].contact.slen) {
contact = pjsua_var.acc[acc_id].contact;
@@ -938,7 +1003,6 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
status);
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
- pjsua_media_channel_deinit(call->index);
goto on_return;
}
}
@@ -949,7 +1013,6 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
if (status != PJ_SUCCESS) {
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
- pjsua_media_channel_deinit(call->index);
goto on_return;
}
@@ -974,7 +1037,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
}
/* Create invite session: */
- status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
+ status = pjsip_inv_create_uas( dlg, rdata, NULL, options, &inv);
if (status != PJ_SUCCESS) {
pjsip_hdr hdr_list;
pjsip_warning_hdr *w;
@@ -994,6 +1057,50 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
goto on_return;
}
+ /* Create and attach pjsua_var data to the dialog: */
+ call->inv = inv;
+ dlg->mod_data[pjsua_var.mod.id] = call;
+ inv->mod_data[pjsua_var.mod.id] = call;
+
+ /* Store variables required for the callback after the async
+ * media transport creation is completed.
+ */
+ call->async_call.dlg = dlg;
+ pj_list_init(&call->async_call.call_var.inc_call.answers);
+
+ /* Init media channel */
+ status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
+ call->secure_level,
+ rdata->tp_info.pool,
+ offer,
+ &sip_err_code, PJ_TRUE,
+ (pjsua_med_tp_state_cb)
+ &on_incoming_call_med_tp_complete);
+ if (status == PJ_SUCCESS) {
+ status = on_incoming_call_med_tp_complete(call_id, NULL);
+ if (status != PJ_SUCCESS) {
+ sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
+ pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL);
+ goto on_return;
+ }
+ } else if (status != PJ_EPENDING) {
+ pjsua_perror(THIS_FILE, "Error initializing media channel", status);
+ pjsip_dlg_respond(dlg, rdata, sip_err_code, NULL, NULL, NULL);
+ goto on_return;
+ }
+
+ /* Create answer */
+/*
+ status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
+ offer, &answer, &sip_err_code);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
+ pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
+ sip_err_code, NULL, NULL, NULL, NULL);
+ goto on_return;
+ }
+*/
+
/* Init Session Timers */
status = pjsip_timer_init_session(inv,
&pjsua_var.acc[acc_id].cfg.timer_setting);
@@ -1012,7 +1119,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
/* Update NAT type of remote endpoint, only when there is SDP in
* incoming INVITE!
*/
- if (pjsua_var.ua_cfg.nat_type_in_sdp &&
+ if (pjsua_var.ua_cfg.nat_type_in_sdp && inv->neg &&
pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
{
const pjmedia_sdp_session *remote_sdp;
@@ -1063,12 +1170,6 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
}
}
- /* Create and attach pjsua_var data to the dialog: */
- call->inv = inv;
-
- dlg->mod_data[pjsua_var.mod.id] = call;
- inv->mod_data[pjsua_var.mod.id] = call;
-
++pjsua_var.call_cnt;
/* Check if this request should replace existing call */
@@ -1637,6 +1738,35 @@ PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
if (status != PJ_SUCCESS)
goto on_return;
+ PJSUA_LOCK();
+ /* If media transport creation is not yet completed, we will answer
+ * the call in the media transport creation callback instead.
+ */
+ if (call->med_ch_cb) {
+ struct call_answer *answer;
+
+ PJ_LOG(4,(THIS_FILE, "Pending answering call %d upon completion "
+ "of media transport", call_id));
+
+ answer = PJ_POOL_ZALLOC_T(call->inv->pool_prov, struct call_answer);
+ answer->code = code;
+ if (reason) {
+ pj_strdup(call->inv->pool_prov, answer->reason, reason);
+ }
+ if (msg_data) {
+ answer->msg_data = pjsua_msg_data_clone(call->inv->pool_prov,
+ msg_data);
+ }
+ pj_list_push_back(&call->async_call.call_var.inc_call.answers,
+ answer);
+
+ PJSUA_UNLOCK();
+ if (dlg) pjsip_dlg_dec_lock(dlg);
+ pj_log_pop_indent();
+ return status;
+ }
+ PJSUA_UNLOCK();
+
if (call->res_time.sec == 0)
pj_gettimeofday(&call->res_time);
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index aee2f1f4..5a9530e9 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -1656,7 +1656,6 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
if (async) {
call->med_ch_cb = cb;
if (rem_sdp) {
- /* TODO: change rem_sdp to non-const parameter. */
call->async_call.rem_sdp =
pjmedia_sdp_session_clone(call->inv->pool_prov, rem_sdp);
}
@@ -2113,6 +2112,13 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
pjsua_call *call = &pjsua_var.calls[call_id];
unsigned mi;
+ for (mi=0; mi<call->med_cnt; ++mi) {
+ pjsua_call_media *call_med = &call->media[mi];
+
+ if (call_med->tp_st == PJSUA_MED_TP_CREATING)
+ return PJ_EBUSY;
+ }
+
PJ_LOG(4,(THIS_FILE, "Call %d: deinitializing media..", call_id));
pj_log_push_indent();