blob: 5a19d95b2210f1642c982259af37992c16387928 [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) {
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000387 pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000388 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
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000394 /* Associate session with account */
395 call->acc_id = acc_id;
396
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000397 /* Create temporary pool */
398 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
399
Benny Prijono320fa4d2006-12-07 10:09:16 +0000400 /* Verify that destination URI is valid before calling
401 * pjsua_acc_create_uac_contact, or otherwise there
402 * a misleading "Invalid Contact URI" error will be printed
403 * when pjsua_acc_create_uac_contact() fails.
404 */
405 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000406 pjsip_uri *uri;
407 pj_str_t dup;
408
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000409 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
410 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000411
412 if (uri == NULL) {
413 pjsua_perror(THIS_FILE, "Unable to make call",
414 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000415 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000416 PJSUA_UNLOCK();
417 return PJSIP_EINVALIDREQURI;
418 }
419 }
420
Benny Prijono093d3022006-09-24 00:07:11 +0000421 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
422 (int)dest_uri->slen, dest_uri->ptr));
423
Benny Prijonoe21e7842006-04-09 16:46:05 +0000424 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000425 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000426
Benny Prijonoe21e7842006-04-09 16:46:05 +0000427 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000428 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000429
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000430 /* Create suitable Contact header unless a Contact header has been
431 * set in the account.
432 */
433 if (acc->contact.slen) {
434 contact = acc->contact;
435 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000436 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000437 acc_id, dest_uri);
438 if (status != PJ_SUCCESS) {
439 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
440 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000441 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000442 PJSUA_UNLOCK();
443 return status;
444 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000445 }
446
Benny Prijonoe21e7842006-04-09 16:46:05 +0000447 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000448 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000449 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000450 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000451 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000452 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000453 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000454 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000455 return status;
456 }
457
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000458 /* Increment the dialog's lock otherwise when invite session creation
459 * fails the dialog will be destroyed prematurely.
460 */
461 pjsip_dlg_inc_lock(dlg);
462
Benny Prijonodb844a42008-02-02 17:07:18 +0000463 /* Calculate call's secure level */
464 call->secure_level = get_secure_level(acc_id, dest_uri);
465
Benny Prijonoc97608e2007-03-23 16:34:20 +0000466 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000467 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000468 call->secure_level, dlg->pool,
469 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000470 if (status != PJ_SUCCESS) {
471 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
472 goto on_error;
473 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000474
Benny Prijono224b4e22008-06-19 14:10:28 +0000475 /* Create offer */
476 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000477 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000478 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000479 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000480 goto on_error;
481 }
482
483 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000484 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000485 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000486 if (acc->cfg.require_100rel)
487 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000488 if (acc->cfg.require_timer)
489 options |= PJSIP_INV_REQUIRE_TIMER;
Benny Prijono84126ab2006-02-09 09:30:09 +0000490
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000491 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000492 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000493 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000494 goto on_error;
495 }
496
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000497 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000498 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
499 if (status != PJ_SUCCESS) {
500 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
501 goto on_error;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000502 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000503
504 /* Create and associate our data in the session. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000505 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000506
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000507 dlg->mod_data[pjsua_var.mod.id] = call;
508 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000509
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000510 /* Attach user data */
511 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000512
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000513 /* If account is locked to specific transport, then lock dialog
514 * to this transport too.
515 */
516 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
517 pjsip_tpselector tp_sel;
518
519 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
520 pjsip_dlg_set_transport(dlg, &tp_sel);
521 }
522
Benny Prijono84126ab2006-02-09 09:30:09 +0000523 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000524 if (!pj_list_empty(&acc->route_set))
525 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000526
527
528 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000529 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000530 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000531 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000532 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000533
Benny Prijono48ab2b72007-11-08 09:24:30 +0000534 /* Set authentication preference */
535 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000536
537 /* Create initial INVITE: */
538
539 status = pjsip_inv_invite(inv, &tdata);
540 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000541 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
542 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000543 goto on_error;
544 }
545
546
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000547 /* Add additional headers etc */
548
549 pjsua_process_msg_data( tdata, msg_data);
550
Benny Prijono093d3022006-09-24 00:07:11 +0000551 /* Must increment call counter now */
552 ++pjsua_var.call_cnt;
553
Benny Prijono84126ab2006-02-09 09:30:09 +0000554 /* Send initial INVITE: */
555
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000556 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000557 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000558 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
559 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000560
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000561 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000562 * session would have been cleared.
563 */
564 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000565 goto on_error;
566 }
567
Benny Prijono84126ab2006-02-09 09:30:09 +0000568 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000569
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000570 if (p_call_id)
571 *p_call_id = call_id;
572
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000573 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000574 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000575 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000576
577 return PJ_SUCCESS;
578
579
580on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000581 if (dlg) {
582 /* This may destroy the dialog */
583 pjsip_dlg_dec_lock(dlg);
584 }
585
Benny Prijono1c2bf462006-03-05 11:54:02 +0000586 if (inv != NULL) {
587 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000588 }
589
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000590 if (call_id != -1) {
591 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000592 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000593 }
594
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000595 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000596 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000597 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000598}
599
600
Benny Prijono91a6a172007-10-31 08:59:29 +0000601/* Get the NAT type information in remote's SDP */
602static void update_remote_nat_type(pjsua_call *call,
603 const pjmedia_sdp_session *sdp)
604{
605 const pjmedia_sdp_attr *xnat;
606
607 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
608 if (xnat) {
609 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
610 } else {
611 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
612 }
613
614 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
615 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
616}
617
618
Benny Prijonodc39fe82006-05-26 12:17:46 +0000619/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000620 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000621 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000622 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000623pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000624{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000625 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000626 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000627 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000628 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
629 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000630 pjsip_tx_data *response = NULL;
631 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000632 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000633 int acc_id;
634 pjsua_call *call;
635 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000636 int sip_err_code;
Benny Prijonod8179652008-01-23 20:39:07 +0000637 pjmedia_sdp_session *offer, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000638 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000639
Benny Prijono26ff9062006-02-21 23:47:00 +0000640 /* Don't want to handle anything but INVITE */
641 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
642 return PJ_FALSE;
643
644 /* Don't want to handle anything that's already associated with
645 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000646 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000647 if (dlg || tsx)
648 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000649
Benny Prijono384dab42009-10-14 01:58:04 +0000650 /* Don't want to accept the call if shutdown is in progress */
651 if (pjsua_var.thread_quit_flag) {
652 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
653 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
654 NULL, NULL);
655 return PJ_TRUE;
656 }
657
Benny Prijono148c9dd2006-09-19 13:37:53 +0000658 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000659
Benny Prijono26ff9062006-02-21 23:47:00 +0000660 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000661 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000662
Benny Prijono5773cd62008-01-19 13:01:42 +0000663 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000664 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000665 PJSIP_SC_BUSY_HERE, NULL,
666 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000667 PJ_LOG(2,(THIS_FILE,
668 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000669 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000670 return PJ_TRUE;
671 }
672
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000673 /* Clear call descriptor */
674 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000675
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000676 call = &pjsua_var.calls[call_id];
677
678 /* Mark call start time. */
679 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000680
Benny Prijono053f5222006-11-11 16:16:04 +0000681 /* Check INVITE request for Replaces header. If Replaces header is
682 * present, the function will make sure that we can handle the request.
683 */
684 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
685 &response);
686 if (status != PJ_SUCCESS) {
687 /*
688 * Something wrong with the Replaces header.
689 */
690 if (response) {
691 pjsip_response_addr res_addr;
692
693 pjsip_get_response_addr(response->pool, rdata, &res_addr);
694 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
695 NULL, NULL);
696
697 } else {
698
699 /* Respond with 500 (Internal Server Error) */
700 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
701 NULL, NULL);
702 }
703
704 PJSUA_UNLOCK();
705 return PJ_TRUE;
706 }
707
708 /* If this INVITE request contains Replaces header, notify application
709 * about the request so that application can do subsequent checking
710 * if it wants to.
711 */
712 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
713 pjsua_call *replaced_call;
714 int st_code = 200;
715 pj_str_t st_text = { "OK", 2 };
716
717 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000718 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000719
720 /* Notify application */
721 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
722 rdata, &st_code, &st_text);
723
724 /* Must specify final response */
725 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
726
727 /* Check if application rejects this request. */
728 if (st_code >= 300) {
729
730 if (st_text.slen == 2)
731 st_text = *pjsip_get_status_text(st_code);
732
733 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
734 st_code, &st_text, NULL, NULL, NULL);
735 PJSUA_UNLOCK();
736 return PJ_TRUE;
737 }
738 }
739
Benny Prijonod8179652008-01-23 20:39:07 +0000740 /*
741 * Get which account is most likely to be associated with this incoming
742 * call. We need the account to find which contact URI to put for
743 * the call.
744 */
745 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
746
Benny Prijonodb844a42008-02-02 17:07:18 +0000747 /* Get call's secure level */
748 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
749 call->secure_level = 2;
750 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
751 call->secure_level = 1;
752 else
753 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000754
Benny Prijonod8179652008-01-23 20:39:07 +0000755 /* Parse SDP from incoming request */
756 if (rdata->msg_info.msg->body) {
757 status = pjmedia_sdp_parse(rdata->tp_info.pool,
Benny Prijono24a21852008-04-14 04:04:30 +0000758 (char*)rdata->msg_info.msg->body->data,
Benny Prijonod8179652008-01-23 20:39:07 +0000759 rdata->msg_info.msg->body->len, &offer);
Benny Prijono2c484e42008-06-26 19:47:23 +0000760 if (status == PJ_SUCCESS) {
761 /* Validate */
762 status = pjmedia_sdp_validate(offer);
763 }
764
Benny Prijonod8179652008-01-23 20:39:07 +0000765 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000766 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000767 pjsip_hdr hdr_list;
768 pjsip_warning_hdr *w;
769
770 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000771 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000772
773 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
774 pjsip_endpt_name(pjsua_var.endpt),
775 status);
776 pj_list_init(&hdr_list);
777 pj_list_push_back(&hdr_list, w);
778
Benny Prijono224b4e22008-06-19 14:10:28 +0000779 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000780 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000781 PJSUA_UNLOCK();
782 return PJ_TRUE;
783 }
Benny Prijono617b8602008-04-07 10:10:31 +0000784
785 /* Do quick checks on SDP before passing it to transports. More elabore
786 * checks will be done in pjsip_inv_verify_request2() below.
787 */
788 if (offer->media_count==0) {
789 const pj_str_t reason = pj_str("Missing media in SDP");
790 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
791 NULL, NULL, NULL);
792 PJSUA_UNLOCK();
793 return PJ_TRUE;
794 }
795
Benny Prijonod8179652008-01-23 20:39:07 +0000796 } else {
797 offer = NULL;
798 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000799
Benny Prijono224b4e22008-06-19 14:10:28 +0000800 /* Init media channel */
801 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
802 call->secure_level,
803 rdata->tp_info.pool, offer,
804 &sip_err_code);
805 if (status != PJ_SUCCESS) {
806 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
807 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
808 sip_err_code, NULL, NULL, NULL, NULL);
809 PJSUA_UNLOCK();
810 return PJ_TRUE;
811 }
812
813 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000814 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000815 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000816 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000817 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000818 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
819 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000820 PJSUA_UNLOCK();
821 return PJ_TRUE;
822 }
823
Benny Prijono224b4e22008-06-19 14:10:28 +0000824
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000825 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000826 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000827 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000828 if (pjsua_var.acc[acc_id].cfg.require_100rel)
829 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000830 if (pjsua_var.acc[acc_id].cfg.require_timer)
831 options |= PJSIP_INV_REQUIRE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000832
Benny Prijonod8179652008-01-23 20:39:07 +0000833 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
834 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000835 if (status != PJ_SUCCESS) {
836
837 /*
838 * No we can't handle the incoming INVITE request.
839 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000840 if (response) {
841 pjsip_response_addr res_addr;
842
843 pjsip_get_response_addr(response->pool, rdata, &res_addr);
844 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
845 NULL, NULL);
846
847 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000848 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000849 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
850 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000851 }
852
Benny Prijonoc97608e2007-03-23 16:34:20 +0000853 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000854 PJSUA_UNLOCK();
855 return PJ_TRUE;
856 }
857
858
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000859 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000860 if (pjsua_var.acc[acc_id].contact.slen) {
861 contact = pjsua_var.acc[acc_id].contact;
862 } else {
863 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
864 acc_id, rdata);
865 if (status != PJ_SUCCESS) {
866 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
867 status);
868 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
869 NULL, NULL);
870 pjsua_media_channel_deinit(call->index);
871 PJSUA_UNLOCK();
872 return PJ_TRUE;
873 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000874 }
875
Benny Prijono26ff9062006-02-21 23:47:00 +0000876 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000877 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000878 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000879 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000880 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000881 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000882 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000883 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000884 return PJ_TRUE;
885 }
886
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000887 /* Set credentials */
888 if (pjsua_var.acc[acc_id].cred_cnt) {
889 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
890 pjsua_var.acc[acc_id].cred_cnt,
891 pjsua_var.acc[acc_id].cred);
892 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000893
Benny Prijono48ab2b72007-11-08 09:24:30 +0000894 /* Set preference */
895 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
896 &pjsua_var.acc[acc_id].cfg.auth_pref);
897
Benny Prijono26ff9062006-02-21 23:47:00 +0000898 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000899 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000900 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000901 pjsip_hdr hdr_list;
902 pjsip_warning_hdr *w;
903
904 w = pjsip_warning_hdr_create_from_status(dlg->pool,
905 pjsip_endpt_name(pjsua_var.endpt),
906 status);
907 pj_list_init(&hdr_list);
908 pj_list_push_back(&hdr_list, w);
909
910 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
911
912 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000913 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000914 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000915 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000916 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000917 return PJ_TRUE;
918 }
919
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000920 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000921 status = pjsip_timer_init_session(inv,
922 &pjsua_var.acc[acc_id].cfg.timer_setting);
923 if (status != PJ_SUCCESS) {
924 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
925 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
926 NULL, &response);
927 if (status == PJ_SUCCESS && response)
928 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000929
Nanang Izzuddin65add622009-08-11 16:26:20 +0000930 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000931
Nanang Izzuddin65add622009-08-11 16:26:20 +0000932 PJSUA_UNLOCK();
933 return PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000934 }
935
Benny Prijonoea9fd392007-11-06 03:41:40 +0000936 /* Update NAT type of remote endpoint, only when there is SDP in
937 * incoming INVITE!
938 */
939 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
940 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
941 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000942 const pjmedia_sdp_session *remote_sdp;
943
944 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
945 update_remote_nat_type(call, remote_sdp);
946 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000947
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000948 /* If account is locked to specific transport, then lock dialog
949 * to this transport too.
950 */
951 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
952 pjsip_tpselector tp_sel;
953
954 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
955 pjsip_dlg_set_transport(dlg, &tp_sel);
956 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000957
Benny Prijono2285e7e2008-12-17 14:28:18 +0000958 /* Must answer with some response to initial INVITE. We'll do this before
959 * attaching the call to the invite session/dialog, so that the application
960 * will not get notification about this event (on another scenario, it is
961 * also possible that inv_send_msg() fails and causes the invite session to
962 * be disconnected. If we have the call attached at this time, this will
963 * cause the disconnection callback to be called before on_incoming_call()
964 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +0000965 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000966 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000967 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000968 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000969 if (response == NULL) {
970 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
971 status);
972 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
973 pjsip_inv_terminate(inv, 500, PJ_FALSE);
974 } else {
975 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +0000976 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000977 PJ_FALSE);
978 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000979 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000980 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000981 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000982
983 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000984 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000985 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000986 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +0000987 PJSUA_UNLOCK();
988 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000989 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000990 }
991
Benny Prijono2285e7e2008-12-17 14:28:18 +0000992 /* Create and attach pjsua_var data to the dialog: */
993 call->inv = inv;
994
995 dlg->mod_data[pjsua_var.mod.id] = call;
996 inv->mod_data[pjsua_var.mod.id] = call;
997
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000998 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000999
Benny Prijono105217f2006-03-06 16:25:59 +00001000
Benny Prijono053f5222006-11-11 16:16:04 +00001001 /* Check if this request should replace existing call */
1002 if (replaced_dlg) {
1003 pjsip_inv_session *replaced_inv;
1004 struct pjsua_call *replaced_call;
1005 pjsip_tx_data *tdata;
1006
1007 /* Get the invite session in the dialog */
1008 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1009
1010 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001011 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001012
1013 /* Notify application */
1014 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1015 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1016 call_id);
1017
1018 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1019 call_id));
1020
1021 /* Answer the new call with 200 response */
1022 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1023 if (status == PJ_SUCCESS)
1024 status = pjsip_inv_send_msg(inv, tdata);
1025
1026 if (status != PJ_SUCCESS)
1027 pjsua_perror(THIS_FILE, "Error answering session", status);
1028
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001029 /* Note that inv may be invalid if 200/OK has caused error in
1030 * starting the media.
1031 */
Benny Prijono053f5222006-11-11 16:16:04 +00001032
1033 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1034 replaced_call->index));
1035
1036 /* Disconnect replaced invite session */
1037 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1038 &tdata);
1039 if (status == PJ_SUCCESS && tdata)
1040 status = pjsip_inv_send_msg(replaced_inv, tdata);
1041
1042 if (status != PJ_SUCCESS)
1043 pjsua_perror(THIS_FILE, "Error terminating session", status);
1044
1045
1046 } else {
1047
Benny Prijonob5388cf2007-01-04 22:45:08 +00001048 /* Notify application if on_incoming_call() is overriden,
1049 * otherwise hangup the call with 480
1050 */
1051 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001052 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001053 } else {
1054 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1055 NULL, NULL);
1056 }
Benny Prijono053f5222006-11-11 16:16:04 +00001057 }
1058
Benny Prijono8b1889b2006-06-06 18:40:40 +00001059
Benny Prijono26ff9062006-02-21 23:47:00 +00001060 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001061 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001062 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001063}
1064
1065
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001066
1067/*
1068 * Check if the specified call has active INVITE session and the INVITE
1069 * session has not been disconnected.
1070 */
1071PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1072{
1073 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1074 PJ_EINVAL);
1075 return pjsua_var.calls[call_id].inv != NULL &&
1076 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1077}
1078
1079
1080/*
1081 * Check if call has an active media session.
1082 */
1083PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1084{
1085 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1086 PJ_EINVAL);
1087 return pjsua_var.calls[call_id].session != NULL;
1088}
1089
1090
Benny Prijonocf986c42008-09-02 11:25:07 +00001091/*
1092 * Retrieve the media session associated with this call.
1093 */
1094PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1095{
1096 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1097 NULL);
1098 return pjsua_var.calls[call_id].session;
1099}
1100
1101
1102/*
1103 * Retrieve the media transport instance that is used for this call.
1104 */
1105PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1106{
1107 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1108 NULL);
1109 return pjsua_var.calls[cid].med_tp;
1110}
1111
1112
Benny Prijono148c9dd2006-09-19 13:37:53 +00001113/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001114pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001115 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001116 pjsua_call **p_call,
1117 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001118{
1119 enum { MAX_RETRY=50 };
1120 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001121 pjsua_call *call = NULL;
1122 pj_bool_t has_pjsua_lock = PJ_FALSE;
1123 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001124
1125 for (retry=0; retry<MAX_RETRY; ++retry) {
1126
1127 has_pjsua_lock = PJ_FALSE;
1128
1129 status = PJSUA_TRY_LOCK();
1130 if (status != PJ_SUCCESS) {
1131 pj_thread_sleep(retry/10);
1132 continue;
1133 }
1134
1135 has_pjsua_lock = PJ_TRUE;
1136 call = &pjsua_var.calls[call_id];
1137
1138 if (call->inv == NULL) {
1139 PJSUA_UNLOCK();
1140 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1141 return PJSIP_ESESSIONTERMINATED;
1142 }
1143
1144 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1145 if (status != PJ_SUCCESS) {
1146 PJSUA_UNLOCK();
1147 pj_thread_sleep(retry/10);
1148 continue;
1149 }
1150
1151 PJSUA_UNLOCK();
1152
1153 break;
1154 }
1155
1156 if (status != PJ_SUCCESS) {
1157 if (has_pjsua_lock == PJ_FALSE)
1158 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1159 "(possibly system has deadlocked) in %s",
1160 title));
1161 else
1162 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1163 "(possibly system has deadlocked) in %s",
1164 title));
1165 return PJ_ETIMEDOUT;
1166 }
1167
1168 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001169 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001170
1171 return PJ_SUCCESS;
1172}
1173
1174
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001175/*
1176 * Get the conference port identification associated with the call.
1177 */
1178PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1179{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001180 pjsua_call *call;
1181 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001182 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001183 pj_status_t status;
1184
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001185 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1186 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001187
Benny Prijonodc752ca2006-09-22 16:55:42 +00001188 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001189 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001190 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001191
1192 port_id = call->conf_slot;
1193
Benny Prijonodc752ca2006-09-22 16:55:42 +00001194 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001195
1196 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001197}
1198
1199
Benny Prijono148c9dd2006-09-19 13:37:53 +00001200
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001201/*
1202 * Obtain detail information about the specified call.
1203 */
1204PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1205 pjsua_call_info *info)
1206{
1207 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001208 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001209 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001210
1211 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1212 PJ_EINVAL);
1213
Benny Prijonoac623b32006-07-03 15:19:31 +00001214 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001215
Benny Prijonodc752ca2006-09-22 16:55:42 +00001216 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001217 if (status != PJ_SUCCESS) {
1218 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001219 }
1220
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001221 /* id and role */
1222 info->id = call_id;
1223 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001224 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001225
1226 /* local info */
1227 info->local_info.ptr = info->buf_.local_info;
1228 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1229 sizeof(info->buf_.local_info));
1230
1231 /* local contact */
1232 info->local_contact.ptr = info->buf_.local_contact;
1233 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1234 call->inv->dlg->local.contact->uri,
1235 info->local_contact.ptr,
1236 sizeof(info->buf_.local_contact));
1237
1238 /* remote info */
1239 info->remote_info.ptr = info->buf_.remote_info;
1240 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1241 sizeof(info->buf_.remote_info));
1242
1243 /* remote contact */
1244 if (call->inv->dlg->remote.contact) {
1245 int len;
1246 info->remote_contact.ptr = info->buf_.remote_contact;
1247 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1248 call->inv->dlg->remote.contact->uri,
1249 info->remote_contact.ptr,
1250 sizeof(info->buf_.remote_contact));
1251 if (len < 0) len = 0;
1252 info->remote_contact.slen = len;
1253 } else {
1254 info->remote_contact.slen = 0;
1255 }
1256
1257 /* call id */
1258 info->call_id.ptr = info->buf_.call_id;
1259 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1260 sizeof(info->buf_.call_id));
1261
1262 /* state, state_text */
1263 info->state = call->inv->state;
1264 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1265
1266 /* If call is disconnected, set the last_status from the cause code */
1267 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1268 /* last_status, last_status_text */
1269 info->last_status = call->inv->cause;
1270
1271 info->last_status_text.ptr = info->buf_.last_status_text;
1272 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1273 sizeof(info->buf_.last_status_text));
1274 } else {
1275 /* last_status, last_status_text */
1276 info->last_status = call->last_code;
1277
1278 info->last_status_text.ptr = info->buf_.last_status_text;
1279 pj_strncpy(&info->last_status_text, &call->last_text,
1280 sizeof(info->buf_.last_status_text));
1281 }
1282
1283 /* media status and dir */
1284 info->media_status = call->media_st;
1285 info->media_dir = call->media_dir;
1286
1287
1288 /* conference slot number */
1289 info->conf_slot = call->conf_slot;
1290
1291 /* calculate duration */
1292 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1293
1294 info->total_duration = call->dis_time;
1295 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1296
1297 if (call->conn_time.sec) {
1298 info->connect_duration = call->dis_time;
1299 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1300 }
1301
1302 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1303
1304 pj_gettimeofday(&info->total_duration);
1305 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1306
1307 pj_gettimeofday(&info->connect_duration);
1308 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1309
1310 } else {
1311 pj_gettimeofday(&info->total_duration);
1312 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1313 }
1314
Benny Prijonodc752ca2006-09-22 16:55:42 +00001315 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001316
1317 return PJ_SUCCESS;
1318}
1319
1320
1321/*
1322 * Attach application specific data to the call.
1323 */
1324PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1325 void *user_data)
1326{
1327 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1328 PJ_EINVAL);
1329 pjsua_var.calls[call_id].user_data = user_data;
1330
1331 return PJ_SUCCESS;
1332}
1333
1334
1335/*
1336 * Get user data attached to the call.
1337 */
1338PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1339{
1340 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1341 NULL);
1342 return pjsua_var.calls[call_id].user_data;
1343}
1344
1345
1346/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001347 * Get remote's NAT type.
1348 */
1349PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1350 pj_stun_nat_type *p_type)
1351{
1352 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1353 PJ_EINVAL);
1354 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1355
1356 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1357 return PJ_SUCCESS;
1358}
1359
1360
1361/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001362 * Send response to incoming INVITE request.
1363 */
1364PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1365 unsigned code,
1366 const pj_str_t *reason,
1367 const pjsua_msg_data *msg_data)
1368{
1369 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001370 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001371 pjsip_tx_data *tdata;
1372 pj_status_t status;
1373
1374 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1375 PJ_EINVAL);
1376
Benny Prijonodc752ca2006-09-22 16:55:42 +00001377 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001378 if (status != PJ_SUCCESS)
1379 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001380
Benny Prijono2e507c22006-06-23 15:04:11 +00001381 if (call->res_time.sec == 0)
1382 pj_gettimeofday(&call->res_time);
1383
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001384 if (reason && reason->slen == 0)
1385 reason = NULL;
1386
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001387 /* Create response message */
1388 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1389 if (status != PJ_SUCCESS) {
1390 pjsua_perror(THIS_FILE, "Error creating response",
1391 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001392 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001393 return status;
1394 }
1395
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001396 /* Call might have been disconnected if application is answering with
1397 * 200/OK and the media failed to start.
1398 */
1399 if (call->inv == NULL) {
1400 pjsip_dlg_dec_lock(dlg);
1401 return PJSIP_ESESSIONTERMINATED;
1402 }
1403
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001404 /* Add additional headers etc */
1405 pjsua_process_msg_data( tdata, msg_data);
1406
1407 /* Send the message */
1408 status = pjsip_inv_send_msg(call->inv, tdata);
1409 if (status != PJ_SUCCESS)
1410 pjsua_perror(THIS_FILE, "Error sending response",
1411 status);
1412
Benny Prijonodc752ca2006-09-22 16:55:42 +00001413 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001414
1415 return status;
1416}
1417
1418
1419/*
1420 * Hangup call by using method that is appropriate according to the
1421 * call state.
1422 */
1423PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1424 unsigned code,
1425 const pj_str_t *reason,
1426 const pjsua_msg_data *msg_data)
1427{
1428 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001429 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001430 pj_status_t status;
1431 pjsip_tx_data *tdata;
1432
1433
Benny Prijono148c9dd2006-09-19 13:37:53 +00001434 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1435 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1436 call_id));
1437 }
1438
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001439 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1440 PJ_EINVAL);
1441
Benny Prijonodc752ca2006-09-22 16:55:42 +00001442 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001443 if (status != PJ_SUCCESS)
1444 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001445
1446 if (code==0) {
1447 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1448 code = PJSIP_SC_OK;
1449 else if (call->inv->role == PJSIP_ROLE_UAS)
1450 code = PJSIP_SC_DECLINE;
1451 else
1452 code = PJSIP_SC_REQUEST_TERMINATED;
1453 }
1454
1455 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1456 if (status != PJ_SUCCESS) {
1457 pjsua_perror(THIS_FILE,
1458 "Failed to create end session message",
1459 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001460 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001461 return status;
1462 }
1463
1464 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1465 * as p_tdata when INVITE transaction has not been answered
1466 * with any provisional responses.
1467 */
1468 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001469 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001470 return PJ_SUCCESS;
1471 }
1472
1473 /* Add additional headers etc */
1474 pjsua_process_msg_data( tdata, msg_data);
1475
1476 /* Send the message */
1477 status = pjsip_inv_send_msg(call->inv, tdata);
1478 if (status != PJ_SUCCESS) {
1479 pjsua_perror(THIS_FILE,
1480 "Failed to send end session message",
1481 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001482 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001483 return status;
1484 }
1485
Benny Prijonodc752ca2006-09-22 16:55:42 +00001486 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001487
1488 return PJ_SUCCESS;
1489}
1490
1491
1492/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001493 * Accept or reject redirection.
1494 */
1495PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1496 pjsip_redirect_op cmd)
1497{
1498 pjsua_call *call;
1499 pjsip_dialog *dlg;
1500 pj_status_t status;
1501
1502 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1503 PJ_EINVAL);
1504
1505 status = acquire_call("pjsua_call_process_redirect()", call_id,
1506 &call, &dlg);
1507 if (status != PJ_SUCCESS)
1508 return status;
1509
1510 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1511
1512 pjsip_dlg_dec_lock(dlg);
1513
1514 return status;
1515}
1516
1517
1518/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001519 * Put the specified call on hold.
1520 */
1521PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1522 const pjsua_msg_data *msg_data)
1523{
1524 pjmedia_sdp_session *sdp;
1525 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001526 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001527 pjsip_tx_data *tdata;
1528 pj_status_t status;
1529
1530 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1531 PJ_EINVAL);
1532
Benny Prijonodc752ca2006-09-22 16:55:42 +00001533 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001534 if (status != PJ_SUCCESS)
1535 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001536
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001537
1538 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1539 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001540 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001541 return PJSIP_ESESSIONSTATE;
1542 }
1543
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001544 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001545 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001546 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001547 return status;
1548 }
1549
1550 /* Create re-INVITE with new offer */
1551 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1552 if (status != PJ_SUCCESS) {
1553 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001554 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001555 return status;
1556 }
1557
1558 /* Add additional headers etc */
1559 pjsua_process_msg_data( tdata, msg_data);
1560
1561 /* Send the request */
1562 status = pjsip_inv_send_msg( call->inv, tdata);
1563 if (status != PJ_SUCCESS) {
1564 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001565 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001566 return status;
1567 }
1568
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001569 /* Set flag that local put the call on hold */
1570 call->local_hold = PJ_TRUE;
1571
Benny Prijonodc752ca2006-09-22 16:55:42 +00001572 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001573
1574 return PJ_SUCCESS;
1575}
1576
1577
1578/*
1579 * Send re-INVITE (to release hold).
1580 */
1581PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1582 pj_bool_t unhold,
1583 const pjsua_msg_data *msg_data)
1584{
1585 pjmedia_sdp_session *sdp;
1586 pjsip_tx_data *tdata;
1587 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001588 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001589 pj_status_t status;
1590
1591
1592 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1593 PJ_EINVAL);
1594
Benny Prijonodc752ca2006-09-22 16:55:42 +00001595 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001596 if (status != PJ_SUCCESS)
1597 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001598
1599 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1600 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001601 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001602 return PJSIP_ESESSIONSTATE;
1603 }
1604
1605 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001606 if (call->local_hold && !unhold) {
1607 status = create_sdp_of_call_hold(call, &sdp);
1608 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001609 status = pjsua_media_channel_create_sdp(call->index,
1610 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001611 NULL, &sdp, NULL);
1612 call->local_hold = PJ_FALSE;
1613 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001614 if (status != PJ_SUCCESS) {
1615 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1616 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001617 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001618 return status;
1619 }
1620
1621 /* Create re-INVITE with new offer */
1622 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1623 if (status != PJ_SUCCESS) {
1624 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001625 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001626 return status;
1627 }
1628
1629 /* Add additional headers etc */
1630 pjsua_process_msg_data( tdata, msg_data);
1631
1632 /* Send the request */
1633 status = pjsip_inv_send_msg( call->inv, tdata);
1634 if (status != PJ_SUCCESS) {
1635 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001636 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001637 return status;
1638 }
1639
Benny Prijonodc752ca2006-09-22 16:55:42 +00001640 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001641
1642 return PJ_SUCCESS;
1643}
1644
1645
1646/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001647 * Send UPDATE request.
1648 */
1649PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1650 unsigned options,
1651 const pjsua_msg_data *msg_data)
1652{
1653 pjmedia_sdp_session *sdp;
1654 pjsip_tx_data *tdata;
1655 pjsua_call *call;
1656 pjsip_dialog *dlg;
1657 pj_status_t status;
1658
1659 PJ_UNUSED_ARG(options);
1660
1661 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1662 PJ_EINVAL);
1663
1664 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1665 if (status != PJ_SUCCESS)
1666 return status;
1667
Benny Prijonoc08682e2007-10-04 06:17:58 +00001668 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001669 status = pjsua_media_channel_create_sdp(call->index,
1670 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001671 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001672 if (status != PJ_SUCCESS) {
1673 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1674 status);
1675 pjsip_dlg_dec_lock(dlg);
1676 return status;
1677 }
1678
Benny Prijono224b4e22008-06-19 14:10:28 +00001679 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001680 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1681 if (status != PJ_SUCCESS) {
1682 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1683 pjsip_dlg_dec_lock(dlg);
1684 return status;
1685 }
1686
1687 /* Add additional headers etc */
1688 pjsua_process_msg_data( tdata, msg_data);
1689
1690 /* Send the request */
1691 status = pjsip_inv_send_msg( call->inv, tdata);
1692 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001693 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001694 pjsip_dlg_dec_lock(dlg);
1695 return status;
1696 }
1697
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001698 call->local_hold = PJ_FALSE;
1699
Benny Prijonoc08682e2007-10-04 06:17:58 +00001700 pjsip_dlg_dec_lock(dlg);
1701
1702 return PJ_SUCCESS;
1703}
1704
1705
1706/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001707 * Initiate call transfer to the specified address.
1708 */
1709PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1710 const pj_str_t *dest,
1711 const pjsua_msg_data *msg_data)
1712{
1713 pjsip_evsub *sub;
1714 pjsip_tx_data *tdata;
1715 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001716 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001717 pjsip_generic_string_hdr *gs_hdr;
1718 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001719 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001720 pj_status_t status;
1721
1722
1723 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1724 PJ_EINVAL);
1725
Benny Prijonodc752ca2006-09-22 16:55:42 +00001726 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001727 if (status != PJ_SUCCESS)
1728 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001729
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001730
Benny Prijonod524e822006-09-22 12:48:18 +00001731 /* Create xfer client subscription. */
1732 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001733 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001734
1735 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001736 if (status != PJ_SUCCESS) {
1737 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001738 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001739 return status;
1740 }
1741
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001742 /* Associate this call with the client subscription */
1743 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1744
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001745 /*
1746 * Create REFER request.
1747 */
1748 status = pjsip_xfer_initiate(sub, dest, &tdata);
1749 if (status != PJ_SUCCESS) {
1750 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001751 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001752 return status;
1753 }
1754
Benny Prijono053f5222006-11-11 16:16:04 +00001755 /* Add Referred-By header */
1756 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1757 &dlg->local.info_str);
1758 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1759
1760
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001761 /* Add additional headers etc */
1762 pjsua_process_msg_data( tdata, msg_data);
1763
1764 /* Send. */
1765 status = pjsip_xfer_send_request(sub, tdata);
1766 if (status != PJ_SUCCESS) {
1767 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001768 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001769 return status;
1770 }
1771
1772 /* For simplicity (that's what this program is intended to be!),
1773 * leave the original invite session as it is. More advanced application
1774 * may want to hold the INVITE, or terminate the invite, or whatever.
1775 */
1776
Benny Prijonodc752ca2006-09-22 16:55:42 +00001777 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001778
1779 return PJ_SUCCESS;
1780
1781}
1782
1783
1784/*
Benny Prijono053f5222006-11-11 16:16:04 +00001785 * Initiate attended call transfer to the specified address.
1786 */
1787PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1788 pjsua_call_id dest_call_id,
1789 unsigned options,
1790 const pjsua_msg_data *msg_data)
1791{
1792 pjsua_call *dest_call;
1793 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001794 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001795 pj_str_t str_dest;
1796 int len;
1797 pjsip_uri *uri;
1798 pj_status_t status;
1799
1800
1801 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1802 PJ_EINVAL);
1803 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1804 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1805 PJ_EINVAL);
1806
1807 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1808 &dest_call, &dest_dlg);
1809 if (status != PJ_SUCCESS)
1810 return status;
1811
1812 /*
1813 * Create REFER destination URI with Replaces field.
1814 */
1815
1816 /* Make sure we have sufficient buffer's length */
1817 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1818 dest_dlg->call_id->id.slen +
1819 dest_dlg->remote.info->tag.slen +
1820 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001821 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001822
1823 /* Print URI */
1824 str_dest_buf[0] = '<';
1825 str_dest.slen = 1;
1826
Benny Prijonoa1e69682007-05-11 15:14:34 +00001827 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001828 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1829 str_dest_buf+1, sizeof(str_dest_buf)-1);
1830 if (len < 0)
1831 return PJSIP_EURITOOLONG;
1832
1833 str_dest.slen += len;
1834
1835
1836 /* Build the URI */
1837 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1838 sizeof(str_dest_buf) - str_dest.slen,
1839 "?%s"
1840 "Replaces=%.*s"
1841 "%%3Bto-tag%%3D%.*s"
1842 "%%3Bfrom-tag%%3D%.*s>",
1843 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1844 "" : "Require=replaces&"),
1845 (int)dest_dlg->call_id->id.slen,
1846 dest_dlg->call_id->id.ptr,
1847 (int)dest_dlg->remote.info->tag.slen,
1848 dest_dlg->remote.info->tag.ptr,
1849 (int)dest_dlg->local.info->tag.slen,
1850 dest_dlg->local.info->tag.ptr);
1851
1852 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1853 PJSIP_EURITOOLONG);
1854
1855 str_dest.ptr = str_dest_buf;
1856 str_dest.slen += len;
1857
1858 pjsip_dlg_dec_lock(dest_dlg);
1859
1860 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1861}
1862
1863
1864/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001865 * Send DTMF digits to remote using RFC 2833 payload formats.
1866 */
1867PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1868 const pj_str_t *digits)
1869{
1870 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001871 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001872 pj_status_t status;
1873
1874 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1875 PJ_EINVAL);
1876
Benny Prijonodc752ca2006-09-22 16:55:42 +00001877 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001878 if (status != PJ_SUCCESS)
1879 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001880
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001881 if (!call->session) {
1882 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001883 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001884 return PJ_EINVALIDOP;
1885 }
1886
1887 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1888
Benny Prijonodc752ca2006-09-22 16:55:42 +00001889 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001890
1891 return status;
1892}
1893
1894
1895/**
1896 * Send instant messaging inside INVITE session.
1897 */
1898PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1899 const pj_str_t *mime_type,
1900 const pj_str_t *content,
1901 const pjsua_msg_data *msg_data,
1902 void *user_data)
1903{
1904 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001905 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001906 const pj_str_t mime_text_plain = pj_str("text/plain");
1907 pjsip_media_type ctype;
1908 pjsua_im_data *im_data;
1909 pjsip_tx_data *tdata;
1910 pj_status_t status;
1911
1912
1913 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1914 PJ_EINVAL);
1915
Benny Prijonodc752ca2006-09-22 16:55:42 +00001916 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001917 if (status != PJ_SUCCESS)
1918 return status;
1919
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001920 /* Set default media type if none is specified */
1921 if (mime_type == NULL) {
1922 mime_type = &mime_text_plain;
1923 }
1924
1925 /* Create request message. */
1926 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1927 -1, &tdata);
1928 if (status != PJ_SUCCESS) {
1929 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1930 goto on_return;
1931 }
1932
1933 /* Add accept header. */
1934 pjsip_msg_add_hdr( tdata->msg,
1935 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1936
1937 /* Parse MIME type */
1938 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1939
1940 /* Create "text/plain" message body. */
1941 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1942 &ctype.subtype, content);
1943 if (tdata->msg->body == NULL) {
1944 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1945 pjsip_tx_data_dec_ref(tdata);
1946 goto on_return;
1947 }
1948
1949 /* Add additional headers etc */
1950 pjsua_process_msg_data( tdata, msg_data);
1951
1952 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001953 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001954 im_data->acc_id = call->acc_id;
1955 im_data->call_id = call_id;
1956 im_data->to = call->inv->dlg->remote.info_str;
1957 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1958 im_data->user_data = user_data;
1959
1960
1961 /* Send the request. */
1962 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1963 pjsua_var.mod.id, im_data);
1964 if (status != PJ_SUCCESS) {
1965 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1966 goto on_return;
1967 }
1968
1969on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001970 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001971 return status;
1972}
1973
1974
1975/*
1976 * Send IM typing indication inside INVITE session.
1977 */
1978PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1979 pj_bool_t is_typing,
1980 const pjsua_msg_data*msg_data)
1981{
1982 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001983 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001984 pjsip_tx_data *tdata;
1985 pj_status_t status;
1986
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001987 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1988 PJ_EINVAL);
1989
Benny Prijonodc752ca2006-09-22 16:55:42 +00001990 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001991 if (status != PJ_SUCCESS)
1992 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001993
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001994 /* Create request message. */
1995 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1996 -1, &tdata);
1997 if (status != PJ_SUCCESS) {
1998 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1999 goto on_return;
2000 }
2001
2002 /* Create "application/im-iscomposing+xml" msg body. */
2003 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2004 NULL, NULL, -1);
2005
2006 /* Add additional headers etc */
2007 pjsua_process_msg_data( tdata, msg_data);
2008
2009 /* Send the request. */
2010 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2011 if (status != PJ_SUCCESS) {
2012 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2013 goto on_return;
2014 }
2015
2016on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002017 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002018 return status;
2019}
2020
2021
2022/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002023 * Send arbitrary request.
2024 */
2025PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2026 const pj_str_t *method_str,
2027 const pjsua_msg_data *msg_data)
2028{
2029 pjsua_call *call;
2030 pjsip_dialog *dlg;
2031 pjsip_method method;
2032 pjsip_tx_data *tdata;
2033 pj_status_t status;
2034
2035 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2036 PJ_EINVAL);
2037
2038 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2039 if (status != PJ_SUCCESS)
2040 return status;
2041
2042 /* Init method */
2043 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2044
2045 /* Create request message. */
2046 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2047 if (status != PJ_SUCCESS) {
2048 pjsua_perror(THIS_FILE, "Unable to create request", status);
2049 goto on_return;
2050 }
2051
2052 /* Add additional headers etc */
2053 pjsua_process_msg_data( tdata, msg_data);
2054
2055 /* Send the request. */
2056 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2057 if (status != PJ_SUCCESS) {
2058 pjsua_perror(THIS_FILE, "Unable to send request", status);
2059 goto on_return;
2060 }
2061
2062on_return:
2063 pjsip_dlg_dec_lock(dlg);
2064 return status;
2065}
2066
2067
2068/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002069 * Terminate all calls.
2070 */
2071PJ_DEF(void) pjsua_call_hangup_all(void)
2072{
2073 unsigned i;
2074
2075 PJSUA_LOCK();
2076
2077 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2078 if (pjsua_var.calls[i].inv)
2079 pjsua_call_hangup(i, 0, NULL, NULL);
2080 }
2081
2082 PJSUA_UNLOCK();
2083}
2084
2085
Benny Prijono627cbb42007-09-25 20:48:49 +00002086const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002087{
2088 if (val < 1000) {
2089 pj_ansi_sprintf(buf, "%d", val);
2090 } else if (val < 1000000) {
2091 pj_ansi_sprintf(buf, "%d.%dK",
2092 val / 1000,
2093 (val % 1000) / 100);
2094 } else {
2095 pj_ansi_sprintf(buf, "%d.%02dM",
2096 val / 1000000,
2097 (val % 1000000) / 10000);
2098 }
2099
2100 return buf;
2101}
2102
2103
2104/* Dump media session */
2105static void dump_media_session(const char *indent,
2106 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002107 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002108{
2109 unsigned i;
2110 char *p = buf, *end = buf+maxlen;
2111 int len;
2112 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002113 pjmedia_session *session = call->session;
2114 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002115
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002116 pjmedia_transport_info_init(&tp_info);
2117
2118 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002119 pjmedia_session_get_info(session, &info);
2120
2121 for (i=0; i<info.stream_cnt; ++i) {
2122 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002123 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002124 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002125 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002126 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002127 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002128 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002129
2130 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002131 // rem_addr will contain actual address of RTP originator, instead of
2132 // remote RTP address specified by stream which is fetched from the SDP.
2133 // Please note that we are assuming only one stream per call.
2134 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2135 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002136 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2137 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002138 sizeof(rem_addr_buf), 3);
2139 } else {
2140 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002141 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002142 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002143
2144 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
2145 dir = "sendonly";
2146 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2147 dir = "recvonly";
2148 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2149 dir = "sendrecv";
2150 else
2151 dir = "inactive";
2152
2153
2154 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002155 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002156 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002157 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002158 info.stream_info[i].fmt.encoding_name.ptr,
2159 info.stream_info[i].fmt.clock_rate / 1000,
2160 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002161 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002162 if (len < 1 || len > end-p) {
2163 *p = '\0';
2164 return;
2165 }
2166
2167 p += len;
2168 *p++ = '\n';
2169 *p = '\0';
2170
2171 if (stat.rx.update_cnt == 0)
2172 strcpy(last_update, "never");
2173 else {
2174 pj_gettimeofday(&now);
2175 PJ_TIME_VAL_SUB(now, stat.rx.update);
2176 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2177 now.sec / 3600,
2178 (now.sec % 3600) / 60,
2179 now.sec % 60,
2180 now.msec);
2181 }
2182
Benny Prijono80019eb2006-08-07 13:22:23 +00002183 pj_gettimeofday(&media_duration);
2184 PJ_TIME_VAL_SUB(media_duration, stat.start);
2185 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2186 media_duration.msec = 1;
2187
Benny Prijono1402a4a2008-01-08 23:41:22 +00002188 /* protect against division by zero */
2189 if (stat.rx.pkt == 0)
2190 stat.rx.pkt = 1;
2191 if (stat.tx.pkt == 0)
2192 stat.tx.pkt = 1;
2193
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002194 len = pj_ansi_snprintf(p, end-p,
2195 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002196 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002197 "%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 +00002198 "%s (msec) min avg max last dev\n"
2199 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2200 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002201 indent, info.stream_info[i].fmt.pt,
2202 last_update,
2203 indent,
2204 good_number(packets, stat.rx.pkt),
2205 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002206 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002207 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2208 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 +00002209 indent,
2210 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002211 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002212 stat.rx.discard,
2213 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002214 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002215 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002216 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002217 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002218 indent, indent,
2219 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002220 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002221 stat.rx.loss_period.max / 1000.0,
2222 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002223 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002224 indent,
2225 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002226 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002227 stat.rx.jitter.max / 1000.0,
2228 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002229 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002230 ""
2231 );
2232
2233 if (len < 1 || len > end-p) {
2234 *p = '\0';
2235 return;
2236 }
2237
2238 p += len;
2239 *p++ = '\n';
2240 *p = '\0';
2241
2242 if (stat.tx.update_cnt == 0)
2243 strcpy(last_update, "never");
2244 else {
2245 pj_gettimeofday(&now);
2246 PJ_TIME_VAL_SUB(now, stat.tx.update);
2247 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2248 now.sec / 3600,
2249 (now.sec % 3600) / 60,
2250 now.sec % 60,
2251 now.msec);
2252 }
2253
2254 len = pj_ansi_snprintf(p, end-p,
2255 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002256 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002257 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002258 "%s (msec) min avg max last dev \n"
2259 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2260 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002261 indent,
2262 info.stream_info[i].tx_pt,
2263 info.stream_info[i].param->info.frm_ptime *
2264 info.stream_info[i].param->setting.frm_per_pkt,
2265 last_update,
2266
2267 indent,
2268 good_number(packets, stat.tx.pkt),
2269 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002270 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002271 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2272 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 +00002273
2274 indent,
2275 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002276 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002277 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002278 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002279 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002280 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002281
2282 indent, indent,
2283 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002284 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002285 stat.tx.loss_period.max / 1000.0,
2286 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002287 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002288 indent,
2289 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002290 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002291 stat.tx.jitter.max / 1000.0,
2292 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002293 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002294 ""
2295 );
2296
2297 if (len < 1 || len > end-p) {
2298 *p = '\0';
2299 return;
2300 }
2301
2302 p += len;
2303 *p++ = '\n';
2304 *p = '\0';
2305
2306 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002307 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002308 indent,
2309 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002310 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002311 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002312 stat.rtt.last / 1000.0,
2313 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002314 );
2315 if (len < 1 || len > end-p) {
2316 *p = '\0';
2317 return;
2318 }
2319
2320 p += len;
2321 *p++ = '\n';
2322 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002323
2324#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2325# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2326 do { \
2327 if (samples <= 4294) \
2328 usec = samples * 1000000 / clock_rate; \
2329 else { \
2330 usec = samples * 1000 / clock_rate; \
2331 usec *= 1000; \
2332 } \
2333 } while(0)
2334
2335# define PRINT_VOIP_MTC_VAL(s, v) \
2336 if (v == 127) \
2337 sprintf(s, "(na)"); \
2338 else \
2339 sprintf(s, "%d", v)
2340
2341# define VALIDATE_PRINT_BUF() \
2342 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2343 p += len; *p++ = '\n'; *p = '\0'
2344
2345
2346 do {
2347 char loss[16], dup[16];
2348 char jitter[80];
2349 char toh[80];
2350 char plc[16], jba[16], jbr[16];
2351 char signal_lvl[16], noise_lvl[16], rerl[16];
2352 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2353 pjmedia_rtcp_xr_stat xr_stat;
2354 unsigned clock_rate;
2355
2356 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2357 PJ_SUCCESS)
2358 {
2359 break;
2360 }
2361
2362 clock_rate = info.stream_info[i].fmt.clock_rate;
2363
2364 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2365 VALIDATE_PRINT_BUF();
2366
2367 /* Statistics Summary */
2368 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2369 VALIDATE_PRINT_BUF();
2370
2371 if (xr_stat.rx.stat_sum.l)
2372 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2373 else
2374 sprintf(loss, "(na)");
2375
2376 if (xr_stat.rx.stat_sum.d)
2377 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2378 else
2379 sprintf(dup, "(na)");
2380
2381 if (xr_stat.rx.stat_sum.j) {
2382 unsigned jmin, jmax, jmean, jdev;
2383
2384 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2385 clock_rate);
2386 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2387 clock_rate);
2388 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2389 clock_rate);
2390 SAMPLES_TO_USEC(jdev,
2391 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2392 clock_rate);
2393 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2394 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2395 } else
2396 sprintf(jitter, "(report not available)");
2397
2398 if (xr_stat.rx.stat_sum.t) {
2399 sprintf(toh, "%11d %11d %11d %11d",
2400 xr_stat.rx.stat_sum.toh.min,
2401 xr_stat.rx.stat_sum.toh.mean,
2402 xr_stat.rx.stat_sum.toh.max,
2403 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2404 } else
2405 sprintf(toh, "(report not available)");
2406
2407 if (xr_stat.rx.stat_sum.update.sec == 0)
2408 strcpy(last_update, "never");
2409 else {
2410 pj_gettimeofday(&now);
2411 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2412 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2413 now.sec / 3600,
2414 (now.sec % 3600) / 60,
2415 now.sec % 60,
2416 now.msec);
2417 }
2418
2419 len = pj_ansi_snprintf(p, end-p,
2420 "%s RX last update: %s\n"
2421 "%s begin seq=%d, end seq=%d\n"
2422 "%s pkt loss=%s, dup=%s\n"
2423 "%s (msec) min avg max dev\n"
2424 "%s jitter : %s\n"
2425 "%s toh : %s",
2426 indent, last_update,
2427 indent,
2428 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2429 indent, loss, dup,
2430 indent,
2431 indent, jitter,
2432 indent, toh
2433 );
2434 VALIDATE_PRINT_BUF();
2435
2436 if (xr_stat.tx.stat_sum.l)
2437 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2438 else
2439 sprintf(loss, "(na)");
2440
2441 if (xr_stat.tx.stat_sum.d)
2442 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2443 else
2444 sprintf(dup, "(na)");
2445
2446 if (xr_stat.tx.stat_sum.j) {
2447 unsigned jmin, jmax, jmean, jdev;
2448
2449 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2450 clock_rate);
2451 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2452 clock_rate);
2453 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2454 clock_rate);
2455 SAMPLES_TO_USEC(jdev,
2456 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2457 clock_rate);
2458 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2459 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2460 } else
2461 sprintf(jitter, "(report not available)");
2462
2463 if (xr_stat.tx.stat_sum.t) {
2464 sprintf(toh, "%11d %11d %11d %11d",
2465 xr_stat.tx.stat_sum.toh.min,
2466 xr_stat.tx.stat_sum.toh.mean,
2467 xr_stat.tx.stat_sum.toh.max,
2468 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2469 } else
2470 sprintf(toh, "(report not available)");
2471
2472 if (xr_stat.tx.stat_sum.update.sec == 0)
2473 strcpy(last_update, "never");
2474 else {
2475 pj_gettimeofday(&now);
2476 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2477 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2478 now.sec / 3600,
2479 (now.sec % 3600) / 60,
2480 now.sec % 60,
2481 now.msec);
2482 }
2483
2484 len = pj_ansi_snprintf(p, end-p,
2485 "%s TX last update: %s\n"
2486 "%s begin seq=%d, end seq=%d\n"
2487 "%s pkt loss=%s, dup=%s\n"
2488 "%s (msec) min avg max dev\n"
2489 "%s jitter : %s\n"
2490 "%s toh : %s",
2491 indent, last_update,
2492 indent,
2493 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2494 indent, loss, dup,
2495 indent,
2496 indent, jitter,
2497 indent, toh
2498 );
2499 VALIDATE_PRINT_BUF();
2500
2501
2502 /* VoIP Metrics */
2503 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2504 VALIDATE_PRINT_BUF();
2505
2506 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2507 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2508 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2509 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2510 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2511 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2512 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2513
2514 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2515 case PJMEDIA_RTCP_XR_PLC_DIS:
2516 sprintf(plc, "DISABLED");
2517 break;
2518 case PJMEDIA_RTCP_XR_PLC_ENH:
2519 sprintf(plc, "ENHANCED");
2520 break;
2521 case PJMEDIA_RTCP_XR_PLC_STD:
2522 sprintf(plc, "STANDARD");
2523 break;
2524 case PJMEDIA_RTCP_XR_PLC_UNK:
2525 default:
2526 sprintf(plc, "UNKNOWN");
2527 break;
2528 }
2529
2530 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2531 case PJMEDIA_RTCP_XR_JB_FIXED:
2532 sprintf(jba, "FIXED");
2533 break;
2534 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2535 sprintf(jba, "ADAPTIVE");
2536 break;
2537 default:
2538 sprintf(jba, "UNKNOWN");
2539 break;
2540 }
2541
2542 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2543
2544 if (xr_stat.rx.voip_mtc.update.sec == 0)
2545 strcpy(last_update, "never");
2546 else {
2547 pj_gettimeofday(&now);
2548 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2549 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2550 now.sec / 3600,
2551 (now.sec % 3600) / 60,
2552 now.sec % 60,
2553 now.msec);
2554 }
2555
2556 len = pj_ansi_snprintf(p, end-p,
2557 "%s RX last update: %s\n"
2558 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2559 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2560 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2561 "%s delay : round trip=%d%s, end system=%d%s\n"
2562 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2563 "%s quality : R factor=%s, ext R factor=%s\n"
2564 "%s MOS LQ=%s, MOS CQ=%s\n"
2565 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2566 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2567 indent,
2568 last_update,
2569 /* packets */
2570 indent,
2571 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2572 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2573 /* burst */
2574 indent,
2575 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2576 xr_stat.rx.voip_mtc.burst_dur, "ms",
2577 /* gap */
2578 indent,
2579 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2580 xr_stat.rx.voip_mtc.gap_dur, "ms",
2581 /* delay */
2582 indent,
2583 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2584 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2585 /* level */
2586 indent,
2587 signal_lvl, "dB",
2588 noise_lvl, "dB",
2589 rerl, "",
2590 /* quality */
2591 indent,
2592 r_factor, ext_r_factor,
2593 indent,
2594 mos_lq, mos_cq,
2595 /* config */
2596 indent,
2597 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2598 /* JB delay */
2599 indent,
2600 xr_stat.rx.voip_mtc.jb_nom, "ms",
2601 xr_stat.rx.voip_mtc.jb_max, "ms",
2602 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2603 );
2604 VALIDATE_PRINT_BUF();
2605
2606 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2607 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2608 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2609 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2610 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2611 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2612 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2613
2614 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2615 case PJMEDIA_RTCP_XR_PLC_DIS:
2616 sprintf(plc, "DISABLED");
2617 break;
2618 case PJMEDIA_RTCP_XR_PLC_ENH:
2619 sprintf(plc, "ENHANCED");
2620 break;
2621 case PJMEDIA_RTCP_XR_PLC_STD:
2622 sprintf(plc, "STANDARD");
2623 break;
2624 case PJMEDIA_RTCP_XR_PLC_UNK:
2625 default:
2626 sprintf(plc, "unknown");
2627 break;
2628 }
2629
2630 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2631 case PJMEDIA_RTCP_XR_JB_FIXED:
2632 sprintf(jba, "FIXED");
2633 break;
2634 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2635 sprintf(jba, "ADAPTIVE");
2636 break;
2637 default:
2638 sprintf(jba, "unknown");
2639 break;
2640 }
2641
2642 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2643
2644 if (xr_stat.tx.voip_mtc.update.sec == 0)
2645 strcpy(last_update, "never");
2646 else {
2647 pj_gettimeofday(&now);
2648 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2649 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2650 now.sec / 3600,
2651 (now.sec % 3600) / 60,
2652 now.sec % 60,
2653 now.msec);
2654 }
2655
2656 len = pj_ansi_snprintf(p, end-p,
2657 "%s TX last update: %s\n"
2658 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2659 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2660 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2661 "%s delay : round trip=%d%s, end system=%d%s\n"
2662 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2663 "%s quality : R factor=%s, ext R factor=%s\n"
2664 "%s MOS LQ=%s, MOS CQ=%s\n"
2665 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2666 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2667 indent,
2668 last_update,
2669 /* pakcets */
2670 indent,
2671 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2672 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2673 /* burst */
2674 indent,
2675 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2676 xr_stat.tx.voip_mtc.burst_dur, "ms",
2677 /* gap */
2678 indent,
2679 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2680 xr_stat.tx.voip_mtc.gap_dur, "ms",
2681 /* delay */
2682 indent,
2683 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2684 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2685 /* level */
2686 indent,
2687 signal_lvl, "dB",
2688 noise_lvl, "dB",
2689 rerl, "",
2690 /* quality */
2691 indent,
2692 r_factor, ext_r_factor,
2693 indent,
2694 mos_lq, mos_cq,
2695 /* config */
2696 indent,
2697 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2698 /* JB delay */
2699 indent,
2700 xr_stat.tx.voip_mtc.jb_nom, "ms",
2701 xr_stat.tx.voip_mtc.jb_max, "ms",
2702 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2703 );
2704 VALIDATE_PRINT_BUF();
2705
2706
2707 /* RTT delay (by receiver side) */
2708 len = pj_ansi_snprintf(p, end-p,
2709 "%s RTT (from recv) min avg max last dev",
2710 indent);
2711 VALIDATE_PRINT_BUF();
2712 len = pj_ansi_snprintf(p, end-p,
2713 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2714 indent,
2715 xr_stat.rtt.min / 1000.0,
2716 xr_stat.rtt.mean / 1000.0,
2717 xr_stat.rtt.max / 1000.0,
2718 xr_stat.rtt.last / 1000.0,
2719 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2720 );
2721 VALIDATE_PRINT_BUF();
2722 } while(0);
2723#endif
2724
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002725 }
2726}
2727
2728
2729/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002730void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002731 int call_id,
2732 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002733{
2734 int len;
2735 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2736 pjsip_dialog *dlg = inv->dlg;
2737 char userinfo[128];
2738
2739 /* Dump invite sesion info. */
2740
2741 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
Benny Prijono329d6382009-05-29 13:04:03 +00002742 if (len < 0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002743 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2744 else
2745 userinfo[len] = '\0';
2746
2747 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2748 title,
2749 pjsip_inv_state_name(inv->state),
2750 userinfo);
2751 if (len < 1 || len >= (int)size) {
2752 pj_ansi_strcpy(buf, "<--uri too long-->");
2753 len = 18;
2754 } else
2755 buf[len] = '\0';
2756}
2757
2758
2759/*
2760 * Dump call and media statistics to string.
2761 */
2762PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2763 pj_bool_t with_media,
2764 char *buffer,
2765 unsigned maxlen,
2766 const char *indent)
2767{
2768 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002769 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002770 pj_time_val duration, res_delay, con_delay;
2771 char tmp[128];
2772 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002773 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002774 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002775 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002776
2777 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2778 PJ_EINVAL);
2779
Benny Prijonodc752ca2006-09-22 16:55:42 +00002780 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002781 if (status != PJ_SUCCESS)
2782 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002783
2784 *buffer = '\0';
2785 p = buffer;
2786 end = buffer + maxlen;
2787 len = 0;
2788
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002789 print_call(indent, call_id, tmp, sizeof(tmp));
2790
2791 len = pj_ansi_strlen(tmp);
2792 pj_ansi_strcpy(buffer, tmp);
2793
2794 p += len;
2795 *p++ = '\r';
2796 *p++ = '\n';
2797
2798 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002799 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002800 pj_gettimeofday(&duration);
2801 PJ_TIME_VAL_SUB(duration, call->conn_time);
2802 con_delay = call->conn_time;
2803 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2804 } else {
2805 duration.sec = duration.msec = 0;
2806 con_delay.sec = con_delay.msec = 0;
2807 }
2808
2809 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002810 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002811 res_delay = call->res_time;
2812 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2813 } else {
2814 res_delay.sec = res_delay.msec = 0;
2815 }
2816
2817 /* Print duration */
2818 len = pj_ansi_snprintf(p, end-p,
2819 "%s Call time: %02dh:%02dm:%02ds, "
2820 "1st res in %d ms, conn in %dms",
2821 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002822 (int)(duration.sec / 3600),
2823 (int)((duration.sec % 3600)/60),
2824 (int)(duration.sec % 60),
2825 (int)PJ_TIME_VAL_MSEC(res_delay),
2826 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002827
2828 if (len > 0 && len < end-p) {
2829 p += len;
2830 *p++ = '\n';
2831 *p = '\0';
2832 }
2833
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002834 /* Get and ICE SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002835 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002836 pjmedia_transport_get_info(call->med_tp, &tp_info);
2837 if (tp_info.specific_info_cnt > 0) {
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002838 unsigned i;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002839 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2840 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2841 {
2842 pjmedia_srtp_info *srtp_info =
2843 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2844
2845 len = pj_ansi_snprintf(p, end-p,
2846 "%s SRTP status: %s Crypto-suite: %s",
2847 indent,
2848 (srtp_info->active?"Active":"Not active"),
2849 srtp_info->tx_policy.name.ptr);
2850 if (len > 0 && len < end-p) {
2851 p += len;
2852 *p++ = '\n';
2853 *p = '\0';
2854 }
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002855 } else if (tp_info.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
2856 const pjmedia_ice_transport_info *ii;
2857
2858 ii = (const pjmedia_ice_transport_info*)
2859 tp_info.spc_info[i].buffer;
2860
2861 len = pj_ansi_snprintf(p, end-p,
2862 "%s ICE role: %s, state: %s, comp_cnt: %u",
2863 indent,
2864 pj_ice_sess_role_name(ii->role),
2865 pj_ice_strans_state_name(ii->sess_state),
2866 ii->comp_cnt);
2867 if (len > 0 && len < end-p) {
2868 p += len;
2869 *p++ = '\n';
2870 *p = '\0';
2871 }
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002872 }
2873 }
2874 }
2875
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002876 /* Dump session statistics */
2877 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002878 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002879
Benny Prijonodc752ca2006-09-22 16:55:42 +00002880 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002881
2882 return PJ_SUCCESS;
2883}
2884
2885
2886/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002887 * This callback receives notification from invite session when the
2888 * session state has changed.
2889 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002890static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2891 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002892{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002893 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002894
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002895 PJSUA_LOCK();
2896
Benny Prijonoa1e69682007-05-11 15:14:34 +00002897 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002898
2899 if (!call) {
2900 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002901 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002902 }
2903
Benny Prijonoe21e7842006-04-09 16:46:05 +00002904
2905 /* Get call times */
2906 switch (inv->state) {
2907 case PJSIP_INV_STATE_EARLY:
2908 case PJSIP_INV_STATE_CONNECTING:
2909 if (call->res_time.sec == 0)
2910 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002911 call->last_code = (pjsip_status_code)
2912 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002913 pj_strncpy(&call->last_text,
2914 &e->body.tsx_state.tsx->status_text,
2915 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002916 break;
2917 case PJSIP_INV_STATE_CONFIRMED:
2918 pj_gettimeofday(&call->conn_time);
2919 break;
2920 case PJSIP_INV_STATE_DISCONNECTED:
2921 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002922 if (call->res_time.sec == 0)
2923 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002924 if (e->type == PJSIP_EVENT_TSX_STATE &&
2925 e->body.tsx_state.tsx->status_code > call->last_code)
2926 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002927 call->last_code = (pjsip_status_code)
2928 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002929 pj_strncpy(&call->last_text,
2930 &e->body.tsx_state.tsx->status_text,
2931 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002932 } else {
2933 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
2934 pj_strncpy(&call->last_text,
2935 pjsip_get_status_text(call->last_code),
2936 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002937 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002938 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002939 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002940 call->last_code = (pjsip_status_code)
2941 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002942 pj_strncpy(&call->last_text,
2943 &e->body.tsx_state.tsx->status_text,
2944 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002945 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002946 }
2947
Benny Prijono26ff9062006-02-21 23:47:00 +00002948 /* If this is an outgoing INVITE that was created because of
2949 * REFER/transfer, send NOTIFY to transferer.
2950 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002951 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002952 int st_code = -1;
2953 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2954
2955
Benny Prijonoa91a0032006-02-26 21:23:45 +00002956 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002957 case PJSIP_INV_STATE_NULL:
2958 case PJSIP_INV_STATE_CALLING:
2959 /* Do nothing */
2960 break;
2961
2962 case PJSIP_INV_STATE_EARLY:
2963 case PJSIP_INV_STATE_CONNECTING:
2964 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002965 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
2966 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2967 else
2968 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002969 break;
2970
Benny Prijono140beae2009-10-11 05:06:43 +00002971 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002972#if 0
2973/* We don't need this, as we've terminated the subscription in
2974 * CONNECTING state.
2975 */
Benny Prijono26ff9062006-02-21 23:47:00 +00002976 /* When state is confirmed, send the final 200/OK and terminate
2977 * subscription.
2978 */
2979 st_code = e->body.tsx_state.tsx->status_code;
2980 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002981#endif
Benny Prijono140beae2009-10-11 05:06:43 +00002982 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002983
2984 case PJSIP_INV_STATE_DISCONNECTED:
2985 st_code = e->body.tsx_state.tsx->status_code;
2986 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2987 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002988
Benny Prijono8b1889b2006-06-06 18:40:40 +00002989 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002990 /* Nothing to do. Just to keep gcc from complaining about
2991 * unused enums.
2992 */
2993 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002994 }
2995
2996 if (st_code != -1) {
2997 pjsip_tx_data *tdata;
2998 pj_status_t status;
2999
Benny Prijonoa91a0032006-02-26 21:23:45 +00003000 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00003001 ev_state, st_code,
3002 NULL, &tdata);
3003 if (status != PJ_SUCCESS) {
3004 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
3005 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003006 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00003007 if (status != PJ_SUCCESS) {
3008 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
3009 }
3010 }
3011 }
3012 }
3013
Benny Prijono84126ab2006-02-09 09:30:09 +00003014
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003015 if (pjsua_var.ua_cfg.cb.on_call_state)
3016 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003017
3018 /* call->inv may be NULL now */
3019
Benny Prijono84126ab2006-02-09 09:30:09 +00003020 /* Destroy media session when invite session is disconnected. */
3021 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003022
Benny Prijonoa91a0032006-02-26 21:23:45 +00003023 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003024
Benny Prijono275fd682006-03-22 11:59:11 +00003025 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003026 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003027
Benny Prijono105217f2006-03-06 16:25:59 +00003028 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003029 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003030 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003031
3032 /* Reset call */
3033 reset_call(call->index);
3034
Benny Prijono84126ab2006-02-09 09:30:09 +00003035 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003036
3037 PJSUA_UNLOCK();
3038}
3039
3040/*
3041 * This callback is called by invite session framework when UAC session
3042 * has forked.
3043 */
3044static void pjsua_call_on_forked( pjsip_inv_session *inv,
3045 pjsip_event *e)
3046{
3047 PJ_UNUSED_ARG(inv);
3048 PJ_UNUSED_ARG(e);
3049
3050 PJ_TODO(HANDLE_FORKED_DIALOG);
3051}
3052
3053
3054/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003055 * Callback from UA layer when forked dialog response is received.
3056 */
3057pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3058{
3059 if (dlg->uac_has_2xx &&
3060 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3061 pjsip_rdata_get_tsx(res) == NULL &&
3062 res->msg_info.msg->line.status.code/100 == 2)
3063 {
3064 pjsip_dialog *forked_dlg;
3065 pjsip_tx_data *bye;
3066 pj_status_t status;
3067
3068 /* Create forked dialog */
3069 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3070 if (status != PJ_SUCCESS)
3071 return NULL;
3072
3073 pjsip_dlg_inc_lock(forked_dlg);
3074
3075 /* Disconnect the call */
3076 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3077 -1, &bye);
3078 if (status == PJ_SUCCESS) {
3079 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3080 }
3081
3082 pjsip_dlg_dec_lock(forked_dlg);
3083
3084 if (status != PJ_SUCCESS) {
3085 return NULL;
3086 }
3087
3088 return forked_dlg;
3089
3090 } else {
3091 return dlg;
3092 }
3093}
3094
3095/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003096 * Disconnect call upon error.
3097 */
3098static void call_disconnect( pjsip_inv_session *inv,
3099 int code )
3100{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003101 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003102 pjsip_tx_data *tdata;
3103 pj_status_t status;
3104
Benny Prijono59b3aed2008-01-15 16:54:54 +00003105 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3106
Benny Prijonoa38ada02006-07-02 14:22:35 +00003107 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003108 if (status != PJ_SUCCESS)
3109 return;
3110
3111 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003112 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3113 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3114 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003115 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003116 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003117
Benny Prijono734fc2d2008-03-17 16:05:35 +00003118 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003119 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003120 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003121 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003122 if (status == PJ_SUCCESS) {
3123 pjsip_create_sdp_body(tdata->pool, local_sdp,
3124 &tdata->msg->body);
3125 }
3126 }
3127
3128 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003129}
3130
3131/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003132 * Callback to be called when SDP offer/answer negotiation has just completed
3133 * in the session. This function will start/update media if negotiation
3134 * has succeeded.
3135 */
3136static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3137 pj_status_t status)
3138{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003139 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003140 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003141 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003142
3143 PJSUA_LOCK();
3144
Benny Prijonoa1e69682007-05-11 15:14:34 +00003145 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003146
3147 if (status != PJ_SUCCESS) {
3148
3149 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3150
Benny Prijono2331d202008-06-26 15:46:52 +00003151 /* Do not deinitialize media since this may be a re-INVITE or
3152 * UPDATE (which in this case the media should not get affected
3153 * by the failed re-INVITE/UPDATE). The media will be shutdown
3154 * when call is disconnected anyway.
3155 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003156 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003157 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003158
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003159 /* Disconnect call if we're not in the middle of initializing an
3160 * UAS dialog and if this is not a re-INVITE
3161 */
3162 if (inv->state != PJSIP_INV_STATE_NULL &&
3163 inv->state != PJSIP_INV_STATE_CONFIRMED)
3164 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003165 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003166 }
3167
3168 PJSUA_UNLOCK();
3169 return;
3170 }
3171
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003172
3173 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003174 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003175 if (status != PJ_SUCCESS) {
3176 pjsua_perror(THIS_FILE,
3177 "Unable to retrieve currently active local SDP",
3178 status);
3179 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3180 PJSUA_UNLOCK();
3181 return;
3182 }
3183
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003184 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3185 if (status != PJ_SUCCESS) {
3186 pjsua_perror(THIS_FILE,
3187 "Unable to retrieve currently active remote SDP",
3188 status);
3189 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3190 PJSUA_UNLOCK();
3191 return;
3192 }
3193
Benny Prijono91a6a172007-10-31 08:59:29 +00003194 /* Update remote's NAT type */
3195 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3196 update_remote_nat_type(call, remote_sdp);
3197 }
3198
3199 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003200 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003201 if (status != PJ_SUCCESS) {
3202 pjsua_perror(THIS_FILE, "Unable to create media session",
3203 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003204 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003205 /* No need to deinitialize; media will be shutdown when call
3206 * state is disconnected anyway.
3207 */
3208 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003209 PJSUA_UNLOCK();
3210 return;
3211 }
3212
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003213
3214 /* Call application callback, if any */
3215 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3216 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3217
3218
3219 PJSUA_UNLOCK();
3220}
3221
3222
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003223/* Create SDP for call hold. */
3224static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3225 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003226{
3227 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003228 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003229 pjmedia_sdp_session *sdp;
3230
Benny Prijono40d62b62009-08-12 17:53:47 +00003231 /* Use call's provisional pool */
3232 pool = call->inv->pool_prov;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003233
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003234 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003235 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3236 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003237 if (status != PJ_SUCCESS) {
3238 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3239 return status;
3240 }
3241
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003242 /* Call-hold is done by set the media direction to 'sendonly'
3243 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3244 * 'inactive' (PJMEDIA_DIR_NONE).
3245 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3246 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003247 /* http://trac.pjsip.org/repos/ticket/880
3248 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3249 */
3250 if (1) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003251 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003252
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003253 /* Remove existing directions attributes */
3254 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3255 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3256 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3257 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003258
Benny Prijonoe0860132009-06-05 10:14:20 +00003259 if (call->media_dir & PJMEDIA_DIR_ENCODING) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003260 /* Add sendonly attribute */
3261 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3262 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3263 } else {
3264 /* Add inactive attribute */
3265 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3266 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3267 }
3268 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003269
3270 *p_answer = sdp;
3271
3272 return status;
3273}
3274
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003275/*
3276 * Called when session received new offer.
3277 */
3278static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3279 const pjmedia_sdp_session *offer)
3280{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003281 pjsua_call *call;
3282 pjmedia_sdp_conn *conn;
3283 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003284 pj_status_t status;
3285
3286 PJSUA_LOCK();
3287
Benny Prijonoa1e69682007-05-11 15:14:34 +00003288 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003289
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003290 conn = offer->media[0]->conn;
3291 if (!conn)
3292 conn = offer->conn;
3293
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003294 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003295 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3296 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003297
Benny Prijono40d62b62009-08-12 17:53:47 +00003298 status = pjsua_media_channel_create_sdp(call->index,
3299 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003300 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003301 if (status != PJ_SUCCESS) {
3302 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3303 PJSUA_UNLOCK();
3304 return;
3305 }
3306
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003307 /* Check if offer's conn address is zero */
3308 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3309 pj_strcmp2(&conn->addr, "0")==0)
3310 {
3311 /* Modify address */
3312 answer->conn->addr = pj_str("0.0.0.0");
3313 }
3314
3315 /* Check if call is on-hold */
3316 if (call->local_hold) {
3317 pjmedia_sdp_attr *attr;
3318
3319 /* Remove existing directions attributes */
3320 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3321 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3322 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3323 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3324
3325 /* Keep call on-hold by setting 'sendonly' attribute.
3326 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3327 */
Benny Prijono40d62b62009-08-12 17:53:47 +00003328 attr = pjmedia_sdp_attr_create(call->inv->pool_prov, "sendonly", NULL);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003329 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3330 }
3331
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003332 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3333 if (status != PJ_SUCCESS) {
3334 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3335 PJSUA_UNLOCK();
3336 return;
3337 }
3338
3339 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003340}
3341
3342
3343/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003344 * Called to generate new offer.
3345 */
3346static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3347 pjmedia_sdp_session **offer)
3348{
3349 pjsua_call *call;
3350 pj_status_t status;
3351
3352 PJSUA_LOCK();
3353
3354 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3355
3356 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003357 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003358 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003359 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003360 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003361 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003362 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003363 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3364 call->index));
3365
Benny Prijono40d62b62009-08-12 17:53:47 +00003366 status = pjsua_media_channel_create_sdp(call->index,
3367 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003368 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003369 }
3370
3371 if (status != PJ_SUCCESS) {
3372 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3373 PJSUA_UNLOCK();
3374 return;
3375 }
3376
Benny Prijono77998ce2007-06-20 10:03:46 +00003377 PJSUA_UNLOCK();
3378}
3379
3380
3381/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003382 * Callback called by event framework when the xfer subscription state
3383 * has changed.
3384 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003385static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3386{
3387
3388 PJ_UNUSED_ARG(event);
3389
3390 /*
3391 * When subscription is accepted (got 200/OK to REFER), check if
3392 * subscription suppressed.
3393 */
3394 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3395
3396 pjsip_rx_data *rdata;
3397 pjsip_generic_string_hdr *refer_sub;
3398 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3399 pjsua_call *call;
3400
Benny Prijonoa1e69682007-05-11 15:14:34 +00003401 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003402
3403 /* Must be receipt of response message */
3404 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3405 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3406 rdata = event->body.tsx_state.src.rdata;
3407
3408 /* Find Refer-Sub header */
3409 refer_sub = (pjsip_generic_string_hdr*)
3410 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3411 &REFER_SUB, NULL);
3412
3413 /* Check if subscription is suppressed */
3414 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3415 /* Since no subscription is desired, assume that call has been
3416 * transfered successfully.
3417 */
3418 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3419 const pj_str_t ACCEPTED = { "Accepted", 8 };
3420 pj_bool_t cont = PJ_FALSE;
3421 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3422 200,
3423 &ACCEPTED,
3424 PJ_TRUE,
3425 &cont);
3426 }
3427
3428 /* Yes, subscription is suppressed.
3429 * Terminate our subscription now.
3430 */
3431 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3432 "event subcription..."));
3433 pjsip_evsub_terminate(sub, PJ_TRUE);
3434
3435 } else {
3436 /* Notify application about call transfer progress.
3437 * Initially notify with 100/Accepted status.
3438 */
3439 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3440 const pj_str_t ACCEPTED = { "Accepted", 8 };
3441 pj_bool_t cont = PJ_FALSE;
3442 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3443 100,
3444 &ACCEPTED,
3445 PJ_FALSE,
3446 &cont);
3447 }
3448 }
3449 }
3450 /*
3451 * On incoming NOTIFY, notify application about call transfer progress.
3452 */
3453 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3454 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3455 {
3456 pjsua_call *call;
3457 pjsip_msg *msg;
3458 pjsip_msg_body *body;
3459 pjsip_status_line status_line;
3460 pj_bool_t is_last;
3461 pj_bool_t cont;
3462 pj_status_t status;
3463
Benny Prijonoa1e69682007-05-11 15:14:34 +00003464 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003465
3466 /* When subscription is terminated, clear the xfer_sub member of
3467 * the inv_data.
3468 */
3469 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3470 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3471 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3472
3473 }
3474
3475 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3476 /* Application is not interested with call progress status */
3477 return;
3478 }
3479
3480 /* This better be a NOTIFY request */
3481 if (event->type == PJSIP_EVENT_TSX_STATE &&
3482 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3483 {
3484 pjsip_rx_data *rdata;
3485
3486 rdata = event->body.tsx_state.src.rdata;
3487
3488 /* Check if there's body */
3489 msg = rdata->msg_info.msg;
3490 body = msg->body;
3491 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003492 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003493 "Warning: received NOTIFY without message body"));
3494 return;
3495 }
3496
3497 /* Check for appropriate content */
3498 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3499 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3500 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003501 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003502 "Warning: received NOTIFY with non message/sipfrag "
3503 "content"));
3504 return;
3505 }
3506
3507 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003508 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003509 &status_line);
3510 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003511 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003512 "Warning: received NOTIFY with invalid "
3513 "message/sipfrag content"));
3514 return;
3515 }
3516
3517 } else {
3518 status_line.code = 500;
3519 status_line.reason = *pjsip_get_status_text(500);
3520 }
3521
3522 /* Notify application */
3523 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3524 cont = !is_last;
3525 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3526 status_line.code,
3527 &status_line.reason,
3528 is_last, &cont);
3529
3530 if (!cont) {
3531 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3532 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003533
3534 /* If the call transfer has completed but the subscription is
3535 * not terminated, terminate it now.
3536 */
3537 if (status_line.code/100 == 2 && !is_last) {
3538 pjsip_tx_data *tdata;
3539
3540 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3541 0, &tdata);
3542 if (status == PJ_SUCCESS)
3543 status = pjsip_evsub_send_request(sub, tdata);
3544 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003545 }
3546}
3547
3548
3549/*
3550 * Callback called by event framework when the xfer subscription state
3551 * has changed.
3552 */
3553static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003554{
3555
3556 PJ_UNUSED_ARG(event);
3557
3558 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003559 * When subscription is terminated, clear the xfer_sub member of
3560 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003561 */
3562 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003563 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003564
Benny Prijonoa1e69682007-05-11 15:14:34 +00003565 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003566 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003567 return;
3568
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003569 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003570 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003571
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003572 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003573 }
3574}
3575
3576
3577/*
3578 * Follow transfer (REFER) request.
3579 */
3580static void on_call_transfered( pjsip_inv_session *inv,
3581 pjsip_rx_data *rdata )
3582{
3583 pj_status_t status;
3584 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003585 pjsua_call *existing_call;
3586 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003587 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003588 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003589 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003590 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003591 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003592 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003593 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003594 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003595 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003596 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003597 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003598 pjsip_evsub *sub;
3599
Benny Prijonoa1e69682007-05-11 15:14:34 +00003600 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003601
Benny Prijono26ff9062006-02-21 23:47:00 +00003602 /* Find the Refer-To header */
3603 refer_to = (pjsip_generic_string_hdr*)
3604 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3605
3606 if (refer_to == NULL) {
3607 /* Invalid Request.
3608 * No Refer-To header!
3609 */
3610 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003611 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003612 return;
3613 }
3614
Benny Prijonoc8141a82006-08-20 09:12:19 +00003615 /* Find optional Refer-Sub header */
3616 refer_sub = (pjsip_generic_string_hdr*)
3617 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3618
3619 if (refer_sub) {
3620 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3621 no_refer_sub = PJ_TRUE;
3622 }
3623
Benny Prijono053f5222006-11-11 16:16:04 +00003624 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3625 * request.
3626 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003627 ref_by_hdr = (pjsip_hdr*)
3628 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003629 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003630
Benny Prijono9fc735d2006-05-28 14:58:12 +00003631 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003632 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003633 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3634 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3635 &refer_to->hvalue,
3636 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003637
3638 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003639 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003640 if (code >= 300) {
3641 /* Application rejects call transfer request */
3642 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3643 return;
3644 }
3645
Benny Prijono26ff9062006-02-21 23:47:00 +00003646 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3647 (int)inv->dlg->remote.info_str.slen,
3648 inv->dlg->remote.info_str.ptr,
3649 (int)refer_to->hvalue.slen,
3650 refer_to->hvalue.ptr));
3651
Benny Prijonoc8141a82006-08-20 09:12:19 +00003652 if (no_refer_sub) {
3653 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003654 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003655 */
3656 pjsip_tx_data *tdata;
3657 const pj_str_t str_false = { "false", 5};
3658 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003659
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003660 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3661 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003662 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003663 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003664 status);
3665 return;
3666 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003667
Benny Prijonoc8141a82006-08-20 09:12:19 +00003668 /* Add Refer-Sub header */
3669 hdr = (pjsip_hdr*)
3670 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3671 &str_false);
3672 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003673
Benny Prijono26ff9062006-02-21 23:47:00 +00003674
Benny Prijonoc8141a82006-08-20 09:12:19 +00003675 /* Send answer */
3676 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3677 tdata);
3678 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003679 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003680 status);
3681 return;
3682 }
3683
3684 /* Don't have subscription */
3685 sub = NULL;
3686
3687 } else {
3688 struct pjsip_evsub_user xfer_cb;
3689 pjsip_hdr hdr_list;
3690
3691 /* Init callback */
3692 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003693 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003694
3695 /* Init additional header list to be sent with REFER response */
3696 pj_list_init(&hdr_list);
3697
3698 /* Create transferee event subscription */
3699 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3700 if (status != PJ_SUCCESS) {
3701 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3702 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3703 return;
3704 }
3705
3706 /* If there's Refer-Sub header and the value is "true", send back
3707 * Refer-Sub in the response with value "true" too.
3708 */
3709 if (refer_sub) {
3710 const pj_str_t str_true = { "true", 4 };
3711 pjsip_hdr *hdr;
3712
3713 hdr = (pjsip_hdr*)
3714 pjsip_generic_string_hdr_create(inv->dlg->pool,
3715 &str_refer_sub,
3716 &str_true);
3717 pj_list_push_back(&hdr_list, hdr);
3718
3719 }
3720
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003721 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003722 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3723
3724 /* Create initial NOTIFY request */
3725 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3726 100, NULL, &tdata);
3727 if (status != PJ_SUCCESS) {
3728 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3729 status);
3730 return;
3731 }
3732
3733 /* Send initial NOTIFY request */
3734 status = pjsip_xfer_send_request( sub, tdata);
3735 if (status != PJ_SUCCESS) {
3736 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3737 return;
3738 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003739 }
3740
3741 /* We're cheating here.
3742 * We need to get a null terminated string from a pj_str_t.
3743 * So grab the pointer from the hvalue and NULL terminate it, knowing
3744 * that the NULL position will be occupied by a newline.
3745 */
3746 uri = refer_to->hvalue.ptr;
3747 uri[refer_to->hvalue.slen] = '\0';
3748
Benny Prijono053f5222006-11-11 16:16:04 +00003749 /* Init msg_data */
3750 pjsua_msg_data_init(&msg_data);
3751
3752 /* If Referred-By header is present in the REFER request, copy this
3753 * to the outgoing INVITE request.
3754 */
3755 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003756 pjsip_hdr *dup = (pjsip_hdr*)
3757 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003758 pj_list_push_back(&msg_data.hdr_list, dup);
3759 }
3760
Benny Prijono26ff9062006-02-21 23:47:00 +00003761 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003762 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003763 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003764 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003765 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003766 if (status != PJ_SUCCESS) {
3767
Benny Prijonoc8141a82006-08-20 09:12:19 +00003768 /* Notify xferer about the error (if we have subscription) */
3769 if (sub) {
3770 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3771 500, NULL, &tdata);
3772 if (status != PJ_SUCCESS) {
3773 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3774 status);
3775 return;
3776 }
3777 status = pjsip_xfer_send_request(sub, tdata);
3778 if (status != PJ_SUCCESS) {
3779 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3780 status);
3781 return;
3782 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003783 }
3784 return;
3785 }
3786
Benny Prijonoc8141a82006-08-20 09:12:19 +00003787 if (sub) {
3788 /* Put the server subscription in inv_data.
3789 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3790 * reported back to the server subscription.
3791 */
3792 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003793
Benny Prijonoc8141a82006-08-20 09:12:19 +00003794 /* Put the invite_data in the subscription. */
3795 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3796 &pjsua_var.calls[new_call]);
3797 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003798}
3799
3800
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003801
Benny Prijono26ff9062006-02-21 23:47:00 +00003802/*
3803 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003804 * session. We use this to trap:
3805 * - incoming REFER request.
3806 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003807 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003808static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3809 pjsip_transaction *tsx,
3810 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003811{
Benny Prijono2285e7e2008-12-17 14:28:18 +00003812 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003813
3814 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003815
Benny Prijono2285e7e2008-12-17 14:28:18 +00003816 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3817
3818 if (call == NULL) {
3819 PJSUA_UNLOCK();
3820 return;
3821 }
3822
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003823 if (call->inv == NULL) {
3824 /* Shouldn't happen. It happens only when we don't terminate the
3825 * server subscription caused by REFER after the call has been
3826 * transfered (and this call has been disconnected), and we
3827 * receive another REFER for this call.
3828 */
3829 PJSUA_UNLOCK();
3830 return;
3831 }
3832
Benny Prijonofeb69f42007-10-05 09:12:26 +00003833 /* Notify application callback first */
3834 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3835 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3836 }
3837
Benny Prijono26ff9062006-02-21 23:47:00 +00003838 if (tsx->role==PJSIP_ROLE_UAS &&
3839 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003840 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003841 {
3842 /*
3843 * Incoming REFER request.
3844 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003845 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003846
Benny Prijono26ff9062006-02-21 23:47:00 +00003847 }
Benny Prijonob0808372006-03-02 21:18:58 +00003848 else if (tsx->role==PJSIP_ROLE_UAS &&
3849 tsx->state==PJSIP_TSX_STATE_TRYING &&
3850 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3851 {
3852 /*
3853 * Incoming MESSAGE request!
3854 */
3855 pjsip_rx_data *rdata;
3856 pjsip_msg *msg;
3857 pjsip_accept_hdr *accept_hdr;
3858 pj_status_t status;
3859
3860 rdata = e->body.tsx_state.src.rdata;
3861 msg = rdata->msg_info.msg;
3862
3863 /* Request MUST have message body, with Content-Type equal to
3864 * "text/plain".
3865 */
3866 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3867
3868 pjsip_hdr hdr_list;
3869
3870 pj_list_init(&hdr_list);
3871 pj_list_push_back(&hdr_list, accept_hdr);
3872
3873 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3874 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003875 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003876 return;
3877 }
3878
3879 /* Respond with 200 first, so that remote doesn't retransmit in case
3880 * the UI takes too long to process the message.
3881 */
3882 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3883
3884 /* Process MESSAGE request */
3885 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3886 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003887
Benny Prijonob0808372006-03-02 21:18:58 +00003888 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003889 else if (tsx->role == PJSIP_ROLE_UAC &&
3890 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003891 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003892 /* Handle outgoing pager status */
3893 if (tsx->status_code >= 200) {
3894 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003895
Benny Prijonoa1e69682007-05-11 15:14:34 +00003896 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003897 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003898
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003899 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3900 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3901 &im_data->to,
3902 &im_data->body,
3903 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003904 (pjsip_status_code)
3905 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003906 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003907 }
Benny Prijonofccab712006-02-22 22:23:22 +00003908 }
Benny Prijono834aee32006-02-19 01:38:06 +00003909 }
Benny Prijono834aee32006-02-19 01:38:06 +00003910
Benny Prijono26ff9062006-02-21 23:47:00 +00003911
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003912 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003913}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003914
3915
3916/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00003917static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
3918 const pjsip_uri *target,
3919 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003920{
3921 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00003922 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003923
3924 PJSUA_LOCK();
3925
3926 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00003927 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
3928 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003929 } else {
3930 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
3931 "(callback not implemented by application). Disconnecting "
3932 "call.",
3933 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00003934 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003935 }
3936
3937 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00003938
3939 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003940}
3941