blob: cc9eaad99ee19904a28963fe62e9101bcfc02f27 [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 Prijono1cd713b2009-11-11 00:33:00 +0000136 /* Verify settings */
137 if (pjsua_var.ua_cfg.max_calls >= PJSUA_MAX_CALLS) {
138 pjsua_var.ua_cfg.max_calls = PJSUA_MAX_CALLS;
139 }
140
Benny Prijono91d06b62008-09-20 12:16:56 +0000141 /* Check the route URI's and force loose route if required */
142 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
143 status = normalize_route_uri(pjsua_var.pool,
144 &pjsua_var.ua_cfg.outbound_proxy[i]);
145 if (status != PJ_SUCCESS)
146 return status;
147 }
148
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000149 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000150 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000151 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
152 inv_cb.on_new_session = &pjsua_call_on_forked;
153 inv_cb.on_media_update = &pjsua_call_on_media_update;
154 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000155 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000156 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000157 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000158
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000159 /* Initialize invite session module: */
160 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
161 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
162
Benny Prijonoc8141a82006-08-20 09:12:19 +0000163 /* Add "norefersub" in Supported header */
164 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
165 NULL, 1, &str_norefersub);
166
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000167 return status;
168}
169
170
171/*
172 * Start call subsystem.
173 */
174pj_status_t pjsua_call_subsys_start(void)
175{
176 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000177 return PJ_SUCCESS;
178}
179
180
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000181/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000182 * Get maximum number of calls configured in pjsua.
183 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000184PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000185{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000186 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000187}
188
189
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190/*
191 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000192 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000193PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000194{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000195 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000196}
197
198
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000199/*
200 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000201 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000202PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
203 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000204{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000205 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000206
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000207 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000208
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000209 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000210
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000211 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
212 if (!pjsua_var.calls[i].inv)
213 continue;
214 ids[c] = i;
215 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000216 }
217
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000218 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000219
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000220 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000221
222 return PJ_SUCCESS;
223}
224
225
Benny Prijono5773cd62008-01-19 13:01:42 +0000226/* Allocate one call id */
227static pjsua_call_id alloc_call_id(void)
228{
229 pjsua_call_id cid;
230
231#if 1
232 /* New algorithm: round-robin */
233 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
234 pjsua_var.next_call_id < 0)
235 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000236 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000237 }
238
239 for (cid=pjsua_var.next_call_id;
240 cid<(int)pjsua_var.ua_cfg.max_calls;
241 ++cid)
242 {
243 if (pjsua_var.calls[cid].inv == NULL) {
244 ++pjsua_var.next_call_id;
245 return cid;
246 }
247 }
248
249 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
250 if (pjsua_var.calls[cid].inv == NULL) {
251 ++pjsua_var.next_call_id;
252 return cid;
253 }
254 }
255
256#else
257 /* Old algorithm */
258 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
259 if (pjsua_var.calls[cid].inv == NULL)
260 return cid;
261 }
262#endif
263
264 return PJSUA_INVALID_ID;
265}
266
Benny Prijonod8179652008-01-23 20:39:07 +0000267/* Get signaling secure level.
268 * Return:
269 * 0: if signaling is not secure
270 * 1: if TLS transport is used for immediate hop
271 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000272 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000273static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000274{
275 const pj_str_t tls = pj_str(";transport=tls");
276 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000277 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000278
279 if (pj_stristr(dst_uri, &sips))
280 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000281
282 if (!pj_list_empty(&acc->route_set)) {
283 pjsip_route_hdr *r = acc->route_set.next;
284 pjsip_uri *uri = r->name_addr.uri;
285 pjsip_sip_uri *sip_uri;
286
287 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
288 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
289 return 1;
290
291 } else {
292 if (pj_stristr(dst_uri, &tls))
293 return 1;
294 }
295
Benny Prijonod8179652008-01-23 20:39:07 +0000296 return 0;
297}
298
Benny Prijono224b4e22008-06-19 14:10:28 +0000299/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000300static int call_get_secure_level(pjsua_call *call)
301{
302 if (call->inv->dlg->secure)
303 return 2;
304
305 if (!pj_list_empty(&call->inv->dlg->route_set)) {
306 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
307 pjsip_uri *uri = r->name_addr.uri;
308 pjsip_sip_uri *sip_uri;
309
310 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
311 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
312 return 1;
313
314 } else {
315 pjsip_sip_uri *sip_uri;
316
317 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
318 return 2;
319 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
320 return 0;
321
322 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
323 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
324 return 1;
325 }
326
327 return 0;
328}
Benny Prijono224b4e22008-06-19 14:10:28 +0000329*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000330
331
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000332/*
333 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000334 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000335PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
336 const pj_str_t *dest_uri,
337 unsigned options,
338 void *user_data,
339 const pjsua_msg_data *msg_data,
340 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000341{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000342 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000343 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000344 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000345 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000346 pjsua_acc *acc;
347 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000348 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000349 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000350 pjsip_tx_data *tdata;
351 pj_status_t status;
352
Benny Prijono9fc735d2006-05-28 14:58:12 +0000353
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000354 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000355 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000356 PJ_EINVAL);
357
Benny Prijono320fa4d2006-12-07 10:09:16 +0000358 /* Check arguments */
359 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
360
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000361 PJSUA_LOCK();
362
Benny Prijonof798e502009-03-09 13:08:16 +0000363 /* Create sound port if none is instantiated, to check if sound device
364 * can be used. But only do this with the conference bridge, as with
365 * audio switchboard (i.e. APS-Direct), we can only open the sound
366 * device once the correct format has been known
367 */
368 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
369 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000370 {
371 pj_status_t status;
372
373 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
374 if (status != PJ_SUCCESS) {
375 PJSUA_UNLOCK();
376 return status;
377 }
378 }
379
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000380 acc = &pjsua_var.acc[acc_id];
381 if (!acc->valid) {
382 pjsua_perror(THIS_FILE, "Unable to make call because account "
383 "is not valid", PJ_EINVALIDOP);
384 PJSUA_UNLOCK();
385 return PJ_EINVALIDOP;
386 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000387
Benny Prijonoa91a0032006-02-26 21:23:45 +0000388 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000389 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000390
Benny Prijono5773cd62008-01-19 13:01:42 +0000391 if (call_id == PJSUA_INVALID_ID) {
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000392 pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000393 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000394 return PJ_ETOOMANY;
395 }
396
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000397 call = &pjsua_var.calls[call_id];
398
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000399 /* Associate session with account */
400 call->acc_id = acc_id;
401
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000402 /* Create temporary pool */
403 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
404
Benny Prijono320fa4d2006-12-07 10:09:16 +0000405 /* Verify that destination URI is valid before calling
406 * pjsua_acc_create_uac_contact, or otherwise there
407 * a misleading "Invalid Contact URI" error will be printed
408 * when pjsua_acc_create_uac_contact() fails.
409 */
410 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000411 pjsip_uri *uri;
412 pj_str_t dup;
413
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000414 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
415 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000416
417 if (uri == NULL) {
418 pjsua_perror(THIS_FILE, "Unable to make call",
419 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000420 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000421 PJSUA_UNLOCK();
422 return PJSIP_EINVALIDREQURI;
423 }
424 }
425
Benny Prijono093d3022006-09-24 00:07:11 +0000426 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
427 (int)dest_uri->slen, dest_uri->ptr));
428
Benny Prijonoe21e7842006-04-09 16:46:05 +0000429 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000430 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000431
Benny Prijonoe21e7842006-04-09 16:46:05 +0000432 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000433 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000434
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000435 /* Create suitable Contact header unless a Contact header has been
436 * set in the account.
437 */
438 if (acc->contact.slen) {
439 contact = acc->contact;
440 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000441 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000442 acc_id, dest_uri);
443 if (status != PJ_SUCCESS) {
444 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
445 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000446 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000447 PJSUA_UNLOCK();
448 return status;
449 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000450 }
451
Benny Prijonoe21e7842006-04-09 16:46:05 +0000452 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000453 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000454 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000455 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000456 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000457 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000458 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000459 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000460 return status;
461 }
462
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000463 /* Increment the dialog's lock otherwise when invite session creation
464 * fails the dialog will be destroyed prematurely.
465 */
466 pjsip_dlg_inc_lock(dlg);
467
Benny Prijonodb844a42008-02-02 17:07:18 +0000468 /* Calculate call's secure level */
469 call->secure_level = get_secure_level(acc_id, dest_uri);
470
Benny Prijonoc97608e2007-03-23 16:34:20 +0000471 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000472 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000473 call->secure_level, dlg->pool,
474 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000475 if (status != PJ_SUCCESS) {
476 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
477 goto on_error;
478 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000479
Benny Prijono224b4e22008-06-19 14:10:28 +0000480 /* Create offer */
481 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000482 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000483 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000484 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000485 goto on_error;
486 }
487
488 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000489 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000490 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000491 if (acc->cfg.require_100rel)
492 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000493 if (acc->cfg.require_timer)
494 options |= PJSIP_INV_REQUIRE_TIMER;
Benny Prijono84126ab2006-02-09 09:30:09 +0000495
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000496 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000497 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000498 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000499 goto on_error;
500 }
501
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000502 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000503 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
504 if (status != PJ_SUCCESS) {
505 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
506 goto on_error;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000507 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000508
509 /* Create and associate our data in the session. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000510 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000511
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000512 dlg->mod_data[pjsua_var.mod.id] = call;
513 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000514
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000515 /* Attach user data */
516 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000517
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000518 /* If account is locked to specific transport, then lock dialog
519 * to this transport too.
520 */
521 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
522 pjsip_tpselector tp_sel;
523
524 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
525 pjsip_dlg_set_transport(dlg, &tp_sel);
526 }
527
Benny Prijono84126ab2006-02-09 09:30:09 +0000528 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000529 if (!pj_list_empty(&acc->route_set))
530 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000531
532
533 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000534 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000535 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000536 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000537 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000538
Benny Prijono48ab2b72007-11-08 09:24:30 +0000539 /* Set authentication preference */
540 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000541
542 /* Create initial INVITE: */
543
544 status = pjsip_inv_invite(inv, &tdata);
545 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000546 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
547 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000548 goto on_error;
549 }
550
551
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000552 /* Add additional headers etc */
553
554 pjsua_process_msg_data( tdata, msg_data);
555
Benny Prijono093d3022006-09-24 00:07:11 +0000556 /* Must increment call counter now */
557 ++pjsua_var.call_cnt;
558
Benny Prijono84126ab2006-02-09 09:30:09 +0000559 /* Send initial INVITE: */
560
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000561 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000562 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000563 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
564 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000565
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000566 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000567 * session would have been cleared.
568 */
569 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000570 goto on_error;
571 }
572
Benny Prijono84126ab2006-02-09 09:30:09 +0000573 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000574
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000575 if (p_call_id)
576 *p_call_id = call_id;
577
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000578 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000579 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000580 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000581
582 return PJ_SUCCESS;
583
584
585on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000586 if (dlg) {
587 /* This may destroy the dialog */
588 pjsip_dlg_dec_lock(dlg);
589 }
590
Benny Prijono1c2bf462006-03-05 11:54:02 +0000591 if (inv != NULL) {
592 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000593 }
594
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000595 if (call_id != -1) {
596 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000597 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000598 }
599
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000600 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000601 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000602 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000603}
604
605
Benny Prijono91a6a172007-10-31 08:59:29 +0000606/* Get the NAT type information in remote's SDP */
607static void update_remote_nat_type(pjsua_call *call,
608 const pjmedia_sdp_session *sdp)
609{
610 const pjmedia_sdp_attr *xnat;
611
612 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
613 if (xnat) {
614 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
615 } else {
616 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
617 }
618
619 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
620 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
621}
622
623
Benny Prijonodc39fe82006-05-26 12:17:46 +0000624/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000625 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000626 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000627 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000628pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000629{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000630 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000631 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000632 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000633 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
634 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000635 pjsip_tx_data *response = NULL;
636 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000637 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000638 int acc_id;
639 pjsua_call *call;
640 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000641 int sip_err_code;
Benny Prijonod8179652008-01-23 20:39:07 +0000642 pjmedia_sdp_session *offer, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000643 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000644
Benny Prijono26ff9062006-02-21 23:47:00 +0000645 /* Don't want to handle anything but INVITE */
646 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
647 return PJ_FALSE;
648
649 /* Don't want to handle anything that's already associated with
650 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000651 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000652 if (dlg || tsx)
653 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000654
Benny Prijono384dab42009-10-14 01:58:04 +0000655 /* Don't want to accept the call if shutdown is in progress */
656 if (pjsua_var.thread_quit_flag) {
657 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
658 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
659 NULL, NULL);
660 return PJ_TRUE;
661 }
662
Benny Prijono148c9dd2006-09-19 13:37:53 +0000663 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000664
Benny Prijono26ff9062006-02-21 23:47:00 +0000665 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000666 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000667
Benny Prijono5773cd62008-01-19 13:01:42 +0000668 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000669 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000670 PJSIP_SC_BUSY_HERE, NULL,
671 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000672 PJ_LOG(2,(THIS_FILE,
673 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000674 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000675 return PJ_TRUE;
676 }
677
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000678 /* Clear call descriptor */
679 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000680
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000681 call = &pjsua_var.calls[call_id];
682
683 /* Mark call start time. */
684 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000685
Benny Prijono053f5222006-11-11 16:16:04 +0000686 /* Check INVITE request for Replaces header. If Replaces header is
687 * present, the function will make sure that we can handle the request.
688 */
689 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
690 &response);
691 if (status != PJ_SUCCESS) {
692 /*
693 * Something wrong with the Replaces header.
694 */
695 if (response) {
696 pjsip_response_addr res_addr;
697
698 pjsip_get_response_addr(response->pool, rdata, &res_addr);
699 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
700 NULL, NULL);
701
702 } else {
703
704 /* Respond with 500 (Internal Server Error) */
705 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
706 NULL, NULL);
707 }
708
709 PJSUA_UNLOCK();
710 return PJ_TRUE;
711 }
712
713 /* If this INVITE request contains Replaces header, notify application
714 * about the request so that application can do subsequent checking
715 * if it wants to.
716 */
717 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
718 pjsua_call *replaced_call;
719 int st_code = 200;
720 pj_str_t st_text = { "OK", 2 };
721
722 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000723 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000724
725 /* Notify application */
726 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
727 rdata, &st_code, &st_text);
728
729 /* Must specify final response */
730 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
731
732 /* Check if application rejects this request. */
733 if (st_code >= 300) {
734
735 if (st_text.slen == 2)
736 st_text = *pjsip_get_status_text(st_code);
737
738 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
739 st_code, &st_text, NULL, NULL, NULL);
740 PJSUA_UNLOCK();
741 return PJ_TRUE;
742 }
743 }
744
Benny Prijonod8179652008-01-23 20:39:07 +0000745 /*
746 * Get which account is most likely to be associated with this incoming
747 * call. We need the account to find which contact URI to put for
748 * the call.
749 */
750 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
751
Benny Prijonodb844a42008-02-02 17:07:18 +0000752 /* Get call's secure level */
753 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
754 call->secure_level = 2;
755 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
756 call->secure_level = 1;
757 else
758 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000759
Benny Prijonod8179652008-01-23 20:39:07 +0000760 /* Parse SDP from incoming request */
761 if (rdata->msg_info.msg->body) {
762 status = pjmedia_sdp_parse(rdata->tp_info.pool,
Benny Prijono24a21852008-04-14 04:04:30 +0000763 (char*)rdata->msg_info.msg->body->data,
Benny Prijonod8179652008-01-23 20:39:07 +0000764 rdata->msg_info.msg->body->len, &offer);
Benny Prijono2c484e42008-06-26 19:47:23 +0000765 if (status == PJ_SUCCESS) {
766 /* Validate */
767 status = pjmedia_sdp_validate(offer);
768 }
769
Benny Prijonod8179652008-01-23 20:39:07 +0000770 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000771 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000772 pjsip_hdr hdr_list;
773 pjsip_warning_hdr *w;
774
775 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000776 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000777
778 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
779 pjsip_endpt_name(pjsua_var.endpt),
780 status);
781 pj_list_init(&hdr_list);
782 pj_list_push_back(&hdr_list, w);
783
Benny Prijono224b4e22008-06-19 14:10:28 +0000784 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000785 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000786 PJSUA_UNLOCK();
787 return PJ_TRUE;
788 }
Benny Prijono617b8602008-04-07 10:10:31 +0000789
790 /* Do quick checks on SDP before passing it to transports. More elabore
791 * checks will be done in pjsip_inv_verify_request2() below.
792 */
793 if (offer->media_count==0) {
794 const pj_str_t reason = pj_str("Missing media in SDP");
795 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
796 NULL, NULL, NULL);
797 PJSUA_UNLOCK();
798 return PJ_TRUE;
799 }
800
Benny Prijonod8179652008-01-23 20:39:07 +0000801 } else {
802 offer = NULL;
803 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000804
Benny Prijono224b4e22008-06-19 14:10:28 +0000805 /* Init media channel */
806 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
807 call->secure_level,
808 rdata->tp_info.pool, offer,
809 &sip_err_code);
810 if (status != PJ_SUCCESS) {
811 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
812 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
813 sip_err_code, NULL, NULL, NULL, NULL);
814 PJSUA_UNLOCK();
815 return PJ_TRUE;
816 }
817
818 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000819 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000820 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000821 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000822 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000823 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
824 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000825 PJSUA_UNLOCK();
826 return PJ_TRUE;
827 }
828
Benny Prijono224b4e22008-06-19 14:10:28 +0000829
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000830 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000831 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000832 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000833 if (pjsua_var.acc[acc_id].cfg.require_100rel)
834 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000835 if (pjsua_var.acc[acc_id].cfg.require_timer)
836 options |= PJSIP_INV_REQUIRE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000837
Benny Prijonod8179652008-01-23 20:39:07 +0000838 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
839 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000840 if (status != PJ_SUCCESS) {
841
842 /*
843 * No we can't handle the incoming INVITE request.
844 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000845 if (response) {
846 pjsip_response_addr res_addr;
847
848 pjsip_get_response_addr(response->pool, rdata, &res_addr);
849 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
850 NULL, NULL);
851
852 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000853 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000854 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
855 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000856 }
857
Benny Prijonoc97608e2007-03-23 16:34:20 +0000858 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000859 PJSUA_UNLOCK();
860 return PJ_TRUE;
861 }
862
863
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000864 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000865 if (pjsua_var.acc[acc_id].contact.slen) {
866 contact = pjsua_var.acc[acc_id].contact;
867 } else {
868 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
869 acc_id, rdata);
870 if (status != PJ_SUCCESS) {
871 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
872 status);
873 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
874 NULL, NULL);
875 pjsua_media_channel_deinit(call->index);
876 PJSUA_UNLOCK();
877 return PJ_TRUE;
878 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000879 }
880
Benny Prijono26ff9062006-02-21 23:47:00 +0000881 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000882 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000883 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000884 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000885 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000886 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000887 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000888 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000889 return PJ_TRUE;
890 }
891
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000892 /* Set credentials */
893 if (pjsua_var.acc[acc_id].cred_cnt) {
894 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
895 pjsua_var.acc[acc_id].cred_cnt,
896 pjsua_var.acc[acc_id].cred);
897 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000898
Benny Prijono48ab2b72007-11-08 09:24:30 +0000899 /* Set preference */
900 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
901 &pjsua_var.acc[acc_id].cfg.auth_pref);
902
Benny Prijono26ff9062006-02-21 23:47:00 +0000903 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000904 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000905 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000906 pjsip_hdr hdr_list;
907 pjsip_warning_hdr *w;
908
909 w = pjsip_warning_hdr_create_from_status(dlg->pool,
910 pjsip_endpt_name(pjsua_var.endpt),
911 status);
912 pj_list_init(&hdr_list);
913 pj_list_push_back(&hdr_list, w);
914
915 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
916
917 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000918 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000919 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000920 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000921 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000922 return PJ_TRUE;
923 }
924
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000925 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000926 status = pjsip_timer_init_session(inv,
927 &pjsua_var.acc[acc_id].cfg.timer_setting);
928 if (status != PJ_SUCCESS) {
929 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
930 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
931 NULL, &response);
932 if (status == PJ_SUCCESS && response)
933 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000934
Nanang Izzuddin65add622009-08-11 16:26:20 +0000935 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000936
Nanang Izzuddin65add622009-08-11 16:26:20 +0000937 PJSUA_UNLOCK();
938 return PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000939 }
940
Benny Prijonoea9fd392007-11-06 03:41:40 +0000941 /* Update NAT type of remote endpoint, only when there is SDP in
942 * incoming INVITE!
943 */
944 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
945 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
946 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000947 const pjmedia_sdp_session *remote_sdp;
948
949 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
950 update_remote_nat_type(call, remote_sdp);
951 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000952
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000953 /* If account is locked to specific transport, then lock dialog
954 * to this transport too.
955 */
956 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
957 pjsip_tpselector tp_sel;
958
959 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
960 pjsip_dlg_set_transport(dlg, &tp_sel);
961 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000962
Benny Prijono2285e7e2008-12-17 14:28:18 +0000963 /* Must answer with some response to initial INVITE. We'll do this before
964 * attaching the call to the invite session/dialog, so that the application
965 * will not get notification about this event (on another scenario, it is
966 * also possible that inv_send_msg() fails and causes the invite session to
967 * be disconnected. If we have the call attached at this time, this will
968 * cause the disconnection callback to be called before on_incoming_call()
969 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +0000970 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000971 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000972 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000973 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000974 if (response == NULL) {
975 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
976 status);
977 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
978 pjsip_inv_terminate(inv, 500, PJ_FALSE);
979 } else {
980 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +0000981 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000982 PJ_FALSE);
983 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000984 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000985 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000986 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000987
988 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000989 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000990 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000991 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +0000992 PJSUA_UNLOCK();
993 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000994 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000995 }
996
Benny Prijono2285e7e2008-12-17 14:28:18 +0000997 /* Create and attach pjsua_var data to the dialog: */
998 call->inv = inv;
999
1000 dlg->mod_data[pjsua_var.mod.id] = call;
1001 inv->mod_data[pjsua_var.mod.id] = call;
1002
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001003 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001004
Benny Prijono105217f2006-03-06 16:25:59 +00001005
Benny Prijono053f5222006-11-11 16:16:04 +00001006 /* Check if this request should replace existing call */
1007 if (replaced_dlg) {
1008 pjsip_inv_session *replaced_inv;
1009 struct pjsua_call *replaced_call;
1010 pjsip_tx_data *tdata;
1011
1012 /* Get the invite session in the dialog */
1013 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1014
1015 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001016 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001017
1018 /* Notify application */
1019 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1020 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1021 call_id);
1022
1023 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1024 call_id));
1025
1026 /* Answer the new call with 200 response */
1027 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1028 if (status == PJ_SUCCESS)
1029 status = pjsip_inv_send_msg(inv, tdata);
1030
1031 if (status != PJ_SUCCESS)
1032 pjsua_perror(THIS_FILE, "Error answering session", status);
1033
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001034 /* Note that inv may be invalid if 200/OK has caused error in
1035 * starting the media.
1036 */
Benny Prijono053f5222006-11-11 16:16:04 +00001037
1038 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1039 replaced_call->index));
1040
1041 /* Disconnect replaced invite session */
1042 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1043 &tdata);
1044 if (status == PJ_SUCCESS && tdata)
1045 status = pjsip_inv_send_msg(replaced_inv, tdata);
1046
1047 if (status != PJ_SUCCESS)
1048 pjsua_perror(THIS_FILE, "Error terminating session", status);
1049
1050
1051 } else {
1052
Benny Prijonob5388cf2007-01-04 22:45:08 +00001053 /* Notify application if on_incoming_call() is overriden,
1054 * otherwise hangup the call with 480
1055 */
1056 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001057 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001058 } else {
1059 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1060 NULL, NULL);
1061 }
Benny Prijono053f5222006-11-11 16:16:04 +00001062 }
1063
Benny Prijono8b1889b2006-06-06 18:40:40 +00001064
Benny Prijono26ff9062006-02-21 23:47:00 +00001065 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001066 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001067 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001068}
1069
1070
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001071
1072/*
1073 * Check if the specified call has active INVITE session and the INVITE
1074 * session has not been disconnected.
1075 */
1076PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1077{
1078 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1079 PJ_EINVAL);
1080 return pjsua_var.calls[call_id].inv != NULL &&
1081 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1082}
1083
1084
1085/*
1086 * Check if call has an active media session.
1087 */
1088PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1089{
1090 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1091 PJ_EINVAL);
1092 return pjsua_var.calls[call_id].session != NULL;
1093}
1094
1095
Benny Prijonocf986c42008-09-02 11:25:07 +00001096/*
1097 * Retrieve the media session associated with this call.
1098 */
1099PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1100{
1101 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1102 NULL);
1103 return pjsua_var.calls[call_id].session;
1104}
1105
1106
1107/*
1108 * Retrieve the media transport instance that is used for this call.
1109 */
1110PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1111{
1112 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1113 NULL);
1114 return pjsua_var.calls[cid].med_tp;
1115}
1116
1117
Benny Prijono148c9dd2006-09-19 13:37:53 +00001118/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001119pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001120 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001121 pjsua_call **p_call,
1122 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001123{
1124 enum { MAX_RETRY=50 };
1125 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001126 pjsua_call *call = NULL;
1127 pj_bool_t has_pjsua_lock = PJ_FALSE;
1128 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001129
1130 for (retry=0; retry<MAX_RETRY; ++retry) {
1131
1132 has_pjsua_lock = PJ_FALSE;
1133
1134 status = PJSUA_TRY_LOCK();
1135 if (status != PJ_SUCCESS) {
1136 pj_thread_sleep(retry/10);
1137 continue;
1138 }
1139
1140 has_pjsua_lock = PJ_TRUE;
1141 call = &pjsua_var.calls[call_id];
1142
1143 if (call->inv == NULL) {
1144 PJSUA_UNLOCK();
1145 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1146 return PJSIP_ESESSIONTERMINATED;
1147 }
1148
1149 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1150 if (status != PJ_SUCCESS) {
1151 PJSUA_UNLOCK();
1152 pj_thread_sleep(retry/10);
1153 continue;
1154 }
1155
1156 PJSUA_UNLOCK();
1157
1158 break;
1159 }
1160
1161 if (status != PJ_SUCCESS) {
1162 if (has_pjsua_lock == PJ_FALSE)
1163 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1164 "(possibly system has deadlocked) in %s",
1165 title));
1166 else
1167 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1168 "(possibly system has deadlocked) in %s",
1169 title));
1170 return PJ_ETIMEDOUT;
1171 }
1172
1173 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001174 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001175
1176 return PJ_SUCCESS;
1177}
1178
1179
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001180/*
1181 * Get the conference port identification associated with the call.
1182 */
1183PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1184{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001185 pjsua_call *call;
1186 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001187 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001188 pj_status_t status;
1189
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001190 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1191 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001192
Benny Prijonodc752ca2006-09-22 16:55:42 +00001193 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001194 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001195 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001196
1197 port_id = call->conf_slot;
1198
Benny Prijonodc752ca2006-09-22 16:55:42 +00001199 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001200
1201 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001202}
1203
1204
Benny Prijono148c9dd2006-09-19 13:37:53 +00001205
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001206/*
1207 * Obtain detail information about the specified call.
1208 */
1209PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1210 pjsua_call_info *info)
1211{
1212 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001213 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001214 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001215
1216 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1217 PJ_EINVAL);
1218
Benny Prijonoac623b32006-07-03 15:19:31 +00001219 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001220
Benny Prijonodc752ca2006-09-22 16:55:42 +00001221 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001222 if (status != PJ_SUCCESS) {
1223 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001224 }
1225
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001226 /* id and role */
1227 info->id = call_id;
1228 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001229 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001230
1231 /* local info */
1232 info->local_info.ptr = info->buf_.local_info;
1233 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1234 sizeof(info->buf_.local_info));
1235
1236 /* local contact */
1237 info->local_contact.ptr = info->buf_.local_contact;
1238 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1239 call->inv->dlg->local.contact->uri,
1240 info->local_contact.ptr,
1241 sizeof(info->buf_.local_contact));
1242
1243 /* remote info */
1244 info->remote_info.ptr = info->buf_.remote_info;
1245 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1246 sizeof(info->buf_.remote_info));
1247
1248 /* remote contact */
1249 if (call->inv->dlg->remote.contact) {
1250 int len;
1251 info->remote_contact.ptr = info->buf_.remote_contact;
1252 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1253 call->inv->dlg->remote.contact->uri,
1254 info->remote_contact.ptr,
1255 sizeof(info->buf_.remote_contact));
1256 if (len < 0) len = 0;
1257 info->remote_contact.slen = len;
1258 } else {
1259 info->remote_contact.slen = 0;
1260 }
1261
1262 /* call id */
1263 info->call_id.ptr = info->buf_.call_id;
1264 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1265 sizeof(info->buf_.call_id));
1266
1267 /* state, state_text */
1268 info->state = call->inv->state;
1269 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1270
1271 /* If call is disconnected, set the last_status from the cause code */
1272 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1273 /* last_status, last_status_text */
1274 info->last_status = call->inv->cause;
1275
1276 info->last_status_text.ptr = info->buf_.last_status_text;
1277 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1278 sizeof(info->buf_.last_status_text));
1279 } else {
1280 /* last_status, last_status_text */
1281 info->last_status = call->last_code;
1282
1283 info->last_status_text.ptr = info->buf_.last_status_text;
1284 pj_strncpy(&info->last_status_text, &call->last_text,
1285 sizeof(info->buf_.last_status_text));
1286 }
1287
1288 /* media status and dir */
1289 info->media_status = call->media_st;
1290 info->media_dir = call->media_dir;
1291
1292
1293 /* conference slot number */
1294 info->conf_slot = call->conf_slot;
1295
1296 /* calculate duration */
1297 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1298
1299 info->total_duration = call->dis_time;
1300 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1301
1302 if (call->conn_time.sec) {
1303 info->connect_duration = call->dis_time;
1304 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1305 }
1306
1307 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1308
1309 pj_gettimeofday(&info->total_duration);
1310 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1311
1312 pj_gettimeofday(&info->connect_duration);
1313 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1314
1315 } else {
1316 pj_gettimeofday(&info->total_duration);
1317 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1318 }
1319
Benny Prijonodc752ca2006-09-22 16:55:42 +00001320 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001321
1322 return PJ_SUCCESS;
1323}
1324
1325
1326/*
1327 * Attach application specific data to the call.
1328 */
1329PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1330 void *user_data)
1331{
1332 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1333 PJ_EINVAL);
1334 pjsua_var.calls[call_id].user_data = user_data;
1335
1336 return PJ_SUCCESS;
1337}
1338
1339
1340/*
1341 * Get user data attached to the call.
1342 */
1343PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1344{
1345 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1346 NULL);
1347 return pjsua_var.calls[call_id].user_data;
1348}
1349
1350
1351/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001352 * Get remote's NAT type.
1353 */
1354PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1355 pj_stun_nat_type *p_type)
1356{
1357 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1358 PJ_EINVAL);
1359 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1360
1361 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1362 return PJ_SUCCESS;
1363}
1364
1365
1366/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001367 * Send response to incoming INVITE request.
1368 */
1369PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1370 unsigned code,
1371 const pj_str_t *reason,
1372 const pjsua_msg_data *msg_data)
1373{
1374 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001375 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001376 pjsip_tx_data *tdata;
1377 pj_status_t status;
1378
1379 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1380 PJ_EINVAL);
1381
Benny Prijonodc752ca2006-09-22 16:55:42 +00001382 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001383 if (status != PJ_SUCCESS)
1384 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001385
Benny Prijono2e507c22006-06-23 15:04:11 +00001386 if (call->res_time.sec == 0)
1387 pj_gettimeofday(&call->res_time);
1388
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001389 if (reason && reason->slen == 0)
1390 reason = NULL;
1391
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001392 /* Create response message */
1393 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1394 if (status != PJ_SUCCESS) {
1395 pjsua_perror(THIS_FILE, "Error creating response",
1396 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001397 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001398 return status;
1399 }
1400
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001401 /* Call might have been disconnected if application is answering with
1402 * 200/OK and the media failed to start.
1403 */
1404 if (call->inv == NULL) {
1405 pjsip_dlg_dec_lock(dlg);
1406 return PJSIP_ESESSIONTERMINATED;
1407 }
1408
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001409 /* Add additional headers etc */
1410 pjsua_process_msg_data( tdata, msg_data);
1411
1412 /* Send the message */
1413 status = pjsip_inv_send_msg(call->inv, tdata);
1414 if (status != PJ_SUCCESS)
1415 pjsua_perror(THIS_FILE, "Error sending response",
1416 status);
1417
Benny Prijonodc752ca2006-09-22 16:55:42 +00001418 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001419
1420 return status;
1421}
1422
1423
1424/*
1425 * Hangup call by using method that is appropriate according to the
1426 * call state.
1427 */
1428PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1429 unsigned code,
1430 const pj_str_t *reason,
1431 const pjsua_msg_data *msg_data)
1432{
1433 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001434 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001435 pj_status_t status;
1436 pjsip_tx_data *tdata;
1437
1438
Benny Prijono148c9dd2006-09-19 13:37:53 +00001439 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1440 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1441 call_id));
1442 }
1443
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001444 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1445 PJ_EINVAL);
1446
Benny Prijonodc752ca2006-09-22 16:55:42 +00001447 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001448 if (status != PJ_SUCCESS)
1449 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001450
1451 if (code==0) {
1452 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1453 code = PJSIP_SC_OK;
1454 else if (call->inv->role == PJSIP_ROLE_UAS)
1455 code = PJSIP_SC_DECLINE;
1456 else
1457 code = PJSIP_SC_REQUEST_TERMINATED;
1458 }
1459
1460 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1461 if (status != PJ_SUCCESS) {
1462 pjsua_perror(THIS_FILE,
1463 "Failed to create end session message",
1464 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001465 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001466 return status;
1467 }
1468
1469 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1470 * as p_tdata when INVITE transaction has not been answered
1471 * with any provisional responses.
1472 */
1473 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001474 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001475 return PJ_SUCCESS;
1476 }
1477
1478 /* Add additional headers etc */
1479 pjsua_process_msg_data( tdata, msg_data);
1480
1481 /* Send the message */
1482 status = pjsip_inv_send_msg(call->inv, tdata);
1483 if (status != PJ_SUCCESS) {
1484 pjsua_perror(THIS_FILE,
1485 "Failed to send end session message",
1486 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001487 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001488 return status;
1489 }
1490
Benny Prijonodc752ca2006-09-22 16:55:42 +00001491 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001492
1493 return PJ_SUCCESS;
1494}
1495
1496
1497/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001498 * Accept or reject redirection.
1499 */
1500PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1501 pjsip_redirect_op cmd)
1502{
1503 pjsua_call *call;
1504 pjsip_dialog *dlg;
1505 pj_status_t status;
1506
1507 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1508 PJ_EINVAL);
1509
1510 status = acquire_call("pjsua_call_process_redirect()", call_id,
1511 &call, &dlg);
1512 if (status != PJ_SUCCESS)
1513 return status;
1514
1515 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1516
1517 pjsip_dlg_dec_lock(dlg);
1518
1519 return status;
1520}
1521
1522
1523/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001524 * Put the specified call on hold.
1525 */
1526PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1527 const pjsua_msg_data *msg_data)
1528{
1529 pjmedia_sdp_session *sdp;
1530 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001531 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001532 pjsip_tx_data *tdata;
1533 pj_status_t status;
1534
1535 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1536 PJ_EINVAL);
1537
Benny Prijonodc752ca2006-09-22 16:55:42 +00001538 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001539 if (status != PJ_SUCCESS)
1540 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001541
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001542
1543 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1544 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001545 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001546 return PJSIP_ESESSIONSTATE;
1547 }
1548
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001549 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001550 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001551 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001552 return status;
1553 }
1554
1555 /* Create re-INVITE with new offer */
1556 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1557 if (status != PJ_SUCCESS) {
1558 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001559 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001560 return status;
1561 }
1562
1563 /* Add additional headers etc */
1564 pjsua_process_msg_data( tdata, msg_data);
1565
1566 /* Send the request */
1567 status = pjsip_inv_send_msg( call->inv, tdata);
1568 if (status != PJ_SUCCESS) {
1569 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001570 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001571 return status;
1572 }
1573
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001574 /* Set flag that local put the call on hold */
1575 call->local_hold = PJ_TRUE;
1576
Benny Prijonodc752ca2006-09-22 16:55:42 +00001577 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001578
1579 return PJ_SUCCESS;
1580}
1581
1582
1583/*
1584 * Send re-INVITE (to release hold).
1585 */
1586PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1587 pj_bool_t unhold,
1588 const pjsua_msg_data *msg_data)
1589{
1590 pjmedia_sdp_session *sdp;
1591 pjsip_tx_data *tdata;
1592 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001593 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001594 pj_status_t status;
1595
1596
1597 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1598 PJ_EINVAL);
1599
Benny Prijonodc752ca2006-09-22 16:55:42 +00001600 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001601 if (status != PJ_SUCCESS)
1602 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001603
1604 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1605 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001606 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001607 return PJSIP_ESESSIONSTATE;
1608 }
1609
1610 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001611 if (call->local_hold && !unhold) {
1612 status = create_sdp_of_call_hold(call, &sdp);
1613 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001614 status = pjsua_media_channel_create_sdp(call->index,
1615 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001616 NULL, &sdp, NULL);
1617 call->local_hold = PJ_FALSE;
1618 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001619 if (status != PJ_SUCCESS) {
1620 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1621 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001622 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001623 return status;
1624 }
1625
1626 /* Create re-INVITE with new offer */
1627 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1628 if (status != PJ_SUCCESS) {
1629 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001630 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001631 return status;
1632 }
1633
1634 /* Add additional headers etc */
1635 pjsua_process_msg_data( tdata, msg_data);
1636
1637 /* Send the request */
1638 status = pjsip_inv_send_msg( call->inv, tdata);
1639 if (status != PJ_SUCCESS) {
1640 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001641 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001642 return status;
1643 }
1644
Benny Prijonodc752ca2006-09-22 16:55:42 +00001645 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001646
1647 return PJ_SUCCESS;
1648}
1649
1650
1651/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001652 * Send UPDATE request.
1653 */
1654PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1655 unsigned options,
1656 const pjsua_msg_data *msg_data)
1657{
1658 pjmedia_sdp_session *sdp;
1659 pjsip_tx_data *tdata;
1660 pjsua_call *call;
1661 pjsip_dialog *dlg;
1662 pj_status_t status;
1663
1664 PJ_UNUSED_ARG(options);
1665
1666 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1667 PJ_EINVAL);
1668
1669 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1670 if (status != PJ_SUCCESS)
1671 return status;
1672
Benny Prijonoc08682e2007-10-04 06:17:58 +00001673 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001674 status = pjsua_media_channel_create_sdp(call->index,
1675 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001676 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001677 if (status != PJ_SUCCESS) {
1678 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1679 status);
1680 pjsip_dlg_dec_lock(dlg);
1681 return status;
1682 }
1683
Benny Prijono224b4e22008-06-19 14:10:28 +00001684 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001685 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1686 if (status != PJ_SUCCESS) {
1687 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1688 pjsip_dlg_dec_lock(dlg);
1689 return status;
1690 }
1691
1692 /* Add additional headers etc */
1693 pjsua_process_msg_data( tdata, msg_data);
1694
1695 /* Send the request */
1696 status = pjsip_inv_send_msg( call->inv, tdata);
1697 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001698 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001699 pjsip_dlg_dec_lock(dlg);
1700 return status;
1701 }
1702
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001703 call->local_hold = PJ_FALSE;
1704
Benny Prijonoc08682e2007-10-04 06:17:58 +00001705 pjsip_dlg_dec_lock(dlg);
1706
1707 return PJ_SUCCESS;
1708}
1709
1710
1711/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001712 * Initiate call transfer to the specified address.
1713 */
1714PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1715 const pj_str_t *dest,
1716 const pjsua_msg_data *msg_data)
1717{
1718 pjsip_evsub *sub;
1719 pjsip_tx_data *tdata;
1720 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001721 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001722 pjsip_generic_string_hdr *gs_hdr;
1723 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001724 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001725 pj_status_t status;
1726
1727
1728 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1729 PJ_EINVAL);
1730
Benny Prijonodc752ca2006-09-22 16:55:42 +00001731 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001732 if (status != PJ_SUCCESS)
1733 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001734
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001735
Benny Prijonod524e822006-09-22 12:48:18 +00001736 /* Create xfer client subscription. */
1737 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001738 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001739
1740 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001741 if (status != PJ_SUCCESS) {
1742 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001743 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001744 return status;
1745 }
1746
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001747 /* Associate this call with the client subscription */
1748 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1749
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001750 /*
1751 * Create REFER request.
1752 */
1753 status = pjsip_xfer_initiate(sub, dest, &tdata);
1754 if (status != PJ_SUCCESS) {
1755 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001756 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001757 return status;
1758 }
1759
Benny Prijono053f5222006-11-11 16:16:04 +00001760 /* Add Referred-By header */
1761 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1762 &dlg->local.info_str);
1763 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1764
1765
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001766 /* Add additional headers etc */
1767 pjsua_process_msg_data( tdata, msg_data);
1768
1769 /* Send. */
1770 status = pjsip_xfer_send_request(sub, tdata);
1771 if (status != PJ_SUCCESS) {
1772 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001773 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001774 return status;
1775 }
1776
1777 /* For simplicity (that's what this program is intended to be!),
1778 * leave the original invite session as it is. More advanced application
1779 * may want to hold the INVITE, or terminate the invite, or whatever.
1780 */
1781
Benny Prijonodc752ca2006-09-22 16:55:42 +00001782 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001783
1784 return PJ_SUCCESS;
1785
1786}
1787
1788
1789/*
Benny Prijono053f5222006-11-11 16:16:04 +00001790 * Initiate attended call transfer to the specified address.
1791 */
1792PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1793 pjsua_call_id dest_call_id,
1794 unsigned options,
1795 const pjsua_msg_data *msg_data)
1796{
1797 pjsua_call *dest_call;
1798 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001799 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001800 pj_str_t str_dest;
1801 int len;
1802 pjsip_uri *uri;
1803 pj_status_t status;
1804
1805
1806 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1807 PJ_EINVAL);
1808 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1809 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1810 PJ_EINVAL);
1811
1812 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1813 &dest_call, &dest_dlg);
1814 if (status != PJ_SUCCESS)
1815 return status;
1816
1817 /*
1818 * Create REFER destination URI with Replaces field.
1819 */
1820
1821 /* Make sure we have sufficient buffer's length */
1822 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1823 dest_dlg->call_id->id.slen +
1824 dest_dlg->remote.info->tag.slen +
1825 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001826 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001827
1828 /* Print URI */
1829 str_dest_buf[0] = '<';
1830 str_dest.slen = 1;
1831
Benny Prijonoa1e69682007-05-11 15:14:34 +00001832 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001833 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1834 str_dest_buf+1, sizeof(str_dest_buf)-1);
1835 if (len < 0)
1836 return PJSIP_EURITOOLONG;
1837
1838 str_dest.slen += len;
1839
1840
1841 /* Build the URI */
1842 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1843 sizeof(str_dest_buf) - str_dest.slen,
1844 "?%s"
1845 "Replaces=%.*s"
1846 "%%3Bto-tag%%3D%.*s"
1847 "%%3Bfrom-tag%%3D%.*s>",
1848 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1849 "" : "Require=replaces&"),
1850 (int)dest_dlg->call_id->id.slen,
1851 dest_dlg->call_id->id.ptr,
1852 (int)dest_dlg->remote.info->tag.slen,
1853 dest_dlg->remote.info->tag.ptr,
1854 (int)dest_dlg->local.info->tag.slen,
1855 dest_dlg->local.info->tag.ptr);
1856
1857 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1858 PJSIP_EURITOOLONG);
1859
1860 str_dest.ptr = str_dest_buf;
1861 str_dest.slen += len;
1862
1863 pjsip_dlg_dec_lock(dest_dlg);
1864
1865 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1866}
1867
1868
1869/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001870 * Send DTMF digits to remote using RFC 2833 payload formats.
1871 */
1872PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1873 const pj_str_t *digits)
1874{
1875 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001876 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001877 pj_status_t status;
1878
1879 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1880 PJ_EINVAL);
1881
Benny Prijonodc752ca2006-09-22 16:55:42 +00001882 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001883 if (status != PJ_SUCCESS)
1884 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001885
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001886 if (!call->session) {
1887 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001888 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001889 return PJ_EINVALIDOP;
1890 }
1891
1892 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1893
Benny Prijonodc752ca2006-09-22 16:55:42 +00001894 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001895
1896 return status;
1897}
1898
1899
1900/**
1901 * Send instant messaging inside INVITE session.
1902 */
1903PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1904 const pj_str_t *mime_type,
1905 const pj_str_t *content,
1906 const pjsua_msg_data *msg_data,
1907 void *user_data)
1908{
1909 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001910 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001911 const pj_str_t mime_text_plain = pj_str("text/plain");
1912 pjsip_media_type ctype;
1913 pjsua_im_data *im_data;
1914 pjsip_tx_data *tdata;
1915 pj_status_t status;
1916
1917
1918 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1919 PJ_EINVAL);
1920
Benny Prijonodc752ca2006-09-22 16:55:42 +00001921 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001922 if (status != PJ_SUCCESS)
1923 return status;
1924
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001925 /* Set default media type if none is specified */
1926 if (mime_type == NULL) {
1927 mime_type = &mime_text_plain;
1928 }
1929
1930 /* Create request message. */
1931 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1932 -1, &tdata);
1933 if (status != PJ_SUCCESS) {
1934 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1935 goto on_return;
1936 }
1937
1938 /* Add accept header. */
1939 pjsip_msg_add_hdr( tdata->msg,
1940 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1941
1942 /* Parse MIME type */
1943 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1944
1945 /* Create "text/plain" message body. */
1946 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1947 &ctype.subtype, content);
1948 if (tdata->msg->body == NULL) {
1949 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1950 pjsip_tx_data_dec_ref(tdata);
1951 goto on_return;
1952 }
1953
1954 /* Add additional headers etc */
1955 pjsua_process_msg_data( tdata, msg_data);
1956
1957 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001958 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001959 im_data->acc_id = call->acc_id;
1960 im_data->call_id = call_id;
1961 im_data->to = call->inv->dlg->remote.info_str;
1962 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1963 im_data->user_data = user_data;
1964
1965
1966 /* Send the request. */
1967 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1968 pjsua_var.mod.id, im_data);
1969 if (status != PJ_SUCCESS) {
1970 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1971 goto on_return;
1972 }
1973
1974on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001975 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001976 return status;
1977}
1978
1979
1980/*
1981 * Send IM typing indication inside INVITE session.
1982 */
1983PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1984 pj_bool_t is_typing,
1985 const pjsua_msg_data*msg_data)
1986{
1987 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001988 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001989 pjsip_tx_data *tdata;
1990 pj_status_t status;
1991
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001992 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1993 PJ_EINVAL);
1994
Benny Prijonodc752ca2006-09-22 16:55:42 +00001995 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001996 if (status != PJ_SUCCESS)
1997 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001998
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001999 /* Create request message. */
2000 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2001 -1, &tdata);
2002 if (status != PJ_SUCCESS) {
2003 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2004 goto on_return;
2005 }
2006
2007 /* Create "application/im-iscomposing+xml" msg body. */
2008 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2009 NULL, NULL, -1);
2010
2011 /* Add additional headers etc */
2012 pjsua_process_msg_data( tdata, msg_data);
2013
2014 /* Send the request. */
2015 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2016 if (status != PJ_SUCCESS) {
2017 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2018 goto on_return;
2019 }
2020
2021on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002022 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002023 return status;
2024}
2025
2026
2027/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002028 * Send arbitrary request.
2029 */
2030PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2031 const pj_str_t *method_str,
2032 const pjsua_msg_data *msg_data)
2033{
2034 pjsua_call *call;
2035 pjsip_dialog *dlg;
2036 pjsip_method method;
2037 pjsip_tx_data *tdata;
2038 pj_status_t status;
2039
2040 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2041 PJ_EINVAL);
2042
2043 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2044 if (status != PJ_SUCCESS)
2045 return status;
2046
2047 /* Init method */
2048 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2049
2050 /* Create request message. */
2051 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2052 if (status != PJ_SUCCESS) {
2053 pjsua_perror(THIS_FILE, "Unable to create request", status);
2054 goto on_return;
2055 }
2056
2057 /* Add additional headers etc */
2058 pjsua_process_msg_data( tdata, msg_data);
2059
2060 /* Send the request. */
2061 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2062 if (status != PJ_SUCCESS) {
2063 pjsua_perror(THIS_FILE, "Unable to send request", status);
2064 goto on_return;
2065 }
2066
2067on_return:
2068 pjsip_dlg_dec_lock(dlg);
2069 return status;
2070}
2071
2072
2073/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002074 * Terminate all calls.
2075 */
2076PJ_DEF(void) pjsua_call_hangup_all(void)
2077{
2078 unsigned i;
2079
2080 PJSUA_LOCK();
2081
2082 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2083 if (pjsua_var.calls[i].inv)
2084 pjsua_call_hangup(i, 0, NULL, NULL);
2085 }
2086
2087 PJSUA_UNLOCK();
2088}
2089
2090
Benny Prijono627cbb42007-09-25 20:48:49 +00002091const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002092{
2093 if (val < 1000) {
2094 pj_ansi_sprintf(buf, "%d", val);
2095 } else if (val < 1000000) {
2096 pj_ansi_sprintf(buf, "%d.%dK",
2097 val / 1000,
2098 (val % 1000) / 100);
2099 } else {
2100 pj_ansi_sprintf(buf, "%d.%02dM",
2101 val / 1000000,
2102 (val % 1000000) / 10000);
2103 }
2104
2105 return buf;
2106}
2107
2108
2109/* Dump media session */
2110static void dump_media_session(const char *indent,
2111 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002112 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002113{
2114 unsigned i;
2115 char *p = buf, *end = buf+maxlen;
2116 int len;
2117 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002118 pjmedia_session *session = call->session;
2119 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002120
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002121 pjmedia_transport_info_init(&tp_info);
2122
2123 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002124 pjmedia_session_get_info(session, &info);
2125
2126 for (i=0; i<info.stream_cnt; ++i) {
2127 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002128 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002129 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002130 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002131 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002132 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002133 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002134
2135 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002136 // rem_addr will contain actual address of RTP originator, instead of
2137 // remote RTP address specified by stream which is fetched from the SDP.
2138 // Please note that we are assuming only one stream per call.
2139 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2140 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002141 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2142 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002143 sizeof(rem_addr_buf), 3);
2144 } else {
2145 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002146 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002147 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002148
2149 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
2150 dir = "sendonly";
2151 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2152 dir = "recvonly";
2153 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2154 dir = "sendrecv";
2155 else
2156 dir = "inactive";
2157
2158
2159 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002160 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002161 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002162 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002163 info.stream_info[i].fmt.encoding_name.ptr,
2164 info.stream_info[i].fmt.clock_rate / 1000,
2165 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002166 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002167 if (len < 1 || len > end-p) {
2168 *p = '\0';
2169 return;
2170 }
2171
2172 p += len;
2173 *p++ = '\n';
2174 *p = '\0';
2175
2176 if (stat.rx.update_cnt == 0)
2177 strcpy(last_update, "never");
2178 else {
2179 pj_gettimeofday(&now);
2180 PJ_TIME_VAL_SUB(now, stat.rx.update);
2181 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2182 now.sec / 3600,
2183 (now.sec % 3600) / 60,
2184 now.sec % 60,
2185 now.msec);
2186 }
2187
Benny Prijono80019eb2006-08-07 13:22:23 +00002188 pj_gettimeofday(&media_duration);
2189 PJ_TIME_VAL_SUB(media_duration, stat.start);
2190 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2191 media_duration.msec = 1;
2192
Benny Prijono1402a4a2008-01-08 23:41:22 +00002193 /* protect against division by zero */
2194 if (stat.rx.pkt == 0)
2195 stat.rx.pkt = 1;
2196 if (stat.tx.pkt == 0)
2197 stat.tx.pkt = 1;
2198
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002199 len = pj_ansi_snprintf(p, end-p,
2200 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002201 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002202 "%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 +00002203 "%s (msec) min avg max last dev\n"
2204 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2205 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002206 indent, info.stream_info[i].fmt.pt,
2207 last_update,
2208 indent,
2209 good_number(packets, stat.rx.pkt),
2210 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002211 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002212 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2213 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 +00002214 indent,
2215 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002216 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002217 stat.rx.discard,
2218 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002219 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002220 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002221 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002222 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002223 indent, indent,
2224 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002225 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002226 stat.rx.loss_period.max / 1000.0,
2227 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002228 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002229 indent,
2230 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002231 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002232 stat.rx.jitter.max / 1000.0,
2233 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002234 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002235 ""
2236 );
2237
2238 if (len < 1 || len > end-p) {
2239 *p = '\0';
2240 return;
2241 }
2242
2243 p += len;
2244 *p++ = '\n';
2245 *p = '\0';
2246
2247 if (stat.tx.update_cnt == 0)
2248 strcpy(last_update, "never");
2249 else {
2250 pj_gettimeofday(&now);
2251 PJ_TIME_VAL_SUB(now, stat.tx.update);
2252 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2253 now.sec / 3600,
2254 (now.sec % 3600) / 60,
2255 now.sec % 60,
2256 now.msec);
2257 }
2258
2259 len = pj_ansi_snprintf(p, end-p,
2260 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002261 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002262 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002263 "%s (msec) min avg max last dev \n"
2264 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2265 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002266 indent,
2267 info.stream_info[i].tx_pt,
2268 info.stream_info[i].param->info.frm_ptime *
2269 info.stream_info[i].param->setting.frm_per_pkt,
2270 last_update,
2271
2272 indent,
2273 good_number(packets, stat.tx.pkt),
2274 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002275 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002276 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2277 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 +00002278
2279 indent,
2280 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002281 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002282 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002283 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002284 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002285 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002286
2287 indent, indent,
2288 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002289 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002290 stat.tx.loss_period.max / 1000.0,
2291 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002292 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002293 indent,
2294 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002295 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002296 stat.tx.jitter.max / 1000.0,
2297 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002298 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002299 ""
2300 );
2301
2302 if (len < 1 || len > end-p) {
2303 *p = '\0';
2304 return;
2305 }
2306
2307 p += len;
2308 *p++ = '\n';
2309 *p = '\0';
2310
2311 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002312 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002313 indent,
2314 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002315 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002316 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002317 stat.rtt.last / 1000.0,
2318 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002319 );
2320 if (len < 1 || len > end-p) {
2321 *p = '\0';
2322 return;
2323 }
2324
2325 p += len;
2326 *p++ = '\n';
2327 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002328
2329#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2330# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2331 do { \
2332 if (samples <= 4294) \
2333 usec = samples * 1000000 / clock_rate; \
2334 else { \
2335 usec = samples * 1000 / clock_rate; \
2336 usec *= 1000; \
2337 } \
2338 } while(0)
2339
2340# define PRINT_VOIP_MTC_VAL(s, v) \
2341 if (v == 127) \
2342 sprintf(s, "(na)"); \
2343 else \
2344 sprintf(s, "%d", v)
2345
2346# define VALIDATE_PRINT_BUF() \
2347 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2348 p += len; *p++ = '\n'; *p = '\0'
2349
2350
2351 do {
2352 char loss[16], dup[16];
2353 char jitter[80];
2354 char toh[80];
2355 char plc[16], jba[16], jbr[16];
2356 char signal_lvl[16], noise_lvl[16], rerl[16];
2357 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2358 pjmedia_rtcp_xr_stat xr_stat;
2359 unsigned clock_rate;
2360
2361 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2362 PJ_SUCCESS)
2363 {
2364 break;
2365 }
2366
2367 clock_rate = info.stream_info[i].fmt.clock_rate;
2368
2369 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2370 VALIDATE_PRINT_BUF();
2371
2372 /* Statistics Summary */
2373 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2374 VALIDATE_PRINT_BUF();
2375
2376 if (xr_stat.rx.stat_sum.l)
2377 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2378 else
2379 sprintf(loss, "(na)");
2380
2381 if (xr_stat.rx.stat_sum.d)
2382 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2383 else
2384 sprintf(dup, "(na)");
2385
2386 if (xr_stat.rx.stat_sum.j) {
2387 unsigned jmin, jmax, jmean, jdev;
2388
2389 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2390 clock_rate);
2391 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2392 clock_rate);
2393 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2394 clock_rate);
2395 SAMPLES_TO_USEC(jdev,
2396 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2397 clock_rate);
2398 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2399 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2400 } else
2401 sprintf(jitter, "(report not available)");
2402
2403 if (xr_stat.rx.stat_sum.t) {
2404 sprintf(toh, "%11d %11d %11d %11d",
2405 xr_stat.rx.stat_sum.toh.min,
2406 xr_stat.rx.stat_sum.toh.mean,
2407 xr_stat.rx.stat_sum.toh.max,
2408 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2409 } else
2410 sprintf(toh, "(report not available)");
2411
2412 if (xr_stat.rx.stat_sum.update.sec == 0)
2413 strcpy(last_update, "never");
2414 else {
2415 pj_gettimeofday(&now);
2416 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2417 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2418 now.sec / 3600,
2419 (now.sec % 3600) / 60,
2420 now.sec % 60,
2421 now.msec);
2422 }
2423
2424 len = pj_ansi_snprintf(p, end-p,
2425 "%s RX last update: %s\n"
2426 "%s begin seq=%d, end seq=%d\n"
2427 "%s pkt loss=%s, dup=%s\n"
2428 "%s (msec) min avg max dev\n"
2429 "%s jitter : %s\n"
2430 "%s toh : %s",
2431 indent, last_update,
2432 indent,
2433 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2434 indent, loss, dup,
2435 indent,
2436 indent, jitter,
2437 indent, toh
2438 );
2439 VALIDATE_PRINT_BUF();
2440
2441 if (xr_stat.tx.stat_sum.l)
2442 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2443 else
2444 sprintf(loss, "(na)");
2445
2446 if (xr_stat.tx.stat_sum.d)
2447 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2448 else
2449 sprintf(dup, "(na)");
2450
2451 if (xr_stat.tx.stat_sum.j) {
2452 unsigned jmin, jmax, jmean, jdev;
2453
2454 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2455 clock_rate);
2456 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2457 clock_rate);
2458 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2459 clock_rate);
2460 SAMPLES_TO_USEC(jdev,
2461 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2462 clock_rate);
2463 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2464 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2465 } else
2466 sprintf(jitter, "(report not available)");
2467
2468 if (xr_stat.tx.stat_sum.t) {
2469 sprintf(toh, "%11d %11d %11d %11d",
2470 xr_stat.tx.stat_sum.toh.min,
2471 xr_stat.tx.stat_sum.toh.mean,
2472 xr_stat.tx.stat_sum.toh.max,
2473 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2474 } else
2475 sprintf(toh, "(report not available)");
2476
2477 if (xr_stat.tx.stat_sum.update.sec == 0)
2478 strcpy(last_update, "never");
2479 else {
2480 pj_gettimeofday(&now);
2481 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2482 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2483 now.sec / 3600,
2484 (now.sec % 3600) / 60,
2485 now.sec % 60,
2486 now.msec);
2487 }
2488
2489 len = pj_ansi_snprintf(p, end-p,
2490 "%s TX last update: %s\n"
2491 "%s begin seq=%d, end seq=%d\n"
2492 "%s pkt loss=%s, dup=%s\n"
2493 "%s (msec) min avg max dev\n"
2494 "%s jitter : %s\n"
2495 "%s toh : %s",
2496 indent, last_update,
2497 indent,
2498 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2499 indent, loss, dup,
2500 indent,
2501 indent, jitter,
2502 indent, toh
2503 );
2504 VALIDATE_PRINT_BUF();
2505
2506
2507 /* VoIP Metrics */
2508 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2509 VALIDATE_PRINT_BUF();
2510
2511 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2512 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2513 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2514 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2515 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2516 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2517 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2518
2519 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2520 case PJMEDIA_RTCP_XR_PLC_DIS:
2521 sprintf(plc, "DISABLED");
2522 break;
2523 case PJMEDIA_RTCP_XR_PLC_ENH:
2524 sprintf(plc, "ENHANCED");
2525 break;
2526 case PJMEDIA_RTCP_XR_PLC_STD:
2527 sprintf(plc, "STANDARD");
2528 break;
2529 case PJMEDIA_RTCP_XR_PLC_UNK:
2530 default:
2531 sprintf(plc, "UNKNOWN");
2532 break;
2533 }
2534
2535 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2536 case PJMEDIA_RTCP_XR_JB_FIXED:
2537 sprintf(jba, "FIXED");
2538 break;
2539 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2540 sprintf(jba, "ADAPTIVE");
2541 break;
2542 default:
2543 sprintf(jba, "UNKNOWN");
2544 break;
2545 }
2546
2547 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2548
2549 if (xr_stat.rx.voip_mtc.update.sec == 0)
2550 strcpy(last_update, "never");
2551 else {
2552 pj_gettimeofday(&now);
2553 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2554 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2555 now.sec / 3600,
2556 (now.sec % 3600) / 60,
2557 now.sec % 60,
2558 now.msec);
2559 }
2560
2561 len = pj_ansi_snprintf(p, end-p,
2562 "%s RX last update: %s\n"
2563 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2564 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2565 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2566 "%s delay : round trip=%d%s, end system=%d%s\n"
2567 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2568 "%s quality : R factor=%s, ext R factor=%s\n"
2569 "%s MOS LQ=%s, MOS CQ=%s\n"
2570 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2571 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2572 indent,
2573 last_update,
2574 /* packets */
2575 indent,
2576 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2577 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2578 /* burst */
2579 indent,
2580 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2581 xr_stat.rx.voip_mtc.burst_dur, "ms",
2582 /* gap */
2583 indent,
2584 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2585 xr_stat.rx.voip_mtc.gap_dur, "ms",
2586 /* delay */
2587 indent,
2588 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2589 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2590 /* level */
2591 indent,
2592 signal_lvl, "dB",
2593 noise_lvl, "dB",
2594 rerl, "",
2595 /* quality */
2596 indent,
2597 r_factor, ext_r_factor,
2598 indent,
2599 mos_lq, mos_cq,
2600 /* config */
2601 indent,
2602 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2603 /* JB delay */
2604 indent,
2605 xr_stat.rx.voip_mtc.jb_nom, "ms",
2606 xr_stat.rx.voip_mtc.jb_max, "ms",
2607 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2608 );
2609 VALIDATE_PRINT_BUF();
2610
2611 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2612 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2613 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2614 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2615 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2616 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2617 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2618
2619 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2620 case PJMEDIA_RTCP_XR_PLC_DIS:
2621 sprintf(plc, "DISABLED");
2622 break;
2623 case PJMEDIA_RTCP_XR_PLC_ENH:
2624 sprintf(plc, "ENHANCED");
2625 break;
2626 case PJMEDIA_RTCP_XR_PLC_STD:
2627 sprintf(plc, "STANDARD");
2628 break;
2629 case PJMEDIA_RTCP_XR_PLC_UNK:
2630 default:
2631 sprintf(plc, "unknown");
2632 break;
2633 }
2634
2635 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2636 case PJMEDIA_RTCP_XR_JB_FIXED:
2637 sprintf(jba, "FIXED");
2638 break;
2639 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2640 sprintf(jba, "ADAPTIVE");
2641 break;
2642 default:
2643 sprintf(jba, "unknown");
2644 break;
2645 }
2646
2647 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2648
2649 if (xr_stat.tx.voip_mtc.update.sec == 0)
2650 strcpy(last_update, "never");
2651 else {
2652 pj_gettimeofday(&now);
2653 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2654 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2655 now.sec / 3600,
2656 (now.sec % 3600) / 60,
2657 now.sec % 60,
2658 now.msec);
2659 }
2660
2661 len = pj_ansi_snprintf(p, end-p,
2662 "%s TX last update: %s\n"
2663 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2664 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2665 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2666 "%s delay : round trip=%d%s, end system=%d%s\n"
2667 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2668 "%s quality : R factor=%s, ext R factor=%s\n"
2669 "%s MOS LQ=%s, MOS CQ=%s\n"
2670 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2671 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2672 indent,
2673 last_update,
2674 /* pakcets */
2675 indent,
2676 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2677 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2678 /* burst */
2679 indent,
2680 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2681 xr_stat.tx.voip_mtc.burst_dur, "ms",
2682 /* gap */
2683 indent,
2684 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2685 xr_stat.tx.voip_mtc.gap_dur, "ms",
2686 /* delay */
2687 indent,
2688 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2689 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2690 /* level */
2691 indent,
2692 signal_lvl, "dB",
2693 noise_lvl, "dB",
2694 rerl, "",
2695 /* quality */
2696 indent,
2697 r_factor, ext_r_factor,
2698 indent,
2699 mos_lq, mos_cq,
2700 /* config */
2701 indent,
2702 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2703 /* JB delay */
2704 indent,
2705 xr_stat.tx.voip_mtc.jb_nom, "ms",
2706 xr_stat.tx.voip_mtc.jb_max, "ms",
2707 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2708 );
2709 VALIDATE_PRINT_BUF();
2710
2711
2712 /* RTT delay (by receiver side) */
2713 len = pj_ansi_snprintf(p, end-p,
2714 "%s RTT (from recv) min avg max last dev",
2715 indent);
2716 VALIDATE_PRINT_BUF();
2717 len = pj_ansi_snprintf(p, end-p,
2718 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2719 indent,
2720 xr_stat.rtt.min / 1000.0,
2721 xr_stat.rtt.mean / 1000.0,
2722 xr_stat.rtt.max / 1000.0,
2723 xr_stat.rtt.last / 1000.0,
2724 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2725 );
2726 VALIDATE_PRINT_BUF();
2727 } while(0);
2728#endif
2729
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002730 }
2731}
2732
2733
2734/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002735void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002736 int call_id,
2737 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002738{
2739 int len;
2740 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2741 pjsip_dialog *dlg = inv->dlg;
2742 char userinfo[128];
2743
2744 /* Dump invite sesion info. */
2745
2746 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
Benny Prijono329d6382009-05-29 13:04:03 +00002747 if (len < 0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002748 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2749 else
2750 userinfo[len] = '\0';
2751
2752 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2753 title,
2754 pjsip_inv_state_name(inv->state),
2755 userinfo);
2756 if (len < 1 || len >= (int)size) {
2757 pj_ansi_strcpy(buf, "<--uri too long-->");
2758 len = 18;
2759 } else
2760 buf[len] = '\0';
2761}
2762
2763
2764/*
2765 * Dump call and media statistics to string.
2766 */
2767PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2768 pj_bool_t with_media,
2769 char *buffer,
2770 unsigned maxlen,
2771 const char *indent)
2772{
2773 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002774 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002775 pj_time_val duration, res_delay, con_delay;
2776 char tmp[128];
2777 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002778 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002779 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002780 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002781
2782 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2783 PJ_EINVAL);
2784
Benny Prijonodc752ca2006-09-22 16:55:42 +00002785 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002786 if (status != PJ_SUCCESS)
2787 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002788
2789 *buffer = '\0';
2790 p = buffer;
2791 end = buffer + maxlen;
2792 len = 0;
2793
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002794 print_call(indent, call_id, tmp, sizeof(tmp));
2795
2796 len = pj_ansi_strlen(tmp);
2797 pj_ansi_strcpy(buffer, tmp);
2798
2799 p += len;
2800 *p++ = '\r';
2801 *p++ = '\n';
2802
2803 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002804 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002805 pj_gettimeofday(&duration);
2806 PJ_TIME_VAL_SUB(duration, call->conn_time);
2807 con_delay = call->conn_time;
2808 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2809 } else {
2810 duration.sec = duration.msec = 0;
2811 con_delay.sec = con_delay.msec = 0;
2812 }
2813
2814 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002815 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002816 res_delay = call->res_time;
2817 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2818 } else {
2819 res_delay.sec = res_delay.msec = 0;
2820 }
2821
2822 /* Print duration */
2823 len = pj_ansi_snprintf(p, end-p,
2824 "%s Call time: %02dh:%02dm:%02ds, "
2825 "1st res in %d ms, conn in %dms",
2826 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002827 (int)(duration.sec / 3600),
2828 (int)((duration.sec % 3600)/60),
2829 (int)(duration.sec % 60),
2830 (int)PJ_TIME_VAL_MSEC(res_delay),
2831 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002832
2833 if (len > 0 && len < end-p) {
2834 p += len;
2835 *p++ = '\n';
2836 *p = '\0';
2837 }
2838
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002839 /* Get and ICE SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002840 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002841 pjmedia_transport_get_info(call->med_tp, &tp_info);
2842 if (tp_info.specific_info_cnt > 0) {
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002843 unsigned i;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002844 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2845 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2846 {
2847 pjmedia_srtp_info *srtp_info =
2848 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2849
2850 len = pj_ansi_snprintf(p, end-p,
2851 "%s SRTP status: %s Crypto-suite: %s",
2852 indent,
2853 (srtp_info->active?"Active":"Not active"),
2854 srtp_info->tx_policy.name.ptr);
2855 if (len > 0 && len < end-p) {
2856 p += len;
2857 *p++ = '\n';
2858 *p = '\0';
2859 }
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002860 } else if (tp_info.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
2861 const pjmedia_ice_transport_info *ii;
2862
2863 ii = (const pjmedia_ice_transport_info*)
2864 tp_info.spc_info[i].buffer;
2865
2866 len = pj_ansi_snprintf(p, end-p,
2867 "%s ICE role: %s, state: %s, comp_cnt: %u",
2868 indent,
2869 pj_ice_sess_role_name(ii->role),
2870 pj_ice_strans_state_name(ii->sess_state),
2871 ii->comp_cnt);
2872 if (len > 0 && len < end-p) {
2873 p += len;
2874 *p++ = '\n';
2875 *p = '\0';
2876 }
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002877 }
2878 }
2879 }
2880
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002881 /* Dump session statistics */
2882 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002883 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002884
Benny Prijonodc752ca2006-09-22 16:55:42 +00002885 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002886
2887 return PJ_SUCCESS;
2888}
2889
2890
2891/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002892 * This callback receives notification from invite session when the
2893 * session state has changed.
2894 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002895static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2896 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002897{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002898 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002899
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002900 PJSUA_LOCK();
2901
Benny Prijonoa1e69682007-05-11 15:14:34 +00002902 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002903
2904 if (!call) {
2905 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002906 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002907 }
2908
Benny Prijonoe21e7842006-04-09 16:46:05 +00002909
2910 /* Get call times */
2911 switch (inv->state) {
2912 case PJSIP_INV_STATE_EARLY:
2913 case PJSIP_INV_STATE_CONNECTING:
2914 if (call->res_time.sec == 0)
2915 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002916 call->last_code = (pjsip_status_code)
2917 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002918 pj_strncpy(&call->last_text,
2919 &e->body.tsx_state.tsx->status_text,
2920 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002921 break;
2922 case PJSIP_INV_STATE_CONFIRMED:
2923 pj_gettimeofday(&call->conn_time);
2924 break;
2925 case PJSIP_INV_STATE_DISCONNECTED:
2926 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002927 if (call->res_time.sec == 0)
2928 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002929 if (e->type == PJSIP_EVENT_TSX_STATE &&
2930 e->body.tsx_state.tsx->status_code > call->last_code)
2931 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002932 call->last_code = (pjsip_status_code)
2933 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002934 pj_strncpy(&call->last_text,
2935 &e->body.tsx_state.tsx->status_text,
2936 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002937 } else {
2938 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
2939 pj_strncpy(&call->last_text,
2940 pjsip_get_status_text(call->last_code),
2941 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002942 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002943 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002944 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002945 call->last_code = (pjsip_status_code)
2946 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002947 pj_strncpy(&call->last_text,
2948 &e->body.tsx_state.tsx->status_text,
2949 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002950 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002951 }
2952
Benny Prijono26ff9062006-02-21 23:47:00 +00002953 /* If this is an outgoing INVITE that was created because of
2954 * REFER/transfer, send NOTIFY to transferer.
2955 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002956 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002957 int st_code = -1;
2958 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2959
2960
Benny Prijonoa91a0032006-02-26 21:23:45 +00002961 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002962 case PJSIP_INV_STATE_NULL:
2963 case PJSIP_INV_STATE_CALLING:
2964 /* Do nothing */
2965 break;
2966
2967 case PJSIP_INV_STATE_EARLY:
2968 case PJSIP_INV_STATE_CONNECTING:
2969 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002970 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
2971 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2972 else
2973 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002974 break;
2975
Benny Prijono140beae2009-10-11 05:06:43 +00002976 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002977#if 0
2978/* We don't need this, as we've terminated the subscription in
2979 * CONNECTING state.
2980 */
Benny Prijono26ff9062006-02-21 23:47:00 +00002981 /* When state is confirmed, send the final 200/OK and terminate
2982 * subscription.
2983 */
2984 st_code = e->body.tsx_state.tsx->status_code;
2985 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002986#endif
Benny Prijono140beae2009-10-11 05:06:43 +00002987 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002988
2989 case PJSIP_INV_STATE_DISCONNECTED:
2990 st_code = e->body.tsx_state.tsx->status_code;
2991 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2992 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002993
Benny Prijono8b1889b2006-06-06 18:40:40 +00002994 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002995 /* Nothing to do. Just to keep gcc from complaining about
2996 * unused enums.
2997 */
2998 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002999 }
3000
3001 if (st_code != -1) {
3002 pjsip_tx_data *tdata;
3003 pj_status_t status;
3004
Benny Prijonoa91a0032006-02-26 21:23:45 +00003005 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00003006 ev_state, st_code,
3007 NULL, &tdata);
3008 if (status != PJ_SUCCESS) {
3009 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
3010 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003011 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00003012 if (status != PJ_SUCCESS) {
3013 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
3014 }
3015 }
3016 }
3017 }
3018
Benny Prijono84126ab2006-02-09 09:30:09 +00003019
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003020 if (pjsua_var.ua_cfg.cb.on_call_state)
3021 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003022
3023 /* call->inv may be NULL now */
3024
Benny Prijono84126ab2006-02-09 09:30:09 +00003025 /* Destroy media session when invite session is disconnected. */
3026 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003027
Benny Prijonoa91a0032006-02-26 21:23:45 +00003028 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003029
Benny Prijono275fd682006-03-22 11:59:11 +00003030 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003031 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003032
Benny Prijono105217f2006-03-06 16:25:59 +00003033 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003034 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003035 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003036
3037 /* Reset call */
3038 reset_call(call->index);
3039
Benny Prijono84126ab2006-02-09 09:30:09 +00003040 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003041
3042 PJSUA_UNLOCK();
3043}
3044
3045/*
3046 * This callback is called by invite session framework when UAC session
3047 * has forked.
3048 */
3049static void pjsua_call_on_forked( pjsip_inv_session *inv,
3050 pjsip_event *e)
3051{
3052 PJ_UNUSED_ARG(inv);
3053 PJ_UNUSED_ARG(e);
3054
3055 PJ_TODO(HANDLE_FORKED_DIALOG);
3056}
3057
3058
3059/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003060 * Callback from UA layer when forked dialog response is received.
3061 */
3062pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3063{
3064 if (dlg->uac_has_2xx &&
3065 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3066 pjsip_rdata_get_tsx(res) == NULL &&
3067 res->msg_info.msg->line.status.code/100 == 2)
3068 {
3069 pjsip_dialog *forked_dlg;
3070 pjsip_tx_data *bye;
3071 pj_status_t status;
3072
3073 /* Create forked dialog */
3074 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3075 if (status != PJ_SUCCESS)
3076 return NULL;
3077
3078 pjsip_dlg_inc_lock(forked_dlg);
3079
3080 /* Disconnect the call */
3081 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3082 -1, &bye);
3083 if (status == PJ_SUCCESS) {
3084 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3085 }
3086
3087 pjsip_dlg_dec_lock(forked_dlg);
3088
3089 if (status != PJ_SUCCESS) {
3090 return NULL;
3091 }
3092
3093 return forked_dlg;
3094
3095 } else {
3096 return dlg;
3097 }
3098}
3099
3100/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003101 * Disconnect call upon error.
3102 */
3103static void call_disconnect( pjsip_inv_session *inv,
3104 int code )
3105{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003106 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003107 pjsip_tx_data *tdata;
3108 pj_status_t status;
3109
Benny Prijono59b3aed2008-01-15 16:54:54 +00003110 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3111
Benny Prijonoa38ada02006-07-02 14:22:35 +00003112 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003113 if (status != PJ_SUCCESS)
3114 return;
3115
3116 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003117 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3118 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3119 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003120 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003121 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003122
Benny Prijono734fc2d2008-03-17 16:05:35 +00003123 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003124 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003125 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003126 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003127 if (status == PJ_SUCCESS) {
3128 pjsip_create_sdp_body(tdata->pool, local_sdp,
3129 &tdata->msg->body);
3130 }
3131 }
3132
3133 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003134}
3135
3136/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003137 * Callback to be called when SDP offer/answer negotiation has just completed
3138 * in the session. This function will start/update media if negotiation
3139 * has succeeded.
3140 */
3141static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3142 pj_status_t status)
3143{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003144 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003145 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003146 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003147
3148 PJSUA_LOCK();
3149
Benny Prijonoa1e69682007-05-11 15:14:34 +00003150 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003151
3152 if (status != PJ_SUCCESS) {
3153
3154 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3155
Benny Prijono2331d202008-06-26 15:46:52 +00003156 /* Do not deinitialize media since this may be a re-INVITE or
3157 * UPDATE (which in this case the media should not get affected
3158 * by the failed re-INVITE/UPDATE). The media will be shutdown
3159 * when call is disconnected anyway.
3160 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003161 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003162 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003163
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003164 /* Disconnect call if we're not in the middle of initializing an
3165 * UAS dialog and if this is not a re-INVITE
3166 */
3167 if (inv->state != PJSIP_INV_STATE_NULL &&
3168 inv->state != PJSIP_INV_STATE_CONFIRMED)
3169 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003170 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003171 }
3172
3173 PJSUA_UNLOCK();
3174 return;
3175 }
3176
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003177
3178 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003179 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003180 if (status != PJ_SUCCESS) {
3181 pjsua_perror(THIS_FILE,
3182 "Unable to retrieve currently active local SDP",
3183 status);
3184 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3185 PJSUA_UNLOCK();
3186 return;
3187 }
3188
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003189 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3190 if (status != PJ_SUCCESS) {
3191 pjsua_perror(THIS_FILE,
3192 "Unable to retrieve currently active remote SDP",
3193 status);
3194 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3195 PJSUA_UNLOCK();
3196 return;
3197 }
3198
Benny Prijono91a6a172007-10-31 08:59:29 +00003199 /* Update remote's NAT type */
3200 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3201 update_remote_nat_type(call, remote_sdp);
3202 }
3203
3204 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003205 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003206 if (status != PJ_SUCCESS) {
3207 pjsua_perror(THIS_FILE, "Unable to create media session",
3208 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003209 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003210 /* No need to deinitialize; media will be shutdown when call
3211 * state is disconnected anyway.
3212 */
3213 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003214 PJSUA_UNLOCK();
3215 return;
3216 }
3217
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003218
3219 /* Call application callback, if any */
3220 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3221 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3222
3223
3224 PJSUA_UNLOCK();
3225}
3226
3227
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003228/* Create SDP for call hold. */
3229static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3230 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003231{
3232 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003233 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003234 pjmedia_sdp_session *sdp;
3235
Benny Prijono40d62b62009-08-12 17:53:47 +00003236 /* Use call's provisional pool */
3237 pool = call->inv->pool_prov;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003238
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003239 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003240 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3241 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003242 if (status != PJ_SUCCESS) {
3243 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3244 return status;
3245 }
3246
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003247 /* Call-hold is done by set the media direction to 'sendonly'
3248 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3249 * 'inactive' (PJMEDIA_DIR_NONE).
3250 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3251 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003252 /* http://trac.pjsip.org/repos/ticket/880
3253 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3254 */
3255 if (1) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003256 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003257
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003258 /* Remove existing directions attributes */
3259 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3260 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3261 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3262 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003263
Benny Prijonoe0860132009-06-05 10:14:20 +00003264 if (call->media_dir & PJMEDIA_DIR_ENCODING) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003265 /* Add sendonly attribute */
3266 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3267 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3268 } else {
3269 /* Add inactive attribute */
3270 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3271 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3272 }
3273 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003274
3275 *p_answer = sdp;
3276
3277 return status;
3278}
3279
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003280/*
3281 * Called when session received new offer.
3282 */
3283static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3284 const pjmedia_sdp_session *offer)
3285{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003286 pjsua_call *call;
3287 pjmedia_sdp_conn *conn;
3288 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003289 pj_status_t status;
3290
3291 PJSUA_LOCK();
3292
Benny Prijonoa1e69682007-05-11 15:14:34 +00003293 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003294
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003295 conn = offer->media[0]->conn;
3296 if (!conn)
3297 conn = offer->conn;
3298
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003299 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003300 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3301 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003302
Benny Prijono40d62b62009-08-12 17:53:47 +00003303 status = pjsua_media_channel_create_sdp(call->index,
3304 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003305 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003306 if (status != PJ_SUCCESS) {
3307 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3308 PJSUA_UNLOCK();
3309 return;
3310 }
3311
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003312 /* Check if offer's conn address is zero */
3313 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3314 pj_strcmp2(&conn->addr, "0")==0)
3315 {
3316 /* Modify address */
3317 answer->conn->addr = pj_str("0.0.0.0");
3318 }
3319
3320 /* Check if call is on-hold */
3321 if (call->local_hold) {
3322 pjmedia_sdp_attr *attr;
3323
3324 /* Remove existing directions attributes */
3325 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3326 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3327 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3328 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3329
3330 /* Keep call on-hold by setting 'sendonly' attribute.
3331 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3332 */
Benny Prijono40d62b62009-08-12 17:53:47 +00003333 attr = pjmedia_sdp_attr_create(call->inv->pool_prov, "sendonly", NULL);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003334 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3335 }
3336
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003337 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3338 if (status != PJ_SUCCESS) {
3339 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3340 PJSUA_UNLOCK();
3341 return;
3342 }
3343
3344 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003345}
3346
3347
3348/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003349 * Called to generate new offer.
3350 */
3351static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3352 pjmedia_sdp_session **offer)
3353{
3354 pjsua_call *call;
3355 pj_status_t status;
3356
3357 PJSUA_LOCK();
3358
3359 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3360
3361 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003362 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003363 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003364 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003365 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003366 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003367 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003368 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3369 call->index));
3370
Benny Prijono40d62b62009-08-12 17:53:47 +00003371 status = pjsua_media_channel_create_sdp(call->index,
3372 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003373 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003374 }
3375
3376 if (status != PJ_SUCCESS) {
3377 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3378 PJSUA_UNLOCK();
3379 return;
3380 }
3381
Benny Prijono77998ce2007-06-20 10:03:46 +00003382 PJSUA_UNLOCK();
3383}
3384
3385
3386/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003387 * Callback called by event framework when the xfer subscription state
3388 * has changed.
3389 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003390static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3391{
3392
3393 PJ_UNUSED_ARG(event);
3394
3395 /*
3396 * When subscription is accepted (got 200/OK to REFER), check if
3397 * subscription suppressed.
3398 */
3399 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3400
3401 pjsip_rx_data *rdata;
3402 pjsip_generic_string_hdr *refer_sub;
3403 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3404 pjsua_call *call;
3405
Benny Prijonoa1e69682007-05-11 15:14:34 +00003406 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003407
3408 /* Must be receipt of response message */
3409 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3410 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3411 rdata = event->body.tsx_state.src.rdata;
3412
3413 /* Find Refer-Sub header */
3414 refer_sub = (pjsip_generic_string_hdr*)
3415 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3416 &REFER_SUB, NULL);
3417
3418 /* Check if subscription is suppressed */
3419 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3420 /* Since no subscription is desired, assume that call has been
3421 * transfered successfully.
3422 */
3423 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3424 const pj_str_t ACCEPTED = { "Accepted", 8 };
3425 pj_bool_t cont = PJ_FALSE;
3426 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3427 200,
3428 &ACCEPTED,
3429 PJ_TRUE,
3430 &cont);
3431 }
3432
3433 /* Yes, subscription is suppressed.
3434 * Terminate our subscription now.
3435 */
3436 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3437 "event subcription..."));
3438 pjsip_evsub_terminate(sub, PJ_TRUE);
3439
3440 } else {
3441 /* Notify application about call transfer progress.
3442 * Initially notify with 100/Accepted status.
3443 */
3444 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3445 const pj_str_t ACCEPTED = { "Accepted", 8 };
3446 pj_bool_t cont = PJ_FALSE;
3447 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3448 100,
3449 &ACCEPTED,
3450 PJ_FALSE,
3451 &cont);
3452 }
3453 }
3454 }
3455 /*
3456 * On incoming NOTIFY, notify application about call transfer progress.
3457 */
3458 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3459 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3460 {
3461 pjsua_call *call;
3462 pjsip_msg *msg;
3463 pjsip_msg_body *body;
3464 pjsip_status_line status_line;
3465 pj_bool_t is_last;
3466 pj_bool_t cont;
3467 pj_status_t status;
3468
Benny Prijonoa1e69682007-05-11 15:14:34 +00003469 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003470
3471 /* When subscription is terminated, clear the xfer_sub member of
3472 * the inv_data.
3473 */
3474 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3475 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3476 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3477
3478 }
3479
3480 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3481 /* Application is not interested with call progress status */
3482 return;
3483 }
3484
3485 /* This better be a NOTIFY request */
3486 if (event->type == PJSIP_EVENT_TSX_STATE &&
3487 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3488 {
3489 pjsip_rx_data *rdata;
3490
3491 rdata = event->body.tsx_state.src.rdata;
3492
3493 /* Check if there's body */
3494 msg = rdata->msg_info.msg;
3495 body = msg->body;
3496 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003497 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003498 "Warning: received NOTIFY without message body"));
3499 return;
3500 }
3501
3502 /* Check for appropriate content */
3503 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3504 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3505 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003506 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003507 "Warning: received NOTIFY with non message/sipfrag "
3508 "content"));
3509 return;
3510 }
3511
3512 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003513 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003514 &status_line);
3515 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003516 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003517 "Warning: received NOTIFY with invalid "
3518 "message/sipfrag content"));
3519 return;
3520 }
3521
3522 } else {
3523 status_line.code = 500;
3524 status_line.reason = *pjsip_get_status_text(500);
3525 }
3526
3527 /* Notify application */
3528 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3529 cont = !is_last;
3530 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3531 status_line.code,
3532 &status_line.reason,
3533 is_last, &cont);
3534
3535 if (!cont) {
3536 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3537 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003538
3539 /* If the call transfer has completed but the subscription is
3540 * not terminated, terminate it now.
3541 */
3542 if (status_line.code/100 == 2 && !is_last) {
3543 pjsip_tx_data *tdata;
3544
3545 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3546 0, &tdata);
3547 if (status == PJ_SUCCESS)
3548 status = pjsip_evsub_send_request(sub, tdata);
3549 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003550 }
3551}
3552
3553
3554/*
3555 * Callback called by event framework when the xfer subscription state
3556 * has changed.
3557 */
3558static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003559{
3560
3561 PJ_UNUSED_ARG(event);
3562
3563 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003564 * When subscription is terminated, clear the xfer_sub member of
3565 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003566 */
3567 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003568 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003569
Benny Prijonoa1e69682007-05-11 15:14:34 +00003570 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003571 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003572 return;
3573
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003574 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003575 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003576
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003577 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003578 }
3579}
3580
3581
3582/*
3583 * Follow transfer (REFER) request.
3584 */
3585static void on_call_transfered( pjsip_inv_session *inv,
3586 pjsip_rx_data *rdata )
3587{
3588 pj_status_t status;
3589 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003590 pjsua_call *existing_call;
3591 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003592 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003593 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003594 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003595 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003596 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003597 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003598 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003599 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003600 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003601 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003602 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003603 pjsip_evsub *sub;
3604
Benny Prijonoa1e69682007-05-11 15:14:34 +00003605 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003606
Benny Prijono26ff9062006-02-21 23:47:00 +00003607 /* Find the Refer-To header */
3608 refer_to = (pjsip_generic_string_hdr*)
3609 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3610
3611 if (refer_to == NULL) {
3612 /* Invalid Request.
3613 * No Refer-To header!
3614 */
3615 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003616 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003617 return;
3618 }
3619
Benny Prijonoc8141a82006-08-20 09:12:19 +00003620 /* Find optional Refer-Sub header */
3621 refer_sub = (pjsip_generic_string_hdr*)
3622 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3623
3624 if (refer_sub) {
3625 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3626 no_refer_sub = PJ_TRUE;
3627 }
3628
Benny Prijono053f5222006-11-11 16:16:04 +00003629 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3630 * request.
3631 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003632 ref_by_hdr = (pjsip_hdr*)
3633 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003634 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003635
Benny Prijono9fc735d2006-05-28 14:58:12 +00003636 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003637 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003638 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3639 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3640 &refer_to->hvalue,
3641 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003642
3643 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003644 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003645 if (code >= 300) {
3646 /* Application rejects call transfer request */
3647 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3648 return;
3649 }
3650
Benny Prijono26ff9062006-02-21 23:47:00 +00003651 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3652 (int)inv->dlg->remote.info_str.slen,
3653 inv->dlg->remote.info_str.ptr,
3654 (int)refer_to->hvalue.slen,
3655 refer_to->hvalue.ptr));
3656
Benny Prijonoc8141a82006-08-20 09:12:19 +00003657 if (no_refer_sub) {
3658 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003659 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003660 */
3661 pjsip_tx_data *tdata;
3662 const pj_str_t str_false = { "false", 5};
3663 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003664
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003665 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3666 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003667 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003668 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003669 status);
3670 return;
3671 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003672
Benny Prijonoc8141a82006-08-20 09:12:19 +00003673 /* Add Refer-Sub header */
3674 hdr = (pjsip_hdr*)
3675 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3676 &str_false);
3677 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003678
Benny Prijono26ff9062006-02-21 23:47:00 +00003679
Benny Prijonoc8141a82006-08-20 09:12:19 +00003680 /* Send answer */
3681 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3682 tdata);
3683 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003684 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003685 status);
3686 return;
3687 }
3688
3689 /* Don't have subscription */
3690 sub = NULL;
3691
3692 } else {
3693 struct pjsip_evsub_user xfer_cb;
3694 pjsip_hdr hdr_list;
3695
3696 /* Init callback */
3697 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003698 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003699
3700 /* Init additional header list to be sent with REFER response */
3701 pj_list_init(&hdr_list);
3702
3703 /* Create transferee event subscription */
3704 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3705 if (status != PJ_SUCCESS) {
3706 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3707 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3708 return;
3709 }
3710
3711 /* If there's Refer-Sub header and the value is "true", send back
3712 * Refer-Sub in the response with value "true" too.
3713 */
3714 if (refer_sub) {
3715 const pj_str_t str_true = { "true", 4 };
3716 pjsip_hdr *hdr;
3717
3718 hdr = (pjsip_hdr*)
3719 pjsip_generic_string_hdr_create(inv->dlg->pool,
3720 &str_refer_sub,
3721 &str_true);
3722 pj_list_push_back(&hdr_list, hdr);
3723
3724 }
3725
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003726 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003727 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3728
3729 /* Create initial NOTIFY request */
3730 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3731 100, NULL, &tdata);
3732 if (status != PJ_SUCCESS) {
3733 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3734 status);
3735 return;
3736 }
3737
3738 /* Send initial NOTIFY request */
3739 status = pjsip_xfer_send_request( sub, tdata);
3740 if (status != PJ_SUCCESS) {
3741 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3742 return;
3743 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003744 }
3745
3746 /* We're cheating here.
3747 * We need to get a null terminated string from a pj_str_t.
3748 * So grab the pointer from the hvalue and NULL terminate it, knowing
3749 * that the NULL position will be occupied by a newline.
3750 */
3751 uri = refer_to->hvalue.ptr;
3752 uri[refer_to->hvalue.slen] = '\0';
3753
Benny Prijono053f5222006-11-11 16:16:04 +00003754 /* Init msg_data */
3755 pjsua_msg_data_init(&msg_data);
3756
3757 /* If Referred-By header is present in the REFER request, copy this
3758 * to the outgoing INVITE request.
3759 */
3760 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003761 pjsip_hdr *dup = (pjsip_hdr*)
3762 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003763 pj_list_push_back(&msg_data.hdr_list, dup);
3764 }
3765
Benny Prijono26ff9062006-02-21 23:47:00 +00003766 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003767 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003768 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003769 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003770 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003771 if (status != PJ_SUCCESS) {
3772
Benny Prijonoc8141a82006-08-20 09:12:19 +00003773 /* Notify xferer about the error (if we have subscription) */
3774 if (sub) {
3775 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3776 500, NULL, &tdata);
3777 if (status != PJ_SUCCESS) {
3778 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3779 status);
3780 return;
3781 }
3782 status = pjsip_xfer_send_request(sub, tdata);
3783 if (status != PJ_SUCCESS) {
3784 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3785 status);
3786 return;
3787 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003788 }
3789 return;
3790 }
3791
Benny Prijonoc8141a82006-08-20 09:12:19 +00003792 if (sub) {
3793 /* Put the server subscription in inv_data.
3794 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3795 * reported back to the server subscription.
3796 */
3797 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003798
Benny Prijonoc8141a82006-08-20 09:12:19 +00003799 /* Put the invite_data in the subscription. */
3800 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3801 &pjsua_var.calls[new_call]);
3802 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003803}
3804
3805
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003806
Benny Prijono26ff9062006-02-21 23:47:00 +00003807/*
3808 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003809 * session. We use this to trap:
3810 * - incoming REFER request.
3811 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003812 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003813static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3814 pjsip_transaction *tsx,
3815 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003816{
Benny Prijono2285e7e2008-12-17 14:28:18 +00003817 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003818
3819 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003820
Benny Prijono2285e7e2008-12-17 14:28:18 +00003821 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3822
3823 if (call == NULL) {
3824 PJSUA_UNLOCK();
3825 return;
3826 }
3827
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003828 if (call->inv == NULL) {
3829 /* Shouldn't happen. It happens only when we don't terminate the
3830 * server subscription caused by REFER after the call has been
3831 * transfered (and this call has been disconnected), and we
3832 * receive another REFER for this call.
3833 */
3834 PJSUA_UNLOCK();
3835 return;
3836 }
3837
Benny Prijonofeb69f42007-10-05 09:12:26 +00003838 /* Notify application callback first */
3839 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3840 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3841 }
3842
Benny Prijono26ff9062006-02-21 23:47:00 +00003843 if (tsx->role==PJSIP_ROLE_UAS &&
3844 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003845 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003846 {
3847 /*
3848 * Incoming REFER request.
3849 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003850 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003851
Benny Prijono26ff9062006-02-21 23:47:00 +00003852 }
Benny Prijonob0808372006-03-02 21:18:58 +00003853 else if (tsx->role==PJSIP_ROLE_UAS &&
3854 tsx->state==PJSIP_TSX_STATE_TRYING &&
3855 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3856 {
3857 /*
3858 * Incoming MESSAGE request!
3859 */
3860 pjsip_rx_data *rdata;
3861 pjsip_msg *msg;
3862 pjsip_accept_hdr *accept_hdr;
3863 pj_status_t status;
3864
3865 rdata = e->body.tsx_state.src.rdata;
3866 msg = rdata->msg_info.msg;
3867
3868 /* Request MUST have message body, with Content-Type equal to
3869 * "text/plain".
3870 */
3871 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3872
3873 pjsip_hdr hdr_list;
3874
3875 pj_list_init(&hdr_list);
3876 pj_list_push_back(&hdr_list, accept_hdr);
3877
3878 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3879 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003880 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003881 return;
3882 }
3883
3884 /* Respond with 200 first, so that remote doesn't retransmit in case
3885 * the UI takes too long to process the message.
3886 */
3887 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3888
3889 /* Process MESSAGE request */
3890 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3891 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003892
Benny Prijonob0808372006-03-02 21:18:58 +00003893 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003894 else if (tsx->role == PJSIP_ROLE_UAC &&
3895 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003896 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003897 /* Handle outgoing pager status */
3898 if (tsx->status_code >= 200) {
3899 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003900
Benny Prijonoa1e69682007-05-11 15:14:34 +00003901 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003902 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003903
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003904 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3905 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3906 &im_data->to,
3907 &im_data->body,
3908 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003909 (pjsip_status_code)
3910 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003911 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003912 }
Benny Prijonofccab712006-02-22 22:23:22 +00003913 }
Benny Prijono834aee32006-02-19 01:38:06 +00003914 }
Benny Prijono834aee32006-02-19 01:38:06 +00003915
Benny Prijono26ff9062006-02-21 23:47:00 +00003916
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003917 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003918}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003919
3920
3921/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00003922static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
3923 const pjsip_uri *target,
3924 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003925{
3926 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00003927 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003928
3929 PJSUA_LOCK();
3930
3931 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00003932 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
3933 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003934 } else {
3935 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
3936 "(callback not implemented by application). Disconnecting "
3937 "call.",
3938 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00003939 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003940 }
3941
3942 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00003943
3944 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003945}
3946