Index: pjproject/trunk/pjsip/src/pjsua/pjsua_call.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua/pjsua_inv.c	(revision 226)
+++ pjproject/trunk/pjsip/src/pjsua/pjsua_call.c	(revision 236)
@@ -33,6 +33,7 @@
  * Make outgoing call.
  */
-pj_status_t pjsua_invite(const char *cstr_dest_uri,
-			 struct pjsua_inv_data **p_inv_data)
+pj_status_t pjsua_make_call(int acc_index,
+			    const char *cstr_dest_uri,
+			    int *p_call_index)
 {
     pj_str_t dest_uri;
@@ -40,7 +41,6 @@
     pjmedia_sdp_session *offer;
     pjsip_inv_session *inv;
-    struct pjsua_inv_data *inv_data;
+    int call_index = -1;
     pjsip_tx_data *tdata;
-    int med_sk_index = 0;
     pj_status_t status;
 
@@ -49,21 +49,21 @@
     dest_uri = pj_str((char*)cstr_dest_uri);
 
-    /* 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])
+    /* Find free call slot. */
+    for (call_index=0; call_index<pjsua.max_calls; ++call_index) {
+	if (pjsua.calls[call_index].inv == NULL)
 	    break;
     }
 
-    if (med_sk_index == PJSUA_MAX_CALLS) {
+    if (call_index == pjsua.max_calls) {
 	PJ_LOG(3,(THIS_FILE, "Error: too many calls!"));
 	return PJ_ETOOMANY;
     }
 
-    pjsua.med_sock_use[med_sk_index] = 1;
-
     /* Create outgoing dialog: */
 
-    status = pjsip_dlg_create_uac( pjsip_ua_instance(), &pjsua.local_uri,
-				   &pjsua.contact_uri, &dest_uri, &dest_uri,
+    status = pjsip_dlg_create_uac( pjsip_ua_instance(), 
+				   &pjsua.acc[acc_index].local_uri,
+				   &pjsua.acc[acc_index].contact_uri, 
+				   &dest_uri, &dest_uri,
 				   &dlg);
     if (status != PJ_SUCCESS) {
@@ -74,6 +74,6 @@
     /* Get media capability from media endpoint: */
 
-    status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool,
-				       1, &pjsua.med_sock_info[med_sk_index], 
+    status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool, 1, 
+				       &pjsua.calls[call_index].skinfo, 
 				       &offer);
     if (status != PJ_SUCCESS) {
@@ -93,15 +93,14 @@
     /* Create and associate our data in the session. */
 
-    inv_data = pj_pool_zalloc( dlg->pool, sizeof(struct pjsua_inv_data));
-    inv_data->inv = inv;
-    inv_data->call_slot = med_sk_index;
-    dlg->mod_data[pjsua.mod.id] = inv_data;
-    inv->mod_data[pjsua.mod.id] = inv_data;
+    pjsua.calls[call_index].inv = inv;
+
+    dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
+    inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
 
 
     /* Set dialog Route-Set: */
 
-    if (!pj_list_empty(&pjsua.route_set))
-	pjsip_dlg_set_route_set(dlg, &pjsua.route_set);
+    if (!pj_list_empty(&pjsua.acc[acc_index].route_set))
+	pjsip_dlg_set_route_set(dlg, &pjsua.acc[acc_index].route_set);
 
 
@@ -122,17 +121,8 @@
 
 
-    /* Add invite session to the list. */
-    
-    pj_list_push_back(&pjsua.inv_list, inv_data);
-
-
     /* Send initial INVITE: */
 
     status = pjsip_inv_send_msg(inv, tdata, NULL);
     if (status != PJ_SUCCESS) {
-	/*
-	 * Note:
-	 *  inv_data will be removed from the list in the callback
-	 */
 	pjsua_perror(THIS_FILE, "Unable to send initial INVITE request", 
 		     status);
@@ -142,6 +132,9 @@
 
     /* Done. */
-    if (p_inv_data)
-	*p_inv_data = inv_data;
+
+    ++pjsua.call_cnt;
+
+    if (p_call_index)
+	*p_call_index = call_index;
 
     return PJ_SUCCESS;
@@ -149,7 +142,8 @@
 
 on_error:
-
     PJ_TODO(DESTROY_DIALOG_ON_FAIL);
-    pjsua.med_sock_use[med_sk_index] = 0;
+    if (call_index != -1) {
+	pjsua.calls[call_index].inv = NULL;
+    }
     return status;
 }
@@ -159,5 +153,5 @@
  * Handle incoming INVITE request.
  */
-pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
+pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
 {
     pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
@@ -167,7 +161,7 @@
     unsigned options = 0;
     pjsip_inv_session *inv;
-    struct pjsua_inv_data *inv_data;
+    int acc_index;
+    int call_index = -1;
     pjmedia_sdp_session *answer;
-    int med_sk_index;
     pj_status_t status;
 
@@ -215,10 +209,10 @@
 
     /* 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])
+    for (call_index=0; call_index < pjsua.max_calls; ++call_index) {
+	if (pjsua.calls[call_index].inv == NULL)
 	    break;
     }
 
-    if (med_sk_index == PJSUA_MAX_CALLS) {
+    if (call_index == PJSUA_MAX_CALLS) {
 	pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 
 				      PJSIP_SC_BUSY_HERE, NULL,
@@ -228,10 +222,8 @@
 
 
-    pjsua.med_sock_use[med_sk_index] = 1;
-
     /* 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], 
+    status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool, 1,
+				       &pjsua.calls[call_index].skinfo, 
 				       &answer );
     if (status != PJ_SUCCESS) {
@@ -239,19 +231,24 @@
 				      NULL, NULL);
 
-	/* Free call socket. */
-	pjsua.med_sock_use[med_sk_index] = 0;
 	return PJ_TRUE;
     }
 
+    /* TODO: 
+     *
+     * Get which account is most likely to be associated with this incoming
+     * call. We need the account to find which contact URI to put for
+     * the call.
+     */
+    acc_index = 0;
+
     /* Create dialog: */
 
     status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
-				   &pjsua.contact_uri, &dlg);
+				   &pjsua.acc[acc_index].contact_uri, 
+				   &dlg);
     if (status != PJ_SUCCESS) {
 	pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
 				      NULL, NULL);
 
-	/* Free call socket. */
-	pjsua.med_sock_use[med_sk_index] = 0;
 	return PJ_TRUE;
     }
@@ -264,7 +261,4 @@
 
 	pjsip_dlg_respond(dlg, rdata, 500, NULL);
-
-	/* Free call socket. */
-	pjsua.med_sock_use[med_sk_index] = 0;
 
 	// TODO: Need to delete dialog
@@ -275,11 +269,8 @@
     /* 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;
-
-    pj_list_push_back(&pjsua.inv_list, inv_data);
+    pjsua.calls[call_index].inv = inv;
+
+    dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
+    inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
 
 
@@ -296,7 +287,4 @@
 
 	pjsip_dlg_respond(dlg, rdata, 500, NULL);
-
-	/* Free call socket. */
-	pjsua.med_sock_use[med_sk_index] = 0;
 
 	// TODO: Need to delete dialog
@@ -329,4 +317,6 @@
     }
 
+    ++pjsua.call_cnt;
+
     /* This INVITE request has been handled. */
     return PJ_TRUE;
@@ -338,20 +328,18 @@
  * session state has changed.
  */
-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];
+static void pjsua_call_on_state_changed(pjsip_inv_session *inv, 
+					pjsip_event *e)
+{
+    pjsua_call *call = 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) 
-    {
+    if (call && call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE)  {
 	int st_code = -1;
 	pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
 	
 
-	switch (inv->state) {
+	switch (call->inv->state) {
 	case PJSIP_INV_STATE_NULL:
 	case PJSIP_INV_STATE_CALLING:
@@ -383,5 +371,5 @@
 	    pj_status_t status;
 
-	    status = pjsip_xfer_notify( inv_data->xfer_sub,
+	    status = pjsip_xfer_notify( call->xfer_sub,
 					ev_state, st_code,
 					NULL, &tdata);
@@ -389,5 +377,5 @@
 		pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
 	    } else {
-		status = pjsip_xfer_send_request(inv_data->xfer_sub, tdata);
+		status = pjsip_xfer_send_request(call->xfer_sub, tdata);
 		if (status != PJ_SUCCESS) {
 		    pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
@@ -398,26 +386,24 @@
 
 
+    pjsua_ui_inv_on_state_changed(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(inv_data != NULL);
-
-	if (inv_data && inv_data->session) {
-	    pjmedia_conf_remove_port(pjsua.mconf, inv_data->conf_slot);
-	    pjmedia_session_destroy(inv_data->session);
-	    pjsua.med_sock_use[inv_data->call_slot] = 0;
-	    inv_data->session = NULL;
+	pj_assert(call != NULL);
+
+	if (call && call->session) {
+	    pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot);
+	    pjmedia_session_destroy(call->session);
+	    call->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);
+	call->inv = NULL;
+	--pjsua.call_cnt;
+    }
 }
 
@@ -437,12 +423,12 @@
      */
     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)
+	pjsua_call *call;
+
+	call = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
+	if (!call)
 	    return;
 
 	pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
-	inv_data->xfer_sub = NULL;
+	call->xfer_sub = NULL;
 
 	PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
@@ -459,5 +445,6 @@
     pj_status_t status;
     pjsip_tx_data *tdata;
-    struct pjsua_inv_data *inv_data;
+    pjsua_call *existing_call;
+    int new_call;
     const pj_str_t str_refer_to = { "Refer-To", 8};
     pjsip_generic_string_hdr *refer_to;
@@ -465,4 +452,6 @@
     struct pjsip_evsub_user xfer_cb;
     pjsip_evsub *sub;
+
+    existing_call = inv->dlg->mod_data[pjsua.mod.id];
 
     /* Find the Refer-To header */
@@ -524,5 +513,5 @@
 
     /* Now make the outgoing call. */
-    status = pjsua_invite(uri, &inv_data);
+    status = pjsua_make_call(existing_call->acc_index, uri, &new_call);
     if (status != PJ_SUCCESS) {
 
@@ -548,8 +537,8 @@
      * reported back to the server subscription.
      */
-    inv_data->xfer_sub = sub;
+    pjsua.calls[new_call].xfer_sub = sub;
 
     /* Put the invite_data in the subscription. */
-    pjsip_evsub_set_mod_data(sub, pjsua.mod.id, inv_data);
+    pjsip_evsub_set_mod_data(sub, pjsua.mod.id, &pjsua.calls[new_call]);
 }
 
@@ -559,8 +548,10 @@
  * 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)
-{
+static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
+					    pjsip_transaction *tsx,
+					    pjsip_event *e)
+{
+    pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id];
+
     if (tsx->role==PJSIP_ROLE_UAS &&
 	tsx->state==PJSIP_TSX_STATE_TRYING &&
@@ -570,5 +561,5 @@
 	 * Incoming REFER request.
 	 */
-	on_call_transfered(inv, e->body.tsx_state.src.rdata);
+	on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
     }
 }
@@ -579,5 +570,6 @@
  * has forked.
  */
-void pjsua_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e)
+static void pjsua_call_on_forked( pjsip_inv_session *inv, 
+				  pjsip_event *e)
 {
     PJ_UNUSED_ARG(inv);
@@ -591,5 +583,5 @@
  * Create inactive SDP for call hold.
  */
-static pj_status_t create_inactive_sdp(struct pjsua_inv_data *inv_session,
+static pj_status_t create_inactive_sdp(pjsua_call *call,
 				       pjmedia_sdp_session **p_answer)
 {
@@ -601,6 +593,5 @@
     /* Create new offer */
     status = pjmedia_endpt_create_sdp(pjsua.med_endpt, pjsua.pool, 1,
-				      &pjsua.med_sock_info[inv_session->call_slot],
-				      &sdp);
+				      &call->skinfo, &sdp);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
@@ -634,8 +625,8 @@
  * 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;
+static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
+				   const pjmedia_sdp_session *offer)
+{
+    pjsua_call *call;
     pjmedia_sdp_conn *conn;
     pjmedia_sdp_session *answer;
@@ -643,5 +634,5 @@
     pj_status_t status;
 
-    inv_data = inv->dlg->mod_data[pjsua.mod.id];
+    call = inv->dlg->mod_data[pjsua.mod.id];
 
     /*
@@ -670,9 +661,8 @@
     /* 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);
+	status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
+					   &call->skinfo, &answer);
     } else {
-	status = create_inactive_sdp( inv_data, &answer );
+	status = create_inactive_sdp( call, &answer );
     }
 
@@ -682,5 +672,5 @@
     }
 
-    status = pjsip_inv_set_sdp_answer(inv, answer);
+    status = pjsip_inv_set_sdp_answer(call->inv, answer);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to set answer", status);
@@ -696,7 +686,8 @@
  * has succeeded.
  */
-void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
-{
-    struct pjsua_inv_data *inv_data;
+static void pjsua_call_on_media_update(pjsip_inv_session *inv,
+				       pj_status_t status)
+{
+    pjsua_call *call;
     const pjmedia_sdp_session *local_sdp;
     const pjmedia_sdp_session *remote_sdp;
@@ -705,4 +696,6 @@
     char tmp[PJSIP_MAX_URL_SIZE];
 
+    call = inv->dlg->mod_data[pjsua.mod.id];
+
     if (status != PJ_SUCCESS) {
 
@@ -714,15 +707,13 @@
     /* Destroy existing media session, if any. */
 
-    inv_data = inv->dlg->mod_data[pjsua.mod.id];
-    if (inv_data && inv_data->session) {
-	pjmedia_conf_remove_port(pjsua.mconf, inv_data->conf_slot);
-	pjmedia_session_destroy(inv_data->session);
-	pjsua.med_sock_use[inv_data->call_slot] = 0;
-	inv_data->session = NULL;
+    if (call && call->session) {
+	pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot);
+	pjmedia_session_destroy(call->session);
+	call->session = NULL;
     }
 
     /* Get local and remote SDP */
 
-    status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
+    status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, 
@@ -733,5 +724,5 @@
 
 
-    status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
+    status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, 
@@ -748,8 +739,8 @@
     
     status = pjmedia_session_create( pjsua.med_endpt, 1, 
-				     &pjsua.med_sock_info[inv_data->call_slot],
+				     &call->skinfo,
 				     local_sdp, remote_sdp, 
-				     inv_data,
-				     &inv_data->session );
+				     call,
+				     &call->session );
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to create media session", 
@@ -762,5 +753,5 @@
      * 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(call->session, 0, &media_port);
 
 
@@ -770,18 +761,18 @@
     port_name.ptr = tmp;
     port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
-				     inv_data->inv->dlg->remote.info->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.mconf, inv->pool,
+    status = pjmedia_conf_add_port( pjsua.mconf, call->inv->pool,
 				    media_port, 
 				    &port_name,
-				    &inv_data->conf_slot);
+				    &call->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;
+	pjmedia_session_destroy(call->session);
+	call->session = NULL;
 	return;
     }
@@ -790,8 +781,33 @@
      * port 
      */
-    if (pjsua.wav_file && inv->role == PJSIP_ROLE_UAS) {
+    if (pjsua.auto_play && pjsua.wav_file && 
+	call->inv->role == PJSIP_ROLE_UAS) 
+    {
 
 	pjmedia_conf_connect_port( pjsua.mconf, pjsua.wav_slot, 
-				   inv_data->conf_slot);
+				   call->conf_slot);
+
+    } else if (pjsua.auto_loop && call->inv->role == PJSIP_ROLE_UAS) {
+
+	pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 
+				   call->conf_slot);
+
+    } else if (pjsua.auto_conf) {
+
+	int i;
+
+	pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot);
+	pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0);
+
+	for (i=0; i < pjsua.max_calls; ++i) {
+
+	    if (!pjsua.calls[i].session)
+		continue;
+
+	    pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 
+				       pjsua.calls[i].conf_slot);
+	    pjmedia_conf_connect_port( pjsua.mconf, pjsua.calls[i].conf_slot,
+				       call->conf_slot);
+	}
 
     } else {
@@ -800,6 +816,6 @@
 	 * 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);
+	pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot);
+	pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0);
     }
 
@@ -812,5 +828,5 @@
 	unsigned i;
 
-	pjmedia_session_get_info(inv_data->session, &sess_info);
+	pjmedia_session_get_info(call->session, &sess_info);
 	for (i=0; i<sess_info.stream_cnt; ++i) {
 	    int len;
@@ -851,11 +867,19 @@
  * Hangup call.
  */
-void pjsua_inv_hangup(struct pjsua_inv_data *inv_session, int code)
-{
+void pjsua_call_hangup(int call_index, int code)
+{
+    pjsua_call *call;
     pj_status_t status;
     pjsip_tx_data *tdata;
 
-    status = pjsip_inv_end_session(inv_session->inv, 
-				   code, NULL, &tdata);
+
+    call = &pjsua.calls[call_index];
+
+    if (!call->inv) {
+	PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+	return;
+    }
+
+    status = pjsip_inv_end_session(call->inv, code, NULL, &tdata);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, 
@@ -872,5 +896,5 @@
 	return;
 
-    status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL);
+    status = pjsip_inv_send_msg(call->inv, tdata, NULL);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, 
@@ -885,22 +909,29 @@
  * Put call on-Hold.
  */
-void pjsua_inv_set_hold(struct pjsua_inv_data *inv_session)
+void pjsua_call_set_hold(int call_index)
 {
     pjmedia_sdp_session *sdp;
-    pjsip_inv_session *inv = inv_session->inv;
+    pjsua_call *call;
     pjsip_tx_data *tdata;
     pj_status_t status;
 
-    if (inv->state != PJSIP_INV_STATE_CONFIRMED) {
+    call = &pjsua.calls[call_index];
+    
+    if (!call->inv) {
+	PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+	return;
+    }
+
+    if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
 	PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
 	return;
     }
 
-    status = create_inactive_sdp(inv_session, &sdp);
+    status = create_inactive_sdp(call, &sdp);
     if (status != PJ_SUCCESS)
 	return;
 
     /* Send re-INVITE with new offer */
-    status = pjsip_inv_reinvite( inv_session->inv, NULL, sdp, &tdata);
+    status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
@@ -908,5 +939,5 @@
     }
 
-    status = pjsip_inv_send_msg( inv_session->inv, tdata, NULL);
+    status = pjsip_inv_send_msg( call->inv, tdata, NULL);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
@@ -919,13 +950,20 @@
  * re-INVITE.
  */
-void pjsua_inv_reinvite(struct pjsua_inv_data *inv_session)
+void pjsua_call_reinvite(int call_index)
 {
     pjmedia_sdp_session *sdp;
     pjsip_tx_data *tdata;
-    pjsip_inv_session *inv = inv_session->inv;
+    pjsua_call *call;
     pj_status_t status;
 
-
-    if (inv->state != PJSIP_INV_STATE_CONFIRMED) {
+    call = &pjsua.calls[call_index];
+
+    if (!call->inv) {
+	PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+	return;
+    }
+
+
+    if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
 	PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
 	return;
@@ -933,14 +971,14 @@
 
     /* 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);
+    status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
+				       &call->skinfo, &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);
+    status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
@@ -948,5 +986,5 @@
     }
 
-    status = pjsip_inv_send_msg( inv_session->inv, tdata, NULL);
+    status = pjsip_inv_send_msg( call->inv, tdata, NULL);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
@@ -959,18 +997,25 @@
  * Transfer call.
  */
-void pjsua_inv_xfer_call(struct pjsua_inv_data *inv_session,
-			 const char *dest)
+void pjsua_call_xfer(int call_index, const char *dest)
 {
     pjsip_evsub *sub;
     pjsip_tx_data *tdata;
+    pjsua_call *call;
     pj_str_t tmp;
     pj_status_t status;
- 
+
     
+    call = &pjsua.calls[call_index];
+
+    if (!call->inv) {
+	PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
+	return;
+    }
+   
     /* 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);
+    status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to create xfer", status);
@@ -1006,20 +1051,40 @@
 void pjsua_inv_shutdown()
 {
-    struct pjsua_inv_data *inv_data, *next;
-
-    inv_data = pjsua.inv_list.next;
-    while (inv_data != &pjsua.inv_list) {
+    int i;
+
+    for (i=0; i<pjsua.max_calls; ++i) {
 	pjsip_tx_data *tdata;
-
-	next = inv_data->next;
-
-	if (pjsip_inv_end_session(inv_data->inv, 410, NULL, &tdata)==0) {
+	pjsua_call *call;
+
+	if (pjsua.calls[i].inv == NULL)
+	    continue;
+
+	call = &pjsua.calls[i];
+
+	if (pjsip_inv_end_session(call->inv, 410, NULL, &tdata)==0) {
 	    if (tdata)
-		pjsip_inv_send_msg(inv_data->inv, tdata, NULL);
+		pjsip_inv_send_msg(call->inv, tdata, NULL);
 	}
-
-	inv_data = next;
-    }
-}
-
-
+    }
+}
+
+
+pj_status_t pjsua_call_init(void)
+{
+    /* Initialize invite session callback. */
+    pjsip_inv_callback inv_cb;
+    pj_status_t status;
+
+    pj_memset(&inv_cb, 0, 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.endpt, &pjsua.mod, &inv_cb);
+    
+    return status;
+}
