📄 pjsua_call.c
字号:
} else {
/* Respond with 500 (Internal Server Error) */
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
}
PJSUA_UNLOCK();
return PJ_TRUE;
}
/* If this INVITE request contains Replaces header, notify application
* about the request so that application can do subsequent checking
* if it wants to.
*/
if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
pjsua_call *replaced_call;
int st_code = 200;
pj_str_t st_text = { "OK", 2 };
/* Get the replaced call instance */
replaced_call = replaced_dlg->mod_data[pjsua_var.mod.id];
/* Notify application */
pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
rdata, &st_code, &st_text);
/* Must specify final response */
PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
/* Check if application rejects this request. */
if (st_code >= 300) {
if (st_text.slen == 2)
st_text = *pjsip_get_status_text(st_code);
pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
st_code, &st_text, NULL, NULL, NULL);
PJSUA_UNLOCK();
return PJ_TRUE;
}
}
/* Get media capability from media endpoint: */
status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt,
rdata->tp_info.pool, 1,
&call->skinfo, &answer );
if (status != PJ_SUCCESS) {
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
PJSUA_UNLOCK();
return PJ_TRUE;
}
/* Verify that we can handle the request. */
status = pjsip_inv_verify_request(rdata, &options, answer, NULL,
pjsua_var.endpt, &response);
if (status != PJ_SUCCESS) {
/*
* No we can't handle the incoming INVITE request.
*/
if (response) {
pjsip_response_addr res_addr;
pjsip_get_response_addr(response->pool, rdata, &res_addr);
pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
NULL, NULL);
} else {
/* Respond with 500 (Internal Server Error) */
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
}
PJSUA_UNLOCK();
return PJ_TRUE;
}
/*
* Get which account is most likely to be associated with this incoming
* call. We need the account to find which contact URI to put for
* the call.
*/
acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
/* Get suitable Contact header */
status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
acc_id, rdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
PJSUA_UNLOCK();
return PJ_TRUE;
}
/* Create dialog: */
status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
&contact, &dlg);
if (status != PJ_SUCCESS) {
pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
NULL, NULL);
PJSUA_UNLOCK();
return PJ_TRUE;
}
/* Set credentials */
if (pjsua_var.acc[acc_id].cred_cnt) {
pjsip_auth_clt_set_credentials(&dlg->auth_sess,
pjsua_var.acc[acc_id].cred_cnt,
pjsua_var.acc[acc_id].cred);
}
/* Create invite session: */
status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
if (status != PJ_SUCCESS) {
pjsip_hdr hdr_list;
pjsip_warning_hdr *w;
w = pjsip_warning_hdr_create_from_status(dlg->pool,
pjsip_endpt_name(pjsua_var.endpt),
status);
pj_list_init(&hdr_list);
pj_list_push_back(&hdr_list, w);
pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
/* Can't terminate dialog because transaction is in progress.
pjsip_dlg_terminate(dlg);
*/
PJSUA_UNLOCK();
return PJ_TRUE;
}
/* 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;
/* If account is locked to specific transport, then lock dialog
* to this transport too.
*/
if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
pjsip_tpselector tp_sel;
pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
pjsip_dlg_set_transport(dlg, &tp_sel);
}
/* Must answer with some response to initial INVITE.
*/
status = pjsip_inv_initial_answer(inv, rdata,
100, NULL, NULL, &response);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
status);
pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
pjsip_inv_terminate(inv, 500, PJ_FALSE);
PJSUA_UNLOCK();
return PJ_TRUE;
} else {
status = pjsip_inv_send_msg(inv, response);
if (status != PJ_SUCCESS)
pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
}
++pjsua_var.call_cnt;
/* Check if this request should replace existing call */
if (replaced_dlg) {
pjsip_inv_session *replaced_inv;
struct pjsua_call *replaced_call;
pjsip_tx_data *tdata;
/* Get the invite session in the dialog */
replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
/* Get the replaced call instance */
replaced_call = replaced_dlg->mod_data[pjsua_var.mod.id];
/* Notify application */
if (pjsua_var.ua_cfg.cb.on_call_replaced)
pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
call_id);
PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
call_id));
/* Answer the new call with 200 response */
status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
if (status == PJ_SUCCESS)
status = pjsip_inv_send_msg(inv, tdata);
if (status != PJ_SUCCESS)
pjsua_perror(THIS_FILE, "Error answering session", status);
/* Note that inv may be invalid if 200/OK has caused error in
* starting the media.
*/
PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
replaced_call->index));
/* Disconnect replaced invite session */
status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
&tdata);
if (status == PJ_SUCCESS && tdata)
status = pjsip_inv_send_msg(replaced_inv, tdata);
if (status != PJ_SUCCESS)
pjsua_perror(THIS_FILE, "Error terminating session", status);
} else {
/* Notify application if on_incoming_call() is overriden,
* otherwise hangup the call with 480
*/
if (pjsua_var.ua_cfg.cb.on_incoming_call) {
pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
} else {
pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
NULL, NULL);
}
}
/* This INVITE request has been handled. */
PJSUA_UNLOCK();
return PJ_TRUE;
}
/*
* Check if the specified call has active INVITE session and the INVITE
* session has not been disconnected.
*/
PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
{
PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
PJ_EINVAL);
return pjsua_var.calls[call_id].inv != NULL &&
pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
}
/*
* Check if call has an active media session.
*/
PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
{
PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
PJ_EINVAL);
return pjsua_var.calls[call_id].session != NULL;
}
/* Acquire lock to the specified call_id */
static pj_status_t acquire_call(const char *title,
pjsua_call_id call_id,
pjsua_call **p_call,
pjsip_dialog **p_dlg)
{
enum { MAX_RETRY=50 };
unsigned retry;
pjsua_call *call = NULL;
pj_bool_t has_pjsua_lock = PJ_FALSE;
pj_status_t status = PJ_SUCCESS;
for (retry=0; retry<MAX_RETRY; ++retry) {
has_pjsua_lock = PJ_FALSE;
status = PJSUA_TRY_LOCK();
if (status != PJ_SUCCESS) {
pj_thread_sleep(retry/10);
continue;
}
has_pjsua_lock = PJ_TRUE;
call = &pjsua_var.calls[call_id];
if (call->inv == NULL) {
PJSUA_UNLOCK();
PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
return PJSIP_ESESSIONTERMINATED;
}
status = pjsip_dlg_try_inc_lock(call->inv->dlg);
if (status != PJ_SUCCESS) {
PJSUA_UNLOCK();
pj_thread_sleep(retry/10);
continue;
}
PJSUA_UNLOCK();
break;
}
if (status != PJ_SUCCESS) {
if (has_pjsua_lock == PJ_FALSE)
PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
"(possibly system has deadlocked) in %s",
title));
else
PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
"(possibly system has deadlocked) in %s",
title));
return PJ_ETIMEDOUT;
}
*p_call = call;
*p_dlg = call->inv->dlg;
return PJ_SUCCESS;
}
/*
* Get the conference port identification associated with the call.
*/
PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
{
pjsua_call *call;
pjsua_conf_port_id port_id;
pjsip_dialog *dlg;
pj_status_t status;
PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
PJ_EINVAL);
status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
if (status != PJ_SUCCESS)
return PJSUA_INVALID_ID;
port_id = call->conf_slot;
pjsip_dlg_dec_lock(dlg);
return port_id;
}
/*
* Obtain detail information about the specified call.
*/
PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
pjsua_call_info *info)
{
pjsua_call *call;
pjsip_dialog *dlg;
pj_status_t status;
PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
PJ_EINVAL);
pj_bzero(info, sizeof(*info));
status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
if (status != PJ_SUCCESS) {
return status;
}
/* id and role */
info->id = call_id;
info->role = call->inv->role;
info->acc_id = call->acc_id;
/* local info */
info->local_info.ptr = info->buf_.local_info;
pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
sizeof(info->buf_.local_info));
/* local contact */
info->local_contact.ptr = info->buf_.local_contact;
info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
call->inv->dlg->local.contact->uri,
info->local_contact.ptr,
sizeof(info->buf_.local_contact));
/* remote info */
info->remote_info.ptr = info->buf_.remote_info;
pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
sizeof(info->buf_.remote_info));
/* remote contact */
if (call->inv->dlg->remote.contact) {
int len;
info->remote_contact.ptr = info->buf_.remote_contact;
len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
call->inv->dlg->remote.contact->uri,
info->remote_contact.ptr,
sizeof(info->buf_.remote_contact));
if (len < 0) len = 0;
info->remote_contact.slen = len;
} else {
info->remote_contact.slen = 0;
}
/* call id */
info->call_id.ptr = info->buf_.call_id;
pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
sizeof(info->buf_.call_id));
/* state, state_text */
info->state = call->inv->state;
info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
/* If call is disconnected, set the last_status from the cause code */
if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
/* last_status, last_status_text */
info->last_status = call->inv->cause;
info->last_status_text.ptr = info->buf_.last_status_text;
pj_strncpy(&info->last_status_text, &call->inv->cause_text,
sizeof(info->buf_.last_status_text));
} else {
/* last_status, last_status_text */
info->last_status = call->last_code;
info->last_status_text.ptr = info->buf_.last_status_text;
pj_strncpy(&info->last_status_text, &call->last_text,
sizeof(info->buf_.last_status_text));
}
/* media status and dir */
info->media_status = call->media_st;
info->media_dir = call->media_dir;
/* conference slot number */
info->conf_slot = call->conf_slot;
/* calculate duration */
if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
info->total_duration = call->dis_time;
PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
if (call->conn_time.sec) {
info->connect_duration = call->dis_time;
PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
}
} else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
pj_gettimeofday(&info->total_duration);
PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
pj_gettimeofday(&info->connect_duration);
PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
} else {
pj_gettimeofday(&info->total_duration);
PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
}
pjsip_dlg_dec_lock(dlg);
return PJ_SUCCESS;
}
/*
* Attach application specific data to the call.
*/
PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
void *user_data)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -