📄 pjsua_call.c
字号:
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){ PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, PJ_EINVAL); pjsua_var.calls[call_id].user_data = user_data; return PJ_SUCCESS;}/* * Get user data attached to the call. */PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id){ PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, NULL); return pjsua_var.calls[call_id].user_data;}/* * Send response to incoming INVITE request. */PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id, unsigned code, const pj_str_t *reason, const pjsua_msg_data *msg_data){ pjsua_call *call; pjsip_dialog *dlg; pjsip_tx_data *tdata; pj_status_t status; PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -