blob: 7c6dbf26e56d63e091d7a2352aa7be9dd64e4653 [file] [log] [blame]
/* $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"));
}
}