blob: 507e3c93da477796c3454920f351cbe939857664 [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijono84126ab2006-02-09 09:30:09 +000020#include <pj/log.h>
Benny Prijonodc39fe82006-05-26 12:17:46 +000021#include "pjsua_imp.h"
Benny Prijono84126ab2006-02-09 09:30:09 +000022
23/*
Benny Prijonob0808372006-03-02 21:18:58 +000024 * pjsua_call.c
Benny Prijono84126ab2006-02-09 09:30:09 +000025 *
Benny Prijonob0808372006-03-02 21:18:58 +000026 * Call (INVITE) related stuffs.
Benny Prijono84126ab2006-02-09 09:30:09 +000027 */
28
Benny Prijono8befd9f2006-05-13 22:46:23 +000029#define THIS_FILE "pjsua_call.c"
Benny Prijono84126ab2006-02-09 09:30:09 +000030
31
Benny Prijono105217f2006-03-06 16:25:59 +000032#define REFRESH_CALL_TIMER 0x63
33#define HANGUP_CALL_TIMER 0x64
34
35/* Proto */
36static void schedule_call_timer( pjsua_call *call, pj_timer_entry *e,
37 int timer_type, int duration );
38
39/*
40 * Timer callback when UAS needs to send re-INVITE to see if remote
41 * is still there.
42 */
43static void call_on_timer(pj_timer_heap_t *ht, pj_timer_entry *e)
44{
45 pjsua_call *call = e->user_data;
46
47 PJ_UNUSED_ARG(ht);
48
49 if (e->id == REFRESH_CALL_TIMER) {
50
51 /* If call is still not connected, hangup. */
52 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
53 PJ_LOG(3,(THIS_FILE, "Refresh call timer is called when "
54 "invite is still not confirmed. Call %d will "
55 "disconnect.", call->index));
56 pjsua_call_hangup(call->index);
57 } else {
58 PJ_LOG(3,(THIS_FILE, "Refreshing call %d", call->index));
Benny Prijonodc39fe82006-05-26 12:17:46 +000059 schedule_call_timer(call,e,REFRESH_CALL_TIMER,
60 pjsua.config.uas_refresh);
Benny Prijono105217f2006-03-06 16:25:59 +000061 pjsua_call_reinvite(call->index);
62 }
63
64 } else if (e->id == HANGUP_CALL_TIMER) {
65 PJ_LOG(3,(THIS_FILE, "Call %d duration exceeded, disconnecting call",
66 call->index));
67 pjsua_call_hangup(call->index);
68
69 }
70}
71
72/*
73 * Schedule call timer.
74 */
75static void schedule_call_timer( pjsua_call *call, pj_timer_entry *e,
76 int timer_type, int duration )
77{
78 pj_time_val timeout;
79
80 if (duration == 0) {
81 /* Cancel timer. */
82 if (e->id != 0) {
83 pjsip_endpt_cancel_timer(pjsua.endpt, e);
84 e->id = 0;
85 }
86
87 } else {
88 /* Schedule timer. */
89 timeout.sec = duration;
90 timeout.msec = 0;
91
92 e->cb = &call_on_timer;
93 e->id = timer_type;
94 e->user_data = call;
95
96 pjsip_endpt_schedule_timer( pjsua.endpt, e, &timeout);
97 }
98}
99
100
Benny Prijono275fd682006-03-22 11:59:11 +0000101/*
102 * Destroy the call's media
103 */
104static pj_status_t call_destroy_media(int call_index)
105{
106 pjsua_call *call = &pjsua.calls[call_index];
107
108 if (call->conf_slot > 0) {
109 pjmedia_conf_remove_port(pjsua.mconf, call->conf_slot);
110 call->conf_slot = 0;
111 }
112
113 if (call->session) {
Benny Prijono46ecff82006-03-30 16:46:36 +0000114 /* Destroy session (this will also close RTP/RTCP sockets). */
115 pjmedia_session_destroy(call->session);
Benny Prijono275fd682006-03-22 11:59:11 +0000116 call->session = NULL;
117
Benny Prijono8befd9f2006-05-13 22:46:23 +0000118 PJ_LOG(3,(THIS_FILE, "Media session for call %d is destroyed",
119 call_index));
Benny Prijono275fd682006-03-22 11:59:11 +0000120
Benny Prijono8befd9f2006-05-13 22:46:23 +0000121 }
Benny Prijono275fd682006-03-22 11:59:11 +0000122
123 return PJ_SUCCESS;
124}
125
126
Benny Prijono84126ab2006-02-09 09:30:09 +0000127/**
Benny Prijono9fc735d2006-05-28 14:58:12 +0000128 * Get maximum number of calls configured in pjsua.
129 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000130PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000131{
132 return pjsua.config.max_calls;
133}
134
135
136/**
137 * Get current number of active calls.
138 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000139PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000140{
141 return pjsua.call_cnt;
142}
143
144
145/**
146 * Check if the specified call is active.
147 */
148PJ_DEF(pj_bool_t) pjsua_call_is_active(unsigned call_index)
149{
150 PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls,
151 PJ_EINVAL);
152 return pjsua.calls[call_index].inv != NULL &&
153 pjsua.calls[call_index].inv->state != PJSIP_INV_STATE_DISCONNECTED;
154}
155
156/**
157 * Check if call has a media session.
158 */
159PJ_DEF(pj_bool_t) pjsua_call_has_media(unsigned call_index)
160{
161 PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, PJ_EINVAL);
162 return pjsua.calls[call_index].session != NULL;
163}
164
165
166/**
167 * Get call info.
168 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000169PJ_DEF(pj_status_t) pjsua_call_get_info( unsigned call_index,
Benny Prijono9fc735d2006-05-28 14:58:12 +0000170 pjsua_call_info *info)
171{
172 pjsua_call *call;
173
174 PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls,
175 PJ_EINVAL);
176
177 pj_memset(info, 0, sizeof(pjsua_call_info));
178
179 call = &pjsua.calls[call_index];
180 info->active = pjsua_call_is_active(call_index);
181
182 if (call->inv == NULL)
183 return PJ_SUCCESS;
184
185 info->index = call_index;
186 info->role = call->inv->role;
187 info->local_info = call->inv->dlg->local.info_str;
188 info->remote_info = call->inv->dlg->remote.info_str;
189 info->state = call->inv->state;
190 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
191
192 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
193
194 info->total_duration = call->dis_time;
195 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
196
197 if (call->conn_time.sec) {
198 info->connect_duration = call->dis_time;
199 PJ_TIME_VAL_SUB(info->total_duration, call->conn_time);
200 }
201
202 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
203
204 pj_gettimeofday(&info->total_duration);
205 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
206
207 pj_gettimeofday(&info->connect_duration);
208 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
209
210 } else {
211 pj_gettimeofday(&info->total_duration);
212 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
213 }
214
Benny Prijono8b1889b2006-06-06 18:40:40 +0000215 info->last_status = call->last_code;
216 info->last_status_text = *pjsip_get_status_text(info->last_status);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000217
218 info->has_media = (call->session != NULL);
219 info->conf_slot = call->conf_slot;
220
221 return PJ_SUCCESS;
222}
223
224
225/**
226 * Duplicate call info.
227 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000228PJ_DEF(void) pjsua_call_info_dup( pj_pool_t *pool,
Benny Prijono9fc735d2006-05-28 14:58:12 +0000229 pjsua_call_info *dst_info,
230 const pjsua_call_info *src_info)
231{
232 PJ_ASSERT_ON_FAIL(pool && dst_info && src_info, return);
233
234 pj_memcpy(dst_info, src_info, sizeof(pjsua_call_info));
235
236 pj_strdup(pool, &dst_info->local_info, &src_info->local_info);
237 pj_strdup(pool, &dst_info->remote_info, &src_info->remote_info);
238
239 /* state_text and cause_text belong to pjsip, so don't need to be
240 * duplicated because they'll always be available.
241 */
242}
243
244
245/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000246 * Make outgoing call.
247 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000248PJ_DEF(pj_status_t) pjsua_call_make_call(unsigned acc_index,
249 const pj_str_t *dest_uri,
250 int *p_call_index)
Benny Prijono84126ab2006-02-09 09:30:09 +0000251{
Benny Prijono1c2bf462006-03-05 11:54:02 +0000252 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000253 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000254 pjsip_inv_session *inv = NULL;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000255 unsigned call_index;
Benny Prijono84126ab2006-02-09 09:30:09 +0000256 pjsip_tx_data *tdata;
257 pj_status_t status;
258
Benny Prijono9fc735d2006-05-28 14:58:12 +0000259
260 PJ_ASSERT_RETURN(acc_index==0 || acc_index < pjsua.config.acc_cnt,
261 PJ_EINVAL);
262
Benny Prijono84126ab2006-02-09 09:30:09 +0000263
Benny Prijonoa91a0032006-02-26 21:23:45 +0000264 /* Find free call slot. */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000265 for (call_index=0; call_index<pjsua.config.max_calls; ++call_index) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000266 if (pjsua.calls[call_index].inv == NULL)
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000267 break;
268 }
269
Benny Prijonodc39fe82006-05-26 12:17:46 +0000270 if (call_index == pjsua.config.max_calls) {
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000271 PJ_LOG(3,(THIS_FILE, "Error: too many calls!"));
272 return PJ_ETOOMANY;
273 }
274
Benny Prijonoe21e7842006-04-09 16:46:05 +0000275 /* Mark call start time. */
276 pj_gettimeofday(&pjsua.calls[call_index].start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000277
Benny Prijonoe21e7842006-04-09 16:46:05 +0000278 /* Reset first response time */
279 pjsua.calls[call_index].res_time.sec = 0;
280
281 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000282 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonodc39fe82006-05-26 12:17:46 +0000283 &pjsua.config.acc_config[acc_index].id,
284 &pjsua.config.acc_config[acc_index].contact,
Benny Prijono9fc735d2006-05-28 14:58:12 +0000285 dest_uri, dest_uri,
Benny Prijono84126ab2006-02-09 09:30:09 +0000286 &dlg);
287 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000288 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000289 return status;
290 }
291
292 /* Get media capability from media endpoint: */
293
Benny Prijonoa91a0032006-02-26 21:23:45 +0000294 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, dlg->pool, 1,
295 &pjsua.calls[call_index].skinfo,
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000296 &offer);
Benny Prijono84126ab2006-02-09 09:30:09 +0000297 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000298 pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000299 goto on_error;
300 }
301
302 /* Create the INVITE session: */
303
304 status = pjsip_inv_create_uac( dlg, offer, 0, &inv);
305 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000306 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000307 goto on_error;
308 }
309
310
311 /* Create and associate our data in the session. */
312
Benny Prijonoa91a0032006-02-26 21:23:45 +0000313 pjsua.calls[call_index].inv = inv;
314
315 dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
316 inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
Benny Prijono84126ab2006-02-09 09:30:09 +0000317
318
319 /* Set dialog Route-Set: */
320
Benny Prijonoa91a0032006-02-26 21:23:45 +0000321 if (!pj_list_empty(&pjsua.acc[acc_index].route_set))
322 pjsip_dlg_set_route_set(dlg, &pjsua.acc[acc_index].route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000323
324
325 /* Set credentials: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000326 if (pjsua.config.acc_config[acc_index].cred_count) {
327 pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index];
328 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
329 acc_cfg->cred_count,
330 acc_cfg->cred_info);
331 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000332
333
334 /* Create initial INVITE: */
335
336 status = pjsip_inv_invite(inv, &tdata);
337 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000338 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
339 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000340 goto on_error;
341 }
342
343
344 /* Send initial INVITE: */
345
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000346 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000347 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000348 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
349 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000350
351 /* Upon failure to send first request, both dialog and invite
352 * session would have been cleared.
353 */
354 inv = NULL;
355 dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000356 goto on_error;
357 }
358
359
360 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000361
362 ++pjsua.call_cnt;
363
364 if (p_call_index)
365 *p_call_index = call_index;
Benny Prijono84126ab2006-02-09 09:30:09 +0000366
367 return PJ_SUCCESS;
368
369
370on_error:
Benny Prijono1c2bf462006-03-05 11:54:02 +0000371 if (inv != NULL) {
372 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000373 } else if (dlg) {
Benny Prijono1c2bf462006-03-05 11:54:02 +0000374 pjsip_dlg_terminate(dlg);
375 }
376
Benny Prijonoa91a0032006-02-26 21:23:45 +0000377 if (call_index != -1) {
378 pjsua.calls[call_index].inv = NULL;
379 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000380 return status;
381}
382
383
384/**
Benny Prijonodc39fe82006-05-26 12:17:46 +0000385 * Answer call.
386 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000387PJ_DEF(pj_status_t) pjsua_call_answer(int call_index, int code)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000388{
389 pjsip_tx_data *tdata;
390 pj_status_t status;
391
Benny Prijono8b1889b2006-06-06 18:40:40 +0000392 PJ_ASSERT_RETURN( call_index >= 0 &&
Benny Prijonodc39fe82006-05-26 12:17:46 +0000393 call_index < (int)pjsua.config.max_calls,
Benny Prijono8b1889b2006-06-06 18:40:40 +0000394 PJ_EINVAL);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000395
396 if (pjsua.calls[call_index].inv == NULL) {
397 PJ_LOG(3,(THIS_FILE, "Call %d already disconnected"));
Benny Prijono8b1889b2006-06-06 18:40:40 +0000398 return PJSIP_ESESSIONTERMINATED;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000399 }
400
401 status = pjsip_inv_answer(pjsua.calls[call_index].inv,
402 code, NULL, NULL, &tdata);
403 if (status == PJ_SUCCESS)
404 status = pjsip_inv_send_msg(pjsua.calls[call_index].inv,
405 tdata);
406
407 if (status != PJ_SUCCESS)
408 pjsua_perror(THIS_FILE, "Unable to create/send response",
409 status);
410
Benny Prijono8b1889b2006-06-06 18:40:40 +0000411 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000412}
413
414
415/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000416 * Handle incoming INVITE request.
417 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000418pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000419{
420 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
421 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
422 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000423 pjsip_tx_data *response = NULL;
424 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000425 pjsip_inv_session *inv = NULL;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000426 int acc_index;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000427 unsigned call_index;
Benny Prijono26ff9062006-02-21 23:47:00 +0000428 pjmedia_sdp_session *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000429 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000430
Benny Prijono26ff9062006-02-21 23:47:00 +0000431 /* Don't want to handle anything but INVITE */
432 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
433 return PJ_FALSE;
434
435 /* Don't want to handle anything that's already associated with
436 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000437 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000438 if (dlg || tsx)
439 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000440
Benny Prijono84126ab2006-02-09 09:30:09 +0000441
Benny Prijono26ff9062006-02-21 23:47:00 +0000442 /* Verify that we can handle the request. */
443 status = pjsip_inv_verify_request(rdata, &options, NULL, NULL,
444 pjsua.endpt, &response);
445 if (status != PJ_SUCCESS) {
Benny Prijono84126ab2006-02-09 09:30:09 +0000446
Benny Prijono26ff9062006-02-21 23:47:00 +0000447 /*
448 * No we can't handle the incoming INVITE request.
449 */
Benny Prijono84126ab2006-02-09 09:30:09 +0000450
Benny Prijono26ff9062006-02-21 23:47:00 +0000451 if (response) {
452 pjsip_response_addr res_addr;
Benny Prijono84126ab2006-02-09 09:30:09 +0000453
Benny Prijono26ff9062006-02-21 23:47:00 +0000454 pjsip_get_response_addr(response->pool, rdata, &res_addr);
455 pjsip_endpt_send_response(pjsua.endpt, &res_addr, response,
456 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000457
458 } else {
Benny Prijono84126ab2006-02-09 09:30:09 +0000459
Benny Prijono26ff9062006-02-21 23:47:00 +0000460 /* Respond with 500 (Internal Server Error) */
461 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
462 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000463 }
464
Benny Prijono26ff9062006-02-21 23:47:00 +0000465 return PJ_TRUE;
466 }
467
468
469 /*
470 * Yes we can handle the incoming INVITE request.
471 */
472
473 /* Find free call slot. */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000474 for (call_index=0; call_index < pjsua.config.max_calls; ++call_index) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000475 if (pjsua.calls[call_index].inv == NULL)
Benny Prijono26ff9062006-02-21 23:47:00 +0000476 break;
477 }
478
Benny Prijonoa91a0032006-02-26 21:23:45 +0000479 if (call_index == PJSUA_MAX_CALLS) {
Benny Prijono26ff9062006-02-21 23:47:00 +0000480 pjsip_endpt_respond_stateless(pjsua.endpt, rdata,
481 PJSIP_SC_BUSY_HERE, NULL,
482 NULL, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000483 return PJ_TRUE;
484 }
485
Benny Prijonoe21e7842006-04-09 16:46:05 +0000486 /* Mark call start time. */
487 pj_gettimeofday(&pjsua.calls[call_index].start_time);
488
489 /* Reset first response time */
490 pjsua.calls[call_index].res_time.sec = 0;
Benny Prijono26ff9062006-02-21 23:47:00 +0000491
Benny Prijono26ff9062006-02-21 23:47:00 +0000492 /* Get media capability from media endpoint: */
493
Benny Prijonoa91a0032006-02-26 21:23:45 +0000494 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, rdata->tp_info.pool, 1,
495 &pjsua.calls[call_index].skinfo,
Benny Prijono26ff9062006-02-21 23:47:00 +0000496 &answer );
497 if (status != PJ_SUCCESS) {
498 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
499 NULL, NULL);
500
Benny Prijono26ff9062006-02-21 23:47:00 +0000501 return PJ_TRUE;
502 }
503
Benny Prijonoa91a0032006-02-26 21:23:45 +0000504 /* TODO:
505 *
506 * Get which account is most likely to be associated with this incoming
507 * call. We need the account to find which contact URI to put for
508 * the call.
509 */
510 acc_index = 0;
511
Benny Prijono26ff9062006-02-21 23:47:00 +0000512 /* Create dialog: */
513
514 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000515 &pjsua.config.acc_config[acc_index].contact,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000516 &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000517 if (status != PJ_SUCCESS) {
518 pjsip_endpt_respond_stateless(pjsua.endpt, rdata, 500, NULL,
519 NULL, NULL);
520
Benny Prijono26ff9062006-02-21 23:47:00 +0000521 return PJ_TRUE;
522 }
523
524
525 /* Create invite session: */
526
527 status = pjsip_inv_create_uas( dlg, rdata, answer, 0, &inv);
528 if (status != PJ_SUCCESS) {
529
Benny Prijonob0808372006-03-02 21:18:58 +0000530 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000531 pjsip_dlg_terminate(dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000532 return PJ_TRUE;
533 }
534
535
536 /* Create and attach pjsua data to the dialog: */
537
Benny Prijonoa91a0032006-02-26 21:23:45 +0000538 pjsua.calls[call_index].inv = inv;
Benny Prijono26ff9062006-02-21 23:47:00 +0000539
Benny Prijonoa91a0032006-02-26 21:23:45 +0000540 dlg->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
541 inv->mod_data[pjsua.mod.id] = &pjsua.calls[call_index];
Benny Prijono26ff9062006-02-21 23:47:00 +0000542
543
Benny Prijono64f851e2006-02-23 13:49:28 +0000544 /* Must answer with some response to initial INVITE.
545 * If auto-answer flag is set, send 200 straight away, otherwise send 100.
546 */
547
548 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000549 (pjsua.config.auto_answer ?
550 pjsua.config.auto_answer : 100),
Benny Prijono64f851e2006-02-23 13:49:28 +0000551 NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000552 if (status != PJ_SUCCESS) {
553
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000554 int st_code;
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000555
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000556 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
557 status);
558
559 /* If failed to send 2xx response, there's a good chance that it is
560 * because SDP negotiation has failed.
561 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000562 if (pjsua.config.auto_answer/100 == 2)
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000563 st_code = PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
564 else
565 st_code = 500;
566
567 pjsip_dlg_respond(dlg, rdata, st_code, NULL, NULL, NULL);
568 pjsip_inv_terminate(inv, st_code, PJ_FALSE);
569 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000570
571 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000572 status = pjsip_inv_send_msg(inv, response);
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000573 if (status != PJ_SUCCESS)
574 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono26ff9062006-02-21 23:47:00 +0000575 }
576
Benny Prijonodc39fe82006-05-26 12:17:46 +0000577 if (pjsua.config.auto_answer < 200) {
Benny Prijono64f851e2006-02-23 13:49:28 +0000578 PJ_LOG(3,(THIS_FILE,
579 "\nIncoming call!!\n"
580 "From: %.*s\n"
581 "To: %.*s\n"
582 "(press 'a' to answer, 'h' to decline)",
583 (int)dlg->remote.info_str.slen,
584 dlg->remote.info_str.ptr,
585 (int)dlg->local.info_str.slen,
586 dlg->local.info_str.ptr));
587 } else {
588 PJ_LOG(3,(THIS_FILE,
589 "Call From:%.*s To:%.*s was answered with %d (%s)",
590 (int)dlg->remote.info_str.slen,
591 dlg->remote.info_str.ptr,
592 (int)dlg->local.info_str.slen,
593 dlg->local.info_str.ptr,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000594 pjsua.config.auto_answer,
595 pjsip_get_status_text(pjsua.config.auto_answer)->ptr ));
Benny Prijono64f851e2006-02-23 13:49:28 +0000596 }
597
Benny Prijonoa91a0032006-02-26 21:23:45 +0000598 ++pjsua.call_cnt;
599
Benny Prijono105217f2006-03-06 16:25:59 +0000600 /* Schedule timer to refresh. */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000601 if (pjsua.config.uas_refresh > 0) {
Benny Prijono105217f2006-03-06 16:25:59 +0000602 schedule_call_timer( &pjsua.calls[call_index],
603 &pjsua.calls[call_index].refresh_tm,
604 REFRESH_CALL_TIMER,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000605 pjsua.config.uas_refresh);
Benny Prijono105217f2006-03-06 16:25:59 +0000606 }
607
608 /* Schedule timer to hangup call. */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000609 if (pjsua.config.uas_duration > 0) {
Benny Prijono105217f2006-03-06 16:25:59 +0000610 schedule_call_timer( &pjsua.calls[call_index],
611 &pjsua.calls[call_index].hangup_tm,
612 HANGUP_CALL_TIMER,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000613 pjsua.config.uas_duration);
Benny Prijono105217f2006-03-06 16:25:59 +0000614 }
615
Benny Prijono8b1889b2006-06-06 18:40:40 +0000616 /* Notify application */
617 if (pjsua.cb.on_incoming_call)
618 pjsua.cb.on_incoming_call(acc_index, call_index, rdata);
619
620
Benny Prijono26ff9062006-02-21 23:47:00 +0000621 /* This INVITE request has been handled. */
622 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000623}
624
625
626/*
627 * This callback receives notification from invite session when the
628 * session state has changed.
629 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000630static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
631 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +0000632{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000633 pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id];
Benny Prijono26ff9062006-02-21 23:47:00 +0000634
Benny Prijonoe21e7842006-04-09 16:46:05 +0000635 if (!call)
636 return;
637
638 /* Get call times */
639 switch (inv->state) {
640 case PJSIP_INV_STATE_EARLY:
641 case PJSIP_INV_STATE_CONNECTING:
642 if (call->res_time.sec == 0)
643 pj_gettimeofday(&call->res_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000644 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000645 break;
646 case PJSIP_INV_STATE_CONFIRMED:
647 pj_gettimeofday(&call->conn_time);
648 break;
649 case PJSIP_INV_STATE_DISCONNECTED:
650 pj_gettimeofday(&call->dis_time);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000651 if (e->body.tsx_state.tsx->status_code > call->last_code) {
652 call->last_code = e->body.tsx_state.tsx->status_code;
653 }
Benny Prijonoe21e7842006-04-09 16:46:05 +0000654 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000655 default:
Benny Prijono8b1889b2006-06-06 18:40:40 +0000656 call->last_code = e->body.tsx_state.tsx->status_code;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000657 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000658 }
659
Benny Prijono26ff9062006-02-21 23:47:00 +0000660 /* If this is an outgoing INVITE that was created because of
661 * REFER/transfer, send NOTIFY to transferer.
662 */
Benny Prijonoe21e7842006-04-09 16:46:05 +0000663 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +0000664 int st_code = -1;
665 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
666
667
Benny Prijonoa91a0032006-02-26 21:23:45 +0000668 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +0000669 case PJSIP_INV_STATE_NULL:
670 case PJSIP_INV_STATE_CALLING:
671 /* Do nothing */
672 break;
673
674 case PJSIP_INV_STATE_EARLY:
675 case PJSIP_INV_STATE_CONNECTING:
676 st_code = e->body.tsx_state.tsx->status_code;
677 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
678 break;
679
680 case PJSIP_INV_STATE_CONFIRMED:
681 /* When state is confirmed, send the final 200/OK and terminate
682 * subscription.
683 */
684 st_code = e->body.tsx_state.tsx->status_code;
685 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
686 break;
687
688 case PJSIP_INV_STATE_DISCONNECTED:
689 st_code = e->body.tsx_state.tsx->status_code;
690 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
691 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +0000692
Benny Prijono8b1889b2006-06-06 18:40:40 +0000693 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +0000694 /* Nothing to do. Just to keep gcc from complaining about
695 * unused enums.
696 */
697 break;
Benny Prijono26ff9062006-02-21 23:47:00 +0000698 }
699
700 if (st_code != -1) {
701 pjsip_tx_data *tdata;
702 pj_status_t status;
703
Benny Prijonoa91a0032006-02-26 21:23:45 +0000704 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +0000705 ev_state, st_code,
706 NULL, &tdata);
707 if (status != PJ_SUCCESS) {
708 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
709 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000710 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +0000711 if (status != PJ_SUCCESS) {
712 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
713 }
714 }
715 }
716 }
717
Benny Prijono84126ab2006-02-09 09:30:09 +0000718
Benny Prijonodc39fe82006-05-26 12:17:46 +0000719 if (pjsua.cb.on_call_state)
720 (*pjsua.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000721
722 /* call->inv may be NULL now */
723
Benny Prijono84126ab2006-02-09 09:30:09 +0000724 /* Destroy media session when invite session is disconnected. */
725 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +0000726
Benny Prijonoa91a0032006-02-26 21:23:45 +0000727 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +0000728
Benny Prijono275fd682006-03-22 11:59:11 +0000729 if (call)
730 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +0000731
Benny Prijono105217f2006-03-06 16:25:59 +0000732 /* Remove timers. */
733 schedule_call_timer(call, &call->refresh_tm, REFRESH_CALL_TIMER, 0);
734 schedule_call_timer(call, &call->hangup_tm, HANGUP_CALL_TIMER, 0);
735
736 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000737 call->inv = NULL;
738 --pjsua.call_cnt;
Benny Prijono84126ab2006-02-09 09:30:09 +0000739 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000740}
741
742
743/*
Benny Prijono26ff9062006-02-21 23:47:00 +0000744 * Callback called by event framework when the xfer subscription state
745 * has changed.
746 */
747static void xfer_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
748{
749
750 PJ_UNUSED_ARG(event);
751
752 /*
753 * We're only interested when subscription is terminated, to
754 * clear the xfer_sub member of the inv_data.
755 */
756 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +0000757 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000758
Benny Prijonoa91a0032006-02-26 21:23:45 +0000759 call = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
760 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +0000761 return;
762
763 pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000764 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +0000765
766 PJ_LOG(3,(THIS_FILE, "Xfer subscription terminated"));
767 }
768}
769
770
771/*
772 * Follow transfer (REFER) request.
773 */
774static void on_call_transfered( pjsip_inv_session *inv,
775 pjsip_rx_data *rdata )
776{
777 pj_status_t status;
778 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000779 pjsua_call *existing_call;
780 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +0000781 const pj_str_t str_refer_to = { "Refer-To", 8};
782 pjsip_generic_string_hdr *refer_to;
783 char *uri;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000784 pj_str_t tmp;
Benny Prijono26ff9062006-02-21 23:47:00 +0000785 struct pjsip_evsub_user xfer_cb;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000786 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +0000787 pjsip_evsub *sub;
788
Benny Prijonoa91a0032006-02-26 21:23:45 +0000789 existing_call = inv->dlg->mod_data[pjsua.mod.id];
790
Benny Prijono26ff9062006-02-21 23:47:00 +0000791 /* Find the Refer-To header */
792 refer_to = (pjsip_generic_string_hdr*)
793 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
794
795 if (refer_to == NULL) {
796 /* Invalid Request.
797 * No Refer-To header!
798 */
799 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +0000800 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000801 return;
802 }
803
Benny Prijono9fc735d2006-05-28 14:58:12 +0000804 /* Notify callback */
805 code = PJSIP_SC_OK;
806 if (pjsua.cb.on_call_transfered)
807 (*pjsua.cb.on_call_transfered)(existing_call->index,
808 &refer_to->hvalue, &code);
809
810 if (code < 200)
811 code = 200;
812 if (code >= 300) {
813 /* Application rejects call transfer request */
814 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
815 return;
816 }
817
Benny Prijono26ff9062006-02-21 23:47:00 +0000818 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
819 (int)inv->dlg->remote.info_str.slen,
820 inv->dlg->remote.info_str.ptr,
821 (int)refer_to->hvalue.slen,
822 refer_to->hvalue.ptr));
823
824 /* Init callback */
825 pj_memset(&xfer_cb, 0, sizeof(xfer_cb));
826 xfer_cb.on_evsub_state = &xfer_on_evsub_state;
827
828 /* Create transferee event subscription */
829 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
830 if (status != PJ_SUCCESS) {
831 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
Benny Prijonob0808372006-03-02 21:18:58 +0000832 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000833 return;
834 }
835
836 /* Accept the REFER request, send 200 (OK). */
Benny Prijono9fc735d2006-05-28 14:58:12 +0000837 pjsip_xfer_accept(sub, rdata, code, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +0000838
839 /* Create initial NOTIFY request */
840 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
841 100, NULL, &tdata);
842 if (status != PJ_SUCCESS) {
843 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER", status);
844 return;
845 }
846
847 /* Send initial NOTIFY request */
848 status = pjsip_xfer_send_request( sub, tdata);
849 if (status != PJ_SUCCESS) {
850 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
851 return;
852 }
853
854 /* We're cheating here.
855 * We need to get a null terminated string from a pj_str_t.
856 * So grab the pointer from the hvalue and NULL terminate it, knowing
857 * that the NULL position will be occupied by a newline.
858 */
859 uri = refer_to->hvalue.ptr;
860 uri[refer_to->hvalue.slen] = '\0';
861
862 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +0000863 tmp = pj_str(uri);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000864 status = pjsua_call_make_call(existing_call->acc_index, &tmp, &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +0000865 if (status != PJ_SUCCESS) {
866
867 /* Notify xferer about the error */
868 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
869 500, NULL, &tdata);
870 if (status != PJ_SUCCESS) {
871 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
872 status);
873 return;
874 }
875 status = pjsip_xfer_send_request(sub, tdata);
876 if (status != PJ_SUCCESS) {
877 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
878 status);
879 return;
880 }
881 return;
882 }
883
884 /* Put the server subscription in inv_data.
885 * Subsequent state changed in pjsua_inv_on_state_changed() will be
886 * reported back to the server subscription.
887 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000888 pjsua.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +0000889
890 /* Put the invite_data in the subscription. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000891 pjsip_evsub_set_mod_data(sub, pjsua.mod.id, &pjsua.calls[new_call]);
Benny Prijono26ff9062006-02-21 23:47:00 +0000892}
893
894
895/*
896 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +0000897 * session. We use this to trap:
898 * - incoming REFER request.
899 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +0000900 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000901static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
902 pjsip_transaction *tsx,
903 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +0000904{
Benny Prijonoa91a0032006-02-26 21:23:45 +0000905 pjsua_call *call = inv->dlg->mod_data[pjsua.mod.id];
906
Benny Prijono26ff9062006-02-21 23:47:00 +0000907 if (tsx->role==PJSIP_ROLE_UAS &&
908 tsx->state==PJSIP_TSX_STATE_TRYING &&
909 pjsip_method_cmp(&tsx->method, &pjsip_refer_method)==0)
910 {
911 /*
912 * Incoming REFER request.
913 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000914 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +0000915
Benny Prijono26ff9062006-02-21 23:47:00 +0000916 }
Benny Prijonob0808372006-03-02 21:18:58 +0000917 else if (tsx->role==PJSIP_ROLE_UAS &&
918 tsx->state==PJSIP_TSX_STATE_TRYING &&
919 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
920 {
921 /*
922 * Incoming MESSAGE request!
923 */
924 pjsip_rx_data *rdata;
925 pjsip_msg *msg;
926 pjsip_accept_hdr *accept_hdr;
927 pj_status_t status;
928
929 rdata = e->body.tsx_state.src.rdata;
930 msg = rdata->msg_info.msg;
931
932 /* Request MUST have message body, with Content-Type equal to
933 * "text/plain".
934 */
935 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
936
937 pjsip_hdr hdr_list;
938
939 pj_list_init(&hdr_list);
940 pj_list_push_back(&hdr_list, accept_hdr);
941
942 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
943 NULL, &hdr_list, NULL );
944 return;
945 }
946
947 /* Respond with 200 first, so that remote doesn't retransmit in case
948 * the UI takes too long to process the message.
949 */
950 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
951
952 /* Process MESSAGE request */
953 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
954 &inv->dlg->local.info_str, rdata);
955 }
956
Benny Prijono26ff9062006-02-21 23:47:00 +0000957}
958
959
960/*
Benny Prijono84126ab2006-02-09 09:30:09 +0000961 * This callback is called by invite session framework when UAC session
962 * has forked.
963 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000964static void pjsua_call_on_forked( pjsip_inv_session *inv,
965 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +0000966{
967 PJ_UNUSED_ARG(inv);
968 PJ_UNUSED_ARG(e);
969
970 PJ_TODO(HANDLE_FORKED_DIALOG);
971}
972
973
974/*
Benny Prijono26ff9062006-02-21 23:47:00 +0000975 * Create inactive SDP for call hold.
976 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000977static pj_status_t create_inactive_sdp(pjsua_call *call,
Benny Prijono26ff9062006-02-21 23:47:00 +0000978 pjmedia_sdp_session **p_answer)
979{
980 pj_status_t status;
981 pjmedia_sdp_conn *conn;
982 pjmedia_sdp_attr *attr;
983 pjmedia_sdp_session *sdp;
984
985 /* Create new offer */
986 status = pjmedia_endpt_create_sdp(pjsua.med_endpt, pjsua.pool, 1,
Benny Prijonoa91a0032006-02-26 21:23:45 +0000987 &call->skinfo, &sdp);
Benny Prijono26ff9062006-02-21 23:47:00 +0000988 if (status != PJ_SUCCESS) {
989 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
990 return status;
991 }
992
993 /* Get SDP media connection line */
994 conn = sdp->media[0]->conn;
995 if (!conn)
996 conn = sdp->conn;
997
998 /* Modify address */
999 conn->addr = pj_str("0.0.0.0");
1000
1001 /* Remove existing directions attributes */
1002 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
1003 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
1004 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
1005 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
1006
1007 /* Add inactive attribute */
1008 attr = pjmedia_sdp_attr_create(pjsua.pool, "inactive", NULL);
1009 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
1010
1011 *p_answer = sdp;
1012
1013 return status;
1014}
1015
1016/*
1017 * Called when session received new offer.
1018 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001019static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
1020 const pjmedia_sdp_session *offer)
Benny Prijono26ff9062006-02-21 23:47:00 +00001021{
Benny Prijonoa91a0032006-02-26 21:23:45 +00001022 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001023 pjmedia_sdp_conn *conn;
1024 pjmedia_sdp_session *answer;
1025 pj_bool_t is_remote_active;
1026 pj_status_t status;
1027
Benny Prijonoa91a0032006-02-26 21:23:45 +00001028 call = inv->dlg->mod_data[pjsua.mod.id];
Benny Prijono26ff9062006-02-21 23:47:00 +00001029
1030 /*
1031 * See if remote is offering active media (i.e. not on-hold)
1032 */
1033 is_remote_active = PJ_TRUE;
1034
1035 conn = offer->media[0]->conn;
1036 if (!conn)
1037 conn = offer->conn;
1038
1039 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
1040 pj_strcmp2(&conn->addr, "0")==0)
1041 {
1042 is_remote_active = PJ_FALSE;
1043
1044 }
1045 else if (pjmedia_sdp_media_find_attr2(offer->media[0], "inactive", NULL))
1046 {
1047 is_remote_active = PJ_FALSE;
1048 }
1049
1050 PJ_LOG(4,(THIS_FILE, "Received SDP offer, remote media is %s",
1051 (is_remote_active ? "active" : "inactive")));
1052
1053 /* Supply candidate answer */
1054 if (is_remote_active) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001055 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
1056 &call->skinfo, &answer);
Benny Prijono26ff9062006-02-21 23:47:00 +00001057 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001058 status = create_inactive_sdp( call, &answer );
Benny Prijono26ff9062006-02-21 23:47:00 +00001059 }
1060
1061 if (status != PJ_SUCCESS) {
1062 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
1063 return;
1064 }
1065
Benny Prijonoa91a0032006-02-26 21:23:45 +00001066 status = pjsip_inv_set_sdp_answer(call->inv, answer);
Benny Prijono26ff9062006-02-21 23:47:00 +00001067 if (status != PJ_SUCCESS) {
1068 pjsua_perror(THIS_FILE, "Unable to set answer", status);
1069 return;
1070 }
1071
1072}
1073
Benny Prijono8befd9f2006-05-13 22:46:23 +00001074#if 0
Benny Prijono1c2bf462006-03-05 11:54:02 +00001075/* Disconnect call */
1076static void call_disconnect(pjsip_inv_session *inv,
1077 int st_code)
1078{
1079 pjsip_tx_data *tdata;
1080 pj_status_t status;
1081
1082 status = pjsip_inv_end_session(inv, st_code, NULL, &tdata);
1083 if (status == PJ_SUCCESS)
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001084 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono1c2bf462006-03-05 11:54:02 +00001085
1086 if (status != PJ_SUCCESS) {
1087 pjsua_perror(THIS_FILE, "Unable to disconnect call", status);
1088 }
1089}
Benny Prijono8befd9f2006-05-13 22:46:23 +00001090#endif
Benny Prijono26ff9062006-02-21 23:47:00 +00001091
1092/*
Benny Prijono84126ab2006-02-09 09:30:09 +00001093 * Callback to be called when SDP offer/answer negotiation has just completed
1094 * in the session. This function will start/update media if negotiation
1095 * has succeeded.
1096 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001097static void pjsua_call_on_media_update(pjsip_inv_session *inv,
1098 pj_status_t status)
Benny Prijono84126ab2006-02-09 09:30:09 +00001099{
Benny Prijonoa91a0032006-02-26 21:23:45 +00001100 pjsua_call *call;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001101 pjmedia_session_info sess_info;
Benny Prijono84126ab2006-02-09 09:30:09 +00001102 const pjmedia_sdp_session *local_sdp;
1103 const pjmedia_sdp_session *remote_sdp;
Benny Prijono26ff9062006-02-21 23:47:00 +00001104 pjmedia_port *media_port;
1105 pj_str_t port_name;
1106 char tmp[PJSIP_MAX_URL_SIZE];
Benny Prijono84126ab2006-02-09 09:30:09 +00001107
Benny Prijonoa91a0032006-02-26 21:23:45 +00001108 call = inv->dlg->mod_data[pjsua.mod.id];
1109
Benny Prijono84126ab2006-02-09 09:30:09 +00001110 if (status != PJ_SUCCESS) {
1111
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001112 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
Benny Prijono1c2bf462006-03-05 11:54:02 +00001113
Benny Prijonoccb03fa2006-03-06 13:35:47 +00001114 /* Disconnect call if we're not in the middle of initializing an
1115 * UAS dialog and if this is not a re-INVITE
1116 */
1117 if (inv->state != PJSIP_INV_STATE_NULL &&
1118 inv->state != PJSIP_INV_STATE_CONFIRMED)
1119 {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001120 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono1c2bf462006-03-05 11:54:02 +00001121 }
Benny Prijono84126ab2006-02-09 09:30:09 +00001122 return;
1123
1124 }
1125
1126 /* Destroy existing media session, if any. */
1127
Benny Prijono275fd682006-03-22 11:59:11 +00001128 if (call)
1129 call_destroy_media(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00001130
1131 /* Get local and remote SDP */
1132
Benny Prijonoa91a0032006-02-26 21:23:45 +00001133 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijono84126ab2006-02-09 09:30:09 +00001134 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001135 pjsua_perror(THIS_FILE,
1136 "Unable to retrieve currently active local SDP",
1137 status);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001138 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono84126ab2006-02-09 09:30:09 +00001139 return;
1140 }
1141
1142
Benny Prijonoa91a0032006-02-26 21:23:45 +00001143 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
Benny Prijono84126ab2006-02-09 09:30:09 +00001144 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001145 pjsua_perror(THIS_FILE,
1146 "Unable to retrieve currently active remote SDP",
1147 status);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001148 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono84126ab2006-02-09 09:30:09 +00001149 return;
1150 }
1151
Benny Prijonodc39fe82006-05-26 12:17:46 +00001152 if (pjsua.config.null_audio)
Benny Prijono26ff9062006-02-21 23:47:00 +00001153 return;
Benny Prijono8befd9f2006-05-13 22:46:23 +00001154
1155 /* Create media session info based on SDP parameters.
1156 * We only support one stream per session at the moment
1157 */
1158 status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
Benny Prijonob04c9e02006-05-17 17:17:39 +00001159 pjsua.med_endpt,
1160 1,&sess_info,
Benny Prijono8befd9f2006-05-13 22:46:23 +00001161 local_sdp, remote_sdp);
1162 if (status != PJ_SUCCESS) {
1163 pjsua_perror(THIS_FILE, "Unable to create media session",
1164 status);
1165 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
1166 return;
1167 }
1168
1169 /* Override ptime, if this option is specified. */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001170 if (pjsua.config.ptime) {
Benny Prijono8befd9f2006-05-13 22:46:23 +00001171 sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
Benny Prijonodc39fe82006-05-26 12:17:46 +00001172 (pjsua.config.ptime /
1173 sess_info.stream_info[0].param->info.frm_ptime);
Benny Prijono8befd9f2006-05-13 22:46:23 +00001174 if (sess_info.stream_info[0].param->setting.frm_per_pkt==0)
1175 sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
1176 }
1177
1178 /* Optionally, application may modify other stream settings here
1179 * (such as jitter buffer parameters, codec ptime, etc.)
1180 */
1181
1182 /* Create session based on session info. */
1183 status = pjmedia_session_create( pjsua.med_endpt, &sess_info,
Benny Prijonob04c9e02006-05-17 17:17:39 +00001184 &call->med_tp,
Benny Prijono8befd9f2006-05-13 22:46:23 +00001185 call, &call->session );
Benny Prijono26ff9062006-02-21 23:47:00 +00001186 if (status != PJ_SUCCESS) {
1187 pjsua_perror(THIS_FILE, "Unable to create media session",
1188 status);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001189 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijono26ff9062006-02-21 23:47:00 +00001190 return;
Benny Prijono84126ab2006-02-09 09:30:09 +00001191 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001192
1193
1194 /* Get the port interface of the first stream in the session.
1195 * We need the port interface to add to the conference bridge.
1196 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001197 pjmedia_session_get_port(call->session, 0, &media_port);
Benny Prijono26ff9062006-02-21 23:47:00 +00001198
1199
1200 /*
1201 * Add the call to conference bridge.
1202 */
1203 port_name.ptr = tmp;
1204 port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
Benny Prijonoa91a0032006-02-26 21:23:45 +00001205 call->inv->dlg->remote.info->uri,
Benny Prijono26ff9062006-02-21 23:47:00 +00001206 tmp, sizeof(tmp));
1207 if (port_name.slen < 1) {
1208 port_name = pj_str("call");
1209 }
Benny Prijonoa91a0032006-02-26 21:23:45 +00001210 status = pjmedia_conf_add_port( pjsua.mconf, call->inv->pool,
Benny Prijono26ff9062006-02-21 23:47:00 +00001211 media_port,
1212 &port_name,
Benny Prijonoa91a0032006-02-26 21:23:45 +00001213 &call->conf_slot);
Benny Prijono26ff9062006-02-21 23:47:00 +00001214 if (status != PJ_SUCCESS) {
1215 pjsua_perror(THIS_FILE, "Unable to create conference slot",
1216 status);
Benny Prijono275fd682006-03-22 11:59:11 +00001217 call_destroy_media(call->index);
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001218 //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
Benny Prijono26ff9062006-02-21 23:47:00 +00001219 return;
1220 }
1221
Benny Prijono64f851e2006-02-23 13:49:28 +00001222 /* If auto-play is configured, connect the call to the file player
1223 * port
Benny Prijono26ff9062006-02-21 23:47:00 +00001224 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001225 if (pjsua.config.auto_play && pjsua.config.wav_file.slen &&
Benny Prijonoa91a0032006-02-26 21:23:45 +00001226 call->inv->role == PJSIP_ROLE_UAS)
1227 {
Benny Prijono64f851e2006-02-23 13:49:28 +00001228
Benny Prijono9fc735d2006-05-28 14:58:12 +00001229 pjmedia_conf_connect_port( pjsua.mconf, pjsua.player[0].slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001230 call->conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001231
Benny Prijonob8c25182006-04-29 08:31:09 +00001232 }
Benny Prijonodc39fe82006-05-26 12:17:46 +00001233 if (pjsua.config.auto_loop && call->inv->role == PJSIP_ROLE_UAS) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001234
1235 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001236 call->conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001237
Benny Prijonob8c25182006-04-29 08:31:09 +00001238 }
Benny Prijonodc39fe82006-05-26 12:17:46 +00001239 if (pjsua.config.auto_conf) {
1240 unsigned i;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001241
Benny Prijonob100d692006-03-17 00:16:01 +00001242 pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0);
1243 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001244
Benny Prijonodc39fe82006-05-26 12:17:46 +00001245 for (i=0; i < pjsua.config.max_calls; ++i) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001246
1247 if (!pjsua.calls[i].session)
1248 continue;
1249
1250 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001251 pjsua.calls[i].conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001252 pjmedia_conf_connect_port( pjsua.mconf, pjsua.calls[i].conf_slot,
Benny Prijonob100d692006-03-17 00:16:01 +00001253 call->conf_slot, 0);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001254 }
Benny Prijono64f851e2006-02-23 13:49:28 +00001255
Benny Prijonob8c25182006-04-29 08:31:09 +00001256 }
1257
1258 /* Normal operation: if no auto_xx is given, connect new call to
1259 * the sound device port (port zero) in the main conference bridge.
1260 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001261 if (pjsua.config.auto_play == 0 && pjsua.config.auto_loop == 0 &&
1262 pjsua.config.auto_conf == 0)
Benny Prijonob8c25182006-04-29 08:31:09 +00001263 {
Benny Prijonob100d692006-03-17 00:16:01 +00001264 pjmedia_conf_connect_port( pjsua.mconf, 0, call->conf_slot, 0);
1265 pjmedia_conf_connect_port( pjsua.mconf, call->conf_slot, 0, 0);
Benny Prijono64f851e2006-02-23 13:49:28 +00001266 }
1267
Benny Prijono26ff9062006-02-21 23:47:00 +00001268
1269 /* Done. */
1270 {
1271 struct pjmedia_session_info sess_info;
1272 char info[80];
1273 int info_len = 0;
1274 unsigned i;
1275
Benny Prijonoa91a0032006-02-26 21:23:45 +00001276 pjmedia_session_get_info(call->session, &sess_info);
Benny Prijono26ff9062006-02-21 23:47:00 +00001277 for (i=0; i<sess_info.stream_cnt; ++i) {
1278 int len;
1279 const char *dir;
1280 pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
1281
1282 switch (strm_info->dir) {
1283 case PJMEDIA_DIR_NONE:
1284 dir = "inactive";
1285 break;
1286 case PJMEDIA_DIR_ENCODING:
1287 dir = "sendonly";
1288 break;
1289 case PJMEDIA_DIR_DECODING:
1290 dir = "recvonly";
1291 break;
1292 case PJMEDIA_DIR_ENCODING_DECODING:
1293 dir = "sendrecv";
1294 break;
1295 default:
1296 dir = "unknown";
1297 break;
1298 }
1299 len = pj_ansi_sprintf( info+info_len,
1300 ", stream #%d: %.*s (%s)", i,
1301 (int)strm_info->fmt.encoding_name.slen,
Benny Prijonoab7399b2006-02-27 00:40:31 +00001302 strm_info->fmt.encoding_name.ptr,
Benny Prijono26ff9062006-02-21 23:47:00 +00001303 dir);
1304 if (len > 0)
1305 info_len += len;
1306 }
1307 PJ_LOG(3,(THIS_FILE,"Media started%s", info));
1308 }
1309}
1310
1311
1312/*
1313 * Hangup call.
1314 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001315PJ_DEF(void) pjsua_call_hangup(int call_index)
Benny Prijono26ff9062006-02-21 23:47:00 +00001316{
Benny Prijonoa91a0032006-02-26 21:23:45 +00001317 pjsua_call *call;
Benny Prijono1c2bf462006-03-05 11:54:02 +00001318 int code;
Benny Prijono26ff9062006-02-21 23:47:00 +00001319 pj_status_t status;
1320 pjsip_tx_data *tdata;
1321
Benny Prijonoa91a0032006-02-26 21:23:45 +00001322
1323 call = &pjsua.calls[call_index];
1324
1325 if (!call->inv) {
1326 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
1327 return;
1328 }
1329
Benny Prijono1c2bf462006-03-05 11:54:02 +00001330 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1331 code = PJSIP_SC_OK;
1332 else if (call->inv->role == PJSIP_ROLE_UAS)
1333 code = PJSIP_SC_DECLINE;
1334 else
1335 code = PJSIP_SC_REQUEST_TERMINATED;
1336
Benny Prijonoa91a0032006-02-26 21:23:45 +00001337 status = pjsip_inv_end_session(call->inv, code, NULL, &tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001338 if (status != PJ_SUCCESS) {
1339 pjsua_perror(THIS_FILE,
1340 "Failed to create end session message",
1341 status);
1342 return;
1343 }
1344
Benny Prijonofccab712006-02-22 22:23:22 +00001345 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1346 * as p_tdata when INVITE transaction has not been answered
1347 * with any provisional responses.
1348 */
1349 if (tdata == NULL)
1350 return;
1351
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001352 status = pjsip_inv_send_msg(call->inv, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001353 if (status != PJ_SUCCESS) {
1354 pjsua_perror(THIS_FILE,
1355 "Failed to send end session message",
1356 status);
1357 return;
1358 }
1359}
1360
1361
1362/*
1363 * Put call on-Hold.
1364 */
Benny Prijono8b1889b2006-06-06 18:40:40 +00001365PJ_DEF(pj_status_t) pjsua_call_set_hold(int call_index)
Benny Prijono26ff9062006-02-21 23:47:00 +00001366{
1367 pjmedia_sdp_session *sdp;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001368 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001369 pjsip_tx_data *tdata;
1370 pj_status_t status;
1371
Benny Prijonoa91a0032006-02-26 21:23:45 +00001372 call = &pjsua.calls[call_index];
1373
1374 if (!call->inv) {
1375 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001376 return PJSIP_ESESSIONTERMINATED;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001377 }
1378
1379 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001380 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001381 return PJSIP_ESESSIONSTATE;
Benny Prijono26ff9062006-02-21 23:47:00 +00001382 }
1383
Benny Prijonoa91a0032006-02-26 21:23:45 +00001384 status = create_inactive_sdp(call, &sdp);
Benny Prijono26ff9062006-02-21 23:47:00 +00001385 if (status != PJ_SUCCESS)
Benny Prijono8b1889b2006-06-06 18:40:40 +00001386 return status;
Benny Prijono26ff9062006-02-21 23:47:00 +00001387
1388 /* Send re-INVITE with new offer */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001389 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001390 if (status != PJ_SUCCESS) {
1391 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001392 return status;
Benny Prijono26ff9062006-02-21 23:47:00 +00001393 }
1394
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001395 status = pjsip_inv_send_msg( call->inv, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001396 if (status != PJ_SUCCESS) {
1397 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001398 return status;
Benny Prijono26ff9062006-02-21 23:47:00 +00001399 }
Benny Prijono8b1889b2006-06-06 18:40:40 +00001400
1401 return PJ_SUCCESS;
Benny Prijono26ff9062006-02-21 23:47:00 +00001402}
1403
1404
1405/*
1406 * re-INVITE.
1407 */
Benny Prijono8b1889b2006-06-06 18:40:40 +00001408PJ_DEF(pj_status_t) pjsua_call_reinvite(int call_index)
Benny Prijono26ff9062006-02-21 23:47:00 +00001409{
1410 pjmedia_sdp_session *sdp;
1411 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001412 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001413 pj_status_t status;
1414
Benny Prijonoa91a0032006-02-26 21:23:45 +00001415 call = &pjsua.calls[call_index];
Benny Prijono26ff9062006-02-21 23:47:00 +00001416
Benny Prijonoa91a0032006-02-26 21:23:45 +00001417 if (!call->inv) {
1418 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001419 return PJSIP_ESESSIONTERMINATED;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001420 }
1421
1422
1423 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
Benny Prijono26ff9062006-02-21 23:47:00 +00001424 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001425 return PJSIP_ESESSIONSTATE;
Benny Prijono26ff9062006-02-21 23:47:00 +00001426 }
1427
1428 /* Create SDP */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001429 status = pjmedia_endpt_create_sdp( pjsua.med_endpt, call->inv->pool, 1,
1430 &call->skinfo, &sdp);
Benny Prijono26ff9062006-02-21 23:47:00 +00001431 if (status != PJ_SUCCESS) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001432 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1433 status);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001434 return status;
Benny Prijono26ff9062006-02-21 23:47:00 +00001435 }
1436
1437 /* Send re-INVITE with new offer */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001438 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001439 if (status != PJ_SUCCESS) {
1440 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001441 return status;
Benny Prijono26ff9062006-02-21 23:47:00 +00001442 }
1443
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001444 status = pjsip_inv_send_msg( call->inv, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001445 if (status != PJ_SUCCESS) {
1446 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001447 return status;
Benny Prijono26ff9062006-02-21 23:47:00 +00001448 }
Benny Prijono8b1889b2006-06-06 18:40:40 +00001449
1450 return PJ_SUCCESS;
Benny Prijono26ff9062006-02-21 23:47:00 +00001451}
1452
1453
1454/*
1455 * Transfer call.
1456 */
Benny Prijono8b1889b2006-06-06 18:40:40 +00001457PJ_DEF(pj_status_t) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest)
Benny Prijono26ff9062006-02-21 23:47:00 +00001458{
1459 pjsip_evsub *sub;
1460 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001461 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00001462 pj_status_t status;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001463
Benny Prijono26ff9062006-02-21 23:47:00 +00001464
Benny Prijonoa91a0032006-02-26 21:23:45 +00001465 call = &pjsua.calls[call_index];
1466
1467 if (!call->inv) {
1468 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001469 return PJSIP_ESESSIONTERMINATED;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001470 }
1471
Benny Prijono26ff9062006-02-21 23:47:00 +00001472 /* Create xfer client subscription.
1473 * We're not interested in knowing the transfer result, so we
1474 * put NULL as the callback.
1475 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00001476 status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub);
Benny Prijono26ff9062006-02-21 23:47:00 +00001477 if (status != PJ_SUCCESS) {
1478 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001479 return status;
Benny Prijono26ff9062006-02-21 23:47:00 +00001480 }
1481
1482 /*
1483 * Create REFER request.
1484 */
Benny Prijono9fc735d2006-05-28 14:58:12 +00001485 status = pjsip_xfer_initiate(sub, dest, &tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00001486 if (status != PJ_SUCCESS) {
1487 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001488 return status;
Benny Prijono26ff9062006-02-21 23:47:00 +00001489 }
1490
1491 /* Send. */
1492 status = pjsip_xfer_send_request(sub, tdata);
1493 if (status != PJ_SUCCESS) {
1494 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001495 return status;
Benny Prijono26ff9062006-02-21 23:47:00 +00001496 }
1497
1498 /* For simplicity (that's what this program is intended to be!),
1499 * leave the original invite session as it is. More advanced application
1500 * may want to hold the INVITE, or terminate the invite, or whatever.
1501 */
Benny Prijono8b1889b2006-06-06 18:40:40 +00001502
1503 return PJ_SUCCESS;
Benny Prijono84126ab2006-02-09 09:30:09 +00001504}
Benny Prijono834aee32006-02-19 01:38:06 +00001505
1506
Benny Prijonob0808372006-03-02 21:18:58 +00001507/**
Benny Prijono9fc735d2006-05-28 14:58:12 +00001508 * Dial DTMF.
1509 */
1510PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( unsigned call_index,
1511 const pj_str_t *digits)
1512{
1513 pjsua_call *call = &pjsua.calls[call_index];
1514
1515 PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, PJ_EINVAL);
1516
1517 if (!call->session) {
1518 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
1519 return -1;
1520 }
1521
1522 return pjmedia_session_dial_dtmf( call->session, 0, digits);
1523}
1524
1525
1526/**
Benny Prijonob0808372006-03-02 21:18:58 +00001527 * Send instant messaging inside INVITE session.
1528 */
Benny Prijono8b1889b2006-06-06 18:40:40 +00001529PJ_DEF(pj_status_t) pjsua_call_send_im(int call_index, const pj_str_t *str)
Benny Prijonob0808372006-03-02 21:18:58 +00001530{
1531 pjsua_call *call;
1532 const pj_str_t mime_text = pj_str("text");
1533 const pj_str_t mime_plain = pj_str("plain");
Benny Prijonob0808372006-03-02 21:18:58 +00001534 pjsip_tx_data *tdata;
1535 pj_status_t status;
1536
1537 call = &pjsua.calls[call_index];
1538
1539 if (!call->inv) {
1540 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001541 return PJSIP_ESESSIONTERMINATED;
Benny Prijonob0808372006-03-02 21:18:58 +00001542 }
1543
1544 /* Lock dialog. */
1545 pjsip_dlg_inc_lock(call->inv->dlg);
1546
1547 /* Create request message. */
1548 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1549 -1, &tdata);
1550 if (status != PJ_SUCCESS) {
1551 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1552 goto on_return;
1553 }
1554
1555 /* Add accept header. */
1556 pjsip_msg_add_hdr( tdata->msg,
1557 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1558
1559 /* Create "text/plain" message body. */
1560 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &mime_text,
Benny Prijono9fc735d2006-05-28 14:58:12 +00001561 &mime_plain, str);
Benny Prijonob0808372006-03-02 21:18:58 +00001562 if (tdata->msg->body == NULL) {
1563 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1564 pjsip_tx_data_dec_ref(tdata);
1565 goto on_return;
1566 }
1567
1568 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001569 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
Benny Prijonob0808372006-03-02 21:18:58 +00001570 if (status != PJ_SUCCESS) {
1571 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1572 goto on_return;
1573 }
1574
1575on_return:
1576 pjsip_dlg_dec_lock(call->inv->dlg);
Benny Prijono8b1889b2006-06-06 18:40:40 +00001577 return status;
Benny Prijonob0808372006-03-02 21:18:58 +00001578}
1579
1580
1581/**
1582 * Send IM typing indication inside INVITE session.
1583 */
Benny Prijono8b1889b2006-06-06 18:40:40 +00001584PJ_DEF(pj_status_t) pjsua_call_send_typing_ind(int call_index,
1585 pj_bool_t is_typing)
Benny Prijonob0808372006-03-02 21:18:58 +00001586{
1587 pjsua_call *call;
1588 pjsip_tx_data *tdata;
1589 pj_status_t status;
1590
1591 call = &pjsua.calls[call_index];
1592
1593 if (!call->inv) {
1594 PJ_LOG(3,(THIS_FILE,"Call has been disconnected"));
Benny Prijono8b1889b2006-06-06 18:40:40 +00001595 return PJSIP_ESESSIONTERMINATED;
Benny Prijonob0808372006-03-02 21:18:58 +00001596 }
1597
1598 /* Lock dialog. */
1599 pjsip_dlg_inc_lock(call->inv->dlg);
1600
1601 /* Create request message. */
1602 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1603 -1, &tdata);
1604 if (status != PJ_SUCCESS) {
1605 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1606 goto on_return;
1607 }
1608
1609 /* Create "application/im-iscomposing+xml" msg body. */
1610 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1611 NULL, NULL, -1);
1612
1613 /* Send the request. */
Benny Prijono64158af2006-04-04 11:06:34 +00001614 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
Benny Prijonob0808372006-03-02 21:18:58 +00001615 if (status != PJ_SUCCESS) {
1616 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1617 goto on_return;
1618 }
1619
1620on_return:
Benny Prijono8b1889b2006-06-06 18:40:40 +00001621 pjsip_dlg_dec_lock(call->inv->dlg);
1622 return status;
1623}
Benny Prijonob0808372006-03-02 21:18:58 +00001624
1625
Benny Prijono834aee32006-02-19 01:38:06 +00001626/*
1627 * Terminate all calls.
1628 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001629PJ_DEF(void) pjsua_call_hangup_all(void)
Benny Prijono834aee32006-02-19 01:38:06 +00001630{
Benny Prijonodc39fe82006-05-26 12:17:46 +00001631 unsigned i;
Benny Prijono834aee32006-02-19 01:38:06 +00001632
Benny Prijonodc39fe82006-05-26 12:17:46 +00001633 for (i=0; i<pjsua.config.max_calls; ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001634 pjsip_tx_data *tdata;
Benny Prijono1c2bf462006-03-05 11:54:02 +00001635 int st_code;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001636 pjsua_call *call;
Benny Prijono834aee32006-02-19 01:38:06 +00001637
Benny Prijonoa91a0032006-02-26 21:23:45 +00001638 if (pjsua.calls[i].inv == NULL)
1639 continue;
Benny Prijono834aee32006-02-19 01:38:06 +00001640
Benny Prijonoa91a0032006-02-26 21:23:45 +00001641 call = &pjsua.calls[i];
1642
Benny Prijono1c2bf462006-03-05 11:54:02 +00001643 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
1644 st_code = 200;
1645 } else {
1646 st_code = PJSIP_SC_GONE;
1647 }
1648
1649 if (pjsip_inv_end_session(call->inv, st_code, NULL, &tdata)==0) {
Benny Prijonofccab712006-02-22 22:23:22 +00001650 if (tdata)
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001651 pjsip_inv_send_msg(call->inv, tdata);
Benny Prijonofccab712006-02-22 22:23:22 +00001652 }
Benny Prijono834aee32006-02-19 01:38:06 +00001653 }
1654}
1655
Benny Prijono26ff9062006-02-21 23:47:00 +00001656
Benny Prijonoa91a0032006-02-26 21:23:45 +00001657pj_status_t pjsua_call_init(void)
1658{
1659 /* Initialize invite session callback. */
1660 pjsip_inv_callback inv_cb;
1661 pj_status_t status;
1662
1663 pj_memset(&inv_cb, 0, sizeof(inv_cb));
1664 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
1665 inv_cb.on_new_session = &pjsua_call_on_forked;
1666 inv_cb.on_media_update = &pjsua_call_on_media_update;
1667 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
1668 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
1669
1670
1671 /* Initialize invite session module: */
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001672 status = pjsip_inv_usage_init(pjsua.endpt, &inv_cb);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001673
1674 return status;
1675}
Benny Prijono9fc735d2006-05-28 14:58:12 +00001676
1677/**
1678 * Replace media transport.
1679 */
1680PJ_DEF(pj_status_t) pjsua_set_call_media_transport( unsigned call_index,
1681 const pjmedia_sock_info *i,
1682 pjmedia_transport *tp)
1683{
1684 pjsua_call *call = &pjsua.calls[call_index];
1685
1686 if (i)
1687 pj_memcpy(&call->skinfo, i, sizeof(pjmedia_sock_info));
1688
1689 if (call->med_tp)
1690 (*call->med_tp->op->destroy)(call->med_tp);
1691
1692 call->med_tp = tp;
1693 return PJ_SUCCESS;
1694}