blob: a845940c83b86f86f049316424e46bad66985eed [file] [log] [blame]
Benny Prijono84126ab2006-02-09 09:30:09 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono84126ab2006-02-09 09:30:09 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000020#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000021#include <pjsua-lib/pjsua_internal.h>
22
23
24#define THIS_FILE "pjsua_call.c"
25
26
27/* This callback receives notification from invite session when the
28 * session state has changed.
29 */
30static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
31 pjsip_event *e);
32
33/* This callback is called by invite session framework when UAC session
34 * has forked.
35 */
36static void pjsua_call_on_forked( pjsip_inv_session *inv,
37 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000038
39/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000040 * Callback to be called when SDP offer/answer negotiation has just completed
41 * in the session. This function will start/update media if negotiation
42 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000043 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000044static void pjsua_call_on_media_update(pjsip_inv_session *inv,
45 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000046
47/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000048 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000049 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000050static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
51 const pjmedia_sdp_session *offer);
52
53/*
Benny Prijono77998ce2007-06-20 10:03:46 +000054 * Called to generate new offer.
55 */
56static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
57 pjmedia_sdp_session **offer);
58
59/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000060 * This callback is called when transaction state has changed in INVITE
61 * session. We use this to trap:
62 * - incoming REFER request.
63 * - incoming MESSAGE request.
64 */
65static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
66 pjsip_transaction *tsx,
67 pjsip_event *e);
68
Benny Prijono5e51a4e2008-11-27 00:06:46 +000069/*
70 * Redirection handler.
71 */
Benny Prijono08a48b82008-11-27 12:42:07 +000072static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
73 const pjsip_uri *target,
74 const pjsip_event *e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +000075
Benny Prijonoeebe9af2006-06-13 22:57:13 +000076
Nanang Izzuddin99d69522008-08-04 15:01:38 +000077/* Create SDP for call hold. */
78static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
79 pjmedia_sdp_session **p_answer);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000080
Benny Prijonod524e822006-09-22 12:48:18 +000081/*
82 * Callback called by event framework when the xfer subscription state
83 * has changed.
84 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000085static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
86static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
87
88/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000089 * Reset call descriptor.
90 */
91static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000092{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000093 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +000094
Benny Prijonoeebe9af2006-06-13 22:57:13 +000095 call->index = id;
96 call->inv = NULL;
97 call->user_data = NULL;
98 call->session = NULL;
Benny Prijonoa310bd22008-06-27 21:19:44 +000099 call->audio_idx = -1;
Benny Prijono8147f402007-11-21 14:50:07 +0000100 call->ssrc = pj_rand();
Nanang Izzuddina815ceb2008-08-26 16:51:28 +0000101 call->rtp_tx_seq = 0;
102 call->rtp_tx_ts = 0;
103 call->rtp_tx_seq_ts_set = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000104 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000105 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000106 call->conf_slot = PJSUA_INVALID_ID;
107 call->last_text.ptr = call->last_text_buf_;
108 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +0000109 call->conn_time.sec = 0;
110 call->conn_time.msec = 0;
111 call->res_time.sec = 0;
112 call->res_time.msec = 0;
Benny Prijono91a6a172007-10-31 08:59:29 +0000113 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
Nanang Izzuddin4375f902008-06-26 19:12:09 +0000114 call->rem_srtp_use = PJMEDIA_SRTP_DISABLED;
Nanang Izzuddin99d69522008-08-04 15:01:38 +0000115 call->local_hold = PJ_FALSE;
Benny Prijono105217f2006-03-06 16:25:59 +0000116}
117
118
Benny Prijono275fd682006-03-22 11:59:11 +0000119/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000120 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000121 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000122pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000123{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000124 pjsip_inv_callback inv_cb;
125 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000126 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000127 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000128
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000129 /* Init calls array. */
130 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
131 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000132
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000133 /* Copy config */
134 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000135
Benny Prijono91d06b62008-09-20 12:16:56 +0000136 /* Check the route URI's and force loose route if required */
137 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
138 status = normalize_route_uri(pjsua_var.pool,
139 &pjsua_var.ua_cfg.outbound_proxy[i]);
140 if (status != PJ_SUCCESS)
141 return status;
142 }
143
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000144 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000145 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000146 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
147 inv_cb.on_new_session = &pjsua_call_on_forked;
148 inv_cb.on_media_update = &pjsua_call_on_media_update;
149 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000150 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000151 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000152 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000153
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000154 /* Initialize invite session module: */
155 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
156 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
157
Benny Prijonoc8141a82006-08-20 09:12:19 +0000158 /* Add "norefersub" in Supported header */
159 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
160 NULL, 1, &str_norefersub);
161
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162 return status;
163}
164
165
166/*
167 * Start call subsystem.
168 */
169pj_status_t pjsua_call_subsys_start(void)
170{
171 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000172 return PJ_SUCCESS;
173}
174
175
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000176/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000177 * Get maximum number of calls configured in pjsua.
178 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000179PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000180{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000181 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000182}
183
184
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000185/*
186 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000187 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000188PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000189{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000191}
192
193
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000194/*
195 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000196 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000197PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
198 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000199{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000201
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000202 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000203
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000204 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000205
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
207 if (!pjsua_var.calls[i].inv)
208 continue;
209 ids[c] = i;
210 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000211 }
212
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000214
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000215 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000216
217 return PJ_SUCCESS;
218}
219
220
Benny Prijono5773cd62008-01-19 13:01:42 +0000221/* Allocate one call id */
222static pjsua_call_id alloc_call_id(void)
223{
224 pjsua_call_id cid;
225
226#if 1
227 /* New algorithm: round-robin */
228 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
229 pjsua_var.next_call_id < 0)
230 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000231 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000232 }
233
234 for (cid=pjsua_var.next_call_id;
235 cid<(int)pjsua_var.ua_cfg.max_calls;
236 ++cid)
237 {
238 if (pjsua_var.calls[cid].inv == NULL) {
239 ++pjsua_var.next_call_id;
240 return cid;
241 }
242 }
243
244 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
245 if (pjsua_var.calls[cid].inv == NULL) {
246 ++pjsua_var.next_call_id;
247 return cid;
248 }
249 }
250
251#else
252 /* Old algorithm */
253 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
254 if (pjsua_var.calls[cid].inv == NULL)
255 return cid;
256 }
257#endif
258
259 return PJSUA_INVALID_ID;
260}
261
Benny Prijonod8179652008-01-23 20:39:07 +0000262/* Get signaling secure level.
263 * Return:
264 * 0: if signaling is not secure
265 * 1: if TLS transport is used for immediate hop
266 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000267 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000268static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000269{
270 const pj_str_t tls = pj_str(";transport=tls");
271 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000272 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000273
274 if (pj_stristr(dst_uri, &sips))
275 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000276
277 if (!pj_list_empty(&acc->route_set)) {
278 pjsip_route_hdr *r = acc->route_set.next;
279 pjsip_uri *uri = r->name_addr.uri;
280 pjsip_sip_uri *sip_uri;
281
282 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
283 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
284 return 1;
285
286 } else {
287 if (pj_stristr(dst_uri, &tls))
288 return 1;
289 }
290
Benny Prijonod8179652008-01-23 20:39:07 +0000291 return 0;
292}
293
Benny Prijono224b4e22008-06-19 14:10:28 +0000294/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000295static int call_get_secure_level(pjsua_call *call)
296{
297 if (call->inv->dlg->secure)
298 return 2;
299
300 if (!pj_list_empty(&call->inv->dlg->route_set)) {
301 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
302 pjsip_uri *uri = r->name_addr.uri;
303 pjsip_sip_uri *sip_uri;
304
305 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
306 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
307 return 1;
308
309 } else {
310 pjsip_sip_uri *sip_uri;
311
312 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
313 return 2;
314 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
315 return 0;
316
317 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
318 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
319 return 1;
320 }
321
322 return 0;
323}
Benny Prijono224b4e22008-06-19 14:10:28 +0000324*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000325
326
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000327/*
328 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000329 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000330PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
331 const pj_str_t *dest_uri,
332 unsigned options,
333 void *user_data,
334 const pjsua_msg_data *msg_data,
335 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000336{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000337 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000338 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000339 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000340 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000341 pjsua_acc *acc;
342 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000343 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000344 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000345 pjsip_tx_data *tdata;
346 pj_status_t status;
347
Benny Prijono9fc735d2006-05-28 14:58:12 +0000348
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000349 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000350 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000351 PJ_EINVAL);
352
Benny Prijono320fa4d2006-12-07 10:09:16 +0000353 /* Check arguments */
354 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
355
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000356 PJSUA_LOCK();
357
Benny Prijonof798e502009-03-09 13:08:16 +0000358 /* Create sound port if none is instantiated, to check if sound device
359 * can be used. But only do this with the conference bridge, as with
360 * audio switchboard (i.e. APS-Direct), we can only open the sound
361 * device once the correct format has been known
362 */
363 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
364 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000365 {
366 pj_status_t status;
367
368 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
369 if (status != PJ_SUCCESS) {
370 PJSUA_UNLOCK();
371 return status;
372 }
373 }
374
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000375 acc = &pjsua_var.acc[acc_id];
376 if (!acc->valid) {
377 pjsua_perror(THIS_FILE, "Unable to make call because account "
378 "is not valid", PJ_EINVALIDOP);
379 PJSUA_UNLOCK();
380 return PJ_EINVALIDOP;
381 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000382
Benny Prijonoa91a0032006-02-26 21:23:45 +0000383 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000384 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000385
Benny Prijono5773cd62008-01-19 13:01:42 +0000386 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000387 pjsua_perror(THIS_FILE, "Error making file", PJ_ETOOMANY);
388 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000389 return PJ_ETOOMANY;
390 }
391
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000392 call = &pjsua_var.calls[call_id];
393
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000394 /* Create temporary pool */
395 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
396
Benny Prijono320fa4d2006-12-07 10:09:16 +0000397 /* Verify that destination URI is valid before calling
398 * pjsua_acc_create_uac_contact, or otherwise there
399 * a misleading "Invalid Contact URI" error will be printed
400 * when pjsua_acc_create_uac_contact() fails.
401 */
402 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000403 pjsip_uri *uri;
404 pj_str_t dup;
405
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000406 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
407 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000408
409 if (uri == NULL) {
410 pjsua_perror(THIS_FILE, "Unable to make call",
411 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000412 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000413 PJSUA_UNLOCK();
414 return PJSIP_EINVALIDREQURI;
415 }
416 }
417
Benny Prijono093d3022006-09-24 00:07:11 +0000418 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
419 (int)dest_uri->slen, dest_uri->ptr));
420
Benny Prijonoe21e7842006-04-09 16:46:05 +0000421 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000422 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000423
Benny Prijonoe21e7842006-04-09 16:46:05 +0000424 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000425 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000426
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000427 /* Create suitable Contact header unless a Contact header has been
428 * set in the account.
429 */
430 if (acc->contact.slen) {
431 contact = acc->contact;
432 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000433 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000434 acc_id, dest_uri);
435 if (status != PJ_SUCCESS) {
436 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
437 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000438 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000439 PJSUA_UNLOCK();
440 return status;
441 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000442 }
443
Benny Prijonoe21e7842006-04-09 16:46:05 +0000444 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000445 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000446 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000447 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000448 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000449 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000450 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000451 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000452 return status;
453 }
454
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000455 /* Increment the dialog's lock otherwise when invite session creation
456 * fails the dialog will be destroyed prematurely.
457 */
458 pjsip_dlg_inc_lock(dlg);
459
Benny Prijonodb844a42008-02-02 17:07:18 +0000460 /* Calculate call's secure level */
461 call->secure_level = get_secure_level(acc_id, dest_uri);
462
Benny Prijonoc97608e2007-03-23 16:34:20 +0000463 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000464 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000465 call->secure_level, dlg->pool,
466 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000467 if (status != PJ_SUCCESS) {
468 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
469 goto on_error;
470 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000471
Benny Prijono224b4e22008-06-19 14:10:28 +0000472 /* Create offer */
473 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000474 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000475 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000476 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000477 goto on_error;
478 }
479
480 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000481 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000482 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000483 if (acc->cfg.require_100rel)
484 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000485 if (acc->cfg.require_timer)
486 options |= PJSIP_INV_REQUIRE_TIMER;
Benny Prijono84126ab2006-02-09 09:30:09 +0000487
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000488 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000489 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000490 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000491 goto on_error;
492 }
493
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000494 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000495 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
496 if (status != PJ_SUCCESS) {
497 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
498 goto on_error;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000499 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000500
501 /* Create and associate our data in the session. */
Benny Prijonob8864a92007-07-20 08:37:29 +0000502 call->acc_id = acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000503 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000504
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000505 dlg->mod_data[pjsua_var.mod.id] = call;
506 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000507
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000508 /* Attach user data */
509 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000510
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000511 /* If account is locked to specific transport, then lock dialog
512 * to this transport too.
513 */
514 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
515 pjsip_tpselector tp_sel;
516
517 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
518 pjsip_dlg_set_transport(dlg, &tp_sel);
519 }
520
Benny Prijono84126ab2006-02-09 09:30:09 +0000521 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000522 if (!pj_list_empty(&acc->route_set))
523 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000524
525
526 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000527 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000528 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000529 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000530 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000531
Benny Prijono48ab2b72007-11-08 09:24:30 +0000532 /* Set authentication preference */
533 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000534
535 /* Create initial INVITE: */
536
537 status = pjsip_inv_invite(inv, &tdata);
538 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000539 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
540 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000541 goto on_error;
542 }
543
544
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000545 /* Add additional headers etc */
546
547 pjsua_process_msg_data( tdata, msg_data);
548
Benny Prijono093d3022006-09-24 00:07:11 +0000549 /* Must increment call counter now */
550 ++pjsua_var.call_cnt;
551
Benny Prijono84126ab2006-02-09 09:30:09 +0000552 /* Send initial INVITE: */
553
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000554 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000555 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000556 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
557 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000558
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000559 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000560 * session would have been cleared.
561 */
562 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000563 goto on_error;
564 }
565
Benny Prijono84126ab2006-02-09 09:30:09 +0000566 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000567
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000568 if (p_call_id)
569 *p_call_id = call_id;
570
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000571 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000572 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000573 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000574
575 return PJ_SUCCESS;
576
577
578on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000579 if (dlg) {
580 /* This may destroy the dialog */
581 pjsip_dlg_dec_lock(dlg);
582 }
583
Benny Prijono1c2bf462006-03-05 11:54:02 +0000584 if (inv != NULL) {
585 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000586 }
587
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000588 if (call_id != -1) {
589 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000590 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000591 }
592
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000593 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000594 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000595 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000596}
597
598
Benny Prijono91a6a172007-10-31 08:59:29 +0000599/* Get the NAT type information in remote's SDP */
600static void update_remote_nat_type(pjsua_call *call,
601 const pjmedia_sdp_session *sdp)
602{
603 const pjmedia_sdp_attr *xnat;
604
605 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
606 if (xnat) {
607 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
608 } else {
609 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
610 }
611
612 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
613 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
614}
615
616
Benny Prijonodc39fe82006-05-26 12:17:46 +0000617/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000618 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000619 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000620 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000621pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000622{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000623 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000624 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000625 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000626 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
627 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000628 pjsip_tx_data *response = NULL;
629 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000630 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000631 int acc_id;
632 pjsua_call *call;
633 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000634 int sip_err_code;
Benny Prijonod8179652008-01-23 20:39:07 +0000635 pjmedia_sdp_session *offer, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000636 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000637
Benny Prijono26ff9062006-02-21 23:47:00 +0000638 /* Don't want to handle anything but INVITE */
639 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
640 return PJ_FALSE;
641
642 /* Don't want to handle anything that's already associated with
643 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000644 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000645 if (dlg || tsx)
646 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000647
Benny Prijono384dab42009-10-14 01:58:04 +0000648 /* Don't want to accept the call if shutdown is in progress */
649 if (pjsua_var.thread_quit_flag) {
650 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
651 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
652 NULL, NULL);
653 return PJ_TRUE;
654 }
655
Benny Prijono148c9dd2006-09-19 13:37:53 +0000656 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000657
Benny Prijono26ff9062006-02-21 23:47:00 +0000658 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000659 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000660
Benny Prijono5773cd62008-01-19 13:01:42 +0000661 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000662 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000663 PJSIP_SC_BUSY_HERE, NULL,
664 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000665 PJ_LOG(2,(THIS_FILE,
666 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000667 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000668 return PJ_TRUE;
669 }
670
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000671 /* Clear call descriptor */
672 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000673
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000674 call = &pjsua_var.calls[call_id];
675
676 /* Mark call start time. */
677 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000678
Benny Prijono053f5222006-11-11 16:16:04 +0000679 /* Check INVITE request for Replaces header. If Replaces header is
680 * present, the function will make sure that we can handle the request.
681 */
682 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
683 &response);
684 if (status != PJ_SUCCESS) {
685 /*
686 * Something wrong with the Replaces header.
687 */
688 if (response) {
689 pjsip_response_addr res_addr;
690
691 pjsip_get_response_addr(response->pool, rdata, &res_addr);
692 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
693 NULL, NULL);
694
695 } else {
696
697 /* Respond with 500 (Internal Server Error) */
698 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
699 NULL, NULL);
700 }
701
702 PJSUA_UNLOCK();
703 return PJ_TRUE;
704 }
705
706 /* If this INVITE request contains Replaces header, notify application
707 * about the request so that application can do subsequent checking
708 * if it wants to.
709 */
710 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
711 pjsua_call *replaced_call;
712 int st_code = 200;
713 pj_str_t st_text = { "OK", 2 };
714
715 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000716 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000717
718 /* Notify application */
719 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
720 rdata, &st_code, &st_text);
721
722 /* Must specify final response */
723 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
724
725 /* Check if application rejects this request. */
726 if (st_code >= 300) {
727
728 if (st_text.slen == 2)
729 st_text = *pjsip_get_status_text(st_code);
730
731 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
732 st_code, &st_text, NULL, NULL, NULL);
733 PJSUA_UNLOCK();
734 return PJ_TRUE;
735 }
736 }
737
Benny Prijonod8179652008-01-23 20:39:07 +0000738 /*
739 * Get which account is most likely to be associated with this incoming
740 * call. We need the account to find which contact URI to put for
741 * the call.
742 */
743 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
744
Benny Prijonodb844a42008-02-02 17:07:18 +0000745 /* Get call's secure level */
746 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
747 call->secure_level = 2;
748 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
749 call->secure_level = 1;
750 else
751 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000752
Benny Prijonod8179652008-01-23 20:39:07 +0000753 /* Parse SDP from incoming request */
754 if (rdata->msg_info.msg->body) {
755 status = pjmedia_sdp_parse(rdata->tp_info.pool,
Benny Prijono24a21852008-04-14 04:04:30 +0000756 (char*)rdata->msg_info.msg->body->data,
Benny Prijonod8179652008-01-23 20:39:07 +0000757 rdata->msg_info.msg->body->len, &offer);
Benny Prijono2c484e42008-06-26 19:47:23 +0000758 if (status == PJ_SUCCESS) {
759 /* Validate */
760 status = pjmedia_sdp_validate(offer);
761 }
762
Benny Prijonod8179652008-01-23 20:39:07 +0000763 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000764 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000765 pjsip_hdr hdr_list;
766 pjsip_warning_hdr *w;
767
768 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000769 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000770
771 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
772 pjsip_endpt_name(pjsua_var.endpt),
773 status);
774 pj_list_init(&hdr_list);
775 pj_list_push_back(&hdr_list, w);
776
Benny Prijono224b4e22008-06-19 14:10:28 +0000777 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000778 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000779 PJSUA_UNLOCK();
780 return PJ_TRUE;
781 }
Benny Prijono617b8602008-04-07 10:10:31 +0000782
783 /* Do quick checks on SDP before passing it to transports. More elabore
784 * checks will be done in pjsip_inv_verify_request2() below.
785 */
786 if (offer->media_count==0) {
787 const pj_str_t reason = pj_str("Missing media in SDP");
788 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
789 NULL, NULL, NULL);
790 PJSUA_UNLOCK();
791 return PJ_TRUE;
792 }
793
Benny Prijonod8179652008-01-23 20:39:07 +0000794 } else {
795 offer = NULL;
796 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000797
Benny Prijono224b4e22008-06-19 14:10:28 +0000798 /* Init media channel */
799 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
800 call->secure_level,
801 rdata->tp_info.pool, offer,
802 &sip_err_code);
803 if (status != PJ_SUCCESS) {
804 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
805 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
806 sip_err_code, NULL, NULL, NULL, NULL);
807 PJSUA_UNLOCK();
808 return PJ_TRUE;
809 }
810
811 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000812 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000813 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000814 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000815 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000816 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
817 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000818 PJSUA_UNLOCK();
819 return PJ_TRUE;
820 }
821
Benny Prijono224b4e22008-06-19 14:10:28 +0000822
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000823 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000824 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000825 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000826 if (pjsua_var.acc[acc_id].cfg.require_100rel)
827 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000828 if (pjsua_var.acc[acc_id].cfg.require_timer)
829 options |= PJSIP_INV_REQUIRE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000830
Benny Prijonod8179652008-01-23 20:39:07 +0000831 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
832 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000833 if (status != PJ_SUCCESS) {
834
835 /*
836 * No we can't handle the incoming INVITE request.
837 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000838 if (response) {
839 pjsip_response_addr res_addr;
840
841 pjsip_get_response_addr(response->pool, rdata, &res_addr);
842 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
843 NULL, NULL);
844
845 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000846 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000847 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
848 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000849 }
850
Benny Prijonoc97608e2007-03-23 16:34:20 +0000851 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000852 PJSUA_UNLOCK();
853 return PJ_TRUE;
854 }
855
856
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000857 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000858 if (pjsua_var.acc[acc_id].contact.slen) {
859 contact = pjsua_var.acc[acc_id].contact;
860 } else {
861 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
862 acc_id, rdata);
863 if (status != PJ_SUCCESS) {
864 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
865 status);
866 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
867 NULL, NULL);
868 pjsua_media_channel_deinit(call->index);
869 PJSUA_UNLOCK();
870 return PJ_TRUE;
871 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000872 }
873
Benny Prijono26ff9062006-02-21 23:47:00 +0000874 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000875 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000876 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000877 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000878 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000879 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000880 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000881 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000882 return PJ_TRUE;
883 }
884
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000885 /* Set credentials */
886 if (pjsua_var.acc[acc_id].cred_cnt) {
887 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
888 pjsua_var.acc[acc_id].cred_cnt,
889 pjsua_var.acc[acc_id].cred);
890 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000891
Benny Prijono48ab2b72007-11-08 09:24:30 +0000892 /* Set preference */
893 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
894 &pjsua_var.acc[acc_id].cfg.auth_pref);
895
Benny Prijono26ff9062006-02-21 23:47:00 +0000896 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000897 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000898 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000899 pjsip_hdr hdr_list;
900 pjsip_warning_hdr *w;
901
902 w = pjsip_warning_hdr_create_from_status(dlg->pool,
903 pjsip_endpt_name(pjsua_var.endpt),
904 status);
905 pj_list_init(&hdr_list);
906 pj_list_push_back(&hdr_list, w);
907
908 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
909
910 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000911 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000912 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000913 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000914 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000915 return PJ_TRUE;
916 }
917
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000918 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000919 status = pjsip_timer_init_session(inv,
920 &pjsua_var.acc[acc_id].cfg.timer_setting);
921 if (status != PJ_SUCCESS) {
922 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
923 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
924 NULL, &response);
925 if (status == PJ_SUCCESS && response)
926 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000927
Nanang Izzuddin65add622009-08-11 16:26:20 +0000928 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000929
Nanang Izzuddin65add622009-08-11 16:26:20 +0000930 PJSUA_UNLOCK();
931 return PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000932 }
933
Benny Prijonoea9fd392007-11-06 03:41:40 +0000934 /* Update NAT type of remote endpoint, only when there is SDP in
935 * incoming INVITE!
936 */
937 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
938 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
939 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000940 const pjmedia_sdp_session *remote_sdp;
941
942 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
943 update_remote_nat_type(call, remote_sdp);
944 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000945
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000946 /* If account is locked to specific transport, then lock dialog
947 * to this transport too.
948 */
949 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
950 pjsip_tpselector tp_sel;
951
952 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
953 pjsip_dlg_set_transport(dlg, &tp_sel);
954 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000955
Benny Prijono2285e7e2008-12-17 14:28:18 +0000956 /* Must answer with some response to initial INVITE. We'll do this before
957 * attaching the call to the invite session/dialog, so that the application
958 * will not get notification about this event (on another scenario, it is
959 * also possible that inv_send_msg() fails and causes the invite session to
960 * be disconnected. If we have the call attached at this time, this will
961 * cause the disconnection callback to be called before on_incoming_call()
962 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +0000963 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000964 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000965 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000966 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000967 if (response == NULL) {
968 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
969 status);
970 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
971 pjsip_inv_terminate(inv, 500, PJ_FALSE);
972 } else {
973 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +0000974 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000975 PJ_FALSE);
976 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000977 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000978 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000979 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000980
981 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000982 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000983 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +0000984 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +0000985 PJSUA_UNLOCK();
986 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +0000987 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000988 }
989
Benny Prijono2285e7e2008-12-17 14:28:18 +0000990 /* Create and attach pjsua_var data to the dialog: */
991 call->inv = inv;
992
993 dlg->mod_data[pjsua_var.mod.id] = call;
994 inv->mod_data[pjsua_var.mod.id] = call;
995
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000996 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +0000997
Benny Prijono105217f2006-03-06 16:25:59 +0000998
Benny Prijono053f5222006-11-11 16:16:04 +0000999 /* Check if this request should replace existing call */
1000 if (replaced_dlg) {
1001 pjsip_inv_session *replaced_inv;
1002 struct pjsua_call *replaced_call;
1003 pjsip_tx_data *tdata;
1004
1005 /* Get the invite session in the dialog */
1006 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1007
1008 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001009 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001010
1011 /* Notify application */
1012 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1013 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1014 call_id);
1015
1016 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1017 call_id));
1018
1019 /* Answer the new call with 200 response */
1020 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1021 if (status == PJ_SUCCESS)
1022 status = pjsip_inv_send_msg(inv, tdata);
1023
1024 if (status != PJ_SUCCESS)
1025 pjsua_perror(THIS_FILE, "Error answering session", status);
1026
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001027 /* Note that inv may be invalid if 200/OK has caused error in
1028 * starting the media.
1029 */
Benny Prijono053f5222006-11-11 16:16:04 +00001030
1031 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1032 replaced_call->index));
1033
1034 /* Disconnect replaced invite session */
1035 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1036 &tdata);
1037 if (status == PJ_SUCCESS && tdata)
1038 status = pjsip_inv_send_msg(replaced_inv, tdata);
1039
1040 if (status != PJ_SUCCESS)
1041 pjsua_perror(THIS_FILE, "Error terminating session", status);
1042
1043
1044 } else {
1045
Benny Prijonob5388cf2007-01-04 22:45:08 +00001046 /* Notify application if on_incoming_call() is overriden,
1047 * otherwise hangup the call with 480
1048 */
1049 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001050 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001051 } else {
1052 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1053 NULL, NULL);
1054 }
Benny Prijono053f5222006-11-11 16:16:04 +00001055 }
1056
Benny Prijono8b1889b2006-06-06 18:40:40 +00001057
Benny Prijono26ff9062006-02-21 23:47:00 +00001058 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001059 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001060 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001061}
1062
1063
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001064
1065/*
1066 * Check if the specified call has active INVITE session and the INVITE
1067 * session has not been disconnected.
1068 */
1069PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1070{
1071 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1072 PJ_EINVAL);
1073 return pjsua_var.calls[call_id].inv != NULL &&
1074 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1075}
1076
1077
1078/*
1079 * Check if call has an active media session.
1080 */
1081PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1082{
1083 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1084 PJ_EINVAL);
1085 return pjsua_var.calls[call_id].session != NULL;
1086}
1087
1088
Benny Prijonocf986c42008-09-02 11:25:07 +00001089/*
1090 * Retrieve the media session associated with this call.
1091 */
1092PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1093{
1094 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1095 NULL);
1096 return pjsua_var.calls[call_id].session;
1097}
1098
1099
1100/*
1101 * Retrieve the media transport instance that is used for this call.
1102 */
1103PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1104{
1105 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1106 NULL);
1107 return pjsua_var.calls[cid].med_tp;
1108}
1109
1110
Benny Prijono148c9dd2006-09-19 13:37:53 +00001111/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001112pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001113 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001114 pjsua_call **p_call,
1115 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001116{
1117 enum { MAX_RETRY=50 };
1118 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001119 pjsua_call *call = NULL;
1120 pj_bool_t has_pjsua_lock = PJ_FALSE;
1121 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001122
1123 for (retry=0; retry<MAX_RETRY; ++retry) {
1124
1125 has_pjsua_lock = PJ_FALSE;
1126
1127 status = PJSUA_TRY_LOCK();
1128 if (status != PJ_SUCCESS) {
1129 pj_thread_sleep(retry/10);
1130 continue;
1131 }
1132
1133 has_pjsua_lock = PJ_TRUE;
1134 call = &pjsua_var.calls[call_id];
1135
1136 if (call->inv == NULL) {
1137 PJSUA_UNLOCK();
1138 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1139 return PJSIP_ESESSIONTERMINATED;
1140 }
1141
1142 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1143 if (status != PJ_SUCCESS) {
1144 PJSUA_UNLOCK();
1145 pj_thread_sleep(retry/10);
1146 continue;
1147 }
1148
1149 PJSUA_UNLOCK();
1150
1151 break;
1152 }
1153
1154 if (status != PJ_SUCCESS) {
1155 if (has_pjsua_lock == PJ_FALSE)
1156 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1157 "(possibly system has deadlocked) in %s",
1158 title));
1159 else
1160 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1161 "(possibly system has deadlocked) in %s",
1162 title));
1163 return PJ_ETIMEDOUT;
1164 }
1165
1166 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001167 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001168
1169 return PJ_SUCCESS;
1170}
1171
1172
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001173/*
1174 * Get the conference port identification associated with the call.
1175 */
1176PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1177{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001178 pjsua_call *call;
1179 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001180 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001181 pj_status_t status;
1182
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001183 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1184 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001185
Benny Prijonodc752ca2006-09-22 16:55:42 +00001186 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001187 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001188 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001189
1190 port_id = call->conf_slot;
1191
Benny Prijonodc752ca2006-09-22 16:55:42 +00001192 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001193
1194 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001195}
1196
1197
Benny Prijono148c9dd2006-09-19 13:37:53 +00001198
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001199/*
1200 * Obtain detail information about the specified call.
1201 */
1202PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1203 pjsua_call_info *info)
1204{
1205 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001206 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001207 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001208
1209 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1210 PJ_EINVAL);
1211
Benny Prijonoac623b32006-07-03 15:19:31 +00001212 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001213
Benny Prijonodc752ca2006-09-22 16:55:42 +00001214 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001215 if (status != PJ_SUCCESS) {
1216 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001217 }
1218
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001219 /* id and role */
1220 info->id = call_id;
1221 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001222 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001223
1224 /* local info */
1225 info->local_info.ptr = info->buf_.local_info;
1226 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1227 sizeof(info->buf_.local_info));
1228
1229 /* local contact */
1230 info->local_contact.ptr = info->buf_.local_contact;
1231 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1232 call->inv->dlg->local.contact->uri,
1233 info->local_contact.ptr,
1234 sizeof(info->buf_.local_contact));
1235
1236 /* remote info */
1237 info->remote_info.ptr = info->buf_.remote_info;
1238 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1239 sizeof(info->buf_.remote_info));
1240
1241 /* remote contact */
1242 if (call->inv->dlg->remote.contact) {
1243 int len;
1244 info->remote_contact.ptr = info->buf_.remote_contact;
1245 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1246 call->inv->dlg->remote.contact->uri,
1247 info->remote_contact.ptr,
1248 sizeof(info->buf_.remote_contact));
1249 if (len < 0) len = 0;
1250 info->remote_contact.slen = len;
1251 } else {
1252 info->remote_contact.slen = 0;
1253 }
1254
1255 /* call id */
1256 info->call_id.ptr = info->buf_.call_id;
1257 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1258 sizeof(info->buf_.call_id));
1259
1260 /* state, state_text */
1261 info->state = call->inv->state;
1262 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1263
1264 /* If call is disconnected, set the last_status from the cause code */
1265 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1266 /* last_status, last_status_text */
1267 info->last_status = call->inv->cause;
1268
1269 info->last_status_text.ptr = info->buf_.last_status_text;
1270 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1271 sizeof(info->buf_.last_status_text));
1272 } else {
1273 /* last_status, last_status_text */
1274 info->last_status = call->last_code;
1275
1276 info->last_status_text.ptr = info->buf_.last_status_text;
1277 pj_strncpy(&info->last_status_text, &call->last_text,
1278 sizeof(info->buf_.last_status_text));
1279 }
1280
1281 /* media status and dir */
1282 info->media_status = call->media_st;
1283 info->media_dir = call->media_dir;
1284
1285
1286 /* conference slot number */
1287 info->conf_slot = call->conf_slot;
1288
1289 /* calculate duration */
1290 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1291
1292 info->total_duration = call->dis_time;
1293 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1294
1295 if (call->conn_time.sec) {
1296 info->connect_duration = call->dis_time;
1297 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1298 }
1299
1300 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1301
1302 pj_gettimeofday(&info->total_duration);
1303 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1304
1305 pj_gettimeofday(&info->connect_duration);
1306 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1307
1308 } else {
1309 pj_gettimeofday(&info->total_duration);
1310 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1311 }
1312
Benny Prijonodc752ca2006-09-22 16:55:42 +00001313 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001314
1315 return PJ_SUCCESS;
1316}
1317
1318
1319/*
1320 * Attach application specific data to the call.
1321 */
1322PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1323 void *user_data)
1324{
1325 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1326 PJ_EINVAL);
1327 pjsua_var.calls[call_id].user_data = user_data;
1328
1329 return PJ_SUCCESS;
1330}
1331
1332
1333/*
1334 * Get user data attached to the call.
1335 */
1336PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1337{
1338 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1339 NULL);
1340 return pjsua_var.calls[call_id].user_data;
1341}
1342
1343
1344/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001345 * Get remote's NAT type.
1346 */
1347PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1348 pj_stun_nat_type *p_type)
1349{
1350 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1351 PJ_EINVAL);
1352 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1353
1354 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1355 return PJ_SUCCESS;
1356}
1357
1358
1359/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001360 * Send response to incoming INVITE request.
1361 */
1362PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1363 unsigned code,
1364 const pj_str_t *reason,
1365 const pjsua_msg_data *msg_data)
1366{
1367 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001368 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001369 pjsip_tx_data *tdata;
1370 pj_status_t status;
1371
1372 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1373 PJ_EINVAL);
1374
Benny Prijonodc752ca2006-09-22 16:55:42 +00001375 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001376 if (status != PJ_SUCCESS)
1377 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001378
Benny Prijono2e507c22006-06-23 15:04:11 +00001379 if (call->res_time.sec == 0)
1380 pj_gettimeofday(&call->res_time);
1381
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001382 if (reason && reason->slen == 0)
1383 reason = NULL;
1384
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001385 /* Create response message */
1386 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1387 if (status != PJ_SUCCESS) {
1388 pjsua_perror(THIS_FILE, "Error creating response",
1389 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001390 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001391 return status;
1392 }
1393
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001394 /* Call might have been disconnected if application is answering with
1395 * 200/OK and the media failed to start.
1396 */
1397 if (call->inv == NULL) {
1398 pjsip_dlg_dec_lock(dlg);
1399 return PJSIP_ESESSIONTERMINATED;
1400 }
1401
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001402 /* Add additional headers etc */
1403 pjsua_process_msg_data( tdata, msg_data);
1404
1405 /* Send the message */
1406 status = pjsip_inv_send_msg(call->inv, tdata);
1407 if (status != PJ_SUCCESS)
1408 pjsua_perror(THIS_FILE, "Error sending response",
1409 status);
1410
Benny Prijonodc752ca2006-09-22 16:55:42 +00001411 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001412
1413 return status;
1414}
1415
1416
1417/*
1418 * Hangup call by using method that is appropriate according to the
1419 * call state.
1420 */
1421PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1422 unsigned code,
1423 const pj_str_t *reason,
1424 const pjsua_msg_data *msg_data)
1425{
1426 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001427 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001428 pj_status_t status;
1429 pjsip_tx_data *tdata;
1430
1431
Benny Prijono148c9dd2006-09-19 13:37:53 +00001432 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1433 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1434 call_id));
1435 }
1436
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001437 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1438 PJ_EINVAL);
1439
Benny Prijonodc752ca2006-09-22 16:55:42 +00001440 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001441 if (status != PJ_SUCCESS)
1442 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001443
1444 if (code==0) {
1445 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1446 code = PJSIP_SC_OK;
1447 else if (call->inv->role == PJSIP_ROLE_UAS)
1448 code = PJSIP_SC_DECLINE;
1449 else
1450 code = PJSIP_SC_REQUEST_TERMINATED;
1451 }
1452
1453 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1454 if (status != PJ_SUCCESS) {
1455 pjsua_perror(THIS_FILE,
1456 "Failed to create end session message",
1457 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001458 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001459 return status;
1460 }
1461
1462 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1463 * as p_tdata when INVITE transaction has not been answered
1464 * with any provisional responses.
1465 */
1466 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001467 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001468 return PJ_SUCCESS;
1469 }
1470
1471 /* Add additional headers etc */
1472 pjsua_process_msg_data( tdata, msg_data);
1473
1474 /* Send the message */
1475 status = pjsip_inv_send_msg(call->inv, tdata);
1476 if (status != PJ_SUCCESS) {
1477 pjsua_perror(THIS_FILE,
1478 "Failed to send end session message",
1479 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001480 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001481 return status;
1482 }
1483
Benny Prijonodc752ca2006-09-22 16:55:42 +00001484 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001485
1486 return PJ_SUCCESS;
1487}
1488
1489
1490/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001491 * Accept or reject redirection.
1492 */
1493PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1494 pjsip_redirect_op cmd)
1495{
1496 pjsua_call *call;
1497 pjsip_dialog *dlg;
1498 pj_status_t status;
1499
1500 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1501 PJ_EINVAL);
1502
1503 status = acquire_call("pjsua_call_process_redirect()", call_id,
1504 &call, &dlg);
1505 if (status != PJ_SUCCESS)
1506 return status;
1507
1508 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1509
1510 pjsip_dlg_dec_lock(dlg);
1511
1512 return status;
1513}
1514
1515
1516/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001517 * Put the specified call on hold.
1518 */
1519PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1520 const pjsua_msg_data *msg_data)
1521{
1522 pjmedia_sdp_session *sdp;
1523 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001524 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001525 pjsip_tx_data *tdata;
1526 pj_status_t status;
1527
1528 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1529 PJ_EINVAL);
1530
Benny Prijonodc752ca2006-09-22 16:55:42 +00001531 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001532 if (status != PJ_SUCCESS)
1533 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001534
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001535
1536 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1537 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001538 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001539 return PJSIP_ESESSIONSTATE;
1540 }
1541
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001542 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001543 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001544 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001545 return status;
1546 }
1547
1548 /* Create re-INVITE with new offer */
1549 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1550 if (status != PJ_SUCCESS) {
1551 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001552 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001553 return status;
1554 }
1555
1556 /* Add additional headers etc */
1557 pjsua_process_msg_data( tdata, msg_data);
1558
1559 /* Send the request */
1560 status = pjsip_inv_send_msg( call->inv, tdata);
1561 if (status != PJ_SUCCESS) {
1562 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001563 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001564 return status;
1565 }
1566
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001567 /* Set flag that local put the call on hold */
1568 call->local_hold = PJ_TRUE;
1569
Benny Prijonodc752ca2006-09-22 16:55:42 +00001570 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001571
1572 return PJ_SUCCESS;
1573}
1574
1575
1576/*
1577 * Send re-INVITE (to release hold).
1578 */
1579PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1580 pj_bool_t unhold,
1581 const pjsua_msg_data *msg_data)
1582{
1583 pjmedia_sdp_session *sdp;
1584 pjsip_tx_data *tdata;
1585 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001586 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001587 pj_status_t status;
1588
1589
1590 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1591 PJ_EINVAL);
1592
Benny Prijonodc752ca2006-09-22 16:55:42 +00001593 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001594 if (status != PJ_SUCCESS)
1595 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001596
1597 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1598 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001599 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001600 return PJSIP_ESESSIONSTATE;
1601 }
1602
1603 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001604 if (call->local_hold && !unhold) {
1605 status = create_sdp_of_call_hold(call, &sdp);
1606 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001607 status = pjsua_media_channel_create_sdp(call->index,
1608 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001609 NULL, &sdp, NULL);
1610 call->local_hold = PJ_FALSE;
1611 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001612 if (status != PJ_SUCCESS) {
1613 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1614 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001615 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001616 return status;
1617 }
1618
1619 /* Create re-INVITE with new offer */
1620 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1621 if (status != PJ_SUCCESS) {
1622 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001623 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001624 return status;
1625 }
1626
1627 /* Add additional headers etc */
1628 pjsua_process_msg_data( tdata, msg_data);
1629
1630 /* Send the request */
1631 status = pjsip_inv_send_msg( call->inv, tdata);
1632 if (status != PJ_SUCCESS) {
1633 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001634 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001635 return status;
1636 }
1637
Benny Prijonodc752ca2006-09-22 16:55:42 +00001638 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001639
1640 return PJ_SUCCESS;
1641}
1642
1643
1644/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001645 * Send UPDATE request.
1646 */
1647PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1648 unsigned options,
1649 const pjsua_msg_data *msg_data)
1650{
1651 pjmedia_sdp_session *sdp;
1652 pjsip_tx_data *tdata;
1653 pjsua_call *call;
1654 pjsip_dialog *dlg;
1655 pj_status_t status;
1656
1657 PJ_UNUSED_ARG(options);
1658
1659 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1660 PJ_EINVAL);
1661
1662 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1663 if (status != PJ_SUCCESS)
1664 return status;
1665
Benny Prijonoc08682e2007-10-04 06:17:58 +00001666 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001667 status = pjsua_media_channel_create_sdp(call->index,
1668 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001669 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001670 if (status != PJ_SUCCESS) {
1671 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1672 status);
1673 pjsip_dlg_dec_lock(dlg);
1674 return status;
1675 }
1676
Benny Prijono224b4e22008-06-19 14:10:28 +00001677 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001678 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1679 if (status != PJ_SUCCESS) {
1680 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1681 pjsip_dlg_dec_lock(dlg);
1682 return status;
1683 }
1684
1685 /* Add additional headers etc */
1686 pjsua_process_msg_data( tdata, msg_data);
1687
1688 /* Send the request */
1689 status = pjsip_inv_send_msg( call->inv, tdata);
1690 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001691 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001692 pjsip_dlg_dec_lock(dlg);
1693 return status;
1694 }
1695
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001696 call->local_hold = PJ_FALSE;
1697
Benny Prijonoc08682e2007-10-04 06:17:58 +00001698 pjsip_dlg_dec_lock(dlg);
1699
1700 return PJ_SUCCESS;
1701}
1702
1703
1704/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001705 * Initiate call transfer to the specified address.
1706 */
1707PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1708 const pj_str_t *dest,
1709 const pjsua_msg_data *msg_data)
1710{
1711 pjsip_evsub *sub;
1712 pjsip_tx_data *tdata;
1713 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001714 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001715 pjsip_generic_string_hdr *gs_hdr;
1716 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001717 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001718 pj_status_t status;
1719
1720
1721 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1722 PJ_EINVAL);
1723
Benny Prijonodc752ca2006-09-22 16:55:42 +00001724 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001725 if (status != PJ_SUCCESS)
1726 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001727
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001728
Benny Prijonod524e822006-09-22 12:48:18 +00001729 /* Create xfer client subscription. */
1730 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001731 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001732
1733 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001734 if (status != PJ_SUCCESS) {
1735 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001736 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001737 return status;
1738 }
1739
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001740 /* Associate this call with the client subscription */
1741 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1742
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001743 /*
1744 * Create REFER request.
1745 */
1746 status = pjsip_xfer_initiate(sub, dest, &tdata);
1747 if (status != PJ_SUCCESS) {
1748 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001749 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001750 return status;
1751 }
1752
Benny Prijono053f5222006-11-11 16:16:04 +00001753 /* Add Referred-By header */
1754 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1755 &dlg->local.info_str);
1756 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1757
1758
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001759 /* Add additional headers etc */
1760 pjsua_process_msg_data( tdata, msg_data);
1761
1762 /* Send. */
1763 status = pjsip_xfer_send_request(sub, tdata);
1764 if (status != PJ_SUCCESS) {
1765 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001766 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001767 return status;
1768 }
1769
1770 /* For simplicity (that's what this program is intended to be!),
1771 * leave the original invite session as it is. More advanced application
1772 * may want to hold the INVITE, or terminate the invite, or whatever.
1773 */
1774
Benny Prijonodc752ca2006-09-22 16:55:42 +00001775 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001776
1777 return PJ_SUCCESS;
1778
1779}
1780
1781
1782/*
Benny Prijono053f5222006-11-11 16:16:04 +00001783 * Initiate attended call transfer to the specified address.
1784 */
1785PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1786 pjsua_call_id dest_call_id,
1787 unsigned options,
1788 const pjsua_msg_data *msg_data)
1789{
1790 pjsua_call *dest_call;
1791 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001792 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001793 pj_str_t str_dest;
1794 int len;
1795 pjsip_uri *uri;
1796 pj_status_t status;
1797
1798
1799 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1800 PJ_EINVAL);
1801 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1802 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1803 PJ_EINVAL);
1804
1805 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1806 &dest_call, &dest_dlg);
1807 if (status != PJ_SUCCESS)
1808 return status;
1809
1810 /*
1811 * Create REFER destination URI with Replaces field.
1812 */
1813
1814 /* Make sure we have sufficient buffer's length */
1815 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1816 dest_dlg->call_id->id.slen +
1817 dest_dlg->remote.info->tag.slen +
1818 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001819 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001820
1821 /* Print URI */
1822 str_dest_buf[0] = '<';
1823 str_dest.slen = 1;
1824
Benny Prijonoa1e69682007-05-11 15:14:34 +00001825 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001826 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1827 str_dest_buf+1, sizeof(str_dest_buf)-1);
1828 if (len < 0)
1829 return PJSIP_EURITOOLONG;
1830
1831 str_dest.slen += len;
1832
1833
1834 /* Build the URI */
1835 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1836 sizeof(str_dest_buf) - str_dest.slen,
1837 "?%s"
1838 "Replaces=%.*s"
1839 "%%3Bto-tag%%3D%.*s"
1840 "%%3Bfrom-tag%%3D%.*s>",
1841 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1842 "" : "Require=replaces&"),
1843 (int)dest_dlg->call_id->id.slen,
1844 dest_dlg->call_id->id.ptr,
1845 (int)dest_dlg->remote.info->tag.slen,
1846 dest_dlg->remote.info->tag.ptr,
1847 (int)dest_dlg->local.info->tag.slen,
1848 dest_dlg->local.info->tag.ptr);
1849
1850 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1851 PJSIP_EURITOOLONG);
1852
1853 str_dest.ptr = str_dest_buf;
1854 str_dest.slen += len;
1855
1856 pjsip_dlg_dec_lock(dest_dlg);
1857
1858 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1859}
1860
1861
1862/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001863 * Send DTMF digits to remote using RFC 2833 payload formats.
1864 */
1865PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1866 const pj_str_t *digits)
1867{
1868 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001869 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001870 pj_status_t status;
1871
1872 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1873 PJ_EINVAL);
1874
Benny Prijonodc752ca2006-09-22 16:55:42 +00001875 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001876 if (status != PJ_SUCCESS)
1877 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001878
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001879 if (!call->session) {
1880 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001881 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001882 return PJ_EINVALIDOP;
1883 }
1884
1885 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1886
Benny Prijonodc752ca2006-09-22 16:55:42 +00001887 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001888
1889 return status;
1890}
1891
1892
1893/**
1894 * Send instant messaging inside INVITE session.
1895 */
1896PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1897 const pj_str_t *mime_type,
1898 const pj_str_t *content,
1899 const pjsua_msg_data *msg_data,
1900 void *user_data)
1901{
1902 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001903 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001904 const pj_str_t mime_text_plain = pj_str("text/plain");
1905 pjsip_media_type ctype;
1906 pjsua_im_data *im_data;
1907 pjsip_tx_data *tdata;
1908 pj_status_t status;
1909
1910
1911 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1912 PJ_EINVAL);
1913
Benny Prijonodc752ca2006-09-22 16:55:42 +00001914 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001915 if (status != PJ_SUCCESS)
1916 return status;
1917
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001918 /* Set default media type if none is specified */
1919 if (mime_type == NULL) {
1920 mime_type = &mime_text_plain;
1921 }
1922
1923 /* Create request message. */
1924 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1925 -1, &tdata);
1926 if (status != PJ_SUCCESS) {
1927 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1928 goto on_return;
1929 }
1930
1931 /* Add accept header. */
1932 pjsip_msg_add_hdr( tdata->msg,
1933 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1934
1935 /* Parse MIME type */
1936 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1937
1938 /* Create "text/plain" message body. */
1939 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1940 &ctype.subtype, content);
1941 if (tdata->msg->body == NULL) {
1942 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1943 pjsip_tx_data_dec_ref(tdata);
1944 goto on_return;
1945 }
1946
1947 /* Add additional headers etc */
1948 pjsua_process_msg_data( tdata, msg_data);
1949
1950 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001951 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001952 im_data->acc_id = call->acc_id;
1953 im_data->call_id = call_id;
1954 im_data->to = call->inv->dlg->remote.info_str;
1955 pj_strdup_with_null(tdata->pool, &im_data->body, content);
1956 im_data->user_data = user_data;
1957
1958
1959 /* Send the request. */
1960 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
1961 pjsua_var.mod.id, im_data);
1962 if (status != PJ_SUCCESS) {
1963 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
1964 goto on_return;
1965 }
1966
1967on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00001968 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001969 return status;
1970}
1971
1972
1973/*
1974 * Send IM typing indication inside INVITE session.
1975 */
1976PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
1977 pj_bool_t is_typing,
1978 const pjsua_msg_data*msg_data)
1979{
1980 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001981 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001982 pjsip_tx_data *tdata;
1983 pj_status_t status;
1984
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001985 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1986 PJ_EINVAL);
1987
Benny Prijonodc752ca2006-09-22 16:55:42 +00001988 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001989 if (status != PJ_SUCCESS)
1990 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001991
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001992 /* Create request message. */
1993 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1994 -1, &tdata);
1995 if (status != PJ_SUCCESS) {
1996 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1997 goto on_return;
1998 }
1999
2000 /* Create "application/im-iscomposing+xml" msg body. */
2001 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2002 NULL, NULL, -1);
2003
2004 /* Add additional headers etc */
2005 pjsua_process_msg_data( tdata, msg_data);
2006
2007 /* Send the request. */
2008 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2009 if (status != PJ_SUCCESS) {
2010 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2011 goto on_return;
2012 }
2013
2014on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002015 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002016 return status;
2017}
2018
2019
2020/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002021 * Send arbitrary request.
2022 */
2023PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2024 const pj_str_t *method_str,
2025 const pjsua_msg_data *msg_data)
2026{
2027 pjsua_call *call;
2028 pjsip_dialog *dlg;
2029 pjsip_method method;
2030 pjsip_tx_data *tdata;
2031 pj_status_t status;
2032
2033 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2034 PJ_EINVAL);
2035
2036 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2037 if (status != PJ_SUCCESS)
2038 return status;
2039
2040 /* Init method */
2041 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2042
2043 /* Create request message. */
2044 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2045 if (status != PJ_SUCCESS) {
2046 pjsua_perror(THIS_FILE, "Unable to create request", status);
2047 goto on_return;
2048 }
2049
2050 /* Add additional headers etc */
2051 pjsua_process_msg_data( tdata, msg_data);
2052
2053 /* Send the request. */
2054 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2055 if (status != PJ_SUCCESS) {
2056 pjsua_perror(THIS_FILE, "Unable to send request", status);
2057 goto on_return;
2058 }
2059
2060on_return:
2061 pjsip_dlg_dec_lock(dlg);
2062 return status;
2063}
2064
2065
2066/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002067 * Terminate all calls.
2068 */
2069PJ_DEF(void) pjsua_call_hangup_all(void)
2070{
2071 unsigned i;
2072
2073 PJSUA_LOCK();
2074
2075 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2076 if (pjsua_var.calls[i].inv)
2077 pjsua_call_hangup(i, 0, NULL, NULL);
2078 }
2079
2080 PJSUA_UNLOCK();
2081}
2082
2083
Benny Prijono627cbb42007-09-25 20:48:49 +00002084const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002085{
2086 if (val < 1000) {
2087 pj_ansi_sprintf(buf, "%d", val);
2088 } else if (val < 1000000) {
2089 pj_ansi_sprintf(buf, "%d.%dK",
2090 val / 1000,
2091 (val % 1000) / 100);
2092 } else {
2093 pj_ansi_sprintf(buf, "%d.%02dM",
2094 val / 1000000,
2095 (val % 1000000) / 10000);
2096 }
2097
2098 return buf;
2099}
2100
2101
2102/* Dump media session */
2103static void dump_media_session(const char *indent,
2104 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002105 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002106{
2107 unsigned i;
2108 char *p = buf, *end = buf+maxlen;
2109 int len;
2110 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002111 pjmedia_session *session = call->session;
2112 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002113
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002114 pjmedia_transport_info_init(&tp_info);
2115
2116 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002117 pjmedia_session_get_info(session, &info);
2118
2119 for (i=0; i<info.stream_cnt; ++i) {
2120 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002121 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002122 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002123 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002124 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002125 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002126 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002127
2128 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002129 // rem_addr will contain actual address of RTP originator, instead of
2130 // remote RTP address specified by stream which is fetched from the SDP.
2131 // Please note that we are assuming only one stream per call.
2132 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2133 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002134 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2135 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002136 sizeof(rem_addr_buf), 3);
2137 } else {
2138 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002139 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002140 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002141
2142 if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
2143 dir = "sendonly";
2144 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2145 dir = "recvonly";
2146 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2147 dir = "sendrecv";
2148 else
2149 dir = "inactive";
2150
2151
2152 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002153 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002154 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002155 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002156 info.stream_info[i].fmt.encoding_name.ptr,
2157 info.stream_info[i].fmt.clock_rate / 1000,
2158 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002159 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002160 if (len < 1 || len > end-p) {
2161 *p = '\0';
2162 return;
2163 }
2164
2165 p += len;
2166 *p++ = '\n';
2167 *p = '\0';
2168
2169 if (stat.rx.update_cnt == 0)
2170 strcpy(last_update, "never");
2171 else {
2172 pj_gettimeofday(&now);
2173 PJ_TIME_VAL_SUB(now, stat.rx.update);
2174 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2175 now.sec / 3600,
2176 (now.sec % 3600) / 60,
2177 now.sec % 60,
2178 now.msec);
2179 }
2180
Benny Prijono80019eb2006-08-07 13:22:23 +00002181 pj_gettimeofday(&media_duration);
2182 PJ_TIME_VAL_SUB(media_duration, stat.start);
2183 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2184 media_duration.msec = 1;
2185
Benny Prijono1402a4a2008-01-08 23:41:22 +00002186 /* protect against division by zero */
2187 if (stat.rx.pkt == 0)
2188 stat.rx.pkt = 1;
2189 if (stat.tx.pkt == 0)
2190 stat.tx.pkt = 1;
2191
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002192 len = pj_ansi_snprintf(p, end-p,
2193 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002194 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002195 "%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 +00002196 "%s (msec) min avg max last dev\n"
2197 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2198 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002199 indent, info.stream_info[i].fmt.pt,
2200 last_update,
2201 indent,
2202 good_number(packets, stat.rx.pkt),
2203 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002204 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002205 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2206 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 +00002207 indent,
2208 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002209 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002210 stat.rx.discard,
2211 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002212 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002213 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002214 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002215 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002216 indent, indent,
2217 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002218 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002219 stat.rx.loss_period.max / 1000.0,
2220 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002221 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002222 indent,
2223 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002224 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002225 stat.rx.jitter.max / 1000.0,
2226 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002227 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002228 ""
2229 );
2230
2231 if (len < 1 || len > end-p) {
2232 *p = '\0';
2233 return;
2234 }
2235
2236 p += len;
2237 *p++ = '\n';
2238 *p = '\0';
2239
2240 if (stat.tx.update_cnt == 0)
2241 strcpy(last_update, "never");
2242 else {
2243 pj_gettimeofday(&now);
2244 PJ_TIME_VAL_SUB(now, stat.tx.update);
2245 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2246 now.sec / 3600,
2247 (now.sec % 3600) / 60,
2248 now.sec % 60,
2249 now.msec);
2250 }
2251
2252 len = pj_ansi_snprintf(p, end-p,
2253 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002254 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002255 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002256 "%s (msec) min avg max last dev \n"
2257 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2258 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002259 indent,
2260 info.stream_info[i].tx_pt,
2261 info.stream_info[i].param->info.frm_ptime *
2262 info.stream_info[i].param->setting.frm_per_pkt,
2263 last_update,
2264
2265 indent,
2266 good_number(packets, stat.tx.pkt),
2267 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002268 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002269 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2270 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 +00002271
2272 indent,
2273 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002274 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002275 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002276 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002277 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002278 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002279
2280 indent, indent,
2281 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002282 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002283 stat.tx.loss_period.max / 1000.0,
2284 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002285 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002286 indent,
2287 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002288 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002289 stat.tx.jitter.max / 1000.0,
2290 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002291 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002292 ""
2293 );
2294
2295 if (len < 1 || len > end-p) {
2296 *p = '\0';
2297 return;
2298 }
2299
2300 p += len;
2301 *p++ = '\n';
2302 *p = '\0';
2303
2304 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002305 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002306 indent,
2307 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002308 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002309 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002310 stat.rtt.last / 1000.0,
2311 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002312 );
2313 if (len < 1 || len > end-p) {
2314 *p = '\0';
2315 return;
2316 }
2317
2318 p += len;
2319 *p++ = '\n';
2320 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002321
2322#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2323# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2324 do { \
2325 if (samples <= 4294) \
2326 usec = samples * 1000000 / clock_rate; \
2327 else { \
2328 usec = samples * 1000 / clock_rate; \
2329 usec *= 1000; \
2330 } \
2331 } while(0)
2332
2333# define PRINT_VOIP_MTC_VAL(s, v) \
2334 if (v == 127) \
2335 sprintf(s, "(na)"); \
2336 else \
2337 sprintf(s, "%d", v)
2338
2339# define VALIDATE_PRINT_BUF() \
2340 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2341 p += len; *p++ = '\n'; *p = '\0'
2342
2343
2344 do {
2345 char loss[16], dup[16];
2346 char jitter[80];
2347 char toh[80];
2348 char plc[16], jba[16], jbr[16];
2349 char signal_lvl[16], noise_lvl[16], rerl[16];
2350 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2351 pjmedia_rtcp_xr_stat xr_stat;
2352 unsigned clock_rate;
2353
2354 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2355 PJ_SUCCESS)
2356 {
2357 break;
2358 }
2359
2360 clock_rate = info.stream_info[i].fmt.clock_rate;
2361
2362 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2363 VALIDATE_PRINT_BUF();
2364
2365 /* Statistics Summary */
2366 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2367 VALIDATE_PRINT_BUF();
2368
2369 if (xr_stat.rx.stat_sum.l)
2370 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2371 else
2372 sprintf(loss, "(na)");
2373
2374 if (xr_stat.rx.stat_sum.d)
2375 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2376 else
2377 sprintf(dup, "(na)");
2378
2379 if (xr_stat.rx.stat_sum.j) {
2380 unsigned jmin, jmax, jmean, jdev;
2381
2382 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2383 clock_rate);
2384 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2385 clock_rate);
2386 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2387 clock_rate);
2388 SAMPLES_TO_USEC(jdev,
2389 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2390 clock_rate);
2391 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2392 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2393 } else
2394 sprintf(jitter, "(report not available)");
2395
2396 if (xr_stat.rx.stat_sum.t) {
2397 sprintf(toh, "%11d %11d %11d %11d",
2398 xr_stat.rx.stat_sum.toh.min,
2399 xr_stat.rx.stat_sum.toh.mean,
2400 xr_stat.rx.stat_sum.toh.max,
2401 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2402 } else
2403 sprintf(toh, "(report not available)");
2404
2405 if (xr_stat.rx.stat_sum.update.sec == 0)
2406 strcpy(last_update, "never");
2407 else {
2408 pj_gettimeofday(&now);
2409 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2410 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2411 now.sec / 3600,
2412 (now.sec % 3600) / 60,
2413 now.sec % 60,
2414 now.msec);
2415 }
2416
2417 len = pj_ansi_snprintf(p, end-p,
2418 "%s RX last update: %s\n"
2419 "%s begin seq=%d, end seq=%d\n"
2420 "%s pkt loss=%s, dup=%s\n"
2421 "%s (msec) min avg max dev\n"
2422 "%s jitter : %s\n"
2423 "%s toh : %s",
2424 indent, last_update,
2425 indent,
2426 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2427 indent, loss, dup,
2428 indent,
2429 indent, jitter,
2430 indent, toh
2431 );
2432 VALIDATE_PRINT_BUF();
2433
2434 if (xr_stat.tx.stat_sum.l)
2435 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2436 else
2437 sprintf(loss, "(na)");
2438
2439 if (xr_stat.tx.stat_sum.d)
2440 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2441 else
2442 sprintf(dup, "(na)");
2443
2444 if (xr_stat.tx.stat_sum.j) {
2445 unsigned jmin, jmax, jmean, jdev;
2446
2447 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2448 clock_rate);
2449 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2450 clock_rate);
2451 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2452 clock_rate);
2453 SAMPLES_TO_USEC(jdev,
2454 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2455 clock_rate);
2456 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2457 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2458 } else
2459 sprintf(jitter, "(report not available)");
2460
2461 if (xr_stat.tx.stat_sum.t) {
2462 sprintf(toh, "%11d %11d %11d %11d",
2463 xr_stat.tx.stat_sum.toh.min,
2464 xr_stat.tx.stat_sum.toh.mean,
2465 xr_stat.tx.stat_sum.toh.max,
2466 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2467 } else
2468 sprintf(toh, "(report not available)");
2469
2470 if (xr_stat.tx.stat_sum.update.sec == 0)
2471 strcpy(last_update, "never");
2472 else {
2473 pj_gettimeofday(&now);
2474 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2475 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2476 now.sec / 3600,
2477 (now.sec % 3600) / 60,
2478 now.sec % 60,
2479 now.msec);
2480 }
2481
2482 len = pj_ansi_snprintf(p, end-p,
2483 "%s TX last update: %s\n"
2484 "%s begin seq=%d, end seq=%d\n"
2485 "%s pkt loss=%s, dup=%s\n"
2486 "%s (msec) min avg max dev\n"
2487 "%s jitter : %s\n"
2488 "%s toh : %s",
2489 indent, last_update,
2490 indent,
2491 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2492 indent, loss, dup,
2493 indent,
2494 indent, jitter,
2495 indent, toh
2496 );
2497 VALIDATE_PRINT_BUF();
2498
2499
2500 /* VoIP Metrics */
2501 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2502 VALIDATE_PRINT_BUF();
2503
2504 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2505 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2506 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2507 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2508 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2509 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2510 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2511
2512 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2513 case PJMEDIA_RTCP_XR_PLC_DIS:
2514 sprintf(plc, "DISABLED");
2515 break;
2516 case PJMEDIA_RTCP_XR_PLC_ENH:
2517 sprintf(plc, "ENHANCED");
2518 break;
2519 case PJMEDIA_RTCP_XR_PLC_STD:
2520 sprintf(plc, "STANDARD");
2521 break;
2522 case PJMEDIA_RTCP_XR_PLC_UNK:
2523 default:
2524 sprintf(plc, "UNKNOWN");
2525 break;
2526 }
2527
2528 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2529 case PJMEDIA_RTCP_XR_JB_FIXED:
2530 sprintf(jba, "FIXED");
2531 break;
2532 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2533 sprintf(jba, "ADAPTIVE");
2534 break;
2535 default:
2536 sprintf(jba, "UNKNOWN");
2537 break;
2538 }
2539
2540 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2541
2542 if (xr_stat.rx.voip_mtc.update.sec == 0)
2543 strcpy(last_update, "never");
2544 else {
2545 pj_gettimeofday(&now);
2546 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2547 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2548 now.sec / 3600,
2549 (now.sec % 3600) / 60,
2550 now.sec % 60,
2551 now.msec);
2552 }
2553
2554 len = pj_ansi_snprintf(p, end-p,
2555 "%s RX last update: %s\n"
2556 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2557 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2558 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2559 "%s delay : round trip=%d%s, end system=%d%s\n"
2560 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2561 "%s quality : R factor=%s, ext R factor=%s\n"
2562 "%s MOS LQ=%s, MOS CQ=%s\n"
2563 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2564 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2565 indent,
2566 last_update,
2567 /* packets */
2568 indent,
2569 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2570 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2571 /* burst */
2572 indent,
2573 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2574 xr_stat.rx.voip_mtc.burst_dur, "ms",
2575 /* gap */
2576 indent,
2577 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2578 xr_stat.rx.voip_mtc.gap_dur, "ms",
2579 /* delay */
2580 indent,
2581 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2582 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2583 /* level */
2584 indent,
2585 signal_lvl, "dB",
2586 noise_lvl, "dB",
2587 rerl, "",
2588 /* quality */
2589 indent,
2590 r_factor, ext_r_factor,
2591 indent,
2592 mos_lq, mos_cq,
2593 /* config */
2594 indent,
2595 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2596 /* JB delay */
2597 indent,
2598 xr_stat.rx.voip_mtc.jb_nom, "ms",
2599 xr_stat.rx.voip_mtc.jb_max, "ms",
2600 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2601 );
2602 VALIDATE_PRINT_BUF();
2603
2604 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2605 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2606 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2607 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2608 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2609 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2610 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2611
2612 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2613 case PJMEDIA_RTCP_XR_PLC_DIS:
2614 sprintf(plc, "DISABLED");
2615 break;
2616 case PJMEDIA_RTCP_XR_PLC_ENH:
2617 sprintf(plc, "ENHANCED");
2618 break;
2619 case PJMEDIA_RTCP_XR_PLC_STD:
2620 sprintf(plc, "STANDARD");
2621 break;
2622 case PJMEDIA_RTCP_XR_PLC_UNK:
2623 default:
2624 sprintf(plc, "unknown");
2625 break;
2626 }
2627
2628 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2629 case PJMEDIA_RTCP_XR_JB_FIXED:
2630 sprintf(jba, "FIXED");
2631 break;
2632 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2633 sprintf(jba, "ADAPTIVE");
2634 break;
2635 default:
2636 sprintf(jba, "unknown");
2637 break;
2638 }
2639
2640 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2641
2642 if (xr_stat.tx.voip_mtc.update.sec == 0)
2643 strcpy(last_update, "never");
2644 else {
2645 pj_gettimeofday(&now);
2646 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2647 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2648 now.sec / 3600,
2649 (now.sec % 3600) / 60,
2650 now.sec % 60,
2651 now.msec);
2652 }
2653
2654 len = pj_ansi_snprintf(p, end-p,
2655 "%s TX last update: %s\n"
2656 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2657 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2658 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2659 "%s delay : round trip=%d%s, end system=%d%s\n"
2660 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2661 "%s quality : R factor=%s, ext R factor=%s\n"
2662 "%s MOS LQ=%s, MOS CQ=%s\n"
2663 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2664 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2665 indent,
2666 last_update,
2667 /* pakcets */
2668 indent,
2669 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2670 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2671 /* burst */
2672 indent,
2673 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2674 xr_stat.tx.voip_mtc.burst_dur, "ms",
2675 /* gap */
2676 indent,
2677 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2678 xr_stat.tx.voip_mtc.gap_dur, "ms",
2679 /* delay */
2680 indent,
2681 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2682 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2683 /* level */
2684 indent,
2685 signal_lvl, "dB",
2686 noise_lvl, "dB",
2687 rerl, "",
2688 /* quality */
2689 indent,
2690 r_factor, ext_r_factor,
2691 indent,
2692 mos_lq, mos_cq,
2693 /* config */
2694 indent,
2695 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2696 /* JB delay */
2697 indent,
2698 xr_stat.tx.voip_mtc.jb_nom, "ms",
2699 xr_stat.tx.voip_mtc.jb_max, "ms",
2700 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2701 );
2702 VALIDATE_PRINT_BUF();
2703
2704
2705 /* RTT delay (by receiver side) */
2706 len = pj_ansi_snprintf(p, end-p,
2707 "%s RTT (from recv) min avg max last dev",
2708 indent);
2709 VALIDATE_PRINT_BUF();
2710 len = pj_ansi_snprintf(p, end-p,
2711 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2712 indent,
2713 xr_stat.rtt.min / 1000.0,
2714 xr_stat.rtt.mean / 1000.0,
2715 xr_stat.rtt.max / 1000.0,
2716 xr_stat.rtt.last / 1000.0,
2717 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2718 );
2719 VALIDATE_PRINT_BUF();
2720 } while(0);
2721#endif
2722
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002723 }
2724}
2725
2726
2727/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002728void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002729 int call_id,
2730 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002731{
2732 int len;
2733 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2734 pjsip_dialog *dlg = inv->dlg;
2735 char userinfo[128];
2736
2737 /* Dump invite sesion info. */
2738
2739 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
Benny Prijono329d6382009-05-29 13:04:03 +00002740 if (len < 0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002741 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2742 else
2743 userinfo[len] = '\0';
2744
2745 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2746 title,
2747 pjsip_inv_state_name(inv->state),
2748 userinfo);
2749 if (len < 1 || len >= (int)size) {
2750 pj_ansi_strcpy(buf, "<--uri too long-->");
2751 len = 18;
2752 } else
2753 buf[len] = '\0';
2754}
2755
2756
2757/*
2758 * Dump call and media statistics to string.
2759 */
2760PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2761 pj_bool_t with_media,
2762 char *buffer,
2763 unsigned maxlen,
2764 const char *indent)
2765{
2766 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002767 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002768 pj_time_val duration, res_delay, con_delay;
2769 char tmp[128];
2770 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002771 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002772 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002773 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002774
2775 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2776 PJ_EINVAL);
2777
Benny Prijonodc752ca2006-09-22 16:55:42 +00002778 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002779 if (status != PJ_SUCCESS)
2780 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002781
2782 *buffer = '\0';
2783 p = buffer;
2784 end = buffer + maxlen;
2785 len = 0;
2786
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002787 print_call(indent, call_id, tmp, sizeof(tmp));
2788
2789 len = pj_ansi_strlen(tmp);
2790 pj_ansi_strcpy(buffer, tmp);
2791
2792 p += len;
2793 *p++ = '\r';
2794 *p++ = '\n';
2795
2796 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002797 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002798 pj_gettimeofday(&duration);
2799 PJ_TIME_VAL_SUB(duration, call->conn_time);
2800 con_delay = call->conn_time;
2801 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2802 } else {
2803 duration.sec = duration.msec = 0;
2804 con_delay.sec = con_delay.msec = 0;
2805 }
2806
2807 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002808 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002809 res_delay = call->res_time;
2810 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2811 } else {
2812 res_delay.sec = res_delay.msec = 0;
2813 }
2814
2815 /* Print duration */
2816 len = pj_ansi_snprintf(p, end-p,
2817 "%s Call time: %02dh:%02dm:%02ds, "
2818 "1st res in %d ms, conn in %dms",
2819 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002820 (int)(duration.sec / 3600),
2821 (int)((duration.sec % 3600)/60),
2822 (int)(duration.sec % 60),
2823 (int)PJ_TIME_VAL_MSEC(res_delay),
2824 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002825
2826 if (len > 0 && len < end-p) {
2827 p += len;
2828 *p++ = '\n';
2829 *p = '\0';
2830 }
2831
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002832 /* Get SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002833 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002834 pjmedia_transport_get_info(call->med_tp, &tp_info);
2835 if (tp_info.specific_info_cnt > 0) {
2836 int i;
2837 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2838 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2839 {
2840 pjmedia_srtp_info *srtp_info =
2841 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2842
2843 len = pj_ansi_snprintf(p, end-p,
2844 "%s SRTP status: %s Crypto-suite: %s",
2845 indent,
2846 (srtp_info->active?"Active":"Not active"),
2847 srtp_info->tx_policy.name.ptr);
2848 if (len > 0 && len < end-p) {
2849 p += len;
2850 *p++ = '\n';
2851 *p = '\0';
2852 }
2853 break;
2854 }
2855 }
2856 }
2857
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002858 /* Dump session statistics */
2859 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002860 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002861
Benny Prijonodc752ca2006-09-22 16:55:42 +00002862 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002863
2864 return PJ_SUCCESS;
2865}
2866
2867
2868/*
Benny Prijono84126ab2006-02-09 09:30:09 +00002869 * This callback receives notification from invite session when the
2870 * session state has changed.
2871 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00002872static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
2873 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00002874{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002875 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00002876
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002877 PJSUA_LOCK();
2878
Benny Prijonoa1e69682007-05-11 15:14:34 +00002879 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002880
2881 if (!call) {
2882 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00002883 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002884 }
2885
Benny Prijonoe21e7842006-04-09 16:46:05 +00002886
2887 /* Get call times */
2888 switch (inv->state) {
2889 case PJSIP_INV_STATE_EARLY:
2890 case PJSIP_INV_STATE_CONNECTING:
2891 if (call->res_time.sec == 0)
2892 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00002893 call->last_code = (pjsip_status_code)
2894 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002895 pj_strncpy(&call->last_text,
2896 &e->body.tsx_state.tsx->status_text,
2897 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00002898 break;
2899 case PJSIP_INV_STATE_CONFIRMED:
2900 pj_gettimeofday(&call->conn_time);
2901 break;
2902 case PJSIP_INV_STATE_DISCONNECTED:
2903 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00002904 if (call->res_time.sec == 0)
2905 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002906 if (e->type == PJSIP_EVENT_TSX_STATE &&
2907 e->body.tsx_state.tsx->status_code > call->last_code)
2908 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00002909 call->last_code = (pjsip_status_code)
2910 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002911 pj_strncpy(&call->last_text,
2912 &e->body.tsx_state.tsx->status_text,
2913 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00002914 } else {
2915 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
2916 pj_strncpy(&call->last_text,
2917 pjsip_get_status_text(call->last_code),
2918 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00002919 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00002920 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002921 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00002922 call->last_code = (pjsip_status_code)
2923 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002924 pj_strncpy(&call->last_text,
2925 &e->body.tsx_state.tsx->status_text,
2926 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00002927 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00002928 }
2929
Benny Prijono26ff9062006-02-21 23:47:00 +00002930 /* If this is an outgoing INVITE that was created because of
2931 * REFER/transfer, send NOTIFY to transferer.
2932 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00002933 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002934 int st_code = -1;
2935 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
2936
2937
Benny Prijonoa91a0032006-02-26 21:23:45 +00002938 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00002939 case PJSIP_INV_STATE_NULL:
2940 case PJSIP_INV_STATE_CALLING:
2941 /* Do nothing */
2942 break;
2943
2944 case PJSIP_INV_STATE_EARLY:
2945 case PJSIP_INV_STATE_CONNECTING:
2946 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002947 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
2948 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2949 else
2950 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00002951 break;
2952
Benny Prijono140beae2009-10-11 05:06:43 +00002953 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002954#if 0
2955/* We don't need this, as we've terminated the subscription in
2956 * CONNECTING state.
2957 */
Benny Prijono26ff9062006-02-21 23:47:00 +00002958 /* When state is confirmed, send the final 200/OK and terminate
2959 * subscription.
2960 */
2961 st_code = e->body.tsx_state.tsx->status_code;
2962 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00002963#endif
Benny Prijono140beae2009-10-11 05:06:43 +00002964 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002965
2966 case PJSIP_INV_STATE_DISCONNECTED:
2967 st_code = e->body.tsx_state.tsx->status_code;
2968 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
2969 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00002970
Benny Prijono8b1889b2006-06-06 18:40:40 +00002971 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00002972 /* Nothing to do. Just to keep gcc from complaining about
2973 * unused enums.
2974 */
2975 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00002976 }
2977
2978 if (st_code != -1) {
2979 pjsip_tx_data *tdata;
2980 pj_status_t status;
2981
Benny Prijonoa91a0032006-02-26 21:23:45 +00002982 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00002983 ev_state, st_code,
2984 NULL, &tdata);
2985 if (status != PJ_SUCCESS) {
2986 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
2987 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00002988 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00002989 if (status != PJ_SUCCESS) {
2990 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
2991 }
2992 }
2993 }
2994 }
2995
Benny Prijono84126ab2006-02-09 09:30:09 +00002996
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002997 if (pjsua_var.ua_cfg.cb.on_call_state)
2998 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00002999
3000 /* call->inv may be NULL now */
3001
Benny Prijono84126ab2006-02-09 09:30:09 +00003002 /* Destroy media session when invite session is disconnected. */
3003 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003004
Benny Prijonoa91a0032006-02-26 21:23:45 +00003005 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003006
Benny Prijono275fd682006-03-22 11:59:11 +00003007 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003008 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003009
Benny Prijono105217f2006-03-06 16:25:59 +00003010 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003011 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003012 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003013
3014 /* Reset call */
3015 reset_call(call->index);
3016
Benny Prijono84126ab2006-02-09 09:30:09 +00003017 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003018
3019 PJSUA_UNLOCK();
3020}
3021
3022/*
3023 * This callback is called by invite session framework when UAC session
3024 * has forked.
3025 */
3026static void pjsua_call_on_forked( pjsip_inv_session *inv,
3027 pjsip_event *e)
3028{
3029 PJ_UNUSED_ARG(inv);
3030 PJ_UNUSED_ARG(e);
3031
3032 PJ_TODO(HANDLE_FORKED_DIALOG);
3033}
3034
3035
3036/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003037 * Callback from UA layer when forked dialog response is received.
3038 */
3039pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3040{
3041 if (dlg->uac_has_2xx &&
3042 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3043 pjsip_rdata_get_tsx(res) == NULL &&
3044 res->msg_info.msg->line.status.code/100 == 2)
3045 {
3046 pjsip_dialog *forked_dlg;
3047 pjsip_tx_data *bye;
3048 pj_status_t status;
3049
3050 /* Create forked dialog */
3051 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3052 if (status != PJ_SUCCESS)
3053 return NULL;
3054
3055 pjsip_dlg_inc_lock(forked_dlg);
3056
3057 /* Disconnect the call */
3058 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3059 -1, &bye);
3060 if (status == PJ_SUCCESS) {
3061 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3062 }
3063
3064 pjsip_dlg_dec_lock(forked_dlg);
3065
3066 if (status != PJ_SUCCESS) {
3067 return NULL;
3068 }
3069
3070 return forked_dlg;
3071
3072 } else {
3073 return dlg;
3074 }
3075}
3076
3077/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003078 * Disconnect call upon error.
3079 */
3080static void call_disconnect( pjsip_inv_session *inv,
3081 int code )
3082{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003083 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003084 pjsip_tx_data *tdata;
3085 pj_status_t status;
3086
Benny Prijono59b3aed2008-01-15 16:54:54 +00003087 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3088
Benny Prijonoa38ada02006-07-02 14:22:35 +00003089 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003090 if (status != PJ_SUCCESS)
3091 return;
3092
3093 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003094 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3095 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3096 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003097 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003098 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003099
Benny Prijono734fc2d2008-03-17 16:05:35 +00003100 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003101 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003102 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003103 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003104 if (status == PJ_SUCCESS) {
3105 pjsip_create_sdp_body(tdata->pool, local_sdp,
3106 &tdata->msg->body);
3107 }
3108 }
3109
3110 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003111}
3112
3113/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003114 * Callback to be called when SDP offer/answer negotiation has just completed
3115 * in the session. This function will start/update media if negotiation
3116 * has succeeded.
3117 */
3118static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3119 pj_status_t status)
3120{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003121 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003122 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003123 const pjmedia_sdp_session *remote_sdp;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003124
3125 PJSUA_LOCK();
3126
Benny Prijonoa1e69682007-05-11 15:14:34 +00003127 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003128
3129 if (status != PJ_SUCCESS) {
3130
3131 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3132
Benny Prijono2331d202008-06-26 15:46:52 +00003133 /* Do not deinitialize media since this may be a re-INVITE or
3134 * UPDATE (which in this case the media should not get affected
3135 * by the failed re-INVITE/UPDATE). The media will be shutdown
3136 * when call is disconnected anyway.
3137 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003138 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003139 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003140
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003141 /* Disconnect call if we're not in the middle of initializing an
3142 * UAS dialog and if this is not a re-INVITE
3143 */
3144 if (inv->state != PJSIP_INV_STATE_NULL &&
3145 inv->state != PJSIP_INV_STATE_CONFIRMED)
3146 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003147 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003148 }
3149
3150 PJSUA_UNLOCK();
3151 return;
3152 }
3153
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003154
3155 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003156 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003157 if (status != PJ_SUCCESS) {
3158 pjsua_perror(THIS_FILE,
3159 "Unable to retrieve currently active local SDP",
3160 status);
3161 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3162 PJSUA_UNLOCK();
3163 return;
3164 }
3165
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003166 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3167 if (status != PJ_SUCCESS) {
3168 pjsua_perror(THIS_FILE,
3169 "Unable to retrieve currently active remote SDP",
3170 status);
3171 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3172 PJSUA_UNLOCK();
3173 return;
3174 }
3175
Benny Prijono91a6a172007-10-31 08:59:29 +00003176 /* Update remote's NAT type */
3177 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3178 update_remote_nat_type(call, remote_sdp);
3179 }
3180
3181 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003182 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003183 if (status != PJ_SUCCESS) {
3184 pjsua_perror(THIS_FILE, "Unable to create media session",
3185 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003186 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003187 /* No need to deinitialize; media will be shutdown when call
3188 * state is disconnected anyway.
3189 */
3190 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003191 PJSUA_UNLOCK();
3192 return;
3193 }
3194
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003195
3196 /* Call application callback, if any */
3197 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3198 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3199
3200
3201 PJSUA_UNLOCK();
3202}
3203
3204
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003205/* Create SDP for call hold. */
3206static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3207 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003208{
3209 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003210 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003211 pjmedia_sdp_session *sdp;
3212
Benny Prijono40d62b62009-08-12 17:53:47 +00003213 /* Use call's provisional pool */
3214 pool = call->inv->pool_prov;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003215
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003216 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003217 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3218 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003219 if (status != PJ_SUCCESS) {
3220 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3221 return status;
3222 }
3223
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003224 /* Call-hold is done by set the media direction to 'sendonly'
3225 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3226 * 'inactive' (PJMEDIA_DIR_NONE).
3227 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3228 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003229 /* http://trac.pjsip.org/repos/ticket/880
3230 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3231 */
3232 if (1) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003233 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003234
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003235 /* Remove existing directions attributes */
3236 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3237 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3238 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3239 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003240
Benny Prijonoe0860132009-06-05 10:14:20 +00003241 if (call->media_dir & PJMEDIA_DIR_ENCODING) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003242 /* Add sendonly attribute */
3243 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3244 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3245 } else {
3246 /* Add inactive attribute */
3247 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3248 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3249 }
3250 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003251
3252 *p_answer = sdp;
3253
3254 return status;
3255}
3256
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003257/*
3258 * Called when session received new offer.
3259 */
3260static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3261 const pjmedia_sdp_session *offer)
3262{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003263 pjsua_call *call;
3264 pjmedia_sdp_conn *conn;
3265 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003266 pj_status_t status;
3267
3268 PJSUA_LOCK();
3269
Benny Prijonoa1e69682007-05-11 15:14:34 +00003270 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003271
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003272 conn = offer->media[0]->conn;
3273 if (!conn)
3274 conn = offer->conn;
3275
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003276 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003277 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3278 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003279
Benny Prijono40d62b62009-08-12 17:53:47 +00003280 status = pjsua_media_channel_create_sdp(call->index,
3281 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003282 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003283 if (status != PJ_SUCCESS) {
3284 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3285 PJSUA_UNLOCK();
3286 return;
3287 }
3288
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003289 /* Check if offer's conn address is zero */
3290 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3291 pj_strcmp2(&conn->addr, "0")==0)
3292 {
3293 /* Modify address */
3294 answer->conn->addr = pj_str("0.0.0.0");
3295 }
3296
3297 /* Check if call is on-hold */
3298 if (call->local_hold) {
3299 pjmedia_sdp_attr *attr;
3300
3301 /* Remove existing directions attributes */
3302 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3303 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3304 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3305 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3306
3307 /* Keep call on-hold by setting 'sendonly' attribute.
3308 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3309 */
Benny Prijono40d62b62009-08-12 17:53:47 +00003310 attr = pjmedia_sdp_attr_create(call->inv->pool_prov, "sendonly", NULL);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003311 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3312 }
3313
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003314 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3315 if (status != PJ_SUCCESS) {
3316 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3317 PJSUA_UNLOCK();
3318 return;
3319 }
3320
3321 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003322}
3323
3324
3325/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003326 * Called to generate new offer.
3327 */
3328static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3329 pjmedia_sdp_session **offer)
3330{
3331 pjsua_call *call;
3332 pj_status_t status;
3333
3334 PJSUA_LOCK();
3335
3336 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3337
3338 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003339 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003340 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003341 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003342 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003343 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003344 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003345 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3346 call->index));
3347
Benny Prijono40d62b62009-08-12 17:53:47 +00003348 status = pjsua_media_channel_create_sdp(call->index,
3349 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003350 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003351 }
3352
3353 if (status != PJ_SUCCESS) {
3354 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3355 PJSUA_UNLOCK();
3356 return;
3357 }
3358
Benny Prijono77998ce2007-06-20 10:03:46 +00003359 PJSUA_UNLOCK();
3360}
3361
3362
3363/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003364 * Callback called by event framework when the xfer subscription state
3365 * has changed.
3366 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003367static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3368{
3369
3370 PJ_UNUSED_ARG(event);
3371
3372 /*
3373 * When subscription is accepted (got 200/OK to REFER), check if
3374 * subscription suppressed.
3375 */
3376 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3377
3378 pjsip_rx_data *rdata;
3379 pjsip_generic_string_hdr *refer_sub;
3380 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3381 pjsua_call *call;
3382
Benny Prijonoa1e69682007-05-11 15:14:34 +00003383 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003384
3385 /* Must be receipt of response message */
3386 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3387 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3388 rdata = event->body.tsx_state.src.rdata;
3389
3390 /* Find Refer-Sub header */
3391 refer_sub = (pjsip_generic_string_hdr*)
3392 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3393 &REFER_SUB, NULL);
3394
3395 /* Check if subscription is suppressed */
3396 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3397 /* Since no subscription is desired, assume that call has been
3398 * transfered successfully.
3399 */
3400 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3401 const pj_str_t ACCEPTED = { "Accepted", 8 };
3402 pj_bool_t cont = PJ_FALSE;
3403 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3404 200,
3405 &ACCEPTED,
3406 PJ_TRUE,
3407 &cont);
3408 }
3409
3410 /* Yes, subscription is suppressed.
3411 * Terminate our subscription now.
3412 */
3413 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3414 "event subcription..."));
3415 pjsip_evsub_terminate(sub, PJ_TRUE);
3416
3417 } else {
3418 /* Notify application about call transfer progress.
3419 * Initially notify with 100/Accepted status.
3420 */
3421 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3422 const pj_str_t ACCEPTED = { "Accepted", 8 };
3423 pj_bool_t cont = PJ_FALSE;
3424 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3425 100,
3426 &ACCEPTED,
3427 PJ_FALSE,
3428 &cont);
3429 }
3430 }
3431 }
3432 /*
3433 * On incoming NOTIFY, notify application about call transfer progress.
3434 */
3435 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3436 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3437 {
3438 pjsua_call *call;
3439 pjsip_msg *msg;
3440 pjsip_msg_body *body;
3441 pjsip_status_line status_line;
3442 pj_bool_t is_last;
3443 pj_bool_t cont;
3444 pj_status_t status;
3445
Benny Prijonoa1e69682007-05-11 15:14:34 +00003446 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003447
3448 /* When subscription is terminated, clear the xfer_sub member of
3449 * the inv_data.
3450 */
3451 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3452 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3453 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3454
3455 }
3456
3457 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3458 /* Application is not interested with call progress status */
3459 return;
3460 }
3461
3462 /* This better be a NOTIFY request */
3463 if (event->type == PJSIP_EVENT_TSX_STATE &&
3464 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3465 {
3466 pjsip_rx_data *rdata;
3467
3468 rdata = event->body.tsx_state.src.rdata;
3469
3470 /* Check if there's body */
3471 msg = rdata->msg_info.msg;
3472 body = msg->body;
3473 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003474 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003475 "Warning: received NOTIFY without message body"));
3476 return;
3477 }
3478
3479 /* Check for appropriate content */
3480 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3481 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3482 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003483 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003484 "Warning: received NOTIFY with non message/sipfrag "
3485 "content"));
3486 return;
3487 }
3488
3489 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003490 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003491 &status_line);
3492 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003493 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003494 "Warning: received NOTIFY with invalid "
3495 "message/sipfrag content"));
3496 return;
3497 }
3498
3499 } else {
3500 status_line.code = 500;
3501 status_line.reason = *pjsip_get_status_text(500);
3502 }
3503
3504 /* Notify application */
3505 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3506 cont = !is_last;
3507 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3508 status_line.code,
3509 &status_line.reason,
3510 is_last, &cont);
3511
3512 if (!cont) {
3513 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3514 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003515
3516 /* If the call transfer has completed but the subscription is
3517 * not terminated, terminate it now.
3518 */
3519 if (status_line.code/100 == 2 && !is_last) {
3520 pjsip_tx_data *tdata;
3521
3522 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3523 0, &tdata);
3524 if (status == PJ_SUCCESS)
3525 status = pjsip_evsub_send_request(sub, tdata);
3526 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003527 }
3528}
3529
3530
3531/*
3532 * Callback called by event framework when the xfer subscription state
3533 * has changed.
3534 */
3535static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003536{
3537
3538 PJ_UNUSED_ARG(event);
3539
3540 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003541 * When subscription is terminated, clear the xfer_sub member of
3542 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003543 */
3544 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003545 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003546
Benny Prijonoa1e69682007-05-11 15:14:34 +00003547 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003548 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003549 return;
3550
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003551 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003552 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003553
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003554 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003555 }
3556}
3557
3558
3559/*
3560 * Follow transfer (REFER) request.
3561 */
3562static void on_call_transfered( pjsip_inv_session *inv,
3563 pjsip_rx_data *rdata )
3564{
3565 pj_status_t status;
3566 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003567 pjsua_call *existing_call;
3568 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003569 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003570 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003571 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003572 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003573 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003574 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003575 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003576 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003577 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003578 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003579 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003580 pjsip_evsub *sub;
3581
Benny Prijonoa1e69682007-05-11 15:14:34 +00003582 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003583
Benny Prijono26ff9062006-02-21 23:47:00 +00003584 /* Find the Refer-To header */
3585 refer_to = (pjsip_generic_string_hdr*)
3586 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3587
3588 if (refer_to == NULL) {
3589 /* Invalid Request.
3590 * No Refer-To header!
3591 */
3592 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003593 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003594 return;
3595 }
3596
Benny Prijonoc8141a82006-08-20 09:12:19 +00003597 /* Find optional Refer-Sub header */
3598 refer_sub = (pjsip_generic_string_hdr*)
3599 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3600
3601 if (refer_sub) {
3602 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3603 no_refer_sub = PJ_TRUE;
3604 }
3605
Benny Prijono053f5222006-11-11 16:16:04 +00003606 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3607 * request.
3608 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003609 ref_by_hdr = (pjsip_hdr*)
3610 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003611 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003612
Benny Prijono9fc735d2006-05-28 14:58:12 +00003613 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003614 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003615 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3616 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3617 &refer_to->hvalue,
3618 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003619
3620 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003621 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003622 if (code >= 300) {
3623 /* Application rejects call transfer request */
3624 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3625 return;
3626 }
3627
Benny Prijono26ff9062006-02-21 23:47:00 +00003628 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3629 (int)inv->dlg->remote.info_str.slen,
3630 inv->dlg->remote.info_str.ptr,
3631 (int)refer_to->hvalue.slen,
3632 refer_to->hvalue.ptr));
3633
Benny Prijonoc8141a82006-08-20 09:12:19 +00003634 if (no_refer_sub) {
3635 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003636 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003637 */
3638 pjsip_tx_data *tdata;
3639 const pj_str_t str_false = { "false", 5};
3640 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003641
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003642 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3643 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003644 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003645 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003646 status);
3647 return;
3648 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003649
Benny Prijonoc8141a82006-08-20 09:12:19 +00003650 /* Add Refer-Sub header */
3651 hdr = (pjsip_hdr*)
3652 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3653 &str_false);
3654 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003655
Benny Prijono26ff9062006-02-21 23:47:00 +00003656
Benny Prijonoc8141a82006-08-20 09:12:19 +00003657 /* Send answer */
3658 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
3659 tdata);
3660 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003661 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003662 status);
3663 return;
3664 }
3665
3666 /* Don't have subscription */
3667 sub = NULL;
3668
3669 } else {
3670 struct pjsip_evsub_user xfer_cb;
3671 pjsip_hdr hdr_list;
3672
3673 /* Init callback */
3674 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003675 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003676
3677 /* Init additional header list to be sent with REFER response */
3678 pj_list_init(&hdr_list);
3679
3680 /* Create transferee event subscription */
3681 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
3682 if (status != PJ_SUCCESS) {
3683 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
3684 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
3685 return;
3686 }
3687
3688 /* If there's Refer-Sub header and the value is "true", send back
3689 * Refer-Sub in the response with value "true" too.
3690 */
3691 if (refer_sub) {
3692 const pj_str_t str_true = { "true", 4 };
3693 pjsip_hdr *hdr;
3694
3695 hdr = (pjsip_hdr*)
3696 pjsip_generic_string_hdr_create(inv->dlg->pool,
3697 &str_refer_sub,
3698 &str_true);
3699 pj_list_push_back(&hdr_list, hdr);
3700
3701 }
3702
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003703 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00003704 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
3705
3706 /* Create initial NOTIFY request */
3707 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
3708 100, NULL, &tdata);
3709 if (status != PJ_SUCCESS) {
3710 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3711 status);
3712 return;
3713 }
3714
3715 /* Send initial NOTIFY request */
3716 status = pjsip_xfer_send_request( sub, tdata);
3717 if (status != PJ_SUCCESS) {
3718 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
3719 return;
3720 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003721 }
3722
3723 /* We're cheating here.
3724 * We need to get a null terminated string from a pj_str_t.
3725 * So grab the pointer from the hvalue and NULL terminate it, knowing
3726 * that the NULL position will be occupied by a newline.
3727 */
3728 uri = refer_to->hvalue.ptr;
3729 uri[refer_to->hvalue.slen] = '\0';
3730
Benny Prijono053f5222006-11-11 16:16:04 +00003731 /* Init msg_data */
3732 pjsua_msg_data_init(&msg_data);
3733
3734 /* If Referred-By header is present in the REFER request, copy this
3735 * to the outgoing INVITE request.
3736 */
3737 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00003738 pjsip_hdr *dup = (pjsip_hdr*)
3739 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00003740 pj_list_push_back(&msg_data.hdr_list, dup);
3741 }
3742
Benny Prijono26ff9062006-02-21 23:47:00 +00003743 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00003744 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003745 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00003746 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003747 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00003748 if (status != PJ_SUCCESS) {
3749
Benny Prijonoc8141a82006-08-20 09:12:19 +00003750 /* Notify xferer about the error (if we have subscription) */
3751 if (sub) {
3752 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
3753 500, NULL, &tdata);
3754 if (status != PJ_SUCCESS) {
3755 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
3756 status);
3757 return;
3758 }
3759 status = pjsip_xfer_send_request(sub, tdata);
3760 if (status != PJ_SUCCESS) {
3761 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
3762 status);
3763 return;
3764 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003765 }
3766 return;
3767 }
3768
Benny Prijonoc8141a82006-08-20 09:12:19 +00003769 if (sub) {
3770 /* Put the server subscription in inv_data.
3771 * Subsequent state changed in pjsua_inv_on_state_changed() will be
3772 * reported back to the server subscription.
3773 */
3774 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00003775
Benny Prijonoc8141a82006-08-20 09:12:19 +00003776 /* Put the invite_data in the subscription. */
3777 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
3778 &pjsua_var.calls[new_call]);
3779 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003780}
3781
3782
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003783
Benny Prijono26ff9062006-02-21 23:47:00 +00003784/*
3785 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00003786 * session. We use this to trap:
3787 * - incoming REFER request.
3788 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00003789 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003790static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
3791 pjsip_transaction *tsx,
3792 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00003793{
Benny Prijono2285e7e2008-12-17 14:28:18 +00003794 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003795
3796 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00003797
Benny Prijono2285e7e2008-12-17 14:28:18 +00003798 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3799
3800 if (call == NULL) {
3801 PJSUA_UNLOCK();
3802 return;
3803 }
3804
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003805 if (call->inv == NULL) {
3806 /* Shouldn't happen. It happens only when we don't terminate the
3807 * server subscription caused by REFER after the call has been
3808 * transfered (and this call has been disconnected), and we
3809 * receive another REFER for this call.
3810 */
3811 PJSUA_UNLOCK();
3812 return;
3813 }
3814
Benny Prijonofeb69f42007-10-05 09:12:26 +00003815 /* Notify application callback first */
3816 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
3817 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
3818 }
3819
Benny Prijono26ff9062006-02-21 23:47:00 +00003820 if (tsx->role==PJSIP_ROLE_UAS &&
3821 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00003822 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003823 {
3824 /*
3825 * Incoming REFER request.
3826 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003827 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00003828
Benny Prijono26ff9062006-02-21 23:47:00 +00003829 }
Benny Prijonob0808372006-03-02 21:18:58 +00003830 else if (tsx->role==PJSIP_ROLE_UAS &&
3831 tsx->state==PJSIP_TSX_STATE_TRYING &&
3832 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
3833 {
3834 /*
3835 * Incoming MESSAGE request!
3836 */
3837 pjsip_rx_data *rdata;
3838 pjsip_msg *msg;
3839 pjsip_accept_hdr *accept_hdr;
3840 pj_status_t status;
3841
3842 rdata = e->body.tsx_state.src.rdata;
3843 msg = rdata->msg_info.msg;
3844
3845 /* Request MUST have message body, with Content-Type equal to
3846 * "text/plain".
3847 */
3848 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
3849
3850 pjsip_hdr hdr_list;
3851
3852 pj_list_init(&hdr_list);
3853 pj_list_push_back(&hdr_list, accept_hdr);
3854
3855 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
3856 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003857 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00003858 return;
3859 }
3860
3861 /* Respond with 200 first, so that remote doesn't retransmit in case
3862 * the UI takes too long to process the message.
3863 */
3864 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
3865
3866 /* Process MESSAGE request */
3867 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
3868 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003869
Benny Prijonob0808372006-03-02 21:18:58 +00003870 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003871 else if (tsx->role == PJSIP_ROLE_UAC &&
3872 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00003873 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003874 /* Handle outgoing pager status */
3875 if (tsx->status_code >= 200) {
3876 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00003877
Benny Prijonoa1e69682007-05-11 15:14:34 +00003878 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003879 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00003880
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003881 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
3882 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
3883 &im_data->to,
3884 &im_data->body,
3885 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00003886 (pjsip_status_code)
3887 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003888 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00003889 }
Benny Prijonofccab712006-02-22 22:23:22 +00003890 }
Benny Prijono834aee32006-02-19 01:38:06 +00003891 }
Benny Prijono834aee32006-02-19 01:38:06 +00003892
Benny Prijono26ff9062006-02-21 23:47:00 +00003893
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003894 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00003895}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003896
3897
3898/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00003899static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
3900 const pjsip_uri *target,
3901 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003902{
3903 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00003904 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003905
3906 PJSUA_LOCK();
3907
3908 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00003909 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
3910 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003911 } else {
3912 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
3913 "(callback not implemented by application). Disconnecting "
3914 "call.",
3915 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00003916 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003917 }
3918
3919 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00003920
3921 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003922}
3923