| /* $Id$ */ |
| /* |
| * Copyright (C) 2003-2006 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.h" |
| #include <pj/log.h> |
| |
| |
| /* |
| * pjsua_inv.c |
| * |
| * Invite session specific functionalities. |
| */ |
| |
| #define THIS_FILE "pjsua_inv.c" |
| |
| |
| /** |
| * Make outgoing call. |
| */ |
| pj_status_t pjsua_invite(const char *cstr_dest_uri, |
| pjsip_inv_session **p_inv) |
| { |
| pj_str_t dest_uri; |
| pjsip_dialog *dlg; |
| pjmedia_sdp_session *offer; |
| pjsip_inv_session *inv; |
| struct pjsua_inv_data *inv_data; |
| pjsip_tx_data *tdata; |
| pj_status_t status; |
| |
| /* Convert cstr_dest_uri to dest_uri */ |
| |
| dest_uri = pj_str((char*)cstr_dest_uri); |
| |
| /* Create outgoing dialog: */ |
| |
| status = pjsip_dlg_create_uac( pjsip_ua_instance(), &pjsua.local_uri, |
| &pjsua.contact_uri, &dest_uri, &dest_uri, |
| &dlg); |
| if (status != PJ_SUCCESS) { |
| pjsua_perror("Dialog creation failed", status); |
| return status; |
| } |
| |
| /* Get media capability from media endpoint: */ |
| |
| status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool, |
| 1, &pjsua.med_skinfo, &offer); |
| if (status != PJ_SUCCESS) { |
| pjsua_perror("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("Invite session creation failed", status); |
| goto on_error; |
| } |
| |
| |
| /* Create and associate our data in the session. */ |
| |
| inv_data = pj_pool_zalloc( dlg->pool, sizeof(struct pjsua_inv_data)); |
| inv_data->inv = inv; |
| dlg->mod_data[pjsua.mod.id] = inv_data; |
| |
| |
| /* Set dialog Route-Set: */ |
| |
| if (!pj_list_empty(&pjsua.route_set)) |
| pjsip_dlg_set_route_set(dlg, &pjsua.route_set); |
| |
| |
| /* Set credentials: */ |
| |
| pjsip_auth_clt_set_credentials( &dlg->auth_sess, pjsua.cred_count, |
| pjsua.cred_info); |
| |
| |
| /* Create initial INVITE: */ |
| |
| status = pjsip_inv_invite(inv, &tdata); |
| if (status != PJ_SUCCESS) { |
| pjsua_perror("Unable to create initial INVITE request", status); |
| goto on_error; |
| } |
| |
| |
| /* Send initial INVITE: */ |
| |
| status = pjsip_inv_send_msg(inv, tdata, NULL); |
| if (status != PJ_SUCCESS) { |
| pjsua_perror("Unable to send initial INVITE request", status); |
| goto on_error; |
| } |
| |
| /* Add invite session to the list. */ |
| |
| pj_list_push_back(&pjsua.inv_list, inv_data); |
| |
| |
| /* Done. */ |
| |
| *p_inv = inv; |
| |
| return PJ_SUCCESS; |
| |
| |
| on_error: |
| |
| PJ_TODO(DESTROY_DIALOG_ON_FAIL); |
| return status; |
| } |
| |
| |
| /** |
| * Handle incoming INVITE request. |
| */ |
| pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata) |
| { |
| pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); |
| pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); |
| pjsip_msg *msg = rdata->msg_info.msg; |
| |
| /* |
| * Handle incoming INVITE outside dialog. |
| */ |
| if (dlg == NULL && tsx == NULL && |
| msg->line.req.method.id == PJSIP_INVITE_METHOD) |
| { |
| pj_status_t status; |
| pjsip_tx_data *response = NULL; |
| unsigned options = 0; |
| |
| /* Verify that we can handle the request. */ |
| status = pjsip_inv_verify_request(rdata, &options, NULL, NULL, |
| pjsua.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.endpt, &res_addr, response, |
| NULL, NULL); |
| |
| } else { |
| |
| /* Respond with 500 (Internal Server Error) */ |
| pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL, |
| NULL, NULL); |
| } |
| |
| } else { |
| /* |
| * Yes we can handle the incoming INVITE request. |
| */ |
| pjsip_inv_session *inv; |
| struct pjsua_inv_data *inv_data; |
| pjmedia_sdp_session *answer; |
| |
| |
| /* Get media capability from media endpoint: */ |
| |
| status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool, |
| 1, &pjsua.med_skinfo, &answer ); |
| if (status != PJ_SUCCESS) { |
| |
| pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL, |
| NULL, NULL); |
| return PJ_TRUE; |
| } |
| |
| /* Create dialog: */ |
| |
| status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata, |
| &pjsua.contact_uri, &dlg); |
| if (status != PJ_SUCCESS) |
| return PJ_TRUE; |
| |
| |
| /* Create invite session: */ |
| |
| status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv); |
| if (status != PJ_SUCCESS) { |
| |
| status = pjsip_dlg_create_response( dlg, rdata, 500, NULL, |
| &response); |
| if (status == PJ_SUCCESS) |
| status = pjsip_dlg_send_response(dlg, |
| pjsip_rdata_get_tsx(rdata), |
| response); |
| return PJ_TRUE; |
| |
| } |
| |
| |
| /* Create and attach pjsua data to the dialog: */ |
| |
| inv_data = pj_pool_zalloc(dlg->pool, sizeof(struct pjsua_inv_data)); |
| inv_data->inv = inv; |
| dlg->mod_data[pjsua.mod.id] = inv_data; |
| |
| pj_list_push_back(&pjsua.inv_list, inv_data); |
| |
| |
| /* Answer with 100 (using the dialog, not invite): */ |
| |
| status = pjsip_dlg_create_response(dlg, rdata, 100, NULL, &response); |
| if (status == PJ_SUCCESS) |
| status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), response); |
| } |
| |
| /* This INVITE request has been handled. */ |
| return PJ_TRUE; |
| } |
| |
| return PJ_FALSE; |
| } |
| |
| |
| /* |
| * This callback receives notification from invite session when the |
| * session state has changed. |
| */ |
| void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) |
| { |
| |
| /* Destroy media session when invite session is disconnected. */ |
| if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { |
| struct pjsua_inv_data *inv_data; |
| |
| inv_data = inv->dlg->mod_data[pjsua.mod.id]; |
| |
| pj_assert(inv_data != NULL); |
| |
| if (inv_data && inv_data->session) { |
| pjmedia_session_destroy(inv_data->session); |
| inv_data->session = NULL; |
| |
| PJ_LOG(3,(THIS_FILE,"Media session is destroyed")); |
| } |
| |
| if (inv_data) { |
| |
| pj_list_erase(inv_data); |
| |
| } |
| } |
| |
| pjsua_ui_inv_on_state_changed(inv, e); |
| } |
| |
| |
| /* |
| * This callback is called by invite session framework when UAC session |
| * has forked. |
| */ |
| void pjsua_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e) |
| { |
| PJ_UNUSED_ARG(inv); |
| PJ_UNUSED_ARG(e); |
| |
| PJ_TODO(HANDLE_FORKED_DIALOG); |
| } |
| |
| |
| /* |
| * 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. |
| */ |
| void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status) |
| { |
| struct pjsua_inv_data *inv_data; |
| const pjmedia_sdp_session *local_sdp; |
| const pjmedia_sdp_session *remote_sdp; |
| |
| if (status != PJ_SUCCESS) { |
| |
| pjsua_perror("SDP negotiation has failed", status); |
| return; |
| |
| } |
| |
| /* Destroy existing media session, if any. */ |
| |
| inv_data = inv->dlg->mod_data[pjsua.mod.id]; |
| if (inv_data && inv_data->session) { |
| pjmedia_session_destroy(inv_data->session); |
| inv_data->session = NULL; |
| } |
| |
| /* Get local and remote SDP */ |
| |
| status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp); |
| if (status != PJ_SUCCESS) { |
| pjsua_perror("Unable to retrieve currently active local SDP", status); |
| return; |
| } |
| |
| |
| status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp); |
| if (status != PJ_SUCCESS) { |
| pjsua_perror("Unable to retrieve currently active remote SDP", status); |
| return; |
| } |
| |
| |
| /* Create new media session. |
| * The media session is active immediately. |
| */ |
| |
| if (!pjsua.null_audio) { |
| |
| status = pjmedia_session_create( pjsua.med_endpt, 1, &pjsua.med_skinfo, |
| local_sdp, remote_sdp, |
| &inv_data->session ); |
| if (status != PJ_SUCCESS) { |
| pjsua_perror("Unable to create media session", status); |
| return; |
| } |
| |
| PJ_LOG(3,(THIS_FILE,"Media has been started successfully")); |
| } |
| } |