⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pjsua_call.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:

	} 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 + -