From dd10c1af79c2a0d4d4720245b7f5d28a63a5292c Mon Sep 17 00:00:00 2001 From: Liong Sauw Ming Date: Mon, 3 Oct 2011 02:04:36 +0000 Subject: 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 --- pjsip/include/pjsip-ua/sip_inv.h | 34 +++++ pjsip/include/pjsua-lib/pjsua_internal.h | 13 ++ pjsip/src/pjsip-ua/sip_inv.c | 149 ++++++++++++++++------ pjsip/src/pjsua-lib/pjsua_call.c | 206 +++++++++++++++++++++++++------ pjsip/src/pjsua-lib/pjsua_media.c | 8 +- 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 @@ -544,6 +544,25 @@ PJ_DECL(pj_status_t) pjsip_inv_verify_request2( pjsip_rx_data *rdata, pjsip_endpoint *endpt, 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, @@ -710,6 +729,21 @@ PJ_DECL(pj_status_t) pjsip_inv_answer( pjsip_inv_session *inv, pjsip_tx_data **p_tdata ); +/** + * 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); @@ -1195,6 +1211,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. */ @@ -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); } @@ -2026,6 +2060,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. */ 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; mimed_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(); -- cgit v1.2.3