summaryrefslogtreecommitdiff
path: root/pjsip/src/pjsua/pjsua_inv.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip/src/pjsua/pjsua_inv.c')
-rw-r--r--pjsip/src/pjsua/pjsua_inv.c775
1 files changed, 649 insertions, 126 deletions
diff --git a/pjsip/src/pjsua/pjsua_inv.c b/pjsip/src/pjsua/pjsua_inv.c
index 434d0c70..34bf651f 100644
--- a/pjsip/src/pjsua/pjsua_inv.c
+++ b/pjsip/src/pjsua/pjsua_inv.c
@@ -33,7 +33,7 @@
* Make outgoing call.
*/
pj_status_t pjsua_invite(const char *cstr_dest_uri,
- pjsip_inv_session **p_inv)
+ struct pjsua_inv_data **p_inv_data)
{
pj_str_t dest_uri;
pjsip_dialog *dlg;
@@ -136,8 +136,8 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri,
/* Done. */
-
- *p_inv = inv;
+ if (p_inv_data)
+ *p_inv_data = inv_data;
return PJ_SUCCESS;
@@ -158,127 +158,145 @@ 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;
+ pjsip_tx_data *response = NULL;
+ unsigned options = 0;
+ pjsip_inv_session *inv;
+ struct pjsua_inv_data *inv_data;
+ pjmedia_sdp_session *answer;
+ int med_sk_index;
+ pj_status_t status;
- /*
- * Handle incoming INVITE outside dialog.
+ /* 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 == NULL && tsx == NULL &&
- msg->line.req.method.id == PJSIP_INVITE_METHOD)
- {
- pj_status_t status;
- pjsip_tx_data *response = NULL;
- unsigned options = 0;
+ if (dlg || tsx)
+ return PJ_FALSE;
- /* 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.
- */
+ /* Verify that we can handle the request. */
+ status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
+ pjsua.endpt, &response);
+ if (status != PJ_SUCCESS) {
- if (response) {
- pjsip_response_addr res_addr;
+ /*
+ * No we can't handle the incoming INVITE request.
+ */
- pjsip_get_response_addr(response->pool, rdata, &res_addr);
- pjsip_endpt_send_response(pjsua.endpt, &res_addr, response,
+ 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 {
+ return PJ_TRUE;
+ }
- /* 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;
- int med_sk_index;
+ /*
+ * Yes we can handle the incoming INVITE request.
+ */
+ /* Find free call slot. */
+ for (med_sk_index=0; med_sk_index<PJSUA_MAX_CALLS; ++med_sk_index) {
+ if (!pjsua.med_sock_use[med_sk_index])
+ break;
+ }
- /* Find free socket. */
- for (med_sk_index=0; med_sk_index<PJSUA_MAX_CALLS; ++med_sk_index) {
- if (!pjsua.med_sock_use[med_sk_index])
- break;
- }
+ if (med_sk_index == PJSUA_MAX_CALLS) {
+ pjsip_endpt_respond_stateless(pjsua.endpt, rdata,
+ PJSIP_SC_BUSY_HERE, NULL,
+ NULL, NULL);
+ return PJ_TRUE;
+ }
- if (med_sk_index == PJSUA_MAX_CALLS) {
- PJ_LOG(3,(THIS_FILE, "Error: too many calls!"));
- return PJ_TRUE;
- }
+ pjsua.med_sock_use[med_sk_index] = 1;
- pjsua.med_sock_use[med_sk_index] = 1;
+ /* Get media capability from media endpoint: */
- /* Get media capability from media endpoint: */
+ status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool,
+ 1, &pjsua.med_sock_info[med_sk_index],
+ &answer );
+ if (status != PJ_SUCCESS) {
+ pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
+ NULL, NULL);
- status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool,
- 1, &pjsua.med_sock_info[med_sk_index],
- &answer );
- if (status != PJ_SUCCESS) {
+ /* Free call socket. */
+ pjsua.med_sock_use[med_sk_index] = 0;
+ return PJ_TRUE;
+ }
- pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
- NULL, NULL);
- pjsua.med_sock_use[med_sk_index] = 0;
- return PJ_TRUE;
- }
+ /* Create dialog: */
- /* Create dialog: */
+ status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
+ &pjsua.contact_uri, &dlg);
+ if (status != PJ_SUCCESS) {
+ pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
+ NULL, NULL);
- status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
- &pjsua.contact_uri, &dlg);
- if (status != PJ_SUCCESS) {
- pjsua.med_sock_use[med_sk_index] = 0;
- return PJ_TRUE;
- }
+ /* Free call socket. */
+ pjsua.med_sock_use[med_sk_index] = 0;
+ return PJ_TRUE;
+ }
- /* Create invite session: */
+ /* Create invite session: */
- status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
- if (status != PJ_SUCCESS) {
+ 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);
- pjsua.med_sock_use[med_sk_index] = 0;
- return PJ_TRUE;
+ pjsip_dlg_respond(dlg, rdata, 500, NULL);
- }
+ /* Free call socket. */
+ pjsua.med_sock_use[med_sk_index] = 0;
+
+ // TODO: Need to delete dialog
+ return PJ_TRUE;
+ }
- /* Create and attach pjsua data to the dialog: */
+ /* Create and attach pjsua data to the dialog: */
- inv_data = pj_pool_zalloc(dlg->pool, sizeof(struct pjsua_inv_data));
- inv_data->inv = inv;
- inv_data->call_slot = inv_data->call_slot = med_sk_index;
- dlg->mod_data[pjsua.mod.id] = inv_data;
- inv->mod_data[pjsua.mod.id] = inv_data;
+ inv_data = pj_pool_zalloc(dlg->pool, sizeof(struct pjsua_inv_data));
+ inv_data->inv = inv;
+ inv_data->call_slot = inv_data->call_slot = med_sk_index;
+ dlg->mod_data[pjsua.mod.id] = inv_data;
+ inv->mod_data[pjsua.mod.id] = inv_data;
- pj_list_push_back(&pjsua.inv_list, inv_data);
+ pj_list_push_back(&pjsua.inv_list, inv_data);
- /* Answer with 100 (using the dialog, not invite): */
+ /* 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);
- }
+ status = pjsip_dlg_create_response(dlg, rdata, 100, NULL, &response);
+ if (status != PJ_SUCCESS) {
+
+ pjsip_dlg_respond(dlg, rdata, 500, NULL);
- /* This INVITE request has been handled. */
- return PJ_TRUE;
+ /* Free call socket. */
+ pjsua.med_sock_use[med_sk_index] = 0;
+
+ // TODO: Need to delete dialog
+
+ } else {
+ status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
+ response);
}
- return PJ_FALSE;
+ /* This INVITE request has been handled. */
+ return PJ_TRUE;
}
@@ -288,12 +306,66 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
*/
void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
{
+ struct pjsua_inv_data *inv_data;
+
+ inv_data = inv->dlg->mod_data[pjsua.mod.id];
+
+ /* If this is an outgoing INVITE that was created because of
+ * REFER/transfer, send NOTIFY to transferer.
+ */
+ if (inv_data && inv_data->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE)
+ {
+ int st_code = -1;
+ pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
+
+
+ switch (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;
+ }
+
+ if (st_code != -1) {
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ status = pjsip_xfer_notify( inv_data->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(inv_data->xfer_sub, tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
+ }
+ }
+ }
+ }
+
/* 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);
@@ -318,6 +390,158 @@ void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
/*
+ * Callback called by event framework when the xfer subscription state
+ * has changed.
+ */
+static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
+{
+
+ PJ_UNUSED_ARG(event);
+
+ /*
+ * We're only interested when subscription is terminated, to
+ * clear the xfer_sub member of the inv_data.
+ */
+ if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+ struct pjsua_inv_data *inv_data;
+
+ inv_data = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
+ if (!inv_data)
+ return;
+
+ pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
+ inv_data->xfer_sub = NULL;
+
+ PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
+ }
+}
+
+
+/*
+ * Follow transfer (REFER) request.
+ */
+static void on_call_transfered( pjsip_inv_session *inv,
+ pjsip_rx_data *rdata )
+{
+ pj_status_t status;
+ pjsip_tx_data *tdata;
+ struct pjsua_inv_data *inv_data;
+ const pj_str_t str_refer_to = { "Refer-To", 8};
+ pjsip_generic_string_hdr *refer_to;
+ char *uri;
+ struct pjsip_evsub_user xfer_cb;
+ pjsip_evsub *sub;
+
+ /* Find the Refer-To header */
+ refer_to = (pjsip_generic_string_hdr*)
+ pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
+
+ if (refer_to == NULL) {
+ /* Invalid Request.
+ * No Refer-To header!
+ */
+ PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
+ pjsip_dlg_respond( inv->dlg, rdata, 400, NULL);
+ return;
+ }
+
+ PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
+ (int)inv->dlg->remote.info_str.slen,
+ inv->dlg->remote.info_str.ptr,
+ (int)refer_to->hvalue.slen,
+ refer_to->hvalue.ptr));
+
+ /* Init callback */
+ pj_memset(&xfer_cb, 0, sizeof(xfer_cb));
+ xfer_cb.on_evsub_state = &xfer_on_evsub_state;
+
+ /* Create transferee event subscription */
+ status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
+ pjsip_dlg_respond( inv->dlg, rdata, 500, NULL);
+ return;
+ }
+
+ /* Accept the REFER request, send 200 (OK). */
+ pjsip_xfer_accept(sub, rdata, 200, NULL);
+
+ /* Create initial NOTIFY request */
+ status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
+ 100, NULL, &tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status);
+ return;
+ }
+
+ /* Send initial NOTIFY request */
+ status = pjsip_xfer_send_request( sub, tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
+ return;
+ }
+
+ /* We're cheating here.
+ * We need to get a null terminated string from a pj_str_t.
+ * So grab the pointer from the hvalue and NULL terminate it, knowing
+ * that the NULL position will be occupied by a newline.
+ */
+ uri = refer_to->hvalue.ptr;
+ uri[refer_to->hvalue.slen] = '\0';
+
+ /* Now make the outgoing call. */
+ status = pjsua_invite(uri, &inv_data);
+ if (status != PJ_SUCCESS) {
+
+ /* Notify xferer about the error */
+ status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
+ 500, NULL, &tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
+ status);
+ return;
+ }
+ status = pjsip_xfer_send_request(sub, tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
+ status);
+ return;
+ }
+ return;
+ }
+
+ /* Put the server subscription in inv_data.
+ * Subsequent state changed in pjsua_inv_on_state_changed() will be
+ * reported back to the server subscription.
+ */
+ inv_data->xfer_sub = sub;
+
+ /* Put the invite_data in the subscription. */
+ pjsip_evsub_set_mod_data(sub, pjsua.mod.id, inv_data);
+}
+
+
+/*
+ * This callback is called when transaction state has changed in INVITE
+ * session. We use this to trap incoming REFER request.
+ */
+void pjsua_inv_on_tsx_state_changed(pjsip_inv_session *inv,
+ pjsip_transaction *tsx,
+ pjsip_event *e)
+{
+ if (tsx->role==PJSIP_ROLE_UAS &&
+ tsx->state==PJSIP_TSX_STATE_TRYING &&
+ pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
+ {
+ /*
+ * Incoming REFER request.
+ */
+ on_call_transfered(inv, e->body.tsx_state.src.rdata);
+ }
+}
+
+
+/*
* This callback is called by invite session framework when UAC session
* has forked.
*/
@@ -331,6 +555,109 @@ void pjsua_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e)
/*
+ * Create inactive SDP for call hold.
+ */
+static pj_status_t create_inactive_sdp(struct pjsua_inv_data *inv_session,
+ 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.med_endpt, pjsua.pool, 1,
+ &pjsua.med_sock_info[inv_session->call_slot],
+ &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.pool, "inactive", NULL);
+ pjmedia_sdp_media_add_attr(sdp->media[0], attr);
+
+ *p_answer = sdp;
+
+ return status;
+}
+
+/*
+ * Called when session received new offer.
+ */
+void pjsua_inv_on_rx_offer( pjsip_inv_session *inv,
+ const pjmedia_sdp_session *offer)
+{
+ struct pjsua_inv_data *inv_data;
+ pjmedia_sdp_conn *conn;
+ pjmedia_sdp_session *answer;
+ pj_bool_t is_remote_active;
+ pj_status_t status;
+
+ inv_data = inv->dlg->mod_data[pjsua.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))
+ {
+ is_remote_active = PJ_FALSE;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "Received SDP offer, remote media is %s",
+ (is_remote_active ? "active" : "inactive")));
+
+ /* Supply candidate answer */
+ if (is_remote_active) {
+ status = pjmedia_endpt_create_sdp( pjsua.med_endpt, inv->pool, 1,
+ &pjsua.med_sock_info[inv_data->call_slot],
+ &answer);
+ } else {
+ status = create_inactive_sdp( inv_data, &answer );
+ }
+
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
+ return;
+ }
+
+ status = pjsip_inv_set_sdp_answer(inv, answer);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to set answer", status);
+ return;
+ }
+
+}
+
+
+/*
* 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.
@@ -340,6 +667,9 @@ 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;
+ pjmedia_port *media_port;
+ pj_str_t port_name;
+ char tmp[PJSIP_MAX_URL_SIZE];
if (status != PJ_SUCCESS) {
@@ -380,52 +710,244 @@ void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
/* Create new media session.
* The media session is active immediately.
*/
+ if (pjsua.null_audio)
+ return;
+
+ status = pjmedia_session_create( pjsua.med_endpt, 1,
+ &pjsua.med_sock_info[inv_data->call_slot],
+ local_sdp, remote_sdp,
+ &inv_data->session );
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create media session",
+ status);
+ return;
+ }
- if (!pjsua.null_audio) {
- pjmedia_port *media_port;
- pj_str_t port_name;
- char tmp[PJSIP_MAX_URL_SIZE];
- status = pjmedia_session_create( pjsua.med_endpt, 1,
- &pjsua.med_sock_info[inv_data->call_slot],
- local_sdp, remote_sdp,
- &inv_data->session );
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to create media session",
- status);
- return;
- }
+ /* 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(inv_data->session, 0, &media_port);
- pjmedia_session_get_port(inv_data->session, 0, &media_port);
- port_name.ptr = tmp;
- port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
- inv_data->inv->dlg->remote.info->uri,
- tmp, sizeof(tmp));
- if (port_name.slen < 1) {
- port_name = pj_str("call");
- }
- status = pjmedia_conf_add_port( pjsua.mconf, inv->pool,
- media_port,
- &port_name,
- &inv_data->conf_slot);
- if (status != PJ_SUCCESS) {
- pjsua_perror(THIS_FILE, "Unable to create conference slot",
- status);
- pjmedia_session_destroy(inv_data->session);
- inv_data->session = NULL;
- return;
+ /*
+ * Add the call to conference bridge.
+ */
+ port_name.ptr = tmp;
+ port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
+ inv_data->inv->dlg->remote.info->uri,
+ tmp, sizeof(tmp));
+ if (port_name.slen < 1) {
+ port_name = pj_str("call");
+ }
+ status = pjmedia_conf_add_port( pjsua.mconf, inv->pool,
+ media_port,
+ &port_name,
+ &inv_data->conf_slot);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create conference slot",
+ status);
+ pjmedia_session_destroy(inv_data->session);
+ inv_data->session = NULL;
+ return;
+ }
+
+ /* Connect new call to the sound device port (port zero) in the
+ * main conference bridge.
+ */
+ pjmedia_conf_connect_port( pjsua.mconf, 0, inv_data->conf_slot);
+ pjmedia_conf_connect_port( pjsua.mconf, inv_data->conf_slot, 0);
+
+ /* Done. */
+ {
+ struct pjmedia_session_info sess_info;
+ char info[80];
+ int info_len = 0;
+ unsigned i;
+
+ pjmedia_session_get_info(inv_data->session, &sess_info);
+ 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,
+ (int)strm_info->fmt.encoding_name.ptr,
+ dir);
+ if (len > 0)
+ info_len += len;
}
+ PJ_LOG(3,(THIS_FILE,"Media started%s", info));
+ }
+}
+
+
+/*
+ * Hangup call.
+ */
+void pjsua_inv_hangup(struct pjsua_inv_data *inv_session, int code)
+{
+ pj_status_t status;
+ pjsip_tx_data *tdata;
+
+ status = pjsip_inv_end_session(inv_session->inv,
+ code, NULL, &tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE,
+ "Failed to create end session message",
+ status);
+ return;
+ }
+
+ status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE,
+ "Failed to send end session message",
+ status);
+ return;
+ }
+}
+
+
+/*
+ * Put call on-Hold.
+ */
+void pjsua_inv_set_hold(struct pjsua_inv_data *inv_session)
+{
+ pjmedia_sdp_session *sdp;
+ pjsip_inv_session *inv = inv_session->inv;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ if (inv->state != PJSIP_INV_STATE_CONFIRMED) {
+ PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
+ return;
+ }
- pjmedia_conf_connect_port( pjsua.mconf, 0, inv_data->conf_slot);
- pjmedia_conf_connect_port( pjsua.mconf, inv_data->conf_slot, 0);
+ status = create_inactive_sdp(inv_session, &sdp);
+ if (status != PJ_SUCCESS)
+ return;
+
+ /* Send re-INVITE with new offer */
+ status = pjsip_inv_reinvite( inv_session->inv, NULL, sdp, &tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
+ return;
+ }
- PJ_LOG(3,(THIS_FILE,"Media has been started successfully"));
+ status = pjsip_inv_send_msg( inv_session->inv, tdata, NULL);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
+ return;
}
}
/*
+ * re-INVITE.
+ */
+void pjsua_inv_reinvite(struct pjsua_inv_data *inv_session)
+{
+ pjmedia_sdp_session *sdp;
+ pjsip_tx_data *tdata;
+ pjsip_inv_session *inv = inv_session->inv;
+ pj_status_t status;
+
+
+ if (inv->state != PJSIP_INV_STATE_CONFIRMED) {
+ PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
+ return;
+ }
+
+ /* Create SDP */
+ status = pjmedia_endpt_create_sdp( pjsua.med_endpt, inv->pool, 1,
+ &pjsua.med_sock_info[inv_session->call_slot],
+ &sdp);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", status);
+ return;
+ }
+
+ /* Send re-INVITE with new offer */
+ status = pjsip_inv_reinvite( inv_session->inv, NULL, sdp, &tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
+ return;
+ }
+
+ status = pjsip_inv_send_msg( inv_session->inv, tdata, NULL);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
+ return;
+ }
+}
+
+
+/*
+ * Transfer call.
+ */
+void pjsua_inv_xfer_call(struct pjsua_inv_data *inv_session,
+ const char *dest)
+{
+ pjsip_evsub *sub;
+ pjsip_tx_data *tdata;
+ pj_str_t tmp;
+ pj_status_t status;
+
+
+ /* Create xfer client subscription.
+ * We're not interested in knowing the transfer result, so we
+ * put NULL as the callback.
+ */
+ status = pjsip_xfer_create_uac(inv_session->inv->dlg, NULL, &sub);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create xfer", status);
+ return;
+ }
+
+ /*
+ * Create REFER request.
+ */
+ status = pjsip_xfer_initiate(sub, pj_cstr(&tmp, dest), &tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
+ return;
+ }
+
+ /* Send. */
+ status = pjsip_xfer_send_request(sub, tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
+ return;
+ }
+
+ /* For simplicity (that's what this program is intended to be!),
+ * leave the original invite session as it is. More advanced application
+ * may want to hold the INVITE, or terminate the invite, or whatever.
+ */
+}
+
+
+/*
* Terminate all calls.
*/
void pjsua_inv_shutdown()
@@ -445,3 +967,4 @@ void pjsua_inv_shutdown()
}
}
+