📄 pjsua_call.c
字号:
pjsua_call *call;
PJSUA_LOCK();
call = inv->dlg->mod_data[pjsua_var.mod.id];
if (!call) {
PJSUA_UNLOCK();
return;
}
/* Get call times */
switch (inv->state) {
case PJSIP_INV_STATE_EARLY:
case PJSIP_INV_STATE_CONNECTING:
if (call->res_time.sec == 0)
pj_gettimeofday(&call->res_time);
call->last_code = e->body.tsx_state.tsx->status_code;
pj_strncpy(&call->last_text,
&e->body.tsx_state.tsx->status_text,
sizeof(call->last_text_buf_));
break;
case PJSIP_INV_STATE_CONFIRMED:
pj_gettimeofday(&call->conn_time);
break;
case PJSIP_INV_STATE_DISCONNECTED:
pj_gettimeofday(&call->dis_time);
if (call->res_time.sec == 0)
pj_gettimeofday(&call->res_time);
if (e->body.tsx_state.tsx->status_code > call->last_code) {
call->last_code = e->body.tsx_state.tsx->status_code;
pj_strncpy(&call->last_text,
&e->body.tsx_state.tsx->status_text,
sizeof(call->last_text_buf_));
}
break;
default:
call->last_code = e->body.tsx_state.tsx->status_code;
pj_strncpy(&call->last_text,
&e->body.tsx_state.tsx->status_text,
sizeof(call->last_text_buf_));
break;
}
/* If this is an outgoing INVITE that was created because of
* REFER/transfer, send NOTIFY to transferer.
*/
if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
int st_code = -1;
pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
switch (call->inv->state) {
case PJSIP_INV_STATE_NULL:
case PJSIP_INV_STATE_CALLING:
/* Do nothing */
break;
case PJSIP_INV_STATE_EARLY:
case PJSIP_INV_STATE_CONNECTING:
st_code = e->body.tsx_state.tsx->status_code;
ev_state = PJSIP_EVSUB_STATE_ACTIVE;
break;
case PJSIP_INV_STATE_CONFIRMED:
/* When state is confirmed, send the final 200/OK and terminate
* subscription.
*/
st_code = e->body.tsx_state.tsx->status_code;
ev_state = PJSIP_EVSUB_STATE_TERMINATED;
break;
case PJSIP_INV_STATE_DISCONNECTED:
st_code = e->body.tsx_state.tsx->status_code;
ev_state = PJSIP_EVSUB_STATE_TERMINATED;
break;
case PJSIP_INV_STATE_INCOMING:
/* Nothing to do. Just to keep gcc from complaining about
* unused enums.
*/
break;
}
if (st_code != -1) {
pjsip_tx_data *tdata;
pj_status_t status;
status = pjsip_xfer_notify( call->xfer_sub,
ev_state, st_code,
NULL, &tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
} else {
status = pjsip_xfer_send_request(call->xfer_sub, tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
}
}
}
}
if (pjsua_var.ua_cfg.cb.on_call_state)
(*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
/* call->inv may be NULL now */
/* Destroy media session when invite session is disconnected. */
if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
pj_assert(call != NULL);
if (call)
call_destroy_media(call->index);
/* Free call */
call->inv = NULL;
--pjsua_var.call_cnt;
/* Reset call */
reset_call(call->index);
}
PJSUA_UNLOCK();
}
/*
* This callback is called by invite session framework when UAC session
* has forked.
*/
static void pjsua_call_on_forked( pjsip_inv_session *inv,
pjsip_event *e)
{
PJ_UNUSED_ARG(inv);
PJ_UNUSED_ARG(e);
PJ_TODO(HANDLE_FORKED_DIALOG);
}
/*
* Disconnect call upon error.
*/
static void call_disconnect( pjsip_inv_session *inv,
int code )
{
pjsip_tx_data *tdata;
pj_status_t status;
status = pjsip_inv_end_session(inv, code, NULL, &tdata);
if (status == PJ_SUCCESS)
pjsip_inv_send_msg(inv, tdata);
}
/*
* DTMF callback from the stream.
*/
static void dtmf_callback(pjmedia_stream *strm, void *user_data,
int digit)
{
PJ_UNUSED_ARG(strm);
if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
pjsua_call_id call_id;
call_id = (pjsua_call_id)user_data;
pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit);
}
}
/*
* Callback to be called when SDP offer/answer negotiation has just completed
* in the session. This function will start/update media if negotiation
* has succeeded.
*/
static void pjsua_call_on_media_update(pjsip_inv_session *inv,
pj_status_t status)
{
int prev_media_st = 0;
pjsua_call *call;
pjmedia_session_info sess_info;
const pjmedia_sdp_session *local_sdp;
const pjmedia_sdp_session *remote_sdp;
pjmedia_port *media_port;
pj_str_t port_name;
char tmp[PJSIP_MAX_URL_SIZE];
PJSUA_LOCK();
call = inv->dlg->mod_data[pjsua_var.mod.id];
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
/* Stop/destroy media, if any */
call_destroy_media(call->index);
/* Disconnect call if we're not in the middle of initializing an
* UAS dialog and if this is not a re-INVITE
*/
if (inv->state != PJSIP_INV_STATE_NULL &&
inv->state != PJSIP_INV_STATE_CONFIRMED)
{
//call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
}
PJSUA_UNLOCK();
return;
}
/* Destroy existing media session, if any. */
if (call) {
prev_media_st = call->media_st;
call_destroy_media(call->index);
}
/* Get local and remote SDP */
status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Unable to retrieve currently active local SDP",
status);
//call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
PJSUA_UNLOCK();
return;
}
status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Unable to retrieve currently active remote SDP",
status);
//call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
PJSUA_UNLOCK();
return;
}
/* Create media session info based on SDP parameters.
* We only support one stream per session at the moment
*/
status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
pjsua_var.med_endpt,
1,&sess_info,
local_sdp, remote_sdp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create media session",
status);
call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
PJSUA_UNLOCK();
return;
}
/* Check if media is put on-hold */
if (sess_info.stream_cnt == 0 ||
sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
{
/* Determine who puts the call on-hold */
if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
/* It was local who offer hold */
call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
} else {
call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
}
}
call->media_dir = PJMEDIA_DIR_NONE;
} else {
/* Override ptime, if this option is specified. */
if (pjsua_var.media_cfg.ptime != 0) {
sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
(pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime);
if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0)
sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
}
/* Disable VAD, if this option is specified. */
if (pjsua_var.media_cfg.no_vad) {
sess_info.stream_info[0].param->setting.vad = 0;
}
/* Optionally, application may modify other stream settings here
* (such as jitter buffer parameters, codec ptime, etc.)
*/
sess_info.stream_info[0].jb_init = pjsua_var.media_cfg.jb_init;
sess_info.stream_info[0].jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
sess_info.stream_info[0].jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
sess_info.stream_info[0].jb_max = pjsua_var.media_cfg.jb_max;
/* Create session based on session info. */
status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
&call->med_tp,
call, &call->session );
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create media session",
status);
//call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
PJSUA_UNLOCK();
return;
}
/* If DTMF callback is installed by application, install our
* callback to the session.
*/
if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
pjmedia_session_set_dtmf_callback(call->session, 0,
&dtmf_callback,
(void*)(call->index));
}
/* Get the port interface of the first stream in the session.
* We need the port interface to add to the conference bridge.
*/
pjmedia_session_get_port(call->session, 0, &media_port);
/*
* Add the call to conference bridge.
*/
port_name.ptr = tmp;
port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
call->inv->dlg->remote.info->uri,
tmp, sizeof(tmp));
if (port_name.slen < 1) {
port_name = pj_str("call");
}
status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
media_port,
&port_name,
(unsigned*)&call->conf_slot);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create conference slot",
status);
call_destroy_media(call->index);
//call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
PJSUA_UNLOCK();
return;
}
/* Call's media state is active */
call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
call->media_dir = sess_info.stream_info[0].dir;
}
/* Print info. */
{
char info[80];
int info_len = 0;
unsigned i;
for (i=0; i<sess_info.stream_cnt; ++i) {
int len;
const char *dir;
pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
switch (strm_info->dir) {
case PJMEDIA_DIR_NONE:
dir = "inactive";
break;
case PJMEDIA_DIR_ENCODING:
dir = "sendonly";
break;
case PJMEDIA_DIR_DECODING:
dir = "recvonly";
break;
case PJMEDIA_DIR_ENCODING_DECODING:
dir = "sendrecv";
break;
default:
dir = "unknown";
break;
}
len = pj_ansi_sprintf( info+info_len,
", stream #%d: %.*s (%s)", i,
(int)strm_info->fmt.encoding_name.slen,
strm_info->fmt.encoding_name.ptr,
dir);
if (len > 0)
info_len += len;
}
PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
}
/* Call application callback, if any */
if (pjsua_var.ua_cfg.cb.on_call_media_state)
pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
PJSUA_UNLOCK();
}
/*
* Create inactive SDP for call hold.
*/
static pj_status_t create_inactive_sdp(pjsua_call *call,
pjmedia_sdp_session **p_answer)
{
pj_status_t status;
pjmedia_sdp_conn *conn;
pjmedia_sdp_attr *attr;
pjmedia_sdp_session *sdp;
/* Create new offer */
status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pjsua_var.pool, 1,
&call->skinfo, &sdp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
return status;
}
/* Get SDP media connection line */
conn = sdp->media[0]->conn;
if (!conn)
conn = sdp->conn;
/* Modify address */
conn->addr = pj_str("0.0.0.0");
/* Remove existing directions attributes */
pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
/* Add inactive attribute */
attr = pjmedia_sdp_attr_create(pjsua_var.pool, "inactive", NULL);
pjmedia_sdp_media_add_attr(sdp->media[0], attr);
*p_answer = sdp;
return status;
}
/*
* Called when session received new offer.
*/
static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
const pjmedia_sdp_session *offer)
{
const char *remote_state;
pjsua_call *call;
pjmedia_sdp_conn *conn;
pjmedia_sdp_session *answer;
pj_bool_t is_remote_active;
pj_status_t status;
PJSUA_LOCK();
call = inv->dlg->mod_data[pjsua_var.mod.id];
/*
* See if remote is offering active media (i.e. not on-hold)
*/
is_remote_active = PJ_TRUE;
conn = offer->media[0]->conn;
if (!conn)
conn = offer->conn;
if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
pj_strcmp2(&conn->addr, "0")==0)
{
is_remote_active = PJ_FALSE;
}
else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -