📄 pjsua_call.c
字号:
/* $Id: pjsua_call.c 1107 2007-03-27 10:53:57Z bennylp $ *//* * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <pjsua-lib/pjsua.h>#include <pjsua-lib/pjsua_internal.h>#define THIS_FILE "pjsua_call.c"/* This callback receives notification from invite session when the * session state has changed. */static void pjsua_call_on_state_changed(pjsip_inv_session *inv, pjsip_event *e);/* 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);/* * 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);/* * Called when session received new offer. */static void pjsua_call_on_rx_offer(pjsip_inv_session *inv, const pjmedia_sdp_session *offer);/* * This callback is called when transaction state has changed in INVITE * session. We use this to trap: * - incoming REFER request. * - incoming MESSAGE request. */static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e);/* Destroy the call's media */static pj_status_t call_destroy_media(int call_id);/* Create inactive SDP for call hold. */static pj_status_t create_inactive_sdp(pjsua_call *call, pjmedia_sdp_session **p_answer);/* * Callback called by event framework when the xfer subscription state * has changed. */static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);/* * Reset call descriptor. */static void reset_call(pjsua_call_id id){ pjsua_call *call = &pjsua_var.calls[id]; call->index = id; call->inv = NULL; call->user_data = NULL; call->session = NULL; call->xfer_sub = NULL; call->last_code = 0; call->conf_slot = PJSUA_INVALID_ID; call->last_text.ptr = call->last_text_buf_; call->last_text.slen = 0; call->conn_time.sec = 0; call->conn_time.msec = 0; call->res_time.sec = 0; call->res_time.msec = 0;}/* * Init call subsystem. */pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg){ pjsip_inv_callback inv_cb; unsigned i; const pj_str_t str_norefersub = { "norefersub", 10 }; pj_status_t status; /* Init calls array. */ for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i) reset_call(i); /* Copy config */ pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg); /* Initialize invite session callback. */ pj_bzero(&inv_cb, sizeof(inv_cb)); inv_cb.on_state_changed = &pjsua_call_on_state_changed; inv_cb.on_new_session = &pjsua_call_on_forked; inv_cb.on_media_update = &pjsua_call_on_media_update; inv_cb.on_rx_offer = &pjsua_call_on_rx_offer; inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed; /* Initialize invite session module: */ status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); /* Add "norefersub" in Supported header */ pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED, NULL, 1, &str_norefersub); return status;}/* * Start call subsystem. */pj_status_t pjsua_call_subsys_start(void){ /* Nothing to do */ return PJ_SUCCESS;}/* * Get maximum number of calls configured in pjsua. */PJ_DEF(unsigned) pjsua_call_get_max_count(void){ return pjsua_var.ua_cfg.max_calls;}/* * Get number of currently active calls. */PJ_DEF(unsigned) pjsua_call_get_count(void){ return pjsua_var.call_cnt;}/* * Enum calls. */PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[], unsigned *count){ unsigned i, c; PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL); PJSUA_LOCK(); for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) { if (!pjsua_var.calls[i].inv) continue; ids[c] = i; ++c; } *count = c; PJSUA_UNLOCK(); return PJ_SUCCESS;}/* * Make outgoing call to the specified URI using the specified account. */PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id, const pj_str_t *dest_uri, unsigned options, void *user_data, const pjsua_msg_data *msg_data, pjsua_call_id *p_call_id){ pjsip_dialog *dlg = NULL; pjmedia_sdp_session *offer; pjsip_inv_session *inv = NULL; pjsua_acc *acc; pjsua_call *call; unsigned call_id; pj_str_t contact; pjsip_tx_data *tdata; pj_status_t status; /* Check that account is valid */ PJ_ASSERT_RETURN(acc_id>=0 || acc_id<PJ_ARRAY_SIZE(pjsua_var.acc), PJ_EINVAL); /* Options must be zero for now */ PJ_ASSERT_RETURN(options == 0, PJ_EINVAL); /* Check arguments */ PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL); PJSUA_LOCK(); acc = &pjsua_var.acc[acc_id]; if (!acc->valid) { pjsua_perror(THIS_FILE, "Unable to make call because account " "is not valid", PJ_EINVALIDOP); PJSUA_UNLOCK(); return PJ_EINVALIDOP; } /* Find free call slot. */ for (call_id=0; call_id<pjsua_var.ua_cfg.max_calls; ++call_id) { if (pjsua_var.calls[call_id].inv == NULL) break; } if (call_id == pjsua_var.ua_cfg.max_calls) { pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY); PJSUA_UNLOCK(); return PJ_ETOOMANY; } call = &pjsua_var.calls[call_id]; /* Verify that destination URI is valid before calling * pjsua_acc_create_uac_contact, or otherwise there * a misleading "Invalid Contact URI" error will be printed * when pjsua_acc_create_uac_contact() fails. */ if (1) { pj_pool_t *pool; pjsip_uri *uri; pj_str_t dup; pool = pjsua_pool_create("tmp-uri", 4000, 4000); if (!pool) { pjsua_perror(THIS_FILE, "Unable to create pool", PJ_ENOMEM); PJSUA_UNLOCK(); return PJ_ENOMEM; } pj_strdup_with_null(pool, &dup, dest_uri); uri = pjsip_parse_uri(pool, dup.ptr, dup.slen, 0); pj_pool_release(pool); if (uri == NULL) { pjsua_perror(THIS_FILE, "Unable to make call", PJSIP_EINVALIDREQURI); PJSUA_UNLOCK(); return PJSIP_EINVALIDREQURI; } } PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id, (int)dest_uri->slen, dest_uri->ptr)); /* Mark call start time. */ pj_gettimeofday(&call->start_time); /* Reset first response time */ call->res_time.sec = 0; /* Create suitable Contact header */ status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact, acc_id, dest_uri); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); PJSUA_UNLOCK(); return status; } /* Create outgoing dialog: */ status = pjsip_dlg_create_uac( pjsip_ua_instance(), &acc->cfg.id, &contact, dest_uri, dest_uri, &dlg); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Dialog creation failed", status); PJSUA_UNLOCK(); return status; } /* Get media capability from media endpoint: */ status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, dlg->pool, 1, &call->skinfo, &offer); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status); goto on_error; } /* Create the INVITE session: */ status = pjsip_inv_create_uac( dlg, offer, 0, &inv); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Invite session creation failed", status); goto on_error; } /* Create and associate our data in the session. */ call->inv = inv; dlg->mod_data[pjsua_var.mod.id] = call; inv->mod_data[pjsua_var.mod.id] = call; /* Attach user data */ call->user_data = user_data; /* If account is locked to specific transport, then lock dialog * to this transport too. */ if (acc->cfg.transport_id != PJSUA_INVALID_ID) { pjsip_tpselector tp_sel; pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel); pjsip_dlg_set_transport(dlg, &tp_sel); } /* Set dialog Route-Set: */ if (!pj_list_empty(&acc->route_set)) pjsip_dlg_set_route_set(dlg, &acc->route_set); /* Set credentials: */ if (acc->cred_cnt) { pjsip_auth_clt_set_credentials( &dlg->auth_sess, acc->cred_cnt, acc->cred); } /* Create initial INVITE: */ status = pjsip_inv_invite(inv, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create initial INVITE request", status); goto on_error; } /* Add additional headers etc */ pjsua_process_msg_data( tdata, msg_data); /* Must increment call counter now */ ++pjsua_var.call_cnt; /* Send initial INVITE: */ status = pjsip_inv_send_msg(inv, tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send initial INVITE request", status); /* Upon failure to send first request, both dialog and invite * session would have been cleared. */ inv = NULL; dlg = NULL; goto on_error; } /* Done. */ if (p_call_id) *p_call_id = call_id; PJSUA_UNLOCK(); return PJ_SUCCESS;on_error: if (inv != NULL) { pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE); } else if (dlg) { pjsip_dlg_terminate(dlg); } if (call_id != -1) { reset_call(call_id); } PJSUA_UNLOCK(); return status;}/** * Handle incoming INVITE request. * Called by pjsua_core.c */pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata){ pj_str_t contact; pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); pjsip_dialog *replaced_dlg = NULL; pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); pjsip_msg *msg = rdata->msg_info.msg; pjsip_tx_data *response = NULL; unsigned options = 0; pjsip_inv_session *inv = NULL; int acc_id; pjsua_call *call; int call_id = -1; pjmedia_sdp_session *answer; pj_status_t status; /* Don't want to handle anything but INVITE */ if (msg->line.req.method.id != PJSIP_INVITE_METHOD) return PJ_FALSE; /* Don't want to handle anything that's already associated with * existing dialog or transaction. */ if (dlg || tsx) return PJ_FALSE; PJSUA_LOCK(); /* Find free call slot. */ for (call_id=0; call_id<(int)pjsua_var.ua_cfg.max_calls; ++call_id) { if (pjsua_var.calls[call_id].inv == NULL) break; } if (call_id == (int)pjsua_var.ua_cfg.max_calls) { pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, PJSIP_SC_BUSY_HERE, NULL, NULL, NULL); PJ_LOG(2,(THIS_FILE, "Unable to accept incoming call (too many calls)")); PJSUA_UNLOCK(); return PJ_TRUE; } /* Clear call descriptor */ reset_call(call_id); call = &pjsua_var.calls[call_id]; /* Mark call start time. */ pj_gettimeofday(&call->start_time); /* Check INVITE request for Replaces header. If Replaces header is * present, the function will make sure that we can handle the request. */ status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE, &response); if (status != PJ_SUCCESS) { /* * Something wrong with the Replaces header. */ 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; } /* 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -