blob: 5c493d03a2fec49b7ab6a97682fb12c52654242a [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono84126ab2006-02-09 09:30:09 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000020#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000021#include <pjsua-lib/pjsua_internal.h>
22
23
24#define THIS_FILE "pjsua_call.c"
25
26
27/* This callback receives notification from invite session when the
28 * session state has changed.
29 */
30static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
31 pjsip_event *e);
32
33/* This callback is called by invite session framework when UAC session
34 * has forked.
35 */
36static void pjsua_call_on_forked( pjsip_inv_session *inv,
37 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000038
39/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000040 * Callback to be called when SDP offer/answer negotiation has just completed
41 * in the session. This function will start/update media if negotiation
42 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000043 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000044static void pjsua_call_on_media_update(pjsip_inv_session *inv,
45 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000046
47/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000048 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000049 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000050static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
51 const pjmedia_sdp_session *offer);
52
53/*
Benny Prijono77998ce2007-06-20 10:03:46 +000054 * Called to generate new offer.
55 */
56static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
57 pjmedia_sdp_session **offer);
58
59/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000060 * This callback is called when transaction state has changed in INVITE
61 * session. We use this to trap:
62 * - incoming REFER request.
63 * - incoming MESSAGE request.
64 */
65static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
66 pjsip_transaction *tsx,
67 pjsip_event *e);
68
Benny Prijono5e51a4e2008-11-27 00:06:46 +000069/*
70 * Redirection handler.
71 */
Benny Prijono08a48b82008-11-27 12:42:07 +000072static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
73 const pjsip_uri *target,
74 const pjsip_event *e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +000075
Benny Prijonoeebe9af2006-06-13 22:57:13 +000076
Nanang Izzuddin99d69522008-08-04 15:01:38 +000077/* Create SDP for call hold. */
78static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
79 pjmedia_sdp_session **p_answer);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000080
Benny Prijonod524e822006-09-22 12:48:18 +000081/*
82 * Callback called by event framework when the xfer subscription state
83 * has changed.
84 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000085static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
86static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
87
88/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000089 * Reset call descriptor.
90 */
91static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000092{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000093 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +000094
Benny Prijonoeebe9af2006-06-13 22:57:13 +000095 call->index = id;
96 call->inv = NULL;
97 call->user_data = NULL;
98 call->session = NULL;
Benny Prijonoa310bd22008-06-27 21:19:44 +000099 call->audio_idx = -1;
Benny Prijono8147f402007-11-21 14:50:07 +0000100 call->ssrc = pj_rand();
Nanang Izzuddina815ceb2008-08-26 16:51:28 +0000101 call->rtp_tx_seq = 0;
102 call->rtp_tx_ts = 0;
103 call->rtp_tx_seq_ts_set = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000104 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000105 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000106 call->conf_slot = PJSUA_INVALID_ID;
107 call->last_text.ptr = call->last_text_buf_;
108 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +0000109 call->conn_time.sec = 0;
110 call->conn_time.msec = 0;
111 call->res_time.sec = 0;
112 call->res_time.msec = 0;
Benny Prijono91a6a172007-10-31 08:59:29 +0000113 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
Nanang Izzuddin4375f902008-06-26 19:12:09 +0000114 call->rem_srtp_use = PJMEDIA_SRTP_DISABLED;
Nanang Izzuddin99d69522008-08-04 15:01:38 +0000115 call->local_hold = PJ_FALSE;
Benny Prijono105217f2006-03-06 16:25:59 +0000116}
117
118
Benny Prijono275fd682006-03-22 11:59:11 +0000119/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000120 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000121 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000122pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000123{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000124 pjsip_inv_callback inv_cb;
125 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000126 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000127 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000128
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000129 /* Init calls array. */
130 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
131 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000132
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000133 /* Copy config */
134 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000135
Benny Prijono91d06b62008-09-20 12:16:56 +0000136 /* Check the route URI's and force loose route if required */
137 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
138 status = normalize_route_uri(pjsua_var.pool,
139 &pjsua_var.ua_cfg.outbound_proxy[i]);
140 if (status != PJ_SUCCESS)
141 return status;
142 }
143
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000144 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000145 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000146 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
147 inv_cb.on_new_session = &pjsua_call_on_forked;
148 inv_cb.on_media_update = &pjsua_call_on_media_update;
149 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000150 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000151 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000152 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000153
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000154 /* Initialize invite session module: */
155 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
156 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
157
Benny Prijonoc8141a82006-08-20 09:12:19 +0000158 /* Add "norefersub" in Supported header */
159 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
160 NULL, 1, &str_norefersub);
161
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162 return status;
163}
164
165
166/*
167 * Start call subsystem.
168 */
169pj_status_t pjsua_call_subsys_start(void)
170{
171 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000172 return PJ_SUCCESS;
173}
174
175
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000176/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000177 * Get maximum number of calls configured in pjsua.
178 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000179PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000180{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000181 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000182}
183
184
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000185/*
186 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000187 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000188PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000189{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000191}
192
193
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000194/*
195 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000196 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000197PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
198 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000199{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000201
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000202 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000203
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000204 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000205
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
207 if (!pjsua_var.calls[i].inv)
208 continue;
209 ids[c] = i;
210 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000211 }
212
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000214
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000215 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000216
217 return PJ_SUCCESS;
218}
219
220
Benny Prijono5773cd62008-01-19 13:01:42 +0000221/* Allocate one call id */
222static pjsua_call_id alloc_call_id(void)
223{
224 pjsua_call_id cid;
225
226#if 1
227 /* New algorithm: round-robin */
228 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
229 pjsua_var.next_call_id < 0)
230 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000231 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000232 }
233
234 for (cid=pjsua_var.next_call_id;
235 cid<(int)pjsua_var.ua_cfg.max_calls;
236 ++cid)
237 {
238 if (pjsua_var.calls[cid].inv == NULL) {
239 ++pjsua_var.next_call_id;
240 return cid;
241 }
242 }
243
244 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
245 if (pjsua_var.calls[cid].inv == NULL) {
246 ++pjsua_var.next_call_id;
247 return cid;
248 }
249 }
250
251#else
252 /* Old algorithm */
253 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
254 if (pjsua_var.calls[cid].inv == NULL)
255 return cid;
256 }
257#endif
258
259 return PJSUA_INVALID_ID;
260}
261
Benny Prijonod8179652008-01-23 20:39:07 +0000262/* Get signaling secure level.
263 * Return:
264 * 0: if signaling is not secure
265 * 1: if TLS transport is used for immediate hop
266 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000267 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000268static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000269{
270 const pj_str_t tls = pj_str(";transport=tls");
271 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000272 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000273
274 if (pj_stristr(dst_uri, &sips))
275 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000276
277 if (!pj_list_empty(&acc->route_set)) {
278 pjsip_route_hdr *r = acc->route_set.next;
279 pjsip_uri *uri = r->name_addr.uri;
280 pjsip_sip_uri *sip_uri;
281
282 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
283 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
284 return 1;
285
286 } else {
287 if (pj_stristr(dst_uri, &tls))
288 return 1;
289 }
290
Benny Prijonod8179652008-01-23 20:39:07 +0000291 return 0;
292}
293
Benny Prijono224b4e22008-06-19 14:10:28 +0000294/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000295static int call_get_secure_level(pjsua_call *call)
296{
297 if (call->inv->dlg->secure)
298 return 2;
299
300 if (!pj_list_empty(&call->inv->dlg->route_set)) {
301 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
302 pjsip_uri *uri = r->name_addr.uri;
303 pjsip_sip_uri *sip_uri;
304
305 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
306 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
307 return 1;
308
309 } else {
310 pjsip_sip_uri *sip_uri;
311
312 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
313 return 2;
314 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
315 return 0;
316
317 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
318 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
319 return 1;
320 }
321
322 return 0;
323}
Benny Prijono224b4e22008-06-19 14:10:28 +0000324*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000325
326
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000327/*
328 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000329 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000330PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
331 const pj_str_t *dest_uri,
332 unsigned options,
333 void *user_data,
334 const pjsua_msg_data *msg_data,
335 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000336{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000337 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000338 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000339 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000340 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000341 pjsua_acc *acc;
342 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000343 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000344 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000345 pjsip_tx_data *tdata;
346 pj_status_t status;
347
Benny Prijono9fc735d2006-05-28 14:58:12 +0000348
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000349 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000350 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000351 PJ_EINVAL);
352
Benny Prijono320fa4d2006-12-07 10:09:16 +0000353 /* Check arguments */
354 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
355
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000356 PJSUA_LOCK();
357
Benny Prijonof798e502009-03-09 13:08:16 +0000358 /* Create sound port if none is instantiated, to check if sound device
359 * can be used. But only do this with the conference bridge, as with
360 * audio switchboard (i.e. APS-Direct), we can only open the sound
361 * device once the correct format has been known
362 */
363 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
364 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000365 {
366 pj_status_t status;
367
368 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
369 if (status != PJ_SUCCESS) {
370 PJSUA_UNLOCK();
371 return status;
372 }
373 }
374
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000375 acc = &pjsua_var.acc[acc_id];
376 if (!acc->valid) {
377 pjsua_perror(THIS_FILE, "Unable to make call because account "
378 "is not valid", PJ_EINVALIDOP);
379 PJSUA_UNLOCK();
380 return PJ_EINVALIDOP;
381 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000382
Benny Prijonoa91a0032006-02-26 21:23:45 +0000383 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000384 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000385
Benny Prijono5773cd62008-01-19 13:01:42 +0000386 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000387 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
388 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000389 return PJ_ETOOMANY;
390 }
391
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000392 call = &pjsua_var.calls[call_id];
393
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000394 /* Create temporary pool */
395 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
396
Benny Prijono320fa4d2006-12-07 10:09:16 +0000397 /* Verify that destination URI is valid before calling
398 * pjsua_acc_create_uac_contact, or otherwise there
399 * a misleading "Invalid Contact URI" error will be printed
400 * when pjsua_acc_create_uac_contact() fails.
401 */
402 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000403 pjsip_uri *uri;
404 pj_str_t dup;
405
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000406 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
407 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000408
409 if (uri == NULL) {
410 pjsua_perror(THIS_FILE, "Unable to make call",
411 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000412 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000413 PJSUA_UNLOCK();
414 return PJSIP_EINVALIDREQURI;
415 }
416 }
417
Benny Prijono093d3022006-09-24 00:07:11 +0000418 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
419 (int)dest_uri->slen, dest_uri->ptr));
420
Benny Prijonoe21e7842006-04-09 16:46:05 +0000421 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000422 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000423
Benny Prijonoe21e7842006-04-09 16:46:05 +0000424 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000425 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000426
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000427 /* Create suitable Contact header unless a Contact header has been
428 * set in the account.
429 */
430 if (acc->contact.slen) {
431 contact = acc->contact;
432 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000433 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000434 acc_id, dest_uri);
435 if (status != PJ_SUCCESS) {
436 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
437 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000438 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000439 PJSUA_UNLOCK();
440 return status;
441 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000442 }
443
Benny Prijonoe21e7842006-04-09 16:46:05 +0000444 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000445 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000446 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000447 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000448 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000449 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000450 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000451 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000452 return status;
453 }
454
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000455 /* Increment the dialog's lock otherwise when invite session creation
456 * fails the dialog will be destroyed prematurely.
457 */
458 pjsip_dlg_inc_lock(dlg);
459
Benny Prijonodb844a42008-02-02 17:07:18 +0000460 /* Calculate call's secure level */
461 call->secure_level = get_secure_level(acc_id, dest_uri);
462
Benny Prijonoc97608e2007-03-23 16:34:20 +0000463 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000464 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000465 call->secure_level, dlg->pool,
466 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000467 if (status != PJ_SUCCESS) {
468 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
469 goto on_error;
470 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000471
Benny Prijono224b4e22008-06-19 14:10:28 +0000472 /* Create offer */
473 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000474 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000475 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000476 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000477 goto on_error;
478 }
479
480 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000481 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000482 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000483 if (acc->cfg.require_100rel)
484 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000485 if (acc->cfg.require_timer)
486 options |= PJSIP_INV_REQUIRE_TIMER;
Benny Prijono84126ab2006-02-09 09:30:09 +0000487
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000488 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000489 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000490 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000491 goto on_error;
492 }
493
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000494 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000495 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
496 if (status != PJ_SUCCESS) {
497 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
498 goto on_error;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000499 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000500
501 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000502 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000503 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000504
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000505 dlg->mod_data[pjsua_var.mod.id] = call;
506 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000507
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000508 /* Attach user data */
509 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000510
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000511 /* If account is locked to specific transport, then lock dialog
512 * to this transport too.
513 */
514 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
515 pjsip_tpselector tp_sel;
516
517 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
518 pjsip_dlg_set_transport(dlg, &tp_sel);
519 }
520
Benny Prijono84126ab2006-02-09 09:30:09 +0000521 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000522 if (!pj_list_empty(&acc->route_set))
523 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000524
525
526 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000527 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000528 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000529 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000530 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000531
Benny Prijono48ab2b72007-11-08 09:24:30 +0000532 /* Set authentication preference */
533 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000534
535 /* Create initial INVITE: */
536
537 status = pjsip_inv_invite(inv, &tdata);
538 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000539 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
540 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000541 goto on_error;
542 }
543
544
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000545 /* Add additional headers etc */
546
547 pjsua_process_msg_data( tdata, msg_data);
548
Benny Prijono093d3022006-09-24 00:07:11 +0000549 /* Must increment call counter now */
550 ++pjsua_var.call_cnt;
551
Benny Prijono84126ab2006-02-09 09:30:09 +0000552 /* Send initial INVITE: */
553
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000554 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000555 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000556 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
557 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000558
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000559 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000560 * session would have been cleared.
561 */
562 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000563 goto on_error;
564 }
565
Benny Prijono84126ab2006-02-09 09:30:09 +0000566 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000567
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000568 if (p_call_id)
569 *p_call_id = call_id;
570
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000571 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000572 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000573 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000574
575 return PJ_SUCCESS;
576
577
578on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000579 if (dlg) {
580 /* This may destroy the dialog */
581 pjsip_dlg_dec_lock(dlg);
582 }
583
Benny Prijono1c2bf462006-03-05 11:54:02 +0000584 if (inv != NULL) {
585 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000586 }
587
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000588 if (call_id != -1) {
589 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000590 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000591 }
592
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000593 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000594 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000595 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000596}
597
598
Benny Prijono91a6a172007-10-31 08:59:29 +0000599/* Get the NAT type information in remote's SDP */
600static void update_remote_nat_type(pjsua_call *call,
601 const pjmedia_sdp_session *sdp)
602{
603 const pjmedia_sdp_attr *xnat;
604
605 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
606 if (xnat) {
607 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
608 } else {
609 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
610 }
611
612 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
613 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
614}
615
616
Benny Prijonodc39fe82006-05-26 12:17:46 +0000617/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000618 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000619 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000620 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000621pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000622{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000623 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000624 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000625 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000626 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
627 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000628 pjsip_tx_data *response = NULL;
629 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000630 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000631 int acc_id;
632 pjsua_call *call;
633 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000634 int sip_err_code;
Benny Prijonod8179652008-01-23 20:39:07 +0000635 pjmedia_sdp_session *offer, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000636 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000637
Benny Prijono26ff9062006-02-21 23:47:00 +0000638 /* Don't want to handle anything but INVITE */
639 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
640 return PJ_FALSE;
641
642 /* Don't want to handle anything that's already associated with
643 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000644 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000645 if (dlg || tsx)
646 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000647
Benny Prijono148c9dd2006-09-19 13:37:53 +0000648 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000649
Benny Prijono26ff9062006-02-21 23:47:00 +0000650 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000651 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000652
Benny Prijono5773cd62008-01-19 13:01:42 +0000653 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000654 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000655 PJSIP_SC_BUSY_HERE, NULL,
656 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000657 PJ_LOG(2,(THIS_FILE,
658 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000659 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000660 return PJ_TRUE;
661 }
662
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000663 /* Clear call descriptor */
664 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000665
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000666 call = &pjsua_var.calls[call_id];
667
668 /* Mark call start time. */
669 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000670
Benny Prijono053f5222006-11-11 16:16:04 +0000671 /* Check INVITE request for Replaces header. If Replaces header is
672 * present, the function will make sure that we can handle the request.
673 */
674 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
675 &response);
676 if (status != PJ_SUCCESS) {
677 /*
678 * Something wrong with the Replaces header.
679 */
680 if (response) {
681 pjsip_response_addr res_addr;
682
683 pjsip_get_response_addr(response->pool, rdata, &res_addr);
684 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
685 NULL, NULL);
686
687 } else {
688
689 /* Respond with 500 (Internal Server Error) */
690 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
691 NULL, NULL);
692 }
693
694 PJSUA_UNLOCK();
695 return PJ_TRUE;
696 }
697
698 /* If this INVITE request contains Replaces header, notify application
699 * about the request so that application can do subsequent checking
700 * if it wants to.
701 */
702 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
703 pjsua_call *replaced_call;
704 int st_code = 200;
705 pj_str_t st_text = { "OK", 2 };
706
707 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000708 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000709
710 /* Notify application */
711 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
712 rdata, &st_code, &st_text);
713
714 /* Must specify final response */
715 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
716
717 /* Check if application rejects this request. */
718 if (st_code >= 300) {
719
720 if (st_text.slen == 2)
721 st_text = *pjsip_get_status_text(st_code);
722
723 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
724 st_code, &st_text, NULL, NULL, NULL);
725 PJSUA_UNLOCK();
726 return PJ_TRUE;
727 }
728 }
729
Benny Prijonod8179652008-01-23 20:39:07 +0000730 /*
731 * Get which account is most likely to be associated with this incoming
732 * call. We need the account to find which contact URI to put for
733 * the call.
734 */
735 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
736
Benny Prijonodb844a42008-02-02 17:07:18 +0000737 /* Get call's secure level */
738 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
739 call->secure_level = 2;
740 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
741 call->secure_level = 1;
742 else
743 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000744
Benny Prijonod8179652008-01-23 20:39:07 +0000745 /* Parse SDP from incoming request */
746 if (rdata->msg_info.msg->body) {
747 status = pjmedia_sdp_parse(rdata->tp_info.pool,
Benny Prijono24a21852008-04-14 04:04:30 +0000748 (char*)rdata->msg_info.msg->body->data,
Benny Prijonod8179652008-01-23 20:39:07 +0000749 rdata->msg_info.msg->body->len, &offer);
Benny Prijono2c484e42008-06-26 19:47:23 +0000750 if (status == PJ_SUCCESS) {
751 /* Validate */
752 status = pjmedia_sdp_validate(offer);
753 }
754
Benny Prijonod8179652008-01-23 20:39:07 +0000755 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000756 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000757 pjsip_hdr hdr_list;
758 pjsip_warning_hdr *w;
759
760 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000761 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000762
763 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
764 pjsip_endpt_name(pjsua_var.endpt),
765 status);
766 pj_list_init(&hdr_list);
767 pj_list_push_back(&hdr_list, w);
768
Benny Prijono224b4e22008-06-19 14:10:28 +0000769 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000770 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000771 PJSUA_UNLOCK();
772 return PJ_TRUE;
773 }
Benny Prijono617b8602008-04-07 10:10:31 +0000774
775 /* Do quick checks on SDP before passing it to transports. More elabore
776 * checks will be done in pjsip_inv_verify_request2() below.
777 */
778 if (offer->media_count==0) {
779 const pj_str_t reason = pj_str("Missing media in SDP");
780 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
781 NULL, NULL, NULL);
782 PJSUA_UNLOCK();
783 return PJ_TRUE;
784 }
785
Benny Prijonod8179652008-01-23 20:39:07 +0000786 } else {
787 offer = NULL;
788 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000789
Benny Prijono224b4e22008-06-19 14:10:28 +0000790 /* Init media channel */
791 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
792 call->secure_level,
793 rdata->tp_info.pool, offer,
794 &sip_err_code);
795 if (status != PJ_SUCCESS) {
796 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
797 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
798 sip_err_code, NULL, NULL, NULL, NULL);
799 PJSUA_UNLOCK();
800 return PJ_TRUE;
801 }
802
803 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000804 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000805 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000806 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000807 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000808 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
809 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000810 PJSUA_UNLOCK();
811 return PJ_TRUE;
812 }
813
Benny Prijono224b4e22008-06-19 14:10:28 +0000814
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000815 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000816 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000817 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000818 if (pjsua_var.acc[acc_id].cfg.require_100rel)
819 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000820 if (pjsua_var.acc[acc_id].cfg.require_timer)
821 options |= PJSIP_INV_REQUIRE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000822
Benny Prijonod8179652008-01-23 20:39:07 +0000823 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
824 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000825 if (status != PJ_SUCCESS) {
826
827 /*
828 * No we can't handle the incoming INVITE request.
829 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000830 if (response) {
831 pjsip_response_addr res_addr;
832
833 pjsip_get_response_addr(response->pool, rdata, &res_addr);
834 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
835 NULL, NULL);
836
837 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000838 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000839 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
840 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000841 }
842
Benny Prijonoc97608e2007-03-23 16:34:20 +0000843 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000844 PJSUA_UNLOCK();
845 return PJ_TRUE;
846 }
847
848
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000849 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000850 if (pjsua_var.acc[acc_id].contact.slen) {
851 contact = pjsua_var.acc[acc_id].contact;
852 } else {
853 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
854 acc_id, rdata);
855 if (status != PJ_SUCCESS) {
856 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
857 status);
858 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
859 NULL, NULL);
860 pjsua_media_channel_deinit(call->index);
861 PJSUA_UNLOCK();
862 return PJ_TRUE;
863 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000864 }
865
Benny Prijono26ff9062006-02-21 23:47:00 +0000866 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000867 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000868 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000869 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000870 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000871 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000872 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000873 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000874 return PJ_TRUE;
875 }
876
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000877 /* Set credentials */
878 if (pjsua_var.acc[acc_id].cred_cnt) {
879 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
880 pjsua_var.acc[acc_id].cred_cnt,
881 pjsua_var.acc[acc_id].cred);
882 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000883
Benny Prijono48ab2b72007-11-08 09:24:30 +0000884 /* Set preference */
885 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
886 &pjsua_var.acc[acc_id].cfg.auth_pref);
887
Benny Prijono26ff9062006-02-21 23:47:00 +0000888 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000889 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000890 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000891 pjsip_hdr hdr_list;
892 pjsip_warning_hdr *w;
893
894 w = pjsip_warning_hdr_create_from_status(dlg->pool,
895 pjsip_endpt_name(pjsua_var.endpt),
896 status);
897 pj_list_init(&hdr_list);
898 pj_list_push_back(&hdr_list, w);
899
900 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
901
902 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000903 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000904 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000905 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000906 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000907 return PJ_TRUE;
908 }
909
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000910 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000911 status = pjsip_timer_init_session(inv,
912 &pjsua_var.acc[acc_id].cfg.timer_setting);
913 if (status != PJ_SUCCESS) {
914 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
915 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
916 NULL, &response);
917 if (status == PJ_SUCCESS && response)
918 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000919
Nanang Izzuddin65add622009-08-11 16:26:20 +0000920 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000921
Nanang Izzuddin65add622009-08-11 16:26:20 +0000922 PJSUA_UNLOCK();
923 return PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000924 }
925
Benny Prijonoea9fd392007-11-06 03:41:40 +0000926 /* Update NAT type of remote endpoint, only when there is SDP in
927 * incoming INVITE!
928 */
929 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
930 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
931 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000932 const pjmedia_sdp_session *remote_sdp;
933
934 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
935 update_remote_nat_type(call, remote_sdp);
936 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000937
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000938 /* If account is locked to specific transport, then lock dialog
939 * to this transport too.
940 */
941 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
942 pjsip_tpselector tp_sel;
943
944 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
945 pjsip_dlg_set_transport(dlg, &tp_sel);
946 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000947
Benny Prijono2285e7e2008-12-17 14:28:18 +0000948 /* Must answer with some response to initial INVITE. We'll do this before
949 * attaching the call to the invite session/dialog, so that the application
950 * will not get notification about this event (on another scenario, it is
951 * also possible that inv_send_msg() fails and causes the invite session to
952 * be disconnected. If we have the call attached at this time, this will
953 * cause the disconnection callback to be called before on_incoming_call()
954 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +0000955 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000956 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000957 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000958 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000959 if (response == NULL) {
960 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
961 status);
962 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
963 pjsip_inv_terminate(inv, 500, PJ_FALSE);
964 } else {
965 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +0000966 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000967 PJ_FALSE);
968 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000969 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000970 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000971 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000972
973 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000974 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000975 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000976 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +0000977 PJSUA_UNLOCK();
978 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000979 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000980 }
981
Benny Prijono2285e7e2008-12-17 14:28:18 +0000982 /* Create and attach pjsua_var data to the dialog: */
983 call->inv = inv;
984
985 dlg->mod_data[pjsua_var.mod.id] = call;
986 inv->mod_data[pjsua_var.mod.id] = call;
987
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000988 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000989
Benny Prijono105217f2006-03-06 16:25:59 +0000990
Benny Prijono053f5222006-11-11 16:16:04 +0000991 /* Check if this request should replace existing call */
992 if (replaced_dlg) {
993 pjsip_inv_session *replaced_inv;
994 struct pjsua_call *replaced_call;
995 pjsip_tx_data *tdata;
996
997 /* Get the invite session in the dialog */
998 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
999
1000 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001001 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001002
1003 /* Notify application */
1004 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1005 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1006 call_id);
1007
1008 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1009 call_id));
1010
1011 /* Answer the new call with 200 response */
1012 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1013 if (status == PJ_SUCCESS)
1014 status = pjsip_inv_send_msg(inv, tdata);
1015
1016 if (status != PJ_SUCCESS)
1017 pjsua_perror(THIS_FILE, "Error answering session", status);
1018
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001019 /* Note that inv may be invalid if 200/OK has caused error in
1020 * starting the media.
1021 */
Benny Prijono053f5222006-11-11 16:16:04 +00001022
1023 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1024 replaced_call->index));
1025
1026 /* Disconnect replaced invite session */
1027 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1028 &tdata);
1029 if (status == PJ_SUCCESS && tdata)
1030 status = pjsip_inv_send_msg(replaced_inv, tdata);
1031
1032 if (status != PJ_SUCCESS)
1033 pjsua_perror(THIS_FILE, "Error terminating session", status);
1034
1035
1036 } else {
1037
Benny Prijonob5388cf2007-01-04 22:45:08 +00001038 /* Notify application if on_incoming_call() is overriden,
1039 * otherwise hangup the call with 480
1040 */
1041 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001042 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001043 } else {
1044 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1045 NULL, NULL);
1046 }
Benny Prijono053f5222006-11-11 16:16:04 +00001047 }
1048
Benny Prijono8b1889b2006-06-06 18:40:40 +00001049
Benny Prijono26ff9062006-02-21 23:47:00 +00001050 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001051 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001052 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001053}
1054
1055
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001056
1057/*
1058 * Check if the specified call has active INVITE session and the INVITE
1059 * session has not been disconnected.
1060 */
1061PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1062{
1063 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1064 PJ_EINVAL);
1065 return pjsua_var.calls[call_id].inv != NULL &&
1066 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1067}
1068
1069
1070/*
1071 * Check if call has an active media session.
1072 */
1073PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1074{
1075 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1076 PJ_EINVAL);
1077 return pjsua_var.calls[call_id].session != NULL;
1078}
1079
1080
Benny Prijonocf986c42008-09-02 11:25:07 +00001081/*
1082 * Retrieve the media session associated with this call.
1083 */
1084PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1085{
1086 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1087 NULL);
1088 return pjsua_var.calls[call_id].session;
1089}
1090
1091
1092/*
1093 * Retrieve the media transport instance that is used for this call.
1094 */
1095PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1096{
1097 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1098 NULL);
1099 return pjsua_var.calls[cid].med_tp;
1100}
1101
1102
Benny Prijono148c9dd2006-09-19 13:37:53 +00001103/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001104pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001105 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001106 pjsua_call **p_call,
1107 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001108{
1109 enum { MAX_RETRY=50 };
1110 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001111 pjsua_call *call = NULL;
1112 pj_bool_t has_pjsua_lock = PJ_FALSE;
1113 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001114
1115 for (retry=0; retry<MAX_RETRY; ++retry) {
1116
1117 has_pjsua_lock = PJ_FALSE;
1118
1119 status = PJSUA_TRY_LOCK();
1120 if (status != PJ_SUCCESS) {
1121 pj_thread_sleep(retry/10);
1122 continue;
1123 }
1124
1125 has_pjsua_lock = PJ_TRUE;
1126 call = &pjsua_var.calls[call_id];
1127
1128 if (call->inv == NULL) {
1129 PJSUA_UNLOCK();
1130 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1131 return PJSIP_ESESSIONTERMINATED;
1132 }
1133
1134 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1135 if (status != PJ_SUCCESS) {
1136 PJSUA_UNLOCK();
1137 pj_thread_sleep(retry/10);
1138 continue;
1139 }
1140
1141 PJSUA_UNLOCK();
1142
1143 break;
1144 }
1145
1146 if (status != PJ_SUCCESS) {
1147 if (has_pjsua_lock == PJ_FALSE)
1148 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1149 "(possibly system has deadlocked) in %s",
1150 title));
1151 else
1152 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1153 "(possibly system has deadlocked) in %s",
1154 title));
1155 return PJ_ETIMEDOUT;
1156 }
1157
1158 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001159 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001160
1161 return PJ_SUCCESS;
1162}
1163
1164
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001165/*
1166 * Get the conference port identification associated with the call.
1167 */
1168PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1169{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001170 pjsua_call *call;
1171 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001172 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001173 pj_status_t status;
1174
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001175 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1176 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001177
Benny Prijonodc752ca2006-09-22 16:55:42 +00001178 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001179 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001180 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001181
1182 port_id = call->conf_slot;
1183
Benny Prijonodc752ca2006-09-22 16:55:42 +00001184 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001185
1186 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001187}
1188
1189
Benny Prijono148c9dd2006-09-19 13:37:53 +00001190
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001191/*
1192 * Obtain detail information about the specified call.
1193 */
1194PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1195 pjsua_call_info *info)
1196{
1197 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001198 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001199 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001200
1201 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1202 PJ_EINVAL);
1203
Benny Prijonoac623b32006-07-03 15:19:31 +00001204 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001205
Benny Prijonodc752ca2006-09-22 16:55:42 +00001206 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001207 if (status != PJ_SUCCESS) {
1208 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001209 }
1210
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001211 /* id and role */
1212 info->id = call_id;
1213 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001214 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001215
1216 /* local info */
1217 info->local_info.ptr = info->buf_.local_info;
1218 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1219 sizeof(info->buf_.local_info));
1220
1221 /* local contact */
1222 info->local_contact.ptr = info->buf_.local_contact;
1223 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1224 call->inv->dlg->local.contact->uri,
1225 info->local_contact.ptr,
1226 sizeof(info->buf_.local_contact));
1227
1228 /* remote info */
1229 info->remote_info.ptr = info->buf_.remote_info;
1230 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1231 sizeof(info->buf_.remote_info));
1232
1233 /* remote contact */
1234 if (call->inv->dlg->remote.contact) {
1235 int len;
1236 info->remote_contact.ptr = info->buf_.remote_contact;
1237 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1238 call->inv->dlg->remote.contact->uri,
1239 info->remote_contact.ptr,
1240 sizeof(info->buf_.remote_contact));
1241 if (len < 0) len = 0;
1242 info->remote_contact.slen = len;
1243 } else {
1244 info->remote_contact.slen = 0;
1245 }
1246
1247 /* call id */
1248 info->call_id.ptr = info->buf_.call_id;
1249 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1250 sizeof(info->buf_.call_id));
1251
1252 /* state, state_text */
1253 info->state = call->inv->state;
1254 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1255
1256 /* If call is disconnected, set the last_status from the cause code */
1257 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1258 /* last_status, last_status_text */
1259 info->last_status = call->inv->cause;
1260
1261 info->last_status_text.ptr = info->buf_.last_status_text;
1262 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1263 sizeof(info->buf_.last_status_text));
1264 } else {
1265 /* last_status, last_status_text */
1266 info->last_status = call->last_code;
1267
1268 info->last_status_text.ptr = info->buf_.last_status_text;
1269 pj_strncpy(&info->last_status_text, &call->last_text,
1270 sizeof(info->buf_.last_status_text));
1271 }
1272
1273 /* media status and dir */
1274 info->media_status = call->media_st;
1275 info->media_dir = call->media_dir;
1276
1277
1278 /* conference slot number */
1279 info->conf_slot = call->conf_slot;
1280
1281 /* calculate duration */
1282 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1283
1284 info->total_duration = call->dis_time;
1285 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1286
1287 if (call->conn_time.sec) {
1288 info->connect_duration = call->dis_time;
1289 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1290 }
1291
1292 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1293
1294 pj_gettimeofday(&info->total_duration);
1295 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1296
1297 pj_gettimeofday(&info->connect_duration);
1298 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1299
1300 } else {
1301 pj_gettimeofday(&info->total_duration);
1302 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1303 }
1304
Benny Prijonodc752ca2006-09-22 16:55:42 +00001305 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001306
1307 return PJ_SUCCESS;
1308}
1309
1310
1311/*
1312 * Attach application specific data to the call.
1313 */
1314PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1315 void *user_data)
1316{
1317 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1318 PJ_EINVAL);
1319 pjsua_var.calls[call_id].user_data = user_data;
1320
1321 return PJ_SUCCESS;
1322}
1323
1324
1325/*
1326 * Get user data attached to the call.
1327 */
1328PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1329{
1330 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1331 NULL);
1332 return pjsua_var.calls[call_id].user_data;
1333}
1334
1335
1336/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001337 * Get remote's NAT type.
1338 */
1339PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1340 pj_stun_nat_type *p_type)
1341{
1342 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1343 PJ_EINVAL);
1344 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1345
1346 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1347 return PJ_SUCCESS;
1348}
1349
1350
1351/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001352 * Send response to incoming INVITE request.
1353 */
1354PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1355 unsigned code,
1356 const pj_str_t *reason,
1357 const pjsua_msg_data *msg_data)
1358{
1359 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001360 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001361 pjsip_tx_data *tdata;
1362 pj_status_t status;
1363
1364 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1365 PJ_EINVAL);
1366
Benny Prijonodc752ca2006-09-22 16:55:42 +00001367 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001368 if (status != PJ_SUCCESS)
1369 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001370
Benny Prijono2e507c22006-06-23 15:04:11 +00001371 if (call->res_time.sec == 0)
1372 pj_gettimeofday(&call->res_time);
1373
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001374 if (reason && reason->slen == 0)
1375 reason = NULL;
1376
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001377 /* Create response message */
1378 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1379 if (status != PJ_SUCCESS) {
1380 pjsua_perror(THIS_FILE, "Error creating response",
1381 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001382 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001383 return status;
1384 }
1385
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001386 /* Call might have been disconnected if application is answering with
1387 * 200/OK and the media failed to start.
1388 */
1389 if (call->inv == NULL) {
1390 pjsip_dlg_dec_lock(dlg);
1391 return PJSIP_ESESSIONTERMINATED;
1392 }
1393
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001394 /* Add additional headers etc */
1395 pjsua_process_msg_data( tdata, msg_data);
1396
1397 /* Send the message */
1398 status = pjsip_inv_send_msg(call->inv, tdata);
1399 if (status != PJ_SUCCESS)
1400 pjsua_perror(THIS_FILE, "Error sending response",
1401 status);
1402
Benny Prijonodc752ca2006-09-22 16:55:42 +00001403 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001404
1405 return status;
1406}
1407
1408
1409/*
1410 * Hangup call by using method that is appropriate according to the
1411 * call state.
1412 */
1413PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1414 unsigned code,
1415 const pj_str_t *reason,
1416 const pjsua_msg_data *msg_data)
1417{
1418 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001419 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001420 pj_status_t status;
1421 pjsip_tx_data *tdata;
1422
1423
Benny Prijono148c9dd2006-09-19 13:37:53 +00001424 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1425 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1426 call_id));
1427 }
1428
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001429 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1430 PJ_EINVAL);
1431
Benny Prijonodc752ca2006-09-22 16:55:42 +00001432 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001433 if (status != PJ_SUCCESS)
1434 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001435
1436 if (code==0) {
1437 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1438 code = PJSIP_SC_OK;
1439 else if (call->inv->role == PJSIP_ROLE_UAS)
1440 code = PJSIP_SC_DECLINE;
1441 else
1442 code = PJSIP_SC_REQUEST_TERMINATED;
1443 }
1444
1445 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1446 if (status != PJ_SUCCESS) {
1447 pjsua_perror(THIS_FILE,
1448 "Failed to create end session message",
1449 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001450 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001451 return status;
1452 }
1453
1454 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1455 * as p_tdata when INVITE transaction has not been answered
1456 * with any provisional responses.
1457 */
1458 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001459 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001460 return PJ_SUCCESS;
1461 }
1462
1463 /* Add additional headers etc */
1464 pjsua_process_msg_data( tdata, msg_data);
1465
1466 /* Send the message */
1467 status = pjsip_inv_send_msg(call->inv, tdata);
1468 if (status != PJ_SUCCESS) {
1469 pjsua_perror(THIS_FILE,
1470 "Failed to send end session message",
1471 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001472 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001473 return status;
1474 }
1475
Benny Prijonodc752ca2006-09-22 16:55:42 +00001476 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001477
1478 return PJ_SUCCESS;
1479}
1480
1481
1482/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001483 * Accept or reject redirection.
1484 */
1485PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1486 pjsip_redirect_op cmd)
1487{
1488 pjsua_call *call;
1489 pjsip_dialog *dlg;
1490 pj_status_t status;
1491
1492 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1493 PJ_EINVAL);
1494
1495 status = acquire_call("pjsua_call_process_redirect()", call_id,
1496 &call, &dlg);
1497 if (status != PJ_SUCCESS)
1498 return status;
1499
1500 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1501
1502 pjsip_dlg_dec_lock(dlg);
1503
1504 return status;
1505}
1506
1507
1508/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001509 * Put the specified call on hold.
1510 */
1511PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1512 const pjsua_msg_data *msg_data)
1513{
1514 pjmedia_sdp_session *sdp;
1515 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001516 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001517 pjsip_tx_data *tdata;
1518 pj_status_t status;
1519
1520 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1521 PJ_EINVAL);
1522
Benny Prijonodc752ca2006-09-22 16:55:42 +00001523 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001524 if (status != PJ_SUCCESS)
1525 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001526
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001527
1528 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1529 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001530 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001531 return PJSIP_ESESSIONSTATE;
1532 }
1533
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001534 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001535 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001536 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001537 return status;
1538 }
1539
1540 /* Create re-INVITE with new offer */
1541 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1542 if (status != PJ_SUCCESS) {
1543 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001544 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001545 return status;
1546 }
1547
1548 /* Add additional headers etc */
1549 pjsua_process_msg_data( tdata, msg_data);
1550
1551 /* Send the request */
1552 status = pjsip_inv_send_msg( call->inv, tdata);
1553 if (status != PJ_SUCCESS) {
1554 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001555 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001556 return status;
1557 }
1558
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001559 /* Set flag that local put the call on hold */
1560 call->local_hold = PJ_TRUE;
1561
Benny Prijonodc752ca2006-09-22 16:55:42 +00001562 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001563
1564 return PJ_SUCCESS;
1565}
1566
1567
1568/*
1569 * Send re-INVITE (to release hold).
1570 */
1571PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1572 pj_bool_t unhold,
1573 const pjsua_msg_data *msg_data)
1574{
1575 pjmedia_sdp_session *sdp;
1576 pjsip_tx_data *tdata;
1577 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001578 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001579 pj_status_t status;
1580
1581
1582 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1583 PJ_EINVAL);
1584
Benny Prijonodc752ca2006-09-22 16:55:42 +00001585 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001586 if (status != PJ_SUCCESS)
1587 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001588
1589 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1590 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001591 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001592 return PJSIP_ESESSIONSTATE;
1593 }
1594
1595 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001596 if (call->local_hold && !unhold) {
1597 status = create_sdp_of_call_hold(call, &sdp);
1598 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001599 status = pjsua_media_channel_create_sdp(call->index,
1600 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001601 NULL, &sdp, NULL);
1602 call->local_hold = PJ_FALSE;
1603 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001604 if (status != PJ_SUCCESS) {
1605 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1606 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001607 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001608 return status;
1609 }
1610
1611 /* Create re-INVITE with new offer */
1612 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1613 if (status != PJ_SUCCESS) {
1614 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001615 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001616 return status;
1617 }
1618
1619 /* Add additional headers etc */
1620 pjsua_process_msg_data( tdata, msg_data);
1621
1622 /* Send the request */
1623 status = pjsip_inv_send_msg( call->inv, tdata);
1624 if (status != PJ_SUCCESS) {
1625 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001626 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001627 return status;
1628 }
1629
Benny Prijonodc752ca2006-09-22 16:55:42 +00001630 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001631
1632 return PJ_SUCCESS;
1633}
1634
1635
1636/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001637 * Send UPDATE request.
1638 */
1639PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1640 unsigned options,
1641 const pjsua_msg_data *msg_data)
1642{
1643 pjmedia_sdp_session *sdp;
1644 pjsip_tx_data *tdata;
1645 pjsua_call *call;
1646 pjsip_dialog *dlg;
1647 pj_status_t status;
1648
1649 PJ_UNUSED_ARG(options);
1650
1651 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1652 PJ_EINVAL);
1653
1654 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1655 if (status != PJ_SUCCESS)
1656 return status;
1657
Benny Prijonoc08682e2007-10-04 06:17:58 +00001658 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001659 status = pjsua_media_channel_create_sdp(call->index,
1660 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001661 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001662 if (status != PJ_SUCCESS) {
1663 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1664 status);
1665 pjsip_dlg_dec_lock(dlg);
1666 return status;
1667 }
1668
Benny Prijono224b4e22008-06-19 14:10:28 +00001669 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001670 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1671 if (status != PJ_SUCCESS) {
1672 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1673 pjsip_dlg_dec_lock(dlg);
1674 return status;
1675 }
1676
1677 /* Add additional headers etc */
1678 pjsua_process_msg_data( tdata, msg_data);
1679
1680 /* Send the request */
1681 status = pjsip_inv_send_msg( call->inv, tdata);
1682 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001683 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001684 pjsip_dlg_dec_lock(dlg);
1685 return status;
1686 }
1687
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001688 call->local_hold = PJ_FALSE;
1689
Benny Prijonoc08682e2007-10-04 06:17:58 +00001690 pjsip_dlg_dec_lock(dlg);
1691
1692 return PJ_SUCCESS;
1693}
1694
1695
1696/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001697 * Initiate call transfer to the specified address.
1698 */
1699PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1700 const pj_str_t *dest,
1701 const pjsua_msg_data *msg_data)
1702{
1703 pjsip_evsub *sub;
1704 pjsip_tx_data *tdata;
1705 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001706 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001707 pjsip_generic_string_hdr *gs_hdr;
1708 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001709 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001710 pj_status_t status;
1711
1712
1713 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1714 PJ_EINVAL);
1715
Benny Prijonodc752ca2006-09-22 16:55:42 +00001716 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001717 if (status != PJ_SUCCESS)
1718 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001719
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001720
Benny Prijonod524e822006-09-22 12:48:18 +00001721 /* Create xfer client subscription. */
1722 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001723 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001724
1725 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001726 if (status != PJ_SUCCESS) {
1727 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001728 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001729 return status;
1730 }
1731
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001732 /* Associate this call with the client subscription */
1733 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1734
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001735 /*
1736 * Create REFER request.
1737 */
1738 status = pjsip_xfer_initiate(sub, dest, &tdata);
1739 if (status != PJ_SUCCESS) {
1740 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001741 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001742 return status;
1743 }
1744
Benny Prijono053f5222006-11-11 16:16:04 +00001745 /* Add Referred-By header */
1746 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1747 &dlg->local.info_str);
1748 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1749
1750
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001751 /* Add additional headers etc */
1752 pjsua_process_msg_data( tdata, msg_data);
1753
1754 /* Send. */
1755 status = pjsip_xfer_send_request(sub, tdata);
1756 if (status != PJ_SUCCESS) {
1757 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001758 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001759 return status;
1760 }
1761
1762 /* For simplicity (that's what this program is intended to be!),
1763 * leave the original invite session as it is. More advanced application
1764 * may want to hold the INVITE, or terminate the invite, or whatever.
1765 */
1766
Benny Prijonodc752ca2006-09-22 16:55:42 +00001767 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001768
1769 return PJ_SUCCESS;
1770
1771}
1772
1773
1774/*
Benny Prijono053f5222006-11-11 16:16:04 +00001775 * Initiate attended call transfer to the specified address.
1776 */
1777PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1778 pjsua_call_id dest_call_id,
1779 unsigned options,
1780 const pjsua_msg_data *msg_data)
1781{
1782 pjsua_call *dest_call;
1783 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001784 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001785 pj_str_t str_dest;
1786 int len;
1787 pjsip_uri *uri;
1788 pj_status_t status;
1789
1790
1791 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1792 PJ_EINVAL);
1793 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1794 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1795 PJ_EINVAL);
1796
1797 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1798 &dest_call, &dest_dlg);
1799 if (status != PJ_SUCCESS)
1800 return status;
1801
1802 /*
1803 * Create REFER destination URI with Replaces field.
1804 */
1805
1806 /* Make sure we have sufficient buffer's length */
1807 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1808 dest_dlg->call_id->id.slen +
1809 dest_dlg->remote.info->tag.slen +
1810 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001811 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001812
1813 /* Print URI */
1814 str_dest_buf[0] = '<';
1815 str_dest.slen = 1;
1816
Benny Prijonoa1e69682007-05-11 15:14:34 +00001817 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001818 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1819 str_dest_buf+1, sizeof(str_dest_buf)-1);
1820 if (len < 0)
1821 return PJSIP_EURITOOLONG;
1822
1823 str_dest.slen += len;
1824
1825
1826 /* Build the URI */
1827 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1828 sizeof(str_dest_buf) - str_dest.slen,
1829 "?%s"
1830 "Replaces=%.*s"
1831 "%%3Bto-tag%%3D%.*s"
1832 "%%3Bfrom-tag%%3D%.*s>",
1833 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1834 "" : "Require=replaces&"),
1835 (int)dest_dlg->call_id->id.slen,
1836 dest_dlg->call_id->id.ptr,
1837 (int)dest_dlg->remote.info->tag.slen,
1838 dest_dlg->remote.info->tag.ptr,
1839 (int)dest_dlg->local.info->tag.slen,
1840 dest_dlg->local.info->tag.ptr);
1841
1842 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1843 PJSIP_EURITOOLONG);
1844
1845 str_dest.ptr = str_dest_buf;
1846 str_dest.slen += len;
1847
1848 pjsip_dlg_dec_lock(dest_dlg);
1849
1850 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1851}
1852
1853
1854/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001855 * Send DTMF digits to remote using RFC 2833 payload formats.
1856 */
1857PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1858 const pj_str_t *digits)
1859{
1860 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001861 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001862 pj_status_t status;
1863
1864 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1865 PJ_EINVAL);
1866
Benny Prijonodc752ca2006-09-22 16:55:42 +00001867 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001868 if (status != PJ_SUCCESS)
1869 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001870
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001871 if (!call->session) {
1872 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001873 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001874 return PJ_EINVALIDOP;
1875 }
1876
1877 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1878
Benny Prijonodc752ca2006-09-22 16:55:42 +00001879 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001880
1881 return status;
1882}
1883
1884
1885/**
1886 * Send instant messaging inside INVITE session.
1887 */
1888PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1889 const pj_str_t *mime_type,
1890 const pj_str_t *content,
1891 const pjsua_msg_data *msg_data,
1892 void *user_data)
1893{
1894 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001895 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001896 const pj_str_t mime_text_plain = pj_str("text/plain");
1897 pjsip_media_type ctype;
1898 pjsua_im_data *im_data;
1899 pjsip_tx_data *tdata;
1900 pj_status_t status;
1901
1902
1903 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1904 PJ_EINVAL);
1905
Benny Prijonodc752ca2006-09-22 16:55:42 +00001906 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001907 if (status != PJ_SUCCESS)
1908 return status;
1909
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001910 /* Set default media type if none is specified */
1911 if (mime_type == NULL) {
1912 mime_type = &mime_text_plain;
1913 }
1914
1915 /* Create request message. */
1916 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1917 -1, &tdata);
1918 if (status != PJ_SUCCESS) {
1919 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1920 goto on_return;
1921 }
1922
1923 /* Add accept header. */
1924 pjsip_msg_add_hdr( tdata->msg,
1925 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1926
1927 /* Parse MIME type */
1928 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1929
1930 /* Create "text/plain" message body. */
1931 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1932 &ctype.subtype, content);
1933 if (tdata->msg->body == NULL) {
1934 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1935 pjsip_tx_data_dec_ref(tdata);
1936 goto on_return;
1937 }
1938
1939 /* Add additional headers etc */
1940 pjsua_process_msg_data( tdata, msg_data);
1941
1942 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001943 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001944 im_data->acc_id = call->acc_id;
1945 im_data->call_id = call_id;
1946 im_data->to = call->inv->dlg->remote.info_str;
1947 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1948 im_data->user_data = user_data;
1949
1950
1951 /* Send the request. */
1952 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1953 pjsua_var.mod.id, im_data);
1954 if (status != PJ_SUCCESS) {
1955 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1956 goto on_return;
1957 }
1958
1959on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001960 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001961 return status;
1962}
1963
1964
1965/*
1966 * Send IM typing indication inside INVITE session.
1967 */
1968PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1969 pj_bool_t is_typing,
1970 const pjsua_msg_data*msg_data)
1971{
1972 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001973 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001974 pjsip_tx_data *tdata;
1975 pj_status_t status;
1976
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001977 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1978 PJ_EINVAL);
1979
Benny Prijonodc752ca2006-09-22 16:55:42 +00001980 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001981 if (status != PJ_SUCCESS)
1982 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001983
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001984 /* Create request message. */
1985 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1986 -1, &tdata);
1987 if (status != PJ_SUCCESS) {
1988 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1989 goto on_return;
1990 }
1991
1992 /* Create "application/im-iscomposing+xml" msg body. */
1993 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
1994 NULL, NULL, -1);
1995
1996 /* Add additional headers etc */
1997 pjsua_process_msg_data( tdata, msg_data);
1998
1999 /* Send the request. */
2000 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2001 if (status != PJ_SUCCESS) {
2002 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2003 goto on_return;
2004 }
2005
2006on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002007 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002008 return status;
2009}
2010
2011
2012/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002013 * Send arbitrary request.
2014 */
2015PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2016 const pj_str_t *method_str,
2017 const pjsua_msg_data *msg_data)
2018{
2019 pjsua_call *call;
2020 pjsip_dialog *dlg;
2021 pjsip_method method;
2022 pjsip_tx_data *tdata;
2023 pj_status_t status;
2024
2025 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2026 PJ_EINVAL);
2027
2028 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2029 if (status != PJ_SUCCESS)
2030 return status;
2031
2032 /* Init method */
2033 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2034
2035 /* Create request message. */
2036 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2037 if (status != PJ_SUCCESS) {
2038 pjsua_perror(THIS_FILE, "Unable to create request", status);
2039 goto on_return;
2040 }
2041
2042 /* Add additional headers etc */
2043 pjsua_process_msg_data( tdata, msg_data);
2044
2045 /* Send the request. */
2046 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2047 if (status != PJ_SUCCESS) {
2048 pjsua_perror(THIS_FILE, "Unable to send request", status);
2049 goto on_return;
2050 }
2051
2052on_return:
2053 pjsip_dlg_dec_lock(dlg);
2054 return status;
2055}
2056
2057
2058/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002059 * Terminate all calls.
2060 */
2061PJ_DEF(void) pjsua_call_hangup_all(void)
2062{
2063 unsigned i;
2064
2065 PJSUA_LOCK();
2066
2067 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2068 if (pjsua_var.calls[i].inv)
2069 pjsua_call_hangup(i, 0, NULL, NULL);
2070 }
2071
2072 PJSUA_UNLOCK();
2073}
2074
2075
Benny Prijono627cbb42007-09-25 20:48:49 +00002076const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002077{
2078 if (val < 1000) {
2079 pj_ansi_sprintf(buf, "%d", val);
2080 } else if (val < 1000000) {
2081 pj_ansi_sprintf(buf, "%d.%dK",
2082 val / 1000,
2083 (val % 1000) / 100);
2084 } else {
2085 pj_ansi_sprintf(buf, "%d.%02dM",
2086 val / 1000000,
2087 (val % 1000000) / 10000);
2088 }
2089
2090 return buf;
2091}
2092
2093
2094/* Dump media session */
2095static void dump_media_session(const char *indent,
2096 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002097 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002098{
2099 unsigned i;
2100 char *p = buf, *end = buf+maxlen;
2101 int len;
2102 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002103 pjmedia_session *session = call->session;
2104 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002105
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002106 pjmedia_transport_info_init(&tp_info);
2107
2108 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002109 pjmedia_session_get_info(session, &info);
2110
2111 for (i=0; i<info.stream_cnt; ++i) {
2112 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002113 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002114 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002115 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002116 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002117 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002118 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002119
2120 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002121 // rem_addr will contain actual address of RTP originator, instead of
2122 // remote RTP address specified by stream which is fetched from the SDP.
2123 // Please note that we are assuming only one stream per call.
2124 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2125 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002126 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2127 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002128 sizeof(rem_addr_buf), 3);
2129 } else {
2130 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002131 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002132 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002133
2134 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
2135 dir = "sendonly";
2136 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2137 dir = "recvonly";
2138 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2139 dir = "sendrecv";
2140 else
2141 dir = "inactive";
2142
2143
2144 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002145 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002146 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002147 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002148 info.stream_info[i].fmt.encoding_name.ptr,
2149 info.stream_info[i].fmt.clock_rate / 1000,
2150 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002151 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002152 if (len < 1 || len > end-p) {
2153 *p = '\0';
2154 return;
2155 }
2156
2157 p += len;
2158 *p++ = '\n';
2159 *p = '\0';
2160
2161 if (stat.rx.update_cnt == 0)
2162 strcpy(last_update, "never");
2163 else {
2164 pj_gettimeofday(&now);
2165 PJ_TIME_VAL_SUB(now, stat.rx.update);
2166 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2167 now.sec / 3600,
2168 (now.sec % 3600) / 60,
2169 now.sec % 60,
2170 now.msec);
2171 }
2172
Benny Prijono80019eb2006-08-07 13:22:23 +00002173 pj_gettimeofday(&media_duration);
2174 PJ_TIME_VAL_SUB(media_duration, stat.start);
2175 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2176 media_duration.msec = 1;
2177
Benny Prijono1402a4a2008-01-08 23:41:22 +00002178 /* protect against division by zero */
2179 if (stat.rx.pkt == 0)
2180 stat.rx.pkt = 1;
2181 if (stat.tx.pkt == 0)
2182 stat.tx.pkt = 1;
2183
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002184 len = pj_ansi_snprintf(p, end-p,
2185 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002186 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002187 "%s pkt loss=%d (%3.1f%%), discrd=%d (%3.1f%%), dup=%d (%2.1f%%), reord=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002188 "%s (msec) min avg max last dev\n"
2189 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2190 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002191 indent, info.stream_info[i].fmt.pt,
2192 last_update,
2193 indent,
2194 good_number(packets, stat.rx.pkt),
2195 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002196 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002197 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2198 good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.rx.bytes + stat.rx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002199 indent,
2200 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002201 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002202 stat.rx.discard,
2203 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002204 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002205 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002206 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002207 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002208 indent, indent,
2209 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002210 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002211 stat.rx.loss_period.max / 1000.0,
2212 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002213 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002214 indent,
2215 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002216 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002217 stat.rx.jitter.max / 1000.0,
2218 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002219 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002220 ""
2221 );
2222
2223 if (len < 1 || len > end-p) {
2224 *p = '\0';
2225 return;
2226 }
2227
2228 p += len;
2229 *p++ = '\n';
2230 *p = '\0';
2231
2232 if (stat.tx.update_cnt == 0)
2233 strcpy(last_update, "never");
2234 else {
2235 pj_gettimeofday(&now);
2236 PJ_TIME_VAL_SUB(now, stat.tx.update);
2237 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2238 now.sec / 3600,
2239 (now.sec % 3600) / 60,
2240 now.sec % 60,
2241 now.msec);
2242 }
2243
2244 len = pj_ansi_snprintf(p, end-p,
2245 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002246 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002247 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002248 "%s (msec) min avg max last dev \n"
2249 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2250 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002251 indent,
2252 info.stream_info[i].tx_pt,
2253 info.stream_info[i].param->info.frm_ptime *
2254 info.stream_info[i].param->setting.frm_per_pkt,
2255 last_update,
2256
2257 indent,
2258 good_number(packets, stat.tx.pkt),
2259 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002260 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002261 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2262 good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.tx.bytes + stat.tx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002263
2264 indent,
2265 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002266 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002267 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002268 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002269 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002270 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002271
2272 indent, indent,
2273 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002274 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002275 stat.tx.loss_period.max / 1000.0,
2276 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002277 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002278 indent,
2279 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002280 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002281 stat.tx.jitter.max / 1000.0,
2282 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002283 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002284 ""
2285 );
2286
2287 if (len < 1 || len > end-p) {
2288 *p = '\0';
2289 return;
2290 }
2291
2292 p += len;
2293 *p++ = '\n';
2294 *p = '\0';
2295
2296 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002297 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002298 indent,
2299 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002300 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002301 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002302 stat.rtt.last / 1000.0,
2303 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002304 );
2305 if (len < 1 || len > end-p) {
2306 *p = '\0';
2307 return;
2308 }
2309
2310 p += len;
2311 *p++ = '\n';
2312 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002313
2314#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2315# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2316 do { \
2317 if (samples <= 4294) \
2318 usec = samples * 1000000 / clock_rate; \
2319 else { \
2320 usec = samples * 1000 / clock_rate; \
2321 usec *= 1000; \
2322 } \
2323 } while(0)
2324
2325# define PRINT_VOIP_MTC_VAL(s, v) \
2326 if (v == 127) \
2327 sprintf(s, "(na)"); \
2328 else \
2329 sprintf(s, "%d", v)
2330
2331# define VALIDATE_PRINT_BUF() \
2332 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2333 p += len; *p++ = '\n'; *p = '\0'
2334
2335
2336 do {
2337 char loss[16], dup[16];
2338 char jitter[80];
2339 char toh[80];
2340 char plc[16], jba[16], jbr[16];
2341 char signal_lvl[16], noise_lvl[16], rerl[16];
2342 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2343 pjmedia_rtcp_xr_stat xr_stat;
2344 unsigned clock_rate;
2345
2346 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2347 PJ_SUCCESS)
2348 {
2349 break;
2350 }
2351
2352 clock_rate = info.stream_info[i].fmt.clock_rate;
2353
2354 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2355 VALIDATE_PRINT_BUF();
2356
2357 /* Statistics Summary */
2358 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2359 VALIDATE_PRINT_BUF();
2360
2361 if (xr_stat.rx.stat_sum.l)
2362 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2363 else
2364 sprintf(loss, "(na)");
2365
2366 if (xr_stat.rx.stat_sum.d)
2367 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2368 else
2369 sprintf(dup, "(na)");
2370
2371 if (xr_stat.rx.stat_sum.j) {
2372 unsigned jmin, jmax, jmean, jdev;
2373
2374 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2375 clock_rate);
2376 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2377 clock_rate);
2378 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2379 clock_rate);
2380 SAMPLES_TO_USEC(jdev,
2381 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2382 clock_rate);
2383 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2384 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2385 } else
2386 sprintf(jitter, "(report not available)");
2387
2388 if (xr_stat.rx.stat_sum.t) {
2389 sprintf(toh, "%11d %11d %11d %11d",
2390 xr_stat.rx.stat_sum.toh.min,
2391 xr_stat.rx.stat_sum.toh.mean,
2392 xr_stat.rx.stat_sum.toh.max,
2393 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2394 } else
2395 sprintf(toh, "(report not available)");
2396
2397 if (xr_stat.rx.stat_sum.update.sec == 0)
2398 strcpy(last_update, "never");
2399 else {
2400 pj_gettimeofday(&now);
2401 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2402 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2403 now.sec / 3600,
2404 (now.sec % 3600) / 60,
2405 now.sec % 60,
2406 now.msec);
2407 }
2408
2409 len = pj_ansi_snprintf(p, end-p,
2410 "%s RX last update: %s\n"
2411 "%s begin seq=%d, end seq=%d\n"
2412 "%s pkt loss=%s, dup=%s\n"
2413 "%s (msec) min avg max dev\n"
2414 "%s jitter : %s\n"
2415 "%s toh : %s",
2416 indent, last_update,
2417 indent,
2418 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2419 indent, loss, dup,
2420 indent,
2421 indent, jitter,
2422 indent, toh
2423 );
2424 VALIDATE_PRINT_BUF();
2425
2426 if (xr_stat.tx.stat_sum.l)
2427 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2428 else
2429 sprintf(loss, "(na)");
2430
2431 if (xr_stat.tx.stat_sum.d)
2432 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2433 else
2434 sprintf(dup, "(na)");
2435
2436 if (xr_stat.tx.stat_sum.j) {
2437 unsigned jmin, jmax, jmean, jdev;
2438
2439 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2440 clock_rate);
2441 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2442 clock_rate);
2443 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2444 clock_rate);
2445 SAMPLES_TO_USEC(jdev,
2446 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2447 clock_rate);
2448 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2449 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2450 } else
2451 sprintf(jitter, "(report not available)");
2452
2453 if (xr_stat.tx.stat_sum.t) {
2454 sprintf(toh, "%11d %11d %11d %11d",
2455 xr_stat.tx.stat_sum.toh.min,
2456 xr_stat.tx.stat_sum.toh.mean,
2457 xr_stat.tx.stat_sum.toh.max,
2458 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2459 } else
2460 sprintf(toh, "(report not available)");
2461
2462 if (xr_stat.tx.stat_sum.update.sec == 0)
2463 strcpy(last_update, "never");
2464 else {
2465 pj_gettimeofday(&now);
2466 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2467 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2468 now.sec / 3600,
2469 (now.sec % 3600) / 60,
2470 now.sec % 60,
2471 now.msec);
2472 }
2473
2474 len = pj_ansi_snprintf(p, end-p,
2475 "%s TX last update: %s\n"
2476 "%s begin seq=%d, end seq=%d\n"
2477 "%s pkt loss=%s, dup=%s\n"
2478 "%s (msec) min avg max dev\n"
2479 "%s jitter : %s\n"
2480 "%s toh : %s",
2481 indent, last_update,
2482 indent,
2483 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2484 indent, loss, dup,
2485 indent,
2486 indent, jitter,
2487 indent, toh
2488 );
2489 VALIDATE_PRINT_BUF();
2490
2491
2492 /* VoIP Metrics */
2493 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2494 VALIDATE_PRINT_BUF();
2495
2496 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2497 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2498 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2499 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2500 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2501 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2502 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2503
2504 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2505 case PJMEDIA_RTCP_XR_PLC_DIS:
2506 sprintf(plc, "DISABLED");
2507 break;
2508 case PJMEDIA_RTCP_XR_PLC_ENH:
2509 sprintf(plc, "ENHANCED");
2510 break;
2511 case PJMEDIA_RTCP_XR_PLC_STD:
2512 sprintf(plc, "STANDARD");
2513 break;
2514 case PJMEDIA_RTCP_XR_PLC_UNK:
2515 default:
2516 sprintf(plc, "UNKNOWN");
2517 break;
2518 }
2519
2520 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2521 case PJMEDIA_RTCP_XR_JB_FIXED:
2522 sprintf(jba, "FIXED");
2523 break;
2524 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2525 sprintf(jba, "ADAPTIVE");
2526 break;
2527 default:
2528 sprintf(jba, "UNKNOWN");
2529 break;
2530 }
2531
2532 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2533
2534 if (xr_stat.rx.voip_mtc.update.sec == 0)
2535 strcpy(last_update, "never");
2536 else {
2537 pj_gettimeofday(&now);
2538 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2539 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2540 now.sec / 3600,
2541 (now.sec % 3600) / 60,
2542 now.sec % 60,
2543 now.msec);
2544 }
2545
2546 len = pj_ansi_snprintf(p, end-p,
2547 "%s RX last update: %s\n"
2548 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2549 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2550 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2551 "%s delay : round trip=%d%s, end system=%d%s\n"
2552 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2553 "%s quality : R factor=%s, ext R factor=%s\n"
2554 "%s MOS LQ=%s, MOS CQ=%s\n"
2555 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2556 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2557 indent,
2558 last_update,
2559 /* packets */
2560 indent,
2561 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2562 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2563 /* burst */
2564 indent,
2565 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2566 xr_stat.rx.voip_mtc.burst_dur, "ms",
2567 /* gap */
2568 indent,
2569 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2570 xr_stat.rx.voip_mtc.gap_dur, "ms",
2571 /* delay */
2572 indent,
2573 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2574 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2575 /* level */
2576 indent,
2577 signal_lvl, "dB",
2578 noise_lvl, "dB",
2579 rerl, "",
2580 /* quality */
2581 indent,
2582 r_factor, ext_r_factor,
2583 indent,
2584 mos_lq, mos_cq,
2585 /* config */
2586 indent,
2587 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2588 /* JB delay */
2589 indent,
2590 xr_stat.rx.voip_mtc.jb_nom, "ms",
2591 xr_stat.rx.voip_mtc.jb_max, "ms",
2592 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2593 );
2594 VALIDATE_PRINT_BUF();
2595
2596 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2597 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2598 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2599 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2600 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2601 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2602 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2603
2604 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2605 case PJMEDIA_RTCP_XR_PLC_DIS:
2606 sprintf(plc, "DISABLED");
2607 break;
2608 case PJMEDIA_RTCP_XR_PLC_ENH:
2609 sprintf(plc, "ENHANCED");
2610 break;
2611 case PJMEDIA_RTCP_XR_PLC_STD:
2612 sprintf(plc, "STANDARD");
2613 break;
2614 case PJMEDIA_RTCP_XR_PLC_UNK:
2615 default:
2616 sprintf(plc, "unknown");
2617 break;
2618 }
2619
2620 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2621 case PJMEDIA_RTCP_XR_JB_FIXED:
2622 sprintf(jba, "FIXED");
2623 break;
2624 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2625 sprintf(jba, "ADAPTIVE");
2626 break;
2627 default:
2628 sprintf(jba, "unknown");
2629 break;
2630 }
2631
2632 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2633
2634 if (xr_stat.tx.voip_mtc.update.sec == 0)
2635 strcpy(last_update, "never");
2636 else {
2637 pj_gettimeofday(&now);
2638 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2639 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2640 now.sec / 3600,
2641 (now.sec % 3600) / 60,
2642 now.sec % 60,
2643 now.msec);
2644 }
2645
2646 len = pj_ansi_snprintf(p, end-p,
2647 "%s TX last update: %s\n"
2648 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2649 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2650 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2651 "%s delay : round trip=%d%s, end system=%d%s\n"
2652 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2653 "%s quality : R factor=%s, ext R factor=%s\n"
2654 "%s MOS LQ=%s, MOS CQ=%s\n"
2655 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2656 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2657 indent,
2658 last_update,
2659 /* pakcets */
2660 indent,
2661 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2662 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2663 /* burst */
2664 indent,
2665 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2666 xr_stat.tx.voip_mtc.burst_dur, "ms",
2667 /* gap */
2668 indent,
2669 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2670 xr_stat.tx.voip_mtc.gap_dur, "ms",
2671 /* delay */
2672 indent,
2673 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2674 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2675 /* level */
2676 indent,
2677 signal_lvl, "dB",
2678 noise_lvl, "dB",
2679 rerl, "",
2680 /* quality */
2681 indent,
2682 r_factor, ext_r_factor,
2683 indent,
2684 mos_lq, mos_cq,
2685 /* config */
2686 indent,
2687 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2688 /* JB delay */
2689 indent,
2690 xr_stat.tx.voip_mtc.jb_nom, "ms",
2691 xr_stat.tx.voip_mtc.jb_max, "ms",
2692 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2693 );
2694 VALIDATE_PRINT_BUF();
2695
2696
2697 /* RTT delay (by receiver side) */
2698 len = pj_ansi_snprintf(p, end-p,
2699 "%s RTT (from recv) min avg max last dev",
2700 indent);
2701 VALIDATE_PRINT_BUF();
2702 len = pj_ansi_snprintf(p, end-p,
2703 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2704 indent,
2705 xr_stat.rtt.min / 1000.0,
2706 xr_stat.rtt.mean / 1000.0,
2707 xr_stat.rtt.max / 1000.0,
2708 xr_stat.rtt.last / 1000.0,
2709 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2710 );
2711 VALIDATE_PRINT_BUF();
2712 } while(0);
2713#endif
2714
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002715 }
2716}
2717
2718
2719/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002720void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002721 int call_id,
2722 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002723{
2724 int len;
2725 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2726 pjsip_dialog *dlg = inv->dlg;
2727 char userinfo[128];
2728
2729 /* Dump invite sesion info. */
2730
2731 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
Benny Prijono329d6382009-05-29 13:04:03 +00002732 if (len < 0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002733 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2734 else
2735 userinfo[len] = '\0';
2736
2737 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2738 title,
2739 pjsip_inv_state_name(inv->state),
2740 userinfo);
2741 if (len < 1 || len >= (int)size) {
2742 pj_ansi_strcpy(buf, "<--uri too long-->");
2743 len = 18;
2744 } else
2745 buf[len] = '\0';
2746}
2747
2748
2749/*
2750 * Dump call and media statistics to string.
2751 */
2752PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2753 pj_bool_t with_media,
2754 char *buffer,
2755 unsigned maxlen,
2756 const char *indent)
2757{
2758 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002759 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002760 pj_time_val duration, res_delay, con_delay;
2761 char tmp[128];
2762 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002763 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002764 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002765 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002766
2767 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2768 PJ_EINVAL);
2769
Benny Prijonodc752ca2006-09-22 16:55:42 +00002770 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002771 if (status != PJ_SUCCESS)
2772 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002773
2774 *buffer = '\0';
2775 p = buffer;
2776 end = buffer + maxlen;
2777 len = 0;
2778
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002779 print_call(indent, call_id, tmp, sizeof(tmp));
2780
2781 len = pj_ansi_strlen(tmp);
2782 pj_ansi_strcpy(buffer, tmp);
2783
2784 p += len;
2785 *p++ = '\r';
2786 *p++ = '\n';
2787
2788 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002789 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002790 pj_gettimeofday(&duration);
2791 PJ_TIME_VAL_SUB(duration, call->conn_time);
2792 con_delay = call->conn_time;
2793 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2794 } else {
2795 duration.sec = duration.msec = 0;
2796 con_delay.sec = con_delay.msec = 0;
2797 }
2798
2799 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002800 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002801 res_delay = call->res_time;
2802 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2803 } else {
2804 res_delay.sec = res_delay.msec = 0;
2805 }
2806
2807 /* Print duration */
2808 len = pj_ansi_snprintf(p, end-p,
2809 "%s Call time: %02dh:%02dm:%02ds, "
2810 "1st res in %d ms, conn in %dms",
2811 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002812 (int)(duration.sec / 3600),
2813 (int)((duration.sec % 3600)/60),
2814 (int)(duration.sec % 60),
2815 (int)PJ_TIME_VAL_MSEC(res_delay),
2816 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002817
2818 if (len > 0 && len < end-p) {
2819 p += len;
2820 *p++ = '\n';
2821 *p = '\0';
2822 }
2823
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002824 /* Get SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002825 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002826 pjmedia_transport_get_info(call->med_tp, &tp_info);
2827 if (tp_info.specific_info_cnt > 0) {
2828 int i;
2829 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2830 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2831 {
2832 pjmedia_srtp_info *srtp_info =
2833 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2834
2835 len = pj_ansi_snprintf(p, end-p,
2836 "%s SRTP status: %s Crypto-suite: %s",
2837 indent,
2838 (srtp_info->active?"Active":"Not active"),
2839 srtp_info->tx_policy.name.ptr);
2840 if (len > 0 && len < end-p) {
2841 p += len;
2842 *p++ = '\n';
2843 *p = '\0';
2844 }
2845 break;
2846 }
2847 }
2848 }
2849
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002850 /* Dump session statistics */
2851 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002852 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002853
Benny Prijonodc752ca2006-09-22 16:55:42 +00002854 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002855
2856 return PJ_SUCCESS;
2857}
2858
2859
2860/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002861 * This callback receives notification from invite session when the
2862 * session state has changed.
2863 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002864static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2865 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002866{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002867 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002868
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002869 PJSUA_LOCK();
2870
Benny Prijonoa1e69682007-05-11 15:14:34 +00002871 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002872
2873 if (!call) {
2874 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002875 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002876 }
2877
Benny Prijonoe21e7842006-04-09 16:46:05 +00002878
2879 /* Get call times */
2880 switch (inv->state) {
2881 case PJSIP_INV_STATE_EARLY:
2882 case PJSIP_INV_STATE_CONNECTING:
2883 if (call->res_time.sec == 0)
2884 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002885 call->last_code = (pjsip_status_code)
2886 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002887 pj_strncpy(&call->last_text,
2888 &e->body.tsx_state.tsx->status_text,
2889 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002890 break;
2891 case PJSIP_INV_STATE_CONFIRMED:
2892 pj_gettimeofday(&call->conn_time);
2893 break;
2894 case PJSIP_INV_STATE_DISCONNECTED:
2895 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002896 if (call->res_time.sec == 0)
2897 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002898 if (e->type == PJSIP_EVENT_TSX_STATE &&
2899 e->body.tsx_state.tsx->status_code > call->last_code)
2900 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002901 call->last_code = (pjsip_status_code)
2902 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002903 pj_strncpy(&call->last_text,
2904 &e->body.tsx_state.tsx->status_text,
2905 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002906 } else {
2907 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
2908 pj_strncpy(&call->last_text,
2909 pjsip_get_status_text(call->last_code),
2910 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002911 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002912 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002913 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002914 call->last_code = (pjsip_status_code)
2915 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002916 pj_strncpy(&call->last_text,
2917 &e->body.tsx_state.tsx->status_text,
2918 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002919 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002920 }
2921
Benny Prijono26ff9062006-02-21 23:47:00 +00002922 /* If this is an outgoing INVITE that was created because of
2923 * REFER/transfer, send NOTIFY to transferer.
2924 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002925 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002926 int st_code = -1;
2927 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2928
2929
Benny Prijonoa91a0032006-02-26 21:23:45 +00002930 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002931 case PJSIP_INV_STATE_NULL:
2932 case PJSIP_INV_STATE_CALLING:
2933 /* Do nothing */
2934 break;
2935
2936 case PJSIP_INV_STATE_EARLY:
2937 case PJSIP_INV_STATE_CONNECTING:
2938 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002939 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
2940 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2941 else
2942 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002943 break;
2944
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002945#if 0
2946/* We don't need this, as we've terminated the subscription in
2947 * CONNECTING state.
2948 */
Benny Prijono26ff9062006-02-21 23:47:00 +00002949 case PJSIP_INV_STATE_CONFIRMED:
2950 /* When state is confirmed, send the final 200/OK and terminate
2951 * subscription.
2952 */
2953 st_code = e->body.tsx_state.tsx->status_code;
2954 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2955 break;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002956#endif
Benny Prijono26ff9062006-02-21 23:47:00 +00002957
2958 case PJSIP_INV_STATE_DISCONNECTED:
2959 st_code = e->body.tsx_state.tsx->status_code;
2960 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2961 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002962
Benny Prijono8b1889b2006-06-06 18:40:40 +00002963 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002964 /* Nothing to do. Just to keep gcc from complaining about
2965 * unused enums.
2966 */
2967 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002968 }
2969
2970 if (st_code != -1) {
2971 pjsip_tx_data *tdata;
2972 pj_status_t status;
2973
Benny Prijonoa91a0032006-02-26 21:23:45 +00002974 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002975 ev_state, st_code,
2976 NULL, &tdata);
2977 if (status != PJ_SUCCESS) {
2978 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2979 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002980 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002981 if (status != PJ_SUCCESS) {
2982 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2983 }
2984 }
2985 }
2986 }
2987
Benny Prijono84126ab2006-02-09 09:30:09 +00002988
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002989 if (pjsua_var.ua_cfg.cb.on_call_state)
2990 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002991
2992 /* call->inv may be NULL now */
2993
Benny Prijono84126ab2006-02-09 09:30:09 +00002994 /* Destroy media session when invite session is disconnected. */
2995 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00002996
Benny Prijonoa91a0032006-02-26 21:23:45 +00002997 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00002998
Benny Prijono275fd682006-03-22 11:59:11 +00002999 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003000 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003001
Benny Prijono105217f2006-03-06 16:25:59 +00003002 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003003 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003004 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003005
3006 /* Reset call */
3007 reset_call(call->index);
3008
Benny Prijono84126ab2006-02-09 09:30:09 +00003009 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003010
3011 PJSUA_UNLOCK();
3012}
3013
3014/*
3015 * This callback is called by invite session framework when UAC session
3016 * has forked.
3017 */
3018static void pjsua_call_on_forked( pjsip_inv_session *inv,
3019 pjsip_event *e)
3020{
3021 PJ_UNUSED_ARG(inv);
3022 PJ_UNUSED_ARG(e);
3023
3024 PJ_TODO(HANDLE_FORKED_DIALOG);
3025}
3026
3027
3028/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003029 * Callback from UA layer when forked dialog response is received.
3030 */
3031pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3032{
3033 if (dlg->uac_has_2xx &&
3034 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3035 pjsip_rdata_get_tsx(res) == NULL &&
3036 res->msg_info.msg->line.status.code/100 == 2)
3037 {
3038 pjsip_dialog *forked_dlg;
3039 pjsip_tx_data *bye;
3040 pj_status_t status;
3041
3042 /* Create forked dialog */
3043 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3044 if (status != PJ_SUCCESS)
3045 return NULL;
3046
3047 pjsip_dlg_inc_lock(forked_dlg);
3048
3049 /* Disconnect the call */
3050 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3051 -1, &bye);
3052 if (status == PJ_SUCCESS) {
3053 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3054 }
3055
3056 pjsip_dlg_dec_lock(forked_dlg);
3057
3058 if (status != PJ_SUCCESS) {
3059 return NULL;
3060 }
3061
3062 return forked_dlg;
3063
3064 } else {
3065 return dlg;
3066 }
3067}
3068
3069/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003070 * Disconnect call upon error.
3071 */
3072static void call_disconnect( pjsip_inv_session *inv,
3073 int code )
3074{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003075 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003076 pjsip_tx_data *tdata;
3077 pj_status_t status;
3078
Benny Prijono59b3aed2008-01-15 16:54:54 +00003079 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3080
Benny Prijonoa38ada02006-07-02 14:22:35 +00003081 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003082 if (status != PJ_SUCCESS)
3083 return;
3084
3085 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003086 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3087 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3088 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003089 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003090 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003091
Benny Prijono734fc2d2008-03-17 16:05:35 +00003092 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003093 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003094 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003095 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003096 if (status == PJ_SUCCESS) {
3097 pjsip_create_sdp_body(tdata->pool, local_sdp,
3098 &tdata->msg->body);
3099 }
3100 }
3101
3102 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003103}
3104
3105/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003106 * Callback to be called when SDP offer/answer negotiation has just completed
3107 * in the session. This function will start/update media if negotiation
3108 * has succeeded.
3109 */
3110static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3111 pj_status_t status)
3112{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003113 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003114 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003115 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003116
3117 PJSUA_LOCK();
3118
Benny Prijonoa1e69682007-05-11 15:14:34 +00003119 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003120
3121 if (status != PJ_SUCCESS) {
3122
3123 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3124
Benny Prijono2331d202008-06-26 15:46:52 +00003125 /* Do not deinitialize media since this may be a re-INVITE or
3126 * UPDATE (which in this case the media should not get affected
3127 * by the failed re-INVITE/UPDATE). The media will be shutdown
3128 * when call is disconnected anyway.
3129 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003130 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003131 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003132
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003133 /* Disconnect call if we're not in the middle of initializing an
3134 * UAS dialog and if this is not a re-INVITE
3135 */
3136 if (inv->state != PJSIP_INV_STATE_NULL &&
3137 inv->state != PJSIP_INV_STATE_CONFIRMED)
3138 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003139 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003140 }
3141
3142 PJSUA_UNLOCK();
3143 return;
3144 }
3145
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003146
3147 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003148 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003149 if (status != PJ_SUCCESS) {
3150 pjsua_perror(THIS_FILE,
3151 "Unable to retrieve currently active local SDP",
3152 status);
3153 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3154 PJSUA_UNLOCK();
3155 return;
3156 }
3157
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003158 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3159 if (status != PJ_SUCCESS) {
3160 pjsua_perror(THIS_FILE,
3161 "Unable to retrieve currently active remote SDP",
3162 status);
3163 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3164 PJSUA_UNLOCK();
3165 return;
3166 }
3167
Benny Prijono91a6a172007-10-31 08:59:29 +00003168 /* Update remote's NAT type */
3169 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3170 update_remote_nat_type(call, remote_sdp);
3171 }
3172
3173 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003174 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003175 if (status != PJ_SUCCESS) {
3176 pjsua_perror(THIS_FILE, "Unable to create media session",
3177 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003178 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003179 /* No need to deinitialize; media will be shutdown when call
3180 * state is disconnected anyway.
3181 */
3182 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003183 PJSUA_UNLOCK();
3184 return;
3185 }
3186
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003187
3188 /* Call application callback, if any */
3189 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3190 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3191
3192
3193 PJSUA_UNLOCK();
3194}
3195
3196
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003197/* Create SDP for call hold. */
3198static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3199 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003200{
3201 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003202 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003203 pjmedia_sdp_session *sdp;
3204
Benny Prijono40d62b62009-08-12 17:53:47 +00003205 /* Use call's provisional pool */
3206 pool = call->inv->pool_prov;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003207
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003208 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003209 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3210 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003211 if (status != PJ_SUCCESS) {
3212 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3213 return status;
3214 }
3215
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003216 /* Call-hold is done by set the media direction to 'sendonly'
3217 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3218 * 'inactive' (PJMEDIA_DIR_NONE).
3219 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3220 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003221 /* http://trac.pjsip.org/repos/ticket/880
3222 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3223 */
3224 if (1) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003225 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003226
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003227 /* Remove existing directions attributes */
3228 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3229 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3230 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3231 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003232
Benny Prijonoe0860132009-06-05 10:14:20 +00003233 if (call->media_dir & PJMEDIA_DIR_ENCODING) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003234 /* Add sendonly attribute */
3235 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3236 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3237 } else {
3238 /* Add inactive attribute */
3239 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3240 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3241 }
3242 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003243
3244 *p_answer = sdp;
3245
3246 return status;
3247}
3248
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003249/*
3250 * Called when session received new offer.
3251 */
3252static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3253 const pjmedia_sdp_session *offer)
3254{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003255 pjsua_call *call;
3256 pjmedia_sdp_conn *conn;
3257 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003258 pj_status_t status;
3259
3260 PJSUA_LOCK();
3261
Benny Prijonoa1e69682007-05-11 15:14:34 +00003262 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003263
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003264 conn = offer->media[0]->conn;
3265 if (!conn)
3266 conn = offer->conn;
3267
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003268 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003269 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3270 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003271
Benny Prijono40d62b62009-08-12 17:53:47 +00003272 status = pjsua_media_channel_create_sdp(call->index,
3273 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003274 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003275 if (status != PJ_SUCCESS) {
3276 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3277 PJSUA_UNLOCK();
3278 return;
3279 }
3280
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003281 /* Check if offer's conn address is zero */
3282 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3283 pj_strcmp2(&conn->addr, "0")==0)
3284 {
3285 /* Modify address */
3286 answer->conn->addr = pj_str("0.0.0.0");
3287 }
3288
3289 /* Check if call is on-hold */
3290 if (call->local_hold) {
3291 pjmedia_sdp_attr *attr;
3292
3293 /* Remove existing directions attributes */
3294 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3295 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3296 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3297 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3298
3299 /* Keep call on-hold by setting 'sendonly' attribute.
3300 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3301 */
Benny Prijono40d62b62009-08-12 17:53:47 +00003302 attr = pjmedia_sdp_attr_create(call->inv->pool_prov, "sendonly", NULL);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003303 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3304 }
3305
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003306 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3307 if (status != PJ_SUCCESS) {
3308 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3309 PJSUA_UNLOCK();
3310 return;
3311 }
3312
3313 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003314}
3315
3316
3317/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003318 * Called to generate new offer.
3319 */
3320static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3321 pjmedia_sdp_session **offer)
3322{
3323 pjsua_call *call;
3324 pj_status_t status;
3325
3326 PJSUA_LOCK();
3327
3328 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3329
3330 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003331 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003332 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003333 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003334 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003335 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003336 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003337 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3338 call->index));
3339
Benny Prijono40d62b62009-08-12 17:53:47 +00003340 status = pjsua_media_channel_create_sdp(call->index,
3341 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003342 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003343 }
3344
3345 if (status != PJ_SUCCESS) {
3346 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3347 PJSUA_UNLOCK();
3348 return;
3349 }
3350
Benny Prijono77998ce2007-06-20 10:03:46 +00003351 PJSUA_UNLOCK();
3352}
3353
3354
3355/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003356 * Callback called by event framework when the xfer subscription state
3357 * has changed.
3358 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003359static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3360{
3361
3362 PJ_UNUSED_ARG(event);
3363
3364 /*
3365 * When subscription is accepted (got 200/OK to REFER), check if
3366 * subscription suppressed.
3367 */
3368 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3369
3370 pjsip_rx_data *rdata;
3371 pjsip_generic_string_hdr *refer_sub;
3372 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3373 pjsua_call *call;
3374
Benny Prijonoa1e69682007-05-11 15:14:34 +00003375 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003376
3377 /* Must be receipt of response message */
3378 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3379 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3380 rdata = event->body.tsx_state.src.rdata;
3381
3382 /* Find Refer-Sub header */
3383 refer_sub = (pjsip_generic_string_hdr*)
3384 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3385 &REFER_SUB, NULL);
3386
3387 /* Check if subscription is suppressed */
3388 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3389 /* Since no subscription is desired, assume that call has been
3390 * transfered successfully.
3391 */
3392 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3393 const pj_str_t ACCEPTED = { "Accepted", 8 };
3394 pj_bool_t cont = PJ_FALSE;
3395 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3396 200,
3397 &ACCEPTED,
3398 PJ_TRUE,
3399 &cont);
3400 }
3401
3402 /* Yes, subscription is suppressed.
3403 * Terminate our subscription now.
3404 */
3405 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3406 "event subcription..."));
3407 pjsip_evsub_terminate(sub, PJ_TRUE);
3408
3409 } else {
3410 /* Notify application about call transfer progress.
3411 * Initially notify with 100/Accepted status.
3412 */
3413 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3414 const pj_str_t ACCEPTED = { "Accepted", 8 };
3415 pj_bool_t cont = PJ_FALSE;
3416 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3417 100,
3418 &ACCEPTED,
3419 PJ_FALSE,
3420 &cont);
3421 }
3422 }
3423 }
3424 /*
3425 * On incoming NOTIFY, notify application about call transfer progress.
3426 */
3427 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3428 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3429 {
3430 pjsua_call *call;
3431 pjsip_msg *msg;
3432 pjsip_msg_body *body;
3433 pjsip_status_line status_line;
3434 pj_bool_t is_last;
3435 pj_bool_t cont;
3436 pj_status_t status;
3437
Benny Prijonoa1e69682007-05-11 15:14:34 +00003438 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003439
3440 /* When subscription is terminated, clear the xfer_sub member of
3441 * the inv_data.
3442 */
3443 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3444 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3445 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3446
3447 }
3448
3449 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3450 /* Application is not interested with call progress status */
3451 return;
3452 }
3453
3454 /* This better be a NOTIFY request */
3455 if (event->type == PJSIP_EVENT_TSX_STATE &&
3456 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3457 {
3458 pjsip_rx_data *rdata;
3459
3460 rdata = event->body.tsx_state.src.rdata;
3461
3462 /* Check if there's body */
3463 msg = rdata->msg_info.msg;
3464 body = msg->body;
3465 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003466 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003467 "Warning: received NOTIFY without message body"));
3468 return;
3469 }
3470
3471 /* Check for appropriate content */
3472 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3473 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3474 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003475 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003476 "Warning: received NOTIFY with non message/sipfrag "
3477 "content"));
3478 return;
3479 }
3480
3481 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003482 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003483 &status_line);
3484 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003485 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003486 "Warning: received NOTIFY with invalid "
3487 "message/sipfrag content"));
3488 return;
3489 }
3490
3491 } else {
3492 status_line.code = 500;
3493 status_line.reason = *pjsip_get_status_text(500);
3494 }
3495
3496 /* Notify application */
3497 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3498 cont = !is_last;
3499 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3500 status_line.code,
3501 &status_line.reason,
3502 is_last, &cont);
3503
3504 if (!cont) {
3505 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3506 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003507
3508 /* If the call transfer has completed but the subscription is
3509 * not terminated, terminate it now.
3510 */
3511 if (status_line.code/100 == 2 && !is_last) {
3512 pjsip_tx_data *tdata;
3513
3514 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3515 0, &tdata);
3516 if (status == PJ_SUCCESS)
3517 status = pjsip_evsub_send_request(sub, tdata);
3518 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003519 }
3520}
3521
3522
3523/*
3524 * Callback called by event framework when the xfer subscription state
3525 * has changed.
3526 */
3527static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003528{
3529
3530 PJ_UNUSED_ARG(event);
3531
3532 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003533 * When subscription is terminated, clear the xfer_sub member of
3534 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003535 */
3536 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003537 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003538
Benny Prijonoa1e69682007-05-11 15:14:34 +00003539 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003540 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003541 return;
3542
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003543 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003544 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003545
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003546 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003547 }
3548}
3549
3550
3551/*
3552 * Follow transfer (REFER) request.
3553 */
3554static void on_call_transfered( pjsip_inv_session *inv,
3555 pjsip_rx_data *rdata )
3556{
3557 pj_status_t status;
3558 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003559 pjsua_call *existing_call;
3560 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003561 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003562 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003563 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003564 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003565 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003566 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003567 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003568 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003569 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003570 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003571 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003572 pjsip_evsub *sub;
3573
Benny Prijonoa1e69682007-05-11 15:14:34 +00003574 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003575
Benny Prijono26ff9062006-02-21 23:47:00 +00003576 /* Find the Refer-To header */
3577 refer_to = (pjsip_generic_string_hdr*)
3578 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3579
3580 if (refer_to == NULL) {
3581 /* Invalid Request.
3582 * No Refer-To header!
3583 */
3584 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003585 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003586 return;
3587 }
3588
Benny Prijonoc8141a82006-08-20 09:12:19 +00003589 /* Find optional Refer-Sub header */
3590 refer_sub = (pjsip_generic_string_hdr*)
3591 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3592
3593 if (refer_sub) {
3594 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3595 no_refer_sub = PJ_TRUE;
3596 }
3597
Benny Prijono053f5222006-11-11 16:16:04 +00003598 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3599 * request.
3600 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003601 ref_by_hdr = (pjsip_hdr*)
3602 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003603 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003604
Benny Prijono9fc735d2006-05-28 14:58:12 +00003605 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003606 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003607 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3608 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3609 &refer_to->hvalue,
3610 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003611
3612 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003613 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003614 if (code >= 300) {
3615 /* Application rejects call transfer request */
3616 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3617 return;
3618 }
3619
Benny Prijono26ff9062006-02-21 23:47:00 +00003620 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3621 (int)inv->dlg->remote.info_str.slen,
3622 inv->dlg->remote.info_str.ptr,
3623 (int)refer_to->hvalue.slen,
3624 refer_to->hvalue.ptr));
3625
Benny Prijonoc8141a82006-08-20 09:12:19 +00003626 if (no_refer_sub) {
3627 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003628 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003629 */
3630 pjsip_tx_data *tdata;
3631 const pj_str_t str_false = { "false", 5};
3632 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003633
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003634 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3635 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003636 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003637 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003638 status);
3639 return;
3640 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003641
Benny Prijonoc8141a82006-08-20 09:12:19 +00003642 /* Add Refer-Sub header */
3643 hdr = (pjsip_hdr*)
3644 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3645 &str_false);
3646 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003647
Benny Prijono26ff9062006-02-21 23:47:00 +00003648
Benny Prijonoc8141a82006-08-20 09:12:19 +00003649 /* Send answer */
3650 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3651 tdata);
3652 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003653 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003654 status);
3655 return;
3656 }
3657
3658 /* Don't have subscription */
3659 sub = NULL;
3660
3661 } else {
3662 struct pjsip_evsub_user xfer_cb;
3663 pjsip_hdr hdr_list;
3664
3665 /* Init callback */
3666 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003667 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003668
3669 /* Init additional header list to be sent with REFER response */
3670 pj_list_init(&hdr_list);
3671
3672 /* Create transferee event subscription */
3673 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3674 if (status != PJ_SUCCESS) {
3675 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3676 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3677 return;
3678 }
3679
3680 /* If there's Refer-Sub header and the value is "true", send back
3681 * Refer-Sub in the response with value "true" too.
3682 */
3683 if (refer_sub) {
3684 const pj_str_t str_true = { "true", 4 };
3685 pjsip_hdr *hdr;
3686
3687 hdr = (pjsip_hdr*)
3688 pjsip_generic_string_hdr_create(inv->dlg->pool,
3689 &str_refer_sub,
3690 &str_true);
3691 pj_list_push_back(&hdr_list, hdr);
3692
3693 }
3694
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003695 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003696 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3697
3698 /* Create initial NOTIFY request */
3699 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3700 100, NULL, &tdata);
3701 if (status != PJ_SUCCESS) {
3702 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3703 status);
3704 return;
3705 }
3706
3707 /* Send initial NOTIFY request */
3708 status = pjsip_xfer_send_request( sub, tdata);
3709 if (status != PJ_SUCCESS) {
3710 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3711 return;
3712 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003713 }
3714
3715 /* We're cheating here.
3716 * We need to get a null terminated string from a pj_str_t.
3717 * So grab the pointer from the hvalue and NULL terminate it, knowing
3718 * that the NULL position will be occupied by a newline.
3719 */
3720 uri = refer_to->hvalue.ptr;
3721 uri[refer_to->hvalue.slen] = '\0';
3722
Benny Prijono053f5222006-11-11 16:16:04 +00003723 /* Init msg_data */
3724 pjsua_msg_data_init(&msg_data);
3725
3726 /* If Referred-By header is present in the REFER request, copy this
3727 * to the outgoing INVITE request.
3728 */
3729 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003730 pjsip_hdr *dup = (pjsip_hdr*)
3731 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003732 pj_list_push_back(&msg_data.hdr_list, dup);
3733 }
3734
Benny Prijono26ff9062006-02-21 23:47:00 +00003735 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003736 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003737 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003738 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003739 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003740 if (status != PJ_SUCCESS) {
3741
Benny Prijonoc8141a82006-08-20 09:12:19 +00003742 /* Notify xferer about the error (if we have subscription) */
3743 if (sub) {
3744 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3745 500, NULL, &tdata);
3746 if (status != PJ_SUCCESS) {
3747 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3748 status);
3749 return;
3750 }
3751 status = pjsip_xfer_send_request(sub, tdata);
3752 if (status != PJ_SUCCESS) {
3753 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3754 status);
3755 return;
3756 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003757 }
3758 return;
3759 }
3760
Benny Prijonoc8141a82006-08-20 09:12:19 +00003761 if (sub) {
3762 /* Put the server subscription in inv_data.
3763 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3764 * reported back to the server subscription.
3765 */
3766 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003767
Benny Prijonoc8141a82006-08-20 09:12:19 +00003768 /* Put the invite_data in the subscription. */
3769 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3770 &pjsua_var.calls[new_call]);
3771 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003772}
3773
3774
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003775
Benny Prijono26ff9062006-02-21 23:47:00 +00003776/*
3777 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003778 * session. We use this to trap:
3779 * - incoming REFER request.
3780 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003781 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003782static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3783 pjsip_transaction *tsx,
3784 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003785{
Benny Prijono2285e7e2008-12-17 14:28:18 +00003786 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003787
3788 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003789
Benny Prijono2285e7e2008-12-17 14:28:18 +00003790 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3791
3792 if (call == NULL) {
3793 PJSUA_UNLOCK();
3794 return;
3795 }
3796
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003797 if (call->inv == NULL) {
3798 /* Shouldn't happen. It happens only when we don't terminate the
3799 * server subscription caused by REFER after the call has been
3800 * transfered (and this call has been disconnected), and we
3801 * receive another REFER for this call.
3802 */
3803 PJSUA_UNLOCK();
3804 return;
3805 }
3806
Benny Prijonofeb69f42007-10-05 09:12:26 +00003807 /* Notify application callback first */
3808 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3809 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3810 }
3811
Benny Prijono26ff9062006-02-21 23:47:00 +00003812 if (tsx->role==PJSIP_ROLE_UAS &&
3813 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003814 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003815 {
3816 /*
3817 * Incoming REFER request.
3818 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003819 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003820
Benny Prijono26ff9062006-02-21 23:47:00 +00003821 }
Benny Prijonob0808372006-03-02 21:18:58 +00003822 else if (tsx->role==PJSIP_ROLE_UAS &&
3823 tsx->state==PJSIP_TSX_STATE_TRYING &&
3824 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3825 {
3826 /*
3827 * Incoming MESSAGE request!
3828 */
3829 pjsip_rx_data *rdata;
3830 pjsip_msg *msg;
3831 pjsip_accept_hdr *accept_hdr;
3832 pj_status_t status;
3833
3834 rdata = e->body.tsx_state.src.rdata;
3835 msg = rdata->msg_info.msg;
3836
3837 /* Request MUST have message body, with Content-Type equal to
3838 * "text/plain".
3839 */
3840 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3841
3842 pjsip_hdr hdr_list;
3843
3844 pj_list_init(&hdr_list);
3845 pj_list_push_back(&hdr_list, accept_hdr);
3846
3847 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3848 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003849 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003850 return;
3851 }
3852
3853 /* Respond with 200 first, so that remote doesn't retransmit in case
3854 * the UI takes too long to process the message.
3855 */
3856 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3857
3858 /* Process MESSAGE request */
3859 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3860 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003861
Benny Prijonob0808372006-03-02 21:18:58 +00003862 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003863 else if (tsx->role == PJSIP_ROLE_UAC &&
3864 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003865 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003866 /* Handle outgoing pager status */
3867 if (tsx->status_code >= 200) {
3868 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003869
Benny Prijonoa1e69682007-05-11 15:14:34 +00003870 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003871 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003872
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003873 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3874 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3875 &im_data->to,
3876 &im_data->body,
3877 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003878 (pjsip_status_code)
3879 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003880 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003881 }
Benny Prijonofccab712006-02-22 22:23:22 +00003882 }
Benny Prijono834aee32006-02-19 01:38:06 +00003883 }
Benny Prijono834aee32006-02-19 01:38:06 +00003884
Benny Prijono26ff9062006-02-21 23:47:00 +00003885
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003886 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003887}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003888
3889
3890/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00003891static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
3892 const pjsip_uri *target,
3893 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003894{
3895 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00003896 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003897
3898 PJSUA_LOCK();
3899
3900 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00003901 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
3902 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003903 } else {
3904 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
3905 "(callback not implemented by application). Disconnecting "
3906 "call.",
3907 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00003908 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003909 }
3910
3911 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00003912
3913 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003914}
3915