blob: d390009efcd0243bf9aae70aa1168d2903aa6c0e [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
Nanang Izzuddin93bacd02010-06-15 09:56:39 +000027/* Retry interval of sending re-INVITE for locking a codec when remote
28 * SDP answer contains multiple codec, in milliseconds.
29 */
30#define LOCK_CODEC_RETRY_INTERVAL 200
31
32
Benny Prijonoeebe9af2006-06-13 22:57:13 +000033/* This callback receives notification from invite session when the
34 * session state has changed.
35 */
36static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
37 pjsip_event *e);
38
39/* This callback is called by invite session framework when UAC session
40 * has forked.
41 */
42static void pjsua_call_on_forked( pjsip_inv_session *inv,
43 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000044
45/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000046 * Callback to be called when SDP offer/answer negotiation has just completed
47 * in the session. This function will start/update media if negotiation
48 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000049 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000050static void pjsua_call_on_media_update(pjsip_inv_session *inv,
51 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000052
53/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000054 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000055 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000056static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
57 const pjmedia_sdp_session *offer);
58
59/*
Benny Prijono77998ce2007-06-20 10:03:46 +000060 * Called to generate new offer.
61 */
62static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
63 pjmedia_sdp_session **offer);
64
65/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000066 * This callback is called when transaction state has changed in INVITE
67 * session. We use this to trap:
68 * - incoming REFER request.
69 * - incoming MESSAGE request.
70 */
71static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
72 pjsip_transaction *tsx,
73 pjsip_event *e);
74
Benny Prijono5e51a4e2008-11-27 00:06:46 +000075/*
76 * Redirection handler.
77 */
Benny Prijono08a48b82008-11-27 12:42:07 +000078static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
79 const pjsip_uri *target,
80 const pjsip_event *e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +000081
Benny Prijonoeebe9af2006-06-13 22:57:13 +000082
Nanang Izzuddin99d69522008-08-04 15:01:38 +000083/* Create SDP for call hold. */
84static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
85 pjmedia_sdp_session **p_answer);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000086
Benny Prijonod524e822006-09-22 12:48:18 +000087/*
88 * Callback called by event framework when the xfer subscription state
89 * has changed.
90 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000091static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
92static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
93
94/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000095 * Reset call descriptor.
96 */
97static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +000098{
Benny Prijonoeebe9af2006-06-13 22:57:13 +000099 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono105217f2006-03-06 16:25:59 +0000100
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000101 call->index = id;
102 call->inv = NULL;
103 call->user_data = NULL;
104 call->session = NULL;
Benny Prijonoa310bd22008-06-27 21:19:44 +0000105 call->audio_idx = -1;
Benny Prijono8147f402007-11-21 14:50:07 +0000106 call->ssrc = pj_rand();
Nanang Izzuddina815ceb2008-08-26 16:51:28 +0000107 call->rtp_tx_seq = 0;
108 call->rtp_tx_ts = 0;
109 call->rtp_tx_seq_ts_set = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000110 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000111 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000112 call->conf_slot = PJSUA_INVALID_ID;
113 call->last_text.ptr = call->last_text_buf_;
114 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +0000115 call->conn_time.sec = 0;
116 call->conn_time.msec = 0;
117 call->res_time.sec = 0;
118 call->res_time.msec = 0;
Benny Prijono91a6a172007-10-31 08:59:29 +0000119 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
Nanang Izzuddin4375f902008-06-26 19:12:09 +0000120 call->rem_srtp_use = PJMEDIA_SRTP_DISABLED;
Nanang Izzuddin99d69522008-08-04 15:01:38 +0000121 call->local_hold = PJ_FALSE;
Benny Prijono105217f2006-03-06 16:25:59 +0000122}
123
124
Benny Prijono275fd682006-03-22 11:59:11 +0000125/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000126 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000127 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000128pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000129{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000130 pjsip_inv_callback inv_cb;
131 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000132 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000133 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000134
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000135 /* Init calls array. */
136 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
137 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000138
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000139 /* Copy config */
140 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000141
Benny Prijono1cd713b2009-11-11 00:33:00 +0000142 /* Verify settings */
143 if (pjsua_var.ua_cfg.max_calls >= PJSUA_MAX_CALLS) {
144 pjsua_var.ua_cfg.max_calls = PJSUA_MAX_CALLS;
145 }
146
Benny Prijono91d06b62008-09-20 12:16:56 +0000147 /* Check the route URI's and force loose route if required */
148 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
149 status = normalize_route_uri(pjsua_var.pool,
150 &pjsua_var.ua_cfg.outbound_proxy[i]);
151 if (status != PJ_SUCCESS)
152 return status;
153 }
154
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000155 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000156 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000157 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
158 inv_cb.on_new_session = &pjsua_call_on_forked;
159 inv_cb.on_media_update = &pjsua_call_on_media_update;
160 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000161 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000163 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000164
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000165 /* Initialize invite session module: */
166 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
167 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
168
Benny Prijonoc8141a82006-08-20 09:12:19 +0000169 /* Add "norefersub" in Supported header */
170 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
171 NULL, 1, &str_norefersub);
172
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000173 return status;
174}
175
176
177/*
178 * Start call subsystem.
179 */
180pj_status_t pjsua_call_subsys_start(void)
181{
182 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000183 return PJ_SUCCESS;
184}
185
186
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000187/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000188 * Get maximum number of calls configured in pjsua.
189 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000190PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000191{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000192 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000193}
194
195
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196/*
197 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000198 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000199PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000200{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000201 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000202}
203
204
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000205/*
206 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000207 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
209 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000210{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000211 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000212
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000214
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000215 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000216
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000217 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
218 if (!pjsua_var.calls[i].inv)
219 continue;
220 ids[c] = i;
221 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000222 }
223
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000224 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000225
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000226 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000227
228 return PJ_SUCCESS;
229}
230
231
Benny Prijono5773cd62008-01-19 13:01:42 +0000232/* Allocate one call id */
233static pjsua_call_id alloc_call_id(void)
234{
235 pjsua_call_id cid;
236
237#if 1
238 /* New algorithm: round-robin */
239 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
240 pjsua_var.next_call_id < 0)
241 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000242 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000243 }
244
245 for (cid=pjsua_var.next_call_id;
246 cid<(int)pjsua_var.ua_cfg.max_calls;
247 ++cid)
248 {
249 if (pjsua_var.calls[cid].inv == NULL) {
250 ++pjsua_var.next_call_id;
251 return cid;
252 }
253 }
254
255 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
256 if (pjsua_var.calls[cid].inv == NULL) {
257 ++pjsua_var.next_call_id;
258 return cid;
259 }
260 }
261
262#else
263 /* Old algorithm */
264 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
265 if (pjsua_var.calls[cid].inv == NULL)
266 return cid;
267 }
268#endif
269
270 return PJSUA_INVALID_ID;
271}
272
Benny Prijonod8179652008-01-23 20:39:07 +0000273/* Get signaling secure level.
274 * Return:
275 * 0: if signaling is not secure
276 * 1: if TLS transport is used for immediate hop
277 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000278 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000279static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000280{
281 const pj_str_t tls = pj_str(";transport=tls");
282 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000283 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000284
285 if (pj_stristr(dst_uri, &sips))
286 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000287
288 if (!pj_list_empty(&acc->route_set)) {
289 pjsip_route_hdr *r = acc->route_set.next;
290 pjsip_uri *uri = r->name_addr.uri;
291 pjsip_sip_uri *sip_uri;
292
293 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
294 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
295 return 1;
296
297 } else {
298 if (pj_stristr(dst_uri, &tls))
299 return 1;
300 }
301
Benny Prijonod8179652008-01-23 20:39:07 +0000302 return 0;
303}
304
Benny Prijono224b4e22008-06-19 14:10:28 +0000305/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000306static int call_get_secure_level(pjsua_call *call)
307{
308 if (call->inv->dlg->secure)
309 return 2;
310
311 if (!pj_list_empty(&call->inv->dlg->route_set)) {
312 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
313 pjsip_uri *uri = r->name_addr.uri;
314 pjsip_sip_uri *sip_uri;
315
316 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
317 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
318 return 1;
319
320 } else {
321 pjsip_sip_uri *sip_uri;
322
323 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
324 return 2;
325 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
326 return 0;
327
328 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
329 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
330 return 1;
331 }
332
333 return 0;
334}
Benny Prijono224b4e22008-06-19 14:10:28 +0000335*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000336
337
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000338/*
339 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000340 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000341PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
342 const pj_str_t *dest_uri,
343 unsigned options,
344 void *user_data,
345 const pjsua_msg_data *msg_data,
346 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000347{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000348 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000349 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000350 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000351 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000352 pjsua_acc *acc;
353 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000354 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000355 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000356 pjsip_tx_data *tdata;
357 pj_status_t status;
358
Benny Prijono9fc735d2006-05-28 14:58:12 +0000359
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000360 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000361 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000362 PJ_EINVAL);
363
Benny Prijono320fa4d2006-12-07 10:09:16 +0000364 /* Check arguments */
365 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
366
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000367 PJSUA_LOCK();
368
Benny Prijonof798e502009-03-09 13:08:16 +0000369 /* Create sound port if none is instantiated, to check if sound device
370 * can be used. But only do this with the conference bridge, as with
371 * audio switchboard (i.e. APS-Direct), we can only open the sound
372 * device once the correct format has been known
373 */
374 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
375 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000376 {
377 pj_status_t status;
378
379 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
380 if (status != PJ_SUCCESS) {
381 PJSUA_UNLOCK();
382 return status;
383 }
384 }
385
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000386 acc = &pjsua_var.acc[acc_id];
387 if (!acc->valid) {
388 pjsua_perror(THIS_FILE, "Unable to make call because account "
389 "is not valid", PJ_EINVALIDOP);
390 PJSUA_UNLOCK();
391 return PJ_EINVALIDOP;
392 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000393
Benny Prijonoa91a0032006-02-26 21:23:45 +0000394 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000395 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000396
Benny Prijono5773cd62008-01-19 13:01:42 +0000397 if (call_id == PJSUA_INVALID_ID) {
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000398 pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000399 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000400 return PJ_ETOOMANY;
401 }
402
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000403 call = &pjsua_var.calls[call_id];
404
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000405 /* Associate session with account */
406 call->acc_id = acc_id;
407
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000408 /* Create temporary pool */
409 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
410
Benny Prijono320fa4d2006-12-07 10:09:16 +0000411 /* Verify that destination URI is valid before calling
412 * pjsua_acc_create_uac_contact, or otherwise there
413 * a misleading "Invalid Contact URI" error will be printed
414 * when pjsua_acc_create_uac_contact() fails.
415 */
416 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000417 pjsip_uri *uri;
418 pj_str_t dup;
419
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000420 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
421 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000422
423 if (uri == NULL) {
424 pjsua_perror(THIS_FILE, "Unable to make call",
425 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000426 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000427 PJSUA_UNLOCK();
428 return PJSIP_EINVALIDREQURI;
429 }
430 }
431
Benny Prijono093d3022006-09-24 00:07:11 +0000432 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
433 (int)dest_uri->slen, dest_uri->ptr));
434
Benny Prijonoe21e7842006-04-09 16:46:05 +0000435 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000436 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000437
Benny Prijonoe21e7842006-04-09 16:46:05 +0000438 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000439 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000440
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000441 /* Create suitable Contact header unless a Contact header has been
442 * set in the account.
443 */
444 if (acc->contact.slen) {
445 contact = acc->contact;
446 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000447 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000448 acc_id, dest_uri);
449 if (status != PJ_SUCCESS) {
450 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
451 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000452 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000453 PJSUA_UNLOCK();
454 return status;
455 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000456 }
457
Benny Prijonoe21e7842006-04-09 16:46:05 +0000458 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000459 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000460 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000461 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000462 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000463 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000464 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000465 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000466 return status;
467 }
468
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000469 /* Increment the dialog's lock otherwise when invite session creation
470 * fails the dialog will be destroyed prematurely.
471 */
472 pjsip_dlg_inc_lock(dlg);
473
Benny Prijonodb844a42008-02-02 17:07:18 +0000474 /* Calculate call's secure level */
475 call->secure_level = get_secure_level(acc_id, dest_uri);
476
Benny Prijonoc97608e2007-03-23 16:34:20 +0000477 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000478 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000479 call->secure_level, dlg->pool,
480 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000481 if (status != PJ_SUCCESS) {
482 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
483 goto on_error;
484 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000485
Benny Prijono224b4e22008-06-19 14:10:28 +0000486 /* Create offer */
487 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000488 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000489 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000490 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000491 goto on_error;
492 }
493
494 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000495 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000496 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000497 if (acc->cfg.require_100rel)
498 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000499 if (acc->cfg.require_timer)
500 options |= PJSIP_INV_REQUIRE_TIMER;
Benny Prijono84126ab2006-02-09 09:30:09 +0000501
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000502 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000503 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000504 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000505 goto on_error;
506 }
507
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000508 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000509 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
510 if (status != PJ_SUCCESS) {
511 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
512 goto on_error;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000513 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000514
515 /* Create and associate our data in the session. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000516 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000517
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000518 dlg->mod_data[pjsua_var.mod.id] = call;
519 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000520
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000521 /* Attach user data */
522 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000523
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000524 /* If account is locked to specific transport, then lock dialog
525 * to this transport too.
526 */
527 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
528 pjsip_tpselector tp_sel;
529
530 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
531 pjsip_dlg_set_transport(dlg, &tp_sel);
532 }
533
Benny Prijono84126ab2006-02-09 09:30:09 +0000534 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000535 if (!pj_list_empty(&acc->route_set))
536 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000537
538
539 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000540 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000541 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000542 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000543 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000544
Benny Prijono48ab2b72007-11-08 09:24:30 +0000545 /* Set authentication preference */
546 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000547
548 /* Create initial INVITE: */
549
550 status = pjsip_inv_invite(inv, &tdata);
551 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000552 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
553 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000554 goto on_error;
555 }
556
557
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000558 /* Add additional headers etc */
559
560 pjsua_process_msg_data( tdata, msg_data);
561
Benny Prijono093d3022006-09-24 00:07:11 +0000562 /* Must increment call counter now */
563 ++pjsua_var.call_cnt;
564
Benny Prijono84126ab2006-02-09 09:30:09 +0000565 /* Send initial INVITE: */
566
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000567 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000568 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000569 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
570 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000571
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000572 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000573 * session would have been cleared.
574 */
575 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000576 goto on_error;
577 }
578
Benny Prijono84126ab2006-02-09 09:30:09 +0000579 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000580
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000581 if (p_call_id)
582 *p_call_id = call_id;
583
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000584 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000585 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000587
588 return PJ_SUCCESS;
589
590
591on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000592 if (dlg) {
593 /* This may destroy the dialog */
594 pjsip_dlg_dec_lock(dlg);
595 }
596
Benny Prijono1c2bf462006-03-05 11:54:02 +0000597 if (inv != NULL) {
598 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000599 }
600
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000601 if (call_id != -1) {
602 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000603 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000604 }
605
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000606 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000607 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000608 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000609}
610
611
Benny Prijono91a6a172007-10-31 08:59:29 +0000612/* Get the NAT type information in remote's SDP */
613static void update_remote_nat_type(pjsua_call *call,
614 const pjmedia_sdp_session *sdp)
615{
616 const pjmedia_sdp_attr *xnat;
617
618 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
619 if (xnat) {
620 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
621 } else {
622 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
623 }
624
625 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
626 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
627}
628
629
Benny Prijonodc39fe82006-05-26 12:17:46 +0000630/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000631 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000632 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000633 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000634pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000635{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000636 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000637 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000638 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000639 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
640 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000641 pjsip_tx_data *response = NULL;
642 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000643 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000644 int acc_id;
645 pjsua_call *call;
646 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000647 int sip_err_code;
Benny Prijono1c1d7342010-08-01 09:48:51 +0000648 pjmedia_sdp_session *offer=NULL, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000649 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000650
Benny Prijono26ff9062006-02-21 23:47:00 +0000651 /* Don't want to handle anything but INVITE */
652 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
653 return PJ_FALSE;
654
655 /* Don't want to handle anything that's already associated with
656 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000657 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000658 if (dlg || tsx)
659 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000660
Benny Prijono384dab42009-10-14 01:58:04 +0000661 /* Don't want to accept the call if shutdown is in progress */
662 if (pjsua_var.thread_quit_flag) {
663 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
664 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
665 NULL, NULL);
666 return PJ_TRUE;
667 }
668
Benny Prijono148c9dd2006-09-19 13:37:53 +0000669 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000670
Benny Prijono26ff9062006-02-21 23:47:00 +0000671 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000672 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000673
Benny Prijono5773cd62008-01-19 13:01:42 +0000674 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000675 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000676 PJSIP_SC_BUSY_HERE, NULL,
677 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000678 PJ_LOG(2,(THIS_FILE,
679 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000680 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000681 return PJ_TRUE;
682 }
683
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000684 /* Clear call descriptor */
685 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000686
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000687 call = &pjsua_var.calls[call_id];
688
689 /* Mark call start time. */
690 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000691
Benny Prijono053f5222006-11-11 16:16:04 +0000692 /* Check INVITE request for Replaces header. If Replaces header is
693 * present, the function will make sure that we can handle the request.
694 */
695 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
696 &response);
697 if (status != PJ_SUCCESS) {
698 /*
699 * Something wrong with the Replaces header.
700 */
701 if (response) {
702 pjsip_response_addr res_addr;
703
704 pjsip_get_response_addr(response->pool, rdata, &res_addr);
705 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
706 NULL, NULL);
707
708 } else {
709
710 /* Respond with 500 (Internal Server Error) */
711 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
712 NULL, NULL);
713 }
714
715 PJSUA_UNLOCK();
716 return PJ_TRUE;
717 }
718
719 /* If this INVITE request contains Replaces header, notify application
720 * about the request so that application can do subsequent checking
721 * if it wants to.
722 */
723 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
724 pjsua_call *replaced_call;
725 int st_code = 200;
726 pj_str_t st_text = { "OK", 2 };
727
728 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000729 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000730
731 /* Notify application */
732 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
733 rdata, &st_code, &st_text);
734
735 /* Must specify final response */
736 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
737
738 /* Check if application rejects this request. */
739 if (st_code >= 300) {
740
741 if (st_text.slen == 2)
742 st_text = *pjsip_get_status_text(st_code);
743
744 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
745 st_code, &st_text, NULL, NULL, NULL);
746 PJSUA_UNLOCK();
747 return PJ_TRUE;
748 }
749 }
750
Benny Prijonod8179652008-01-23 20:39:07 +0000751 /*
752 * Get which account is most likely to be associated with this incoming
753 * call. We need the account to find which contact URI to put for
754 * the call.
755 */
756 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
757
Benny Prijonodb844a42008-02-02 17:07:18 +0000758 /* Get call's secure level */
759 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
760 call->secure_level = 2;
761 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
762 call->secure_level = 1;
763 else
764 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000765
Benny Prijonod8179652008-01-23 20:39:07 +0000766 /* Parse SDP from incoming request */
767 if (rdata->msg_info.msg->body) {
Benny Prijono1c1d7342010-08-01 09:48:51 +0000768 pjsip_rdata_sdp_info *sdp_info;
769
770 sdp_info = pjsip_rdata_get_sdp_info(rdata);
771 offer = sdp_info->sdp;
772
773 status = sdp_info->sdp_err;
774 if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
775 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono2c484e42008-06-26 19:47:23 +0000776
Benny Prijonod8179652008-01-23 20:39:07 +0000777 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000778 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000779 pjsip_hdr hdr_list;
780 pjsip_warning_hdr *w;
781
782 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000783 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000784
785 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
786 pjsip_endpt_name(pjsua_var.endpt),
787 status);
788 pj_list_init(&hdr_list);
789 pj_list_push_back(&hdr_list, w);
790
Benny Prijono224b4e22008-06-19 14:10:28 +0000791 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000792 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000793 PJSUA_UNLOCK();
794 return PJ_TRUE;
795 }
Benny Prijono617b8602008-04-07 10:10:31 +0000796
797 /* Do quick checks on SDP before passing it to transports. More elabore
798 * checks will be done in pjsip_inv_verify_request2() below.
799 */
800 if (offer->media_count==0) {
801 const pj_str_t reason = pj_str("Missing media in SDP");
802 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
803 NULL, NULL, NULL);
804 PJSUA_UNLOCK();
805 return PJ_TRUE;
806 }
807
Benny Prijonod8179652008-01-23 20:39:07 +0000808 } else {
809 offer = NULL;
810 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000811
Benny Prijono224b4e22008-06-19 14:10:28 +0000812 /* Init media channel */
813 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
814 call->secure_level,
815 rdata->tp_info.pool, offer,
816 &sip_err_code);
817 if (status != PJ_SUCCESS) {
818 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
819 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
820 sip_err_code, NULL, NULL, NULL, NULL);
821 PJSUA_UNLOCK();
822 return PJ_TRUE;
823 }
824
825 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000826 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000827 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000828 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000829 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000830 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
831 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000832 PJSUA_UNLOCK();
833 return PJ_TRUE;
834 }
835
Benny Prijono224b4e22008-06-19 14:10:28 +0000836
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000837 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000838 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000839 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000840 if (pjsua_var.acc[acc_id].cfg.require_100rel)
841 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000842 if (pjsua_var.acc[acc_id].cfg.require_timer)
843 options |= PJSIP_INV_REQUIRE_TIMER;
Benny Prijono07fe2302010-06-24 12:33:18 +0000844 if (pjsua_var.media_cfg.enable_ice)
845 options |= PJSIP_INV_SUPPORT_ICE;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000846
Benny Prijonod8179652008-01-23 20:39:07 +0000847 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
848 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000849 if (status != PJ_SUCCESS) {
850
851 /*
852 * No we can't handle the incoming INVITE request.
853 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000854 if (response) {
855 pjsip_response_addr res_addr;
856
857 pjsip_get_response_addr(response->pool, rdata, &res_addr);
858 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
859 NULL, NULL);
860
861 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000862 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000863 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
864 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000865 }
866
Benny Prijonoc97608e2007-03-23 16:34:20 +0000867 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000868 PJSUA_UNLOCK();
869 return PJ_TRUE;
870 }
871
872
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000873 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000874 if (pjsua_var.acc[acc_id].contact.slen) {
875 contact = pjsua_var.acc[acc_id].contact;
876 } else {
877 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
878 acc_id, rdata);
879 if (status != PJ_SUCCESS) {
880 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
881 status);
882 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
883 NULL, NULL);
884 pjsua_media_channel_deinit(call->index);
885 PJSUA_UNLOCK();
886 return PJ_TRUE;
887 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000888 }
889
Benny Prijono26ff9062006-02-21 23:47:00 +0000890 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000891 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000892 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000893 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000894 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000895 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000896 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000897 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000898 return PJ_TRUE;
899 }
900
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000901 /* Set credentials */
902 if (pjsua_var.acc[acc_id].cred_cnt) {
903 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
904 pjsua_var.acc[acc_id].cred_cnt,
905 pjsua_var.acc[acc_id].cred);
906 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000907
Benny Prijono48ab2b72007-11-08 09:24:30 +0000908 /* Set preference */
909 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
910 &pjsua_var.acc[acc_id].cfg.auth_pref);
911
Benny Prijono26ff9062006-02-21 23:47:00 +0000912 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000913 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000914 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000915 pjsip_hdr hdr_list;
916 pjsip_warning_hdr *w;
917
918 w = pjsip_warning_hdr_create_from_status(dlg->pool,
919 pjsip_endpt_name(pjsua_var.endpt),
920 status);
921 pj_list_init(&hdr_list);
922 pj_list_push_back(&hdr_list, w);
923
924 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
925
926 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000927 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000928 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000929 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000930 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000931 return PJ_TRUE;
932 }
933
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000934 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000935 status = pjsip_timer_init_session(inv,
936 &pjsua_var.acc[acc_id].cfg.timer_setting);
937 if (status != PJ_SUCCESS) {
938 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
939 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
940 NULL, &response);
941 if (status == PJ_SUCCESS && response)
942 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000943
Nanang Izzuddin65add622009-08-11 16:26:20 +0000944 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000945
Nanang Izzuddin65add622009-08-11 16:26:20 +0000946 PJSUA_UNLOCK();
947 return PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000948 }
949
Benny Prijonoea9fd392007-11-06 03:41:40 +0000950 /* Update NAT type of remote endpoint, only when there is SDP in
951 * incoming INVITE!
952 */
953 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
954 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
955 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000956 const pjmedia_sdp_session *remote_sdp;
957
958 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
959 update_remote_nat_type(call, remote_sdp);
960 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000961
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000962 /* If account is locked to specific transport, then lock dialog
963 * to this transport too.
964 */
965 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
966 pjsip_tpselector tp_sel;
967
968 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
969 pjsip_dlg_set_transport(dlg, &tp_sel);
970 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000971
Benny Prijono2285e7e2008-12-17 14:28:18 +0000972 /* Must answer with some response to initial INVITE. We'll do this before
973 * attaching the call to the invite session/dialog, so that the application
974 * will not get notification about this event (on another scenario, it is
975 * also possible that inv_send_msg() fails and causes the invite session to
976 * be disconnected. If we have the call attached at this time, this will
977 * cause the disconnection callback to be called before on_incoming_call()
978 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +0000979 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000980 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000981 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000982 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000983 if (response == NULL) {
984 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
985 status);
986 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
987 pjsip_inv_terminate(inv, 500, PJ_FALSE);
988 } else {
989 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +0000990 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000991 PJ_FALSE);
992 }
Benny Prijonoc97608e2007-03-23 16:34:20 +0000993 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000994 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +0000995 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +0000996
997 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000998 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000999 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +00001000 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +00001001 PJSUA_UNLOCK();
1002 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001003 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001004 }
1005
Benny Prijono2285e7e2008-12-17 14:28:18 +00001006 /* Create and attach pjsua_var data to the dialog: */
1007 call->inv = inv;
1008
1009 dlg->mod_data[pjsua_var.mod.id] = call;
1010 inv->mod_data[pjsua_var.mod.id] = call;
1011
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001012 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001013
Benny Prijono105217f2006-03-06 16:25:59 +00001014
Benny Prijono053f5222006-11-11 16:16:04 +00001015 /* Check if this request should replace existing call */
1016 if (replaced_dlg) {
1017 pjsip_inv_session *replaced_inv;
1018 struct pjsua_call *replaced_call;
1019 pjsip_tx_data *tdata;
1020
1021 /* Get the invite session in the dialog */
1022 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1023
1024 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001025 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001026
1027 /* Notify application */
1028 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1029 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1030 call_id);
1031
1032 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1033 call_id));
1034
1035 /* Answer the new call with 200 response */
1036 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1037 if (status == PJ_SUCCESS)
1038 status = pjsip_inv_send_msg(inv, tdata);
1039
1040 if (status != PJ_SUCCESS)
1041 pjsua_perror(THIS_FILE, "Error answering session", status);
1042
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001043 /* Note that inv may be invalid if 200/OK has caused error in
1044 * starting the media.
1045 */
Benny Prijono053f5222006-11-11 16:16:04 +00001046
1047 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1048 replaced_call->index));
1049
1050 /* Disconnect replaced invite session */
1051 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1052 &tdata);
1053 if (status == PJ_SUCCESS && tdata)
1054 status = pjsip_inv_send_msg(replaced_inv, tdata);
1055
1056 if (status != PJ_SUCCESS)
1057 pjsua_perror(THIS_FILE, "Error terminating session", status);
1058
1059
1060 } else {
1061
Benny Prijonob5388cf2007-01-04 22:45:08 +00001062 /* Notify application if on_incoming_call() is overriden,
1063 * otherwise hangup the call with 480
1064 */
1065 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001066 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001067 } else {
1068 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1069 NULL, NULL);
1070 }
Benny Prijono053f5222006-11-11 16:16:04 +00001071 }
1072
Benny Prijono8b1889b2006-06-06 18:40:40 +00001073
Benny Prijono26ff9062006-02-21 23:47:00 +00001074 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001075 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001076 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001077}
1078
1079
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001080
1081/*
1082 * Check if the specified call has active INVITE session and the INVITE
1083 * session has not been disconnected.
1084 */
1085PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1086{
1087 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1088 PJ_EINVAL);
1089 return pjsua_var.calls[call_id].inv != NULL &&
1090 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1091}
1092
1093
1094/*
1095 * Check if call has an active media session.
1096 */
1097PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1098{
1099 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1100 PJ_EINVAL);
1101 return pjsua_var.calls[call_id].session != NULL;
1102}
1103
1104
Benny Prijonocf986c42008-09-02 11:25:07 +00001105/*
1106 * Retrieve the media session associated with this call.
1107 */
1108PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1109{
1110 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1111 NULL);
1112 return pjsua_var.calls[call_id].session;
1113}
1114
1115
1116/*
1117 * Retrieve the media transport instance that is used for this call.
1118 */
1119PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1120{
1121 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1122 NULL);
1123 return pjsua_var.calls[cid].med_tp;
1124}
1125
1126
Benny Prijono148c9dd2006-09-19 13:37:53 +00001127/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001128pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001129 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001130 pjsua_call **p_call,
1131 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001132{
1133 enum { MAX_RETRY=50 };
1134 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001135 pjsua_call *call = NULL;
1136 pj_bool_t has_pjsua_lock = PJ_FALSE;
1137 pj_status_t status = PJ_SUCCESS;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001138
1139 for (retry=0; retry<MAX_RETRY; ++retry) {
1140
1141 has_pjsua_lock = PJ_FALSE;
1142
1143 status = PJSUA_TRY_LOCK();
1144 if (status != PJ_SUCCESS) {
1145 pj_thread_sleep(retry/10);
1146 continue;
1147 }
1148
1149 has_pjsua_lock = PJ_TRUE;
1150 call = &pjsua_var.calls[call_id];
1151
1152 if (call->inv == NULL) {
1153 PJSUA_UNLOCK();
1154 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1155 return PJSIP_ESESSIONTERMINATED;
1156 }
1157
1158 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1159 if (status != PJ_SUCCESS) {
1160 PJSUA_UNLOCK();
1161 pj_thread_sleep(retry/10);
1162 continue;
1163 }
1164
1165 PJSUA_UNLOCK();
1166
1167 break;
1168 }
1169
1170 if (status != PJ_SUCCESS) {
1171 if (has_pjsua_lock == PJ_FALSE)
1172 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1173 "(possibly system has deadlocked) in %s",
1174 title));
1175 else
1176 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1177 "(possibly system has deadlocked) in %s",
1178 title));
1179 return PJ_ETIMEDOUT;
1180 }
1181
1182 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001183 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001184
1185 return PJ_SUCCESS;
1186}
1187
1188
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001189/*
1190 * Get the conference port identification associated with the call.
1191 */
1192PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1193{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001194 pjsua_call *call;
1195 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001196 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001197 pj_status_t status;
1198
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001199 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1200 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001201
Benny Prijonodc752ca2006-09-22 16:55:42 +00001202 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001203 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001204 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001205
1206 port_id = call->conf_slot;
1207
Benny Prijonodc752ca2006-09-22 16:55:42 +00001208 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001209
1210 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001211}
1212
1213
Benny Prijono148c9dd2006-09-19 13:37:53 +00001214
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001215/*
1216 * Obtain detail information about the specified call.
1217 */
1218PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1219 pjsua_call_info *info)
1220{
1221 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001222 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001223 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001224
1225 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1226 PJ_EINVAL);
1227
Benny Prijonoac623b32006-07-03 15:19:31 +00001228 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001229
Benny Prijonodc752ca2006-09-22 16:55:42 +00001230 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001231 if (status != PJ_SUCCESS) {
1232 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001233 }
1234
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001235 /* id and role */
1236 info->id = call_id;
1237 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001238 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001239
1240 /* local info */
1241 info->local_info.ptr = info->buf_.local_info;
1242 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1243 sizeof(info->buf_.local_info));
1244
1245 /* local contact */
1246 info->local_contact.ptr = info->buf_.local_contact;
1247 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1248 call->inv->dlg->local.contact->uri,
1249 info->local_contact.ptr,
1250 sizeof(info->buf_.local_contact));
1251
1252 /* remote info */
1253 info->remote_info.ptr = info->buf_.remote_info;
1254 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1255 sizeof(info->buf_.remote_info));
1256
1257 /* remote contact */
1258 if (call->inv->dlg->remote.contact) {
1259 int len;
1260 info->remote_contact.ptr = info->buf_.remote_contact;
1261 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1262 call->inv->dlg->remote.contact->uri,
1263 info->remote_contact.ptr,
1264 sizeof(info->buf_.remote_contact));
1265 if (len < 0) len = 0;
1266 info->remote_contact.slen = len;
1267 } else {
1268 info->remote_contact.slen = 0;
1269 }
1270
1271 /* call id */
1272 info->call_id.ptr = info->buf_.call_id;
1273 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1274 sizeof(info->buf_.call_id));
1275
1276 /* state, state_text */
1277 info->state = call->inv->state;
1278 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1279
1280 /* If call is disconnected, set the last_status from the cause code */
1281 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1282 /* last_status, last_status_text */
1283 info->last_status = call->inv->cause;
1284
1285 info->last_status_text.ptr = info->buf_.last_status_text;
1286 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1287 sizeof(info->buf_.last_status_text));
1288 } else {
1289 /* last_status, last_status_text */
1290 info->last_status = call->last_code;
1291
1292 info->last_status_text.ptr = info->buf_.last_status_text;
1293 pj_strncpy(&info->last_status_text, &call->last_text,
1294 sizeof(info->buf_.last_status_text));
1295 }
1296
1297 /* media status and dir */
1298 info->media_status = call->media_st;
1299 info->media_dir = call->media_dir;
1300
1301
1302 /* conference slot number */
1303 info->conf_slot = call->conf_slot;
1304
1305 /* calculate duration */
1306 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1307
1308 info->total_duration = call->dis_time;
1309 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1310
1311 if (call->conn_time.sec) {
1312 info->connect_duration = call->dis_time;
1313 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1314 }
1315
1316 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1317
1318 pj_gettimeofday(&info->total_duration);
1319 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1320
1321 pj_gettimeofday(&info->connect_duration);
1322 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1323
1324 } else {
1325 pj_gettimeofday(&info->total_duration);
1326 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1327 }
1328
Benny Prijonodc752ca2006-09-22 16:55:42 +00001329 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001330
1331 return PJ_SUCCESS;
1332}
1333
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001334/*
1335 * Check if call remote peer support the specified capability.
1336 */
1337PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1338 pjsua_call_id call_id,
1339 int htype,
1340 const pj_str_t *hname,
1341 const pj_str_t *token)
1342{
1343 pjsua_call *call;
1344 pjsip_dialog *dlg;
1345 pj_status_t status;
1346 pjsip_dialog_cap_status cap_status;
1347
1348 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1349 if (status != PJ_SUCCESS)
1350 return PJSIP_DIALOG_CAP_UNKNOWN;
1351
1352 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1353
1354 pjsip_dlg_dec_lock(dlg);
1355
1356 return cap_status;
1357}
1358
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001359
1360/*
1361 * Attach application specific data to the call.
1362 */
1363PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1364 void *user_data)
1365{
1366 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1367 PJ_EINVAL);
1368 pjsua_var.calls[call_id].user_data = user_data;
1369
1370 return PJ_SUCCESS;
1371}
1372
1373
1374/*
1375 * Get user data attached to the call.
1376 */
1377PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1378{
1379 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1380 NULL);
1381 return pjsua_var.calls[call_id].user_data;
1382}
1383
1384
1385/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001386 * Get remote's NAT type.
1387 */
1388PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1389 pj_stun_nat_type *p_type)
1390{
1391 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1392 PJ_EINVAL);
1393 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1394
1395 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1396 return PJ_SUCCESS;
1397}
1398
1399
1400/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001401 * Send response to incoming INVITE request.
1402 */
1403PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1404 unsigned code,
1405 const pj_str_t *reason,
1406 const pjsua_msg_data *msg_data)
1407{
1408 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001409 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001410 pjsip_tx_data *tdata;
1411 pj_status_t status;
1412
1413 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1414 PJ_EINVAL);
1415
Benny Prijonodc752ca2006-09-22 16:55:42 +00001416 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001417 if (status != PJ_SUCCESS)
1418 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001419
Benny Prijono2e507c22006-06-23 15:04:11 +00001420 if (call->res_time.sec == 0)
1421 pj_gettimeofday(&call->res_time);
1422
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001423 if (reason && reason->slen == 0)
1424 reason = NULL;
1425
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001426 /* Create response message */
1427 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1428 if (status != PJ_SUCCESS) {
1429 pjsua_perror(THIS_FILE, "Error creating response",
1430 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001431 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001432 return status;
1433 }
1434
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001435 /* Call might have been disconnected if application is answering with
1436 * 200/OK and the media failed to start.
1437 */
1438 if (call->inv == NULL) {
1439 pjsip_dlg_dec_lock(dlg);
1440 return PJSIP_ESESSIONTERMINATED;
1441 }
1442
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001443 /* Add additional headers etc */
1444 pjsua_process_msg_data( tdata, msg_data);
1445
1446 /* Send the message */
1447 status = pjsip_inv_send_msg(call->inv, tdata);
1448 if (status != PJ_SUCCESS)
1449 pjsua_perror(THIS_FILE, "Error sending response",
1450 status);
1451
Benny Prijonodc752ca2006-09-22 16:55:42 +00001452 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001453
1454 return status;
1455}
1456
1457
1458/*
1459 * Hangup call by using method that is appropriate according to the
1460 * call state.
1461 */
1462PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1463 unsigned code,
1464 const pj_str_t *reason,
1465 const pjsua_msg_data *msg_data)
1466{
1467 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001468 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001469 pj_status_t status;
1470 pjsip_tx_data *tdata;
1471
1472
Benny Prijono148c9dd2006-09-19 13:37:53 +00001473 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1474 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1475 call_id));
1476 }
1477
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001478 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1479 PJ_EINVAL);
1480
Benny Prijonodc752ca2006-09-22 16:55:42 +00001481 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001482 if (status != PJ_SUCCESS)
1483 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001484
1485 if (code==0) {
1486 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1487 code = PJSIP_SC_OK;
1488 else if (call->inv->role == PJSIP_ROLE_UAS)
1489 code = PJSIP_SC_DECLINE;
1490 else
1491 code = PJSIP_SC_REQUEST_TERMINATED;
1492 }
1493
1494 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1495 if (status != PJ_SUCCESS) {
1496 pjsua_perror(THIS_FILE,
1497 "Failed to create end session message",
1498 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001499 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001500 return status;
1501 }
1502
1503 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1504 * as p_tdata when INVITE transaction has not been answered
1505 * with any provisional responses.
1506 */
1507 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001508 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001509 return PJ_SUCCESS;
1510 }
1511
1512 /* Add additional headers etc */
1513 pjsua_process_msg_data( tdata, msg_data);
1514
1515 /* Send the message */
1516 status = pjsip_inv_send_msg(call->inv, tdata);
1517 if (status != PJ_SUCCESS) {
1518 pjsua_perror(THIS_FILE,
1519 "Failed to send end session message",
1520 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001521 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001522 return status;
1523 }
1524
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00001525 /* Stop lock codec timer, if it is active */
1526 if (call->lock_codec.reinv_timer.id) {
1527 pjsip_endpt_cancel_timer(pjsua_var.endpt,
1528 &call->lock_codec.reinv_timer);
1529 call->lock_codec.reinv_timer.id = PJ_FALSE;
1530 }
1531
Benny Prijonodc752ca2006-09-22 16:55:42 +00001532 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001533
1534 return PJ_SUCCESS;
1535}
1536
1537
1538/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001539 * Accept or reject redirection.
1540 */
1541PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1542 pjsip_redirect_op cmd)
1543{
1544 pjsua_call *call;
1545 pjsip_dialog *dlg;
1546 pj_status_t status;
1547
1548 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1549 PJ_EINVAL);
1550
1551 status = acquire_call("pjsua_call_process_redirect()", call_id,
1552 &call, &dlg);
1553 if (status != PJ_SUCCESS)
1554 return status;
1555
1556 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1557
1558 pjsip_dlg_dec_lock(dlg);
1559
1560 return status;
1561}
1562
1563
1564/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001565 * Put the specified call on hold.
1566 */
1567PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1568 const pjsua_msg_data *msg_data)
1569{
1570 pjmedia_sdp_session *sdp;
1571 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001572 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001573 pjsip_tx_data *tdata;
1574 pj_status_t status;
1575
1576 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1577 PJ_EINVAL);
1578
Benny Prijonodc752ca2006-09-22 16:55:42 +00001579 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001580 if (status != PJ_SUCCESS)
1581 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001582
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001583
1584 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1585 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001586 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001587 return PJSIP_ESESSIONSTATE;
1588 }
1589
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001590 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001591 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001592 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001593 return status;
1594 }
1595
1596 /* Create re-INVITE with new offer */
1597 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1598 if (status != PJ_SUCCESS) {
1599 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001600 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001601 return status;
1602 }
1603
1604 /* Add additional headers etc */
1605 pjsua_process_msg_data( tdata, msg_data);
1606
1607 /* Send the request */
1608 status = pjsip_inv_send_msg( call->inv, tdata);
1609 if (status != PJ_SUCCESS) {
1610 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001611 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001612 return status;
1613 }
1614
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001615 /* Set flag that local put the call on hold */
1616 call->local_hold = PJ_TRUE;
1617
Benny Prijonodc752ca2006-09-22 16:55:42 +00001618 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001619
1620 return PJ_SUCCESS;
1621}
1622
1623
1624/*
1625 * Send re-INVITE (to release hold).
1626 */
1627PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1628 pj_bool_t unhold,
1629 const pjsua_msg_data *msg_data)
1630{
1631 pjmedia_sdp_session *sdp;
1632 pjsip_tx_data *tdata;
1633 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001634 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001635 pj_status_t status;
1636
1637
1638 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1639 PJ_EINVAL);
1640
Benny Prijonodc752ca2006-09-22 16:55:42 +00001641 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001642 if (status != PJ_SUCCESS)
1643 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001644
1645 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1646 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001647 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001648 return PJSIP_ESESSIONSTATE;
1649 }
1650
1651 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001652 if (call->local_hold && !unhold) {
1653 status = create_sdp_of_call_hold(call, &sdp);
1654 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001655 status = pjsua_media_channel_create_sdp(call->index,
1656 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001657 NULL, &sdp, NULL);
1658 call->local_hold = PJ_FALSE;
1659 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001660 if (status != PJ_SUCCESS) {
1661 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1662 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001663 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001664 return status;
1665 }
1666
1667 /* Create re-INVITE with new offer */
1668 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1669 if (status != PJ_SUCCESS) {
1670 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001671 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001672 return status;
1673 }
1674
1675 /* Add additional headers etc */
1676 pjsua_process_msg_data( tdata, msg_data);
1677
1678 /* Send the request */
1679 status = pjsip_inv_send_msg( call->inv, tdata);
1680 if (status != PJ_SUCCESS) {
1681 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001682 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001683 return status;
1684 }
1685
Benny Prijonodc752ca2006-09-22 16:55:42 +00001686 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001687
1688 return PJ_SUCCESS;
1689}
1690
1691
1692/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001693 * Send UPDATE request.
1694 */
1695PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1696 unsigned options,
1697 const pjsua_msg_data *msg_data)
1698{
1699 pjmedia_sdp_session *sdp;
1700 pjsip_tx_data *tdata;
1701 pjsua_call *call;
1702 pjsip_dialog *dlg;
1703 pj_status_t status;
1704
1705 PJ_UNUSED_ARG(options);
1706
1707 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1708 PJ_EINVAL);
1709
1710 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1711 if (status != PJ_SUCCESS)
1712 return status;
1713
Benny Prijonoc08682e2007-10-04 06:17:58 +00001714 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001715 status = pjsua_media_channel_create_sdp(call->index,
1716 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001717 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001718 if (status != PJ_SUCCESS) {
1719 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1720 status);
1721 pjsip_dlg_dec_lock(dlg);
1722 return status;
1723 }
1724
Benny Prijono224b4e22008-06-19 14:10:28 +00001725 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001726 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1727 if (status != PJ_SUCCESS) {
1728 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1729 pjsip_dlg_dec_lock(dlg);
1730 return status;
1731 }
1732
1733 /* Add additional headers etc */
1734 pjsua_process_msg_data( tdata, msg_data);
1735
1736 /* Send the request */
1737 status = pjsip_inv_send_msg( call->inv, tdata);
1738 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001739 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001740 pjsip_dlg_dec_lock(dlg);
1741 return status;
1742 }
1743
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001744 call->local_hold = PJ_FALSE;
1745
Benny Prijonoc08682e2007-10-04 06:17:58 +00001746 pjsip_dlg_dec_lock(dlg);
1747
1748 return PJ_SUCCESS;
1749}
1750
1751
1752/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001753 * Initiate call transfer to the specified address.
1754 */
1755PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1756 const pj_str_t *dest,
1757 const pjsua_msg_data *msg_data)
1758{
1759 pjsip_evsub *sub;
1760 pjsip_tx_data *tdata;
1761 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001762 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001763 pjsip_generic_string_hdr *gs_hdr;
1764 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001765 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001766 pj_status_t status;
1767
1768
1769 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1770 PJ_EINVAL);
1771
Benny Prijonodc752ca2006-09-22 16:55:42 +00001772 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001773 if (status != PJ_SUCCESS)
1774 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001775
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001776
Benny Prijonod524e822006-09-22 12:48:18 +00001777 /* Create xfer client subscription. */
1778 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001779 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001780
1781 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001782 if (status != PJ_SUCCESS) {
1783 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001784 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001785 return status;
1786 }
1787
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001788 /* Associate this call with the client subscription */
1789 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1790
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001791 /*
1792 * Create REFER request.
1793 */
1794 status = pjsip_xfer_initiate(sub, dest, &tdata);
1795 if (status != PJ_SUCCESS) {
1796 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001797 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001798 return status;
1799 }
1800
Benny Prijono053f5222006-11-11 16:16:04 +00001801 /* Add Referred-By header */
1802 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1803 &dlg->local.info_str);
1804 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1805
1806
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001807 /* Add additional headers etc */
1808 pjsua_process_msg_data( tdata, msg_data);
1809
1810 /* Send. */
1811 status = pjsip_xfer_send_request(sub, tdata);
1812 if (status != PJ_SUCCESS) {
1813 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001814 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001815 return status;
1816 }
1817
1818 /* For simplicity (that's what this program is intended to be!),
1819 * leave the original invite session as it is. More advanced application
1820 * may want to hold the INVITE, or terminate the invite, or whatever.
1821 */
1822
Benny Prijonodc752ca2006-09-22 16:55:42 +00001823 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001824
1825 return PJ_SUCCESS;
1826
1827}
1828
1829
1830/*
Benny Prijono053f5222006-11-11 16:16:04 +00001831 * Initiate attended call transfer to the specified address.
1832 */
1833PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1834 pjsua_call_id dest_call_id,
1835 unsigned options,
1836 const pjsua_msg_data *msg_data)
1837{
1838 pjsua_call *dest_call;
1839 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001840 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001841 pj_str_t str_dest;
1842 int len;
1843 pjsip_uri *uri;
1844 pj_status_t status;
1845
1846
1847 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1848 PJ_EINVAL);
1849 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1850 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1851 PJ_EINVAL);
1852
1853 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1854 &dest_call, &dest_dlg);
1855 if (status != PJ_SUCCESS)
1856 return status;
1857
1858 /*
1859 * Create REFER destination URI with Replaces field.
1860 */
1861
1862 /* Make sure we have sufficient buffer's length */
1863 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1864 dest_dlg->call_id->id.slen +
1865 dest_dlg->remote.info->tag.slen +
1866 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001867 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001868
1869 /* Print URI */
1870 str_dest_buf[0] = '<';
1871 str_dest.slen = 1;
1872
Benny Prijonoa1e69682007-05-11 15:14:34 +00001873 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001874 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1875 str_dest_buf+1, sizeof(str_dest_buf)-1);
1876 if (len < 0)
1877 return PJSIP_EURITOOLONG;
1878
1879 str_dest.slen += len;
1880
1881
1882 /* Build the URI */
1883 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1884 sizeof(str_dest_buf) - str_dest.slen,
1885 "?%s"
1886 "Replaces=%.*s"
1887 "%%3Bto-tag%%3D%.*s"
1888 "%%3Bfrom-tag%%3D%.*s>",
1889 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1890 "" : "Require=replaces&"),
1891 (int)dest_dlg->call_id->id.slen,
1892 dest_dlg->call_id->id.ptr,
1893 (int)dest_dlg->remote.info->tag.slen,
1894 dest_dlg->remote.info->tag.ptr,
1895 (int)dest_dlg->local.info->tag.slen,
1896 dest_dlg->local.info->tag.ptr);
1897
1898 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1899 PJSIP_EURITOOLONG);
1900
1901 str_dest.ptr = str_dest_buf;
1902 str_dest.slen += len;
1903
1904 pjsip_dlg_dec_lock(dest_dlg);
1905
1906 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1907}
1908
1909
1910/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001911 * Send DTMF digits to remote using RFC 2833 payload formats.
1912 */
1913PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1914 const pj_str_t *digits)
1915{
1916 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001917 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001918 pj_status_t status;
1919
1920 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1921 PJ_EINVAL);
1922
Benny Prijonodc752ca2006-09-22 16:55:42 +00001923 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001924 if (status != PJ_SUCCESS)
1925 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001926
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001927 if (!call->session) {
1928 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001929 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001930 return PJ_EINVALIDOP;
1931 }
1932
1933 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1934
Benny Prijonodc752ca2006-09-22 16:55:42 +00001935 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001936
1937 return status;
1938}
1939
1940
1941/**
1942 * Send instant messaging inside INVITE session.
1943 */
1944PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1945 const pj_str_t *mime_type,
1946 const pj_str_t *content,
1947 const pjsua_msg_data *msg_data,
1948 void *user_data)
1949{
1950 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001951 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001952 const pj_str_t mime_text_plain = pj_str("text/plain");
1953 pjsip_media_type ctype;
1954 pjsua_im_data *im_data;
1955 pjsip_tx_data *tdata;
1956 pj_status_t status;
1957
1958
1959 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1960 PJ_EINVAL);
1961
Benny Prijonodc752ca2006-09-22 16:55:42 +00001962 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001963 if (status != PJ_SUCCESS)
1964 return status;
1965
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001966 /* Set default media type if none is specified */
1967 if (mime_type == NULL) {
1968 mime_type = &mime_text_plain;
1969 }
1970
1971 /* Create request message. */
1972 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1973 -1, &tdata);
1974 if (status != PJ_SUCCESS) {
1975 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1976 goto on_return;
1977 }
1978
1979 /* Add accept header. */
1980 pjsip_msg_add_hdr( tdata->msg,
1981 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1982
1983 /* Parse MIME type */
1984 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1985
1986 /* Create "text/plain" message body. */
1987 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
1988 &ctype.subtype, content);
1989 if (tdata->msg->body == NULL) {
1990 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
1991 pjsip_tx_data_dec_ref(tdata);
1992 goto on_return;
1993 }
1994
1995 /* Add additional headers etc */
1996 pjsua_process_msg_data( tdata, msg_data);
1997
1998 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001999 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002000 im_data->acc_id = call->acc_id;
2001 im_data->call_id = call_id;
2002 im_data->to = call->inv->dlg->remote.info_str;
2003 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2004 im_data->user_data = user_data;
2005
2006
2007 /* Send the request. */
2008 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2009 pjsua_var.mod.id, im_data);
2010 if (status != PJ_SUCCESS) {
2011 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2012 goto on_return;
2013 }
2014
2015on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002016 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002017 return status;
2018}
2019
2020
2021/*
2022 * Send IM typing indication inside INVITE session.
2023 */
2024PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2025 pj_bool_t is_typing,
2026 const pjsua_msg_data*msg_data)
2027{
2028 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002029 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002030 pjsip_tx_data *tdata;
2031 pj_status_t status;
2032
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002033 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2034 PJ_EINVAL);
2035
Benny Prijonodc752ca2006-09-22 16:55:42 +00002036 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002037 if (status != PJ_SUCCESS)
2038 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002039
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002040 /* Create request message. */
2041 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2042 -1, &tdata);
2043 if (status != PJ_SUCCESS) {
2044 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2045 goto on_return;
2046 }
2047
2048 /* Create "application/im-iscomposing+xml" msg body. */
2049 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2050 NULL, NULL, -1);
2051
2052 /* Add additional headers etc */
2053 pjsua_process_msg_data( tdata, msg_data);
2054
2055 /* Send the request. */
2056 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2057 if (status != PJ_SUCCESS) {
2058 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2059 goto on_return;
2060 }
2061
2062on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002063 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002064 return status;
2065}
2066
2067
2068/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002069 * Send arbitrary request.
2070 */
2071PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2072 const pj_str_t *method_str,
2073 const pjsua_msg_data *msg_data)
2074{
2075 pjsua_call *call;
2076 pjsip_dialog *dlg;
2077 pjsip_method method;
2078 pjsip_tx_data *tdata;
2079 pj_status_t status;
2080
2081 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2082 PJ_EINVAL);
2083
2084 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2085 if (status != PJ_SUCCESS)
2086 return status;
2087
2088 /* Init method */
2089 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2090
2091 /* Create request message. */
2092 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2093 if (status != PJ_SUCCESS) {
2094 pjsua_perror(THIS_FILE, "Unable to create request", status);
2095 goto on_return;
2096 }
2097
2098 /* Add additional headers etc */
2099 pjsua_process_msg_data( tdata, msg_data);
2100
2101 /* Send the request. */
2102 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2103 if (status != PJ_SUCCESS) {
2104 pjsua_perror(THIS_FILE, "Unable to send request", status);
2105 goto on_return;
2106 }
2107
2108on_return:
2109 pjsip_dlg_dec_lock(dlg);
2110 return status;
2111}
2112
2113
2114/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002115 * Terminate all calls.
2116 */
2117PJ_DEF(void) pjsua_call_hangup_all(void)
2118{
2119 unsigned i;
2120
2121 PJSUA_LOCK();
2122
2123 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2124 if (pjsua_var.calls[i].inv)
2125 pjsua_call_hangup(i, 0, NULL, NULL);
2126 }
2127
2128 PJSUA_UNLOCK();
2129}
2130
2131
Benny Prijono627cbb42007-09-25 20:48:49 +00002132const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002133{
2134 if (val < 1000) {
2135 pj_ansi_sprintf(buf, "%d", val);
2136 } else if (val < 1000000) {
2137 pj_ansi_sprintf(buf, "%d.%dK",
2138 val / 1000,
2139 (val % 1000) / 100);
2140 } else {
2141 pj_ansi_sprintf(buf, "%d.%02dM",
2142 val / 1000000,
2143 (val % 1000000) / 10000);
2144 }
2145
2146 return buf;
2147}
2148
2149
2150/* Dump media session */
2151static void dump_media_session(const char *indent,
2152 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002153 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002154{
2155 unsigned i;
2156 char *p = buf, *end = buf+maxlen;
2157 int len;
2158 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002159 pjmedia_session *session = call->session;
2160 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002161
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002162 pjmedia_transport_info_init(&tp_info);
2163
2164 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002165 pjmedia_session_get_info(session, &info);
2166
2167 for (i=0; i<info.stream_cnt; ++i) {
2168 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002169 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002170 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002171 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002172 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002173 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002174 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002175
2176 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002177 // rem_addr will contain actual address of RTP originator, instead of
2178 // remote RTP address specified by stream which is fetched from the SDP.
2179 // Please note that we are assuming only one stream per call.
2180 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2181 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002182 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2183 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002184 sizeof(rem_addr_buf), 3);
2185 } else {
2186 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002187 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002188 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002189
Benny Prijono2dbf5072010-06-23 12:38:28 +00002190 if (call->media_dir == PJMEDIA_DIR_NONE) {
2191 /* To handle when the stream that is currently being paused
2192 * (http://trac.pjsip.org/repos/ticket/1079)
2193 */
2194 dir = "inactive";
2195 } else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002196 dir = "sendonly";
2197 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2198 dir = "recvonly";
2199 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2200 dir = "sendrecv";
2201 else
2202 dir = "inactive";
2203
2204
2205 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002206 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002207 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002208 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002209 info.stream_info[i].fmt.encoding_name.ptr,
2210 info.stream_info[i].fmt.clock_rate / 1000,
2211 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002212 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002213 if (len < 1 || len > end-p) {
2214 *p = '\0';
2215 return;
2216 }
2217
2218 p += len;
2219 *p++ = '\n';
2220 *p = '\0';
2221
2222 if (stat.rx.update_cnt == 0)
2223 strcpy(last_update, "never");
2224 else {
2225 pj_gettimeofday(&now);
2226 PJ_TIME_VAL_SUB(now, stat.rx.update);
2227 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2228 now.sec / 3600,
2229 (now.sec % 3600) / 60,
2230 now.sec % 60,
2231 now.msec);
2232 }
2233
Benny Prijono80019eb2006-08-07 13:22:23 +00002234 pj_gettimeofday(&media_duration);
2235 PJ_TIME_VAL_SUB(media_duration, stat.start);
2236 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2237 media_duration.msec = 1;
2238
Benny Prijono1402a4a2008-01-08 23:41:22 +00002239 /* protect against division by zero */
2240 if (stat.rx.pkt == 0)
2241 stat.rx.pkt = 1;
2242 if (stat.tx.pkt == 0)
2243 stat.tx.pkt = 1;
2244
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002245 len = pj_ansi_snprintf(p, end-p,
2246 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002247 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002248 "%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 +00002249 "%s (msec) min avg max last dev\n"
2250 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
Nanang Izzuddin78bec1a2010-07-15 14:45:47 +00002251 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f"
2252#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2253 "\n"
2254 "%s raw jitter : %7.3f %7.3f %7.3f %7.3f %7.3f"
2255#endif
2256#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2257 "\n"
2258 "%s IPDV : %7.3f %7.3f %7.3f %7.3f %7.3f"
2259#endif
2260 "%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002261 indent, info.stream_info[i].fmt.pt,
2262 last_update,
2263 indent,
2264 good_number(packets, stat.rx.pkt),
2265 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002266 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002267 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2268 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 +00002269 indent,
2270 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002271 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002272 stat.rx.discard,
2273 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002274 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002275 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002276 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002277 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002278 indent, indent,
2279 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002280 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002281 stat.rx.loss_period.max / 1000.0,
2282 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002283 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002284 indent,
2285 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002286 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002287 stat.rx.jitter.max / 1000.0,
2288 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002289 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Nanang Izzuddin78bec1a2010-07-15 14:45:47 +00002290#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2291 indent,
2292 stat.rx_raw_jitter.min / 1000.0,
2293 stat.rx_raw_jitter.mean / 1000.0,
2294 stat.rx_raw_jitter.max / 1000.0,
2295 stat.rx_raw_jitter.last / 1000.0,
2296 pj_math_stat_get_stddev(&stat.rx_raw_jitter) / 1000.0,
2297#endif
2298#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2299 indent,
2300 stat.rx_ipdv.min / 1000.0,
2301 stat.rx_ipdv.mean / 1000.0,
2302 stat.rx_ipdv.max / 1000.0,
2303 stat.rx_ipdv.last / 1000.0,
2304 pj_math_stat_get_stddev(&stat.rx_ipdv) / 1000.0,
2305#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002306 ""
2307 );
2308
2309 if (len < 1 || len > end-p) {
2310 *p = '\0';
2311 return;
2312 }
2313
2314 p += len;
2315 *p++ = '\n';
2316 *p = '\0';
2317
2318 if (stat.tx.update_cnt == 0)
2319 strcpy(last_update, "never");
2320 else {
2321 pj_gettimeofday(&now);
2322 PJ_TIME_VAL_SUB(now, stat.tx.update);
2323 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2324 now.sec / 3600,
2325 (now.sec % 3600) / 60,
2326 now.sec % 60,
2327 now.msec);
2328 }
2329
2330 len = pj_ansi_snprintf(p, end-p,
2331 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002332 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002333 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002334 "%s (msec) min avg max last dev \n"
2335 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2336 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002337 indent,
2338 info.stream_info[i].tx_pt,
2339 info.stream_info[i].param->info.frm_ptime *
2340 info.stream_info[i].param->setting.frm_per_pkt,
2341 last_update,
2342
2343 indent,
2344 good_number(packets, stat.tx.pkt),
2345 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002346 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002347 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2348 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 +00002349
2350 indent,
2351 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002352 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002353 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002354 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002355 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002356 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002357
2358 indent, indent,
2359 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002360 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002361 stat.tx.loss_period.max / 1000.0,
2362 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002363 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002364 indent,
2365 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002366 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002367 stat.tx.jitter.max / 1000.0,
2368 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002369 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002370 ""
2371 );
2372
2373 if (len < 1 || len > end-p) {
2374 *p = '\0';
2375 return;
2376 }
2377
2378 p += len;
2379 *p++ = '\n';
2380 *p = '\0';
2381
2382 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002383 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002384 indent,
2385 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002386 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002387 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002388 stat.rtt.last / 1000.0,
2389 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002390 );
2391 if (len < 1 || len > end-p) {
2392 *p = '\0';
2393 return;
2394 }
2395
2396 p += len;
2397 *p++ = '\n';
2398 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002399
2400#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2401# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2402 do { \
2403 if (samples <= 4294) \
2404 usec = samples * 1000000 / clock_rate; \
2405 else { \
2406 usec = samples * 1000 / clock_rate; \
2407 usec *= 1000; \
2408 } \
2409 } while(0)
2410
2411# define PRINT_VOIP_MTC_VAL(s, v) \
2412 if (v == 127) \
2413 sprintf(s, "(na)"); \
2414 else \
2415 sprintf(s, "%d", v)
2416
2417# define VALIDATE_PRINT_BUF() \
2418 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2419 p += len; *p++ = '\n'; *p = '\0'
2420
2421
2422 do {
2423 char loss[16], dup[16];
2424 char jitter[80];
2425 char toh[80];
2426 char plc[16], jba[16], jbr[16];
2427 char signal_lvl[16], noise_lvl[16], rerl[16];
2428 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2429 pjmedia_rtcp_xr_stat xr_stat;
2430 unsigned clock_rate;
2431
2432 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2433 PJ_SUCCESS)
2434 {
2435 break;
2436 }
2437
2438 clock_rate = info.stream_info[i].fmt.clock_rate;
2439
2440 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2441 VALIDATE_PRINT_BUF();
2442
2443 /* Statistics Summary */
2444 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2445 VALIDATE_PRINT_BUF();
2446
2447 if (xr_stat.rx.stat_sum.l)
2448 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2449 else
2450 sprintf(loss, "(na)");
2451
2452 if (xr_stat.rx.stat_sum.d)
2453 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2454 else
2455 sprintf(dup, "(na)");
2456
2457 if (xr_stat.rx.stat_sum.j) {
2458 unsigned jmin, jmax, jmean, jdev;
2459
2460 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2461 clock_rate);
2462 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2463 clock_rate);
2464 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2465 clock_rate);
2466 SAMPLES_TO_USEC(jdev,
2467 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2468 clock_rate);
2469 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2470 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2471 } else
2472 sprintf(jitter, "(report not available)");
2473
2474 if (xr_stat.rx.stat_sum.t) {
2475 sprintf(toh, "%11d %11d %11d %11d",
2476 xr_stat.rx.stat_sum.toh.min,
2477 xr_stat.rx.stat_sum.toh.mean,
2478 xr_stat.rx.stat_sum.toh.max,
2479 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2480 } else
2481 sprintf(toh, "(report not available)");
2482
2483 if (xr_stat.rx.stat_sum.update.sec == 0)
2484 strcpy(last_update, "never");
2485 else {
2486 pj_gettimeofday(&now);
2487 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2488 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2489 now.sec / 3600,
2490 (now.sec % 3600) / 60,
2491 now.sec % 60,
2492 now.msec);
2493 }
2494
2495 len = pj_ansi_snprintf(p, end-p,
2496 "%s RX last update: %s\n"
2497 "%s begin seq=%d, end seq=%d\n"
2498 "%s pkt loss=%s, dup=%s\n"
2499 "%s (msec) min avg max dev\n"
2500 "%s jitter : %s\n"
2501 "%s toh : %s",
2502 indent, last_update,
2503 indent,
2504 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2505 indent, loss, dup,
2506 indent,
2507 indent, jitter,
2508 indent, toh
2509 );
2510 VALIDATE_PRINT_BUF();
2511
2512 if (xr_stat.tx.stat_sum.l)
2513 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2514 else
2515 sprintf(loss, "(na)");
2516
2517 if (xr_stat.tx.stat_sum.d)
2518 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2519 else
2520 sprintf(dup, "(na)");
2521
2522 if (xr_stat.tx.stat_sum.j) {
2523 unsigned jmin, jmax, jmean, jdev;
2524
2525 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2526 clock_rate);
2527 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2528 clock_rate);
2529 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2530 clock_rate);
2531 SAMPLES_TO_USEC(jdev,
2532 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2533 clock_rate);
2534 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2535 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2536 } else
2537 sprintf(jitter, "(report not available)");
2538
2539 if (xr_stat.tx.stat_sum.t) {
2540 sprintf(toh, "%11d %11d %11d %11d",
2541 xr_stat.tx.stat_sum.toh.min,
2542 xr_stat.tx.stat_sum.toh.mean,
2543 xr_stat.tx.stat_sum.toh.max,
2544 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2545 } else
2546 sprintf(toh, "(report not available)");
2547
2548 if (xr_stat.tx.stat_sum.update.sec == 0)
2549 strcpy(last_update, "never");
2550 else {
2551 pj_gettimeofday(&now);
2552 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2553 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2554 now.sec / 3600,
2555 (now.sec % 3600) / 60,
2556 now.sec % 60,
2557 now.msec);
2558 }
2559
2560 len = pj_ansi_snprintf(p, end-p,
2561 "%s TX last update: %s\n"
2562 "%s begin seq=%d, end seq=%d\n"
2563 "%s pkt loss=%s, dup=%s\n"
2564 "%s (msec) min avg max dev\n"
2565 "%s jitter : %s\n"
2566 "%s toh : %s",
2567 indent, last_update,
2568 indent,
2569 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2570 indent, loss, dup,
2571 indent,
2572 indent, jitter,
2573 indent, toh
2574 );
2575 VALIDATE_PRINT_BUF();
2576
2577
2578 /* VoIP Metrics */
2579 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2580 VALIDATE_PRINT_BUF();
2581
2582 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2583 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2584 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2585 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2586 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2587 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2588 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2589
2590 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2591 case PJMEDIA_RTCP_XR_PLC_DIS:
2592 sprintf(plc, "DISABLED");
2593 break;
2594 case PJMEDIA_RTCP_XR_PLC_ENH:
2595 sprintf(plc, "ENHANCED");
2596 break;
2597 case PJMEDIA_RTCP_XR_PLC_STD:
2598 sprintf(plc, "STANDARD");
2599 break;
2600 case PJMEDIA_RTCP_XR_PLC_UNK:
2601 default:
2602 sprintf(plc, "UNKNOWN");
2603 break;
2604 }
2605
2606 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2607 case PJMEDIA_RTCP_XR_JB_FIXED:
2608 sprintf(jba, "FIXED");
2609 break;
2610 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2611 sprintf(jba, "ADAPTIVE");
2612 break;
2613 default:
2614 sprintf(jba, "UNKNOWN");
2615 break;
2616 }
2617
2618 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2619
2620 if (xr_stat.rx.voip_mtc.update.sec == 0)
2621 strcpy(last_update, "never");
2622 else {
2623 pj_gettimeofday(&now);
2624 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2625 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2626 now.sec / 3600,
2627 (now.sec % 3600) / 60,
2628 now.sec % 60,
2629 now.msec);
2630 }
2631
2632 len = pj_ansi_snprintf(p, end-p,
2633 "%s RX last update: %s\n"
2634 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2635 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2636 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2637 "%s delay : round trip=%d%s, end system=%d%s\n"
2638 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2639 "%s quality : R factor=%s, ext R factor=%s\n"
2640 "%s MOS LQ=%s, MOS CQ=%s\n"
2641 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2642 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2643 indent,
2644 last_update,
2645 /* packets */
2646 indent,
2647 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2648 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2649 /* burst */
2650 indent,
2651 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2652 xr_stat.rx.voip_mtc.burst_dur, "ms",
2653 /* gap */
2654 indent,
2655 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2656 xr_stat.rx.voip_mtc.gap_dur, "ms",
2657 /* delay */
2658 indent,
2659 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2660 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2661 /* level */
2662 indent,
2663 signal_lvl, "dB",
2664 noise_lvl, "dB",
2665 rerl, "",
2666 /* quality */
2667 indent,
2668 r_factor, ext_r_factor,
2669 indent,
2670 mos_lq, mos_cq,
2671 /* config */
2672 indent,
2673 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2674 /* JB delay */
2675 indent,
2676 xr_stat.rx.voip_mtc.jb_nom, "ms",
2677 xr_stat.rx.voip_mtc.jb_max, "ms",
2678 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2679 );
2680 VALIDATE_PRINT_BUF();
2681
2682 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2683 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2684 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2685 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2686 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2687 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2688 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2689
2690 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2691 case PJMEDIA_RTCP_XR_PLC_DIS:
2692 sprintf(plc, "DISABLED");
2693 break;
2694 case PJMEDIA_RTCP_XR_PLC_ENH:
2695 sprintf(plc, "ENHANCED");
2696 break;
2697 case PJMEDIA_RTCP_XR_PLC_STD:
2698 sprintf(plc, "STANDARD");
2699 break;
2700 case PJMEDIA_RTCP_XR_PLC_UNK:
2701 default:
2702 sprintf(plc, "unknown");
2703 break;
2704 }
2705
2706 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2707 case PJMEDIA_RTCP_XR_JB_FIXED:
2708 sprintf(jba, "FIXED");
2709 break;
2710 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2711 sprintf(jba, "ADAPTIVE");
2712 break;
2713 default:
2714 sprintf(jba, "unknown");
2715 break;
2716 }
2717
2718 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2719
2720 if (xr_stat.tx.voip_mtc.update.sec == 0)
2721 strcpy(last_update, "never");
2722 else {
2723 pj_gettimeofday(&now);
2724 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2725 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2726 now.sec / 3600,
2727 (now.sec % 3600) / 60,
2728 now.sec % 60,
2729 now.msec);
2730 }
2731
2732 len = pj_ansi_snprintf(p, end-p,
2733 "%s TX last update: %s\n"
2734 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2735 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2736 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2737 "%s delay : round trip=%d%s, end system=%d%s\n"
2738 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2739 "%s quality : R factor=%s, ext R factor=%s\n"
2740 "%s MOS LQ=%s, MOS CQ=%s\n"
2741 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2742 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2743 indent,
2744 last_update,
2745 /* pakcets */
2746 indent,
2747 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2748 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2749 /* burst */
2750 indent,
2751 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2752 xr_stat.tx.voip_mtc.burst_dur, "ms",
2753 /* gap */
2754 indent,
2755 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2756 xr_stat.tx.voip_mtc.gap_dur, "ms",
2757 /* delay */
2758 indent,
2759 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2760 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2761 /* level */
2762 indent,
2763 signal_lvl, "dB",
2764 noise_lvl, "dB",
2765 rerl, "",
2766 /* quality */
2767 indent,
2768 r_factor, ext_r_factor,
2769 indent,
2770 mos_lq, mos_cq,
2771 /* config */
2772 indent,
2773 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2774 /* JB delay */
2775 indent,
2776 xr_stat.tx.voip_mtc.jb_nom, "ms",
2777 xr_stat.tx.voip_mtc.jb_max, "ms",
2778 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2779 );
2780 VALIDATE_PRINT_BUF();
2781
2782
2783 /* RTT delay (by receiver side) */
2784 len = pj_ansi_snprintf(p, end-p,
2785 "%s RTT (from recv) min avg max last dev",
2786 indent);
2787 VALIDATE_PRINT_BUF();
2788 len = pj_ansi_snprintf(p, end-p,
2789 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2790 indent,
2791 xr_stat.rtt.min / 1000.0,
2792 xr_stat.rtt.mean / 1000.0,
2793 xr_stat.rtt.max / 1000.0,
2794 xr_stat.rtt.last / 1000.0,
2795 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2796 );
2797 VALIDATE_PRINT_BUF();
2798 } while(0);
2799#endif
2800
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002801 }
2802}
2803
2804
2805/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002806void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002807 int call_id,
2808 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002809{
2810 int len;
2811 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2812 pjsip_dialog *dlg = inv->dlg;
2813 char userinfo[128];
2814
2815 /* Dump invite sesion info. */
2816
2817 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
Benny Prijono329d6382009-05-29 13:04:03 +00002818 if (len < 0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002819 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2820 else
2821 userinfo[len] = '\0';
2822
2823 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2824 title,
2825 pjsip_inv_state_name(inv->state),
2826 userinfo);
2827 if (len < 1 || len >= (int)size) {
2828 pj_ansi_strcpy(buf, "<--uri too long-->");
2829 len = 18;
2830 } else
2831 buf[len] = '\0';
2832}
2833
2834
2835/*
2836 * Dump call and media statistics to string.
2837 */
2838PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2839 pj_bool_t with_media,
2840 char *buffer,
2841 unsigned maxlen,
2842 const char *indent)
2843{
2844 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002845 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002846 pj_time_val duration, res_delay, con_delay;
2847 char tmp[128];
2848 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002849 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002850 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002851 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002852
2853 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2854 PJ_EINVAL);
2855
Benny Prijonodc752ca2006-09-22 16:55:42 +00002856 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002857 if (status != PJ_SUCCESS)
2858 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002859
2860 *buffer = '\0';
2861 p = buffer;
2862 end = buffer + maxlen;
2863 len = 0;
2864
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002865 print_call(indent, call_id, tmp, sizeof(tmp));
2866
2867 len = pj_ansi_strlen(tmp);
2868 pj_ansi_strcpy(buffer, tmp);
2869
2870 p += len;
2871 *p++ = '\r';
2872 *p++ = '\n';
2873
2874 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002875 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002876 pj_gettimeofday(&duration);
2877 PJ_TIME_VAL_SUB(duration, call->conn_time);
2878 con_delay = call->conn_time;
2879 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2880 } else {
2881 duration.sec = duration.msec = 0;
2882 con_delay.sec = con_delay.msec = 0;
2883 }
2884
2885 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002886 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002887 res_delay = call->res_time;
2888 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2889 } else {
2890 res_delay.sec = res_delay.msec = 0;
2891 }
2892
2893 /* Print duration */
2894 len = pj_ansi_snprintf(p, end-p,
2895 "%s Call time: %02dh:%02dm:%02ds, "
2896 "1st res in %d ms, conn in %dms",
2897 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002898 (int)(duration.sec / 3600),
2899 (int)((duration.sec % 3600)/60),
2900 (int)(duration.sec % 60),
2901 (int)PJ_TIME_VAL_MSEC(res_delay),
2902 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002903
2904 if (len > 0 && len < end-p) {
2905 p += len;
2906 *p++ = '\n';
2907 *p = '\0';
2908 }
2909
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002910 /* Get and ICE SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002911 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002912 pjmedia_transport_get_info(call->med_tp, &tp_info);
2913 if (tp_info.specific_info_cnt > 0) {
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002914 unsigned i;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002915 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2916 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2917 {
2918 pjmedia_srtp_info *srtp_info =
2919 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2920
2921 len = pj_ansi_snprintf(p, end-p,
2922 "%s SRTP status: %s Crypto-suite: %s",
2923 indent,
2924 (srtp_info->active?"Active":"Not active"),
2925 srtp_info->tx_policy.name.ptr);
2926 if (len > 0 && len < end-p) {
2927 p += len;
2928 *p++ = '\n';
2929 *p = '\0';
2930 }
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002931 } else if (tp_info.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
2932 const pjmedia_ice_transport_info *ii;
2933
2934 ii = (const pjmedia_ice_transport_info*)
2935 tp_info.spc_info[i].buffer;
2936
2937 len = pj_ansi_snprintf(p, end-p,
2938 "%s ICE role: %s, state: %s, comp_cnt: %u",
2939 indent,
2940 pj_ice_sess_role_name(ii->role),
2941 pj_ice_strans_state_name(ii->sess_state),
2942 ii->comp_cnt);
2943 if (len > 0 && len < end-p) {
2944 p += len;
2945 *p++ = '\n';
2946 *p = '\0';
2947 }
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002948 }
2949 }
2950 }
2951
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002952 /* Dump session statistics */
2953 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002954 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002955
Benny Prijonodc752ca2006-09-22 16:55:42 +00002956 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002957
2958 return PJ_SUCCESS;
2959}
2960
2961
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002962/* Timer callback to close sound device */
2963static void reinv_timer_cb(pj_timer_heap_t *th,
2964 pj_timer_entry *entry)
2965{
2966 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
2967 pjsip_dialog *dlg;
2968 pjsua_call *call;
2969 pjsip_tx_data *tdata;
2970 pj_status_t status;
2971
2972 PJ_UNUSED_ARG(th);
2973
2974 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
2975
2976 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
2977 if (status != PJ_SUCCESS)
2978 return;
2979
2980 /* Verify if another SDP negotiation is in progress, e.g: session timer
2981 * or another re-INVITE.
2982 */
2983 if (call->inv==NULL || call->inv->neg==NULL ||
2984 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
2985 {
2986 goto on_return;
2987 }
2988
2989 /* Verify if another SDP negotiation has been completed by comparing
2990 * the SDP version.
2991 */
2992 {
2993 const pjmedia_sdp_session *sdp;
2994
2995 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
2996 if (status == PJ_SUCCESS &&
2997 sdp->origin.version > call->lock_codec.new_sdp->origin.version)
2998 {
2999 goto on_return;
3000 }
3001 }
3002
3003 /* Create re-INVITE with the new offer */
3004 status = pjsip_inv_reinvite(call->inv, NULL, call->lock_codec.new_sdp,
3005 &tdata);
3006 if (status == PJ_EINVALIDOP) {
3007 /* Ups, let's reschedule again */
3008 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
3009 call->lock_codec.reinv_timer.id = PJ_TRUE;
3010 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3011 &call->lock_codec.reinv_timer, &delay);
3012 } else if (status != PJ_SUCCESS) {
3013 pjsua_perror(THIS_FILE, "Failed creating re-INVITE in lock codec",
3014 status);
3015 }
3016
3017 /* Send the UPDATE/re-INVITE request */
3018 status = pjsip_inv_send_msg(call->inv, tdata);
3019 if (status != PJ_SUCCESS) {
3020 pjsua_perror(THIS_FILE, "Failed sending re-INVITE in lock codec",
3021 status);
3022 }
3023
3024on_return:
3025 pjsip_dlg_dec_lock(dlg);
3026}
3027
3028
3029/* Check if the specified format can be skipped in counting codecs */
3030static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
3031 const pj_str_t *fmt)
3032{
3033 unsigned pt;
3034
3035 pt = pj_strtoul(fmt);
3036
3037 /* Check for comfort noise */
3038 if (pt == PJMEDIA_RTP_PT_CN)
3039 return PJ_TRUE;
3040
3041 /* Dynamic PT, check the format name */
3042 if (pt >= 96) {
3043 pjmedia_sdp_attr *a;
3044 pjmedia_sdp_rtpmap rtpmap;
3045
3046 /* Get the format name */
3047 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3048 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
3049 /* Check for telephone-event */
3050 if (pj_stricmp2(&rtpmap.enc_name, "telephone-event")==0)
3051 return PJ_TRUE;
3052 } else {
3053 /* Invalid SDP, should not reach here */
3054 pj_assert(!"SDP should have been validated!");
3055 return PJ_TRUE;
3056 }
3057 }
3058
3059 return PJ_FALSE;
3060}
3061
3062
3063/* Check if remote answerer has given us more than one codecs. If so,
3064 * create another offer with one codec only to lock down the codec.
3065 */
3066static pj_status_t lock_codec(pjsua_call *call)
3067{
3068 const pj_str_t st_update = {"UPDATE", 6};
3069 pjsip_inv_session *inv = call->inv;
3070 const pjmedia_sdp_session *local_sdp;
3071 const pjmedia_sdp_session *remote_sdp;
3072 const pjmedia_sdp_media *rem_m;
3073 pjmedia_sdp_session *new_sdp;
3074 pjmedia_sdp_media *m;
3075 pjsip_tx_data *tdata;
3076 unsigned i, codec_cnt = 0;
3077 pj_status_t status;
3078
3079 if (!pjmedia_sdp_neg_was_answer_remote(inv->neg))
3080 return PJ_SUCCESS;
3081
3082 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
3083 if (status != PJ_SUCCESS)
3084 return status;
3085 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3086 if (status != PJ_SUCCESS)
3087 return status;
3088
3089 PJ_ASSERT_RETURN(call->audio_idx>=0 &&
3090 call->audio_idx < (int)remote_sdp->media_count,
3091 PJ_EINVALIDOP);
3092
3093 rem_m = remote_sdp->media[call->audio_idx];
3094
3095 /* Check if media is disabled or only one format in the answer. */
3096 if (rem_m->desc.port==0 || rem_m->desc.fmt_count==1)
3097 return PJ_SUCCESS;
3098
3099 /* Count the formats in the answer. */
3100 for (i=0; i<rem_m->desc.fmt_count && codec_cnt <= 1; ++i) {
3101 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[i]))
3102 ++codec_cnt;
3103 }
3104
3105 if (codec_cnt <= 1) {
3106 /* Answer contains single codec. */
3107 return PJ_SUCCESS;
3108 }
3109
3110 PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, start "
3111 "updating media session to use only one codec.."));
3112
3113 /* Clone the offer */
3114 new_sdp = pjmedia_sdp_session_clone(inv->pool_prov, local_sdp);
3115 /* Note that the usage of pool_prov above is risky when locking codec
3116 * delays the re-INVITE (using timer) and there are two SDP negotiations
3117 * done before the re-INVITE.
3118 */
3119
3120 /* Update the new offer so it contains only a codec. Note that formats
3121 * order in the offer should have been matched to the answer, so we can
3122 * just directly update the offer without looking-up the answer.
3123 */
3124 m = new_sdp->media[call->audio_idx];
3125 codec_cnt = 0;
3126 i = 0;
3127 while (i < m->desc.fmt_count) {
3128 pjmedia_sdp_attr *a;
3129 pj_str_t *fmt = &m->desc.fmt[i];
3130
3131 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
3132 ++i;
3133 continue;
3134 }
3135
3136 /* Remove format */
3137 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3138 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3139 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
3140 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3141 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
3142 m->desc.fmt_count, i);
3143 --m->desc.fmt_count;
3144 }
3145
3146 /* Send new SDP offer via UPDATE or re-INVITE */
3147 if (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)==
3148 PJSIP_DIALOG_CAP_SUPPORTED)
3149 {
3150 /* Create UPDATE with the new offer */
3151 status = pjsip_inv_update(inv, NULL, new_sdp, &tdata);
3152 if (status != PJ_SUCCESS)
3153 return status;
3154
3155 } else {
3156 /* Create re-INVITE with the new offer */
3157 status = pjsip_inv_reinvite(inv, NULL, new_sdp, &tdata);
3158 if (status == PJ_EINVALIDOP) {
3159 /* Current INVITE transaction is pending, reschedule re-INVITE. */
3160 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
3161
3162 call->lock_codec.new_sdp = new_sdp;
3163 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
3164 (void*)(pj_size_t)call->index,
3165 &reinv_timer_cb);
3166 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3167 &call->lock_codec.reinv_timer, &delay);
3168 return PJ_SUCCESS;
3169
3170 } else if (status != PJ_SUCCESS)
3171 return status;
3172 }
3173
3174 /* Send the UPDATE/re-INVITE request */
3175 status = pjsip_inv_send_msg(inv, tdata);
3176 if (status != PJ_SUCCESS)
3177 return status;
3178
3179 return PJ_SUCCESS;
3180}
3181
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003182/*
Benny Prijono84126ab2006-02-09 09:30:09 +00003183 * This callback receives notification from invite session when the
3184 * session state has changed.
3185 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003186static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
3187 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00003188{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003189 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003190
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003191 PJSUA_LOCK();
3192
Benny Prijonoa1e69682007-05-11 15:14:34 +00003193 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003194
3195 if (!call) {
3196 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00003197 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003198 }
3199
Benny Prijonoe21e7842006-04-09 16:46:05 +00003200
3201 /* Get call times */
3202 switch (inv->state) {
3203 case PJSIP_INV_STATE_EARLY:
3204 case PJSIP_INV_STATE_CONNECTING:
3205 if (call->res_time.sec == 0)
3206 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00003207 call->last_code = (pjsip_status_code)
3208 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003209 pj_strncpy(&call->last_text,
3210 &e->body.tsx_state.tsx->status_text,
3211 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00003212 break;
3213 case PJSIP_INV_STATE_CONFIRMED:
3214 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003215
3216 /* Ticket #476, locking a codec in the media session. */
3217 {
3218 pj_status_t status;
3219 status = lock_codec(call);
3220 if (status != PJ_SUCCESS) {
3221 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
3222 }
3223 }
3224
Benny Prijonoe21e7842006-04-09 16:46:05 +00003225 break;
3226 case PJSIP_INV_STATE_DISCONNECTED:
3227 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00003228 if (call->res_time.sec == 0)
3229 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003230 if (e->type == PJSIP_EVENT_TSX_STATE &&
3231 e->body.tsx_state.tsx->status_code > call->last_code)
3232 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00003233 call->last_code = (pjsip_status_code)
3234 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003235 pj_strncpy(&call->last_text,
3236 &e->body.tsx_state.tsx->status_text,
3237 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003238 } else {
3239 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
3240 pj_strncpy(&call->last_text,
3241 pjsip_get_status_text(call->last_code),
3242 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00003243 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003244
3245 /* Stop lock codec timer, if it is active */
3246 if (call->lock_codec.reinv_timer.id) {
3247 pjsip_endpt_cancel_timer(pjsua_var.endpt,
3248 &call->lock_codec.reinv_timer);
3249 call->lock_codec.reinv_timer.id = PJ_FALSE;
3250 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00003251 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003252 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00003253 call->last_code = (pjsip_status_code)
3254 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003255 pj_strncpy(&call->last_text,
3256 &e->body.tsx_state.tsx->status_text,
3257 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00003258 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00003259 }
3260
Benny Prijono26ff9062006-02-21 23:47:00 +00003261 /* If this is an outgoing INVITE that was created because of
3262 * REFER/transfer, send NOTIFY to transferer.
3263 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00003264 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003265 int st_code = -1;
3266 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
3267
3268
Benny Prijonoa91a0032006-02-26 21:23:45 +00003269 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003270 case PJSIP_INV_STATE_NULL:
3271 case PJSIP_INV_STATE_CALLING:
3272 /* Do nothing */
3273 break;
3274
3275 case PJSIP_INV_STATE_EARLY:
3276 case PJSIP_INV_STATE_CONNECTING:
3277 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003278 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
3279 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3280 else
3281 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003282 break;
3283
Benny Prijono140beae2009-10-11 05:06:43 +00003284 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003285#if 0
3286/* We don't need this, as we've terminated the subscription in
3287 * CONNECTING state.
3288 */
Benny Prijono26ff9062006-02-21 23:47:00 +00003289 /* When state is confirmed, send the final 200/OK and terminate
3290 * subscription.
3291 */
3292 st_code = e->body.tsx_state.tsx->status_code;
3293 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003294#endif
Benny Prijono140beae2009-10-11 05:06:43 +00003295 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003296
3297 case PJSIP_INV_STATE_DISCONNECTED:
3298 st_code = e->body.tsx_state.tsx->status_code;
3299 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3300 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003301
Benny Prijono8b1889b2006-06-06 18:40:40 +00003302 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00003303 /* Nothing to do. Just to keep gcc from complaining about
3304 * unused enums.
3305 */
3306 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003307 }
3308
3309 if (st_code != -1) {
3310 pjsip_tx_data *tdata;
3311 pj_status_t status;
3312
Benny Prijonoa91a0032006-02-26 21:23:45 +00003313 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00003314 ev_state, st_code,
3315 NULL, &tdata);
3316 if (status != PJ_SUCCESS) {
3317 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
3318 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003319 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00003320 if (status != PJ_SUCCESS) {
3321 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
3322 }
3323 }
3324 }
3325 }
3326
Benny Prijono84126ab2006-02-09 09:30:09 +00003327
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003328 if (pjsua_var.ua_cfg.cb.on_call_state)
3329 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003330
3331 /* call->inv may be NULL now */
3332
Benny Prijono84126ab2006-02-09 09:30:09 +00003333 /* Destroy media session when invite session is disconnected. */
3334 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003335
Benny Prijonoa91a0032006-02-26 21:23:45 +00003336 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003337
Benny Prijono275fd682006-03-22 11:59:11 +00003338 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003339 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003340
Benny Prijono105217f2006-03-06 16:25:59 +00003341 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003342 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003343 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003344
3345 /* Reset call */
3346 reset_call(call->index);
3347
Benny Prijono84126ab2006-02-09 09:30:09 +00003348 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003349
3350 PJSUA_UNLOCK();
3351}
3352
3353/*
3354 * This callback is called by invite session framework when UAC session
3355 * has forked.
3356 */
3357static void pjsua_call_on_forked( pjsip_inv_session *inv,
3358 pjsip_event *e)
3359{
3360 PJ_UNUSED_ARG(inv);
3361 PJ_UNUSED_ARG(e);
3362
3363 PJ_TODO(HANDLE_FORKED_DIALOG);
3364}
3365
3366
3367/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003368 * Callback from UA layer when forked dialog response is received.
3369 */
3370pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3371{
3372 if (dlg->uac_has_2xx &&
3373 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3374 pjsip_rdata_get_tsx(res) == NULL &&
3375 res->msg_info.msg->line.status.code/100 == 2)
3376 {
3377 pjsip_dialog *forked_dlg;
3378 pjsip_tx_data *bye;
3379 pj_status_t status;
3380
3381 /* Create forked dialog */
3382 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3383 if (status != PJ_SUCCESS)
3384 return NULL;
3385
3386 pjsip_dlg_inc_lock(forked_dlg);
3387
3388 /* Disconnect the call */
3389 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3390 -1, &bye);
3391 if (status == PJ_SUCCESS) {
3392 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3393 }
3394
3395 pjsip_dlg_dec_lock(forked_dlg);
3396
3397 if (status != PJ_SUCCESS) {
3398 return NULL;
3399 }
3400
3401 return forked_dlg;
3402
3403 } else {
3404 return dlg;
3405 }
3406}
3407
3408/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003409 * Disconnect call upon error.
3410 */
3411static void call_disconnect( pjsip_inv_session *inv,
3412 int code )
3413{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003414 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003415 pjsip_tx_data *tdata;
3416 pj_status_t status;
3417
Benny Prijono59b3aed2008-01-15 16:54:54 +00003418 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3419
Benny Prijonoa38ada02006-07-02 14:22:35 +00003420 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003421 if (status != PJ_SUCCESS)
3422 return;
3423
3424 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003425 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3426 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3427 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003428 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003429 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003430
Benny Prijono734fc2d2008-03-17 16:05:35 +00003431 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003432 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003433 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003434 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003435 if (status == PJ_SUCCESS) {
3436 pjsip_create_sdp_body(tdata->pool, local_sdp,
3437 &tdata->msg->body);
3438 }
3439 }
3440
3441 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003442}
3443
3444/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003445 * Callback to be called when SDP offer/answer negotiation has just completed
3446 * in the session. This function will start/update media if negotiation
3447 * has succeeded.
3448 */
3449static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3450 pj_status_t status)
3451{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003452 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003453 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003454 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003455 const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003456
3457 PJSUA_LOCK();
3458
Benny Prijonoa1e69682007-05-11 15:14:34 +00003459 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003460
3461 if (status != PJ_SUCCESS) {
3462
3463 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3464
Benny Prijono2331d202008-06-26 15:46:52 +00003465 /* Do not deinitialize media since this may be a re-INVITE or
3466 * UPDATE (which in this case the media should not get affected
3467 * by the failed re-INVITE/UPDATE). The media will be shutdown
3468 * when call is disconnected anyway.
3469 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003470 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003471 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003472
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003473 /* Disconnect call if we're not in the middle of initializing an
3474 * UAS dialog and if this is not a re-INVITE
3475 */
3476 if (inv->state != PJSIP_INV_STATE_NULL &&
3477 inv->state != PJSIP_INV_STATE_CONFIRMED)
3478 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003479 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003480 }
3481
3482 PJSUA_UNLOCK();
3483 return;
3484 }
3485
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003486
3487 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003488 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003489 if (status != PJ_SUCCESS) {
3490 pjsua_perror(THIS_FILE,
3491 "Unable to retrieve currently active local SDP",
3492 status);
3493 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3494 PJSUA_UNLOCK();
3495 return;
3496 }
3497
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003498 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3499 if (status != PJ_SUCCESS) {
3500 pjsua_perror(THIS_FILE,
3501 "Unable to retrieve currently active remote SDP",
3502 status);
3503 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3504 PJSUA_UNLOCK();
3505 return;
3506 }
3507
Benny Prijono91a6a172007-10-31 08:59:29 +00003508 /* Update remote's NAT type */
3509 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3510 update_remote_nat_type(call, remote_sdp);
3511 }
3512
3513 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003514 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003515 if (status != PJ_SUCCESS) {
3516 pjsua_perror(THIS_FILE, "Unable to create media session",
3517 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003518 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003519 /* No need to deinitialize; media will be shutdown when call
3520 * state is disconnected anyway.
3521 */
3522 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003523 PJSUA_UNLOCK();
3524 return;
3525 }
3526
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003527 /* Ticket #476, handle the case of early media and remote support UPDATE */
3528 if (inv->state == PJSIP_INV_STATE_EARLY &&
3529 pjmedia_sdp_neg_was_answer_remote(inv->neg) &&
3530 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)==
3531 PJSIP_DIALOG_CAP_SUPPORTED)
3532 {
3533 status = lock_codec(call);
3534 if (status != PJ_SUCCESS) {
3535 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
3536 }
3537 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003538
3539 /* Call application callback, if any */
3540 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3541 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3542
3543
3544 PJSUA_UNLOCK();
3545}
3546
3547
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003548/* Create SDP for call hold. */
3549static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3550 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003551{
3552 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003553 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003554 pjmedia_sdp_session *sdp;
3555
Benny Prijono40d62b62009-08-12 17:53:47 +00003556 /* Use call's provisional pool */
3557 pool = call->inv->pool_prov;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003558
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003559 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003560 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3561 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003562 if (status != PJ_SUCCESS) {
3563 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3564 return status;
3565 }
3566
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003567 /* Call-hold is done by set the media direction to 'sendonly'
3568 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3569 * 'inactive' (PJMEDIA_DIR_NONE).
3570 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3571 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003572 /* http://trac.pjsip.org/repos/ticket/880
3573 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3574 */
3575 if (1) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003576 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003577
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003578 /* Remove existing directions attributes */
3579 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3580 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3581 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3582 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003583
Benny Prijonoe0860132009-06-05 10:14:20 +00003584 if (call->media_dir & PJMEDIA_DIR_ENCODING) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003585 /* Add sendonly attribute */
3586 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3587 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3588 } else {
3589 /* Add inactive attribute */
3590 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3591 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3592 }
3593 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003594
3595 *p_answer = sdp;
3596
3597 return status;
3598}
3599
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003600/*
3601 * Called when session received new offer.
3602 */
3603static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3604 const pjmedia_sdp_session *offer)
3605{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003606 pjsua_call *call;
3607 pjmedia_sdp_conn *conn;
3608 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003609 pj_status_t status;
3610
3611 PJSUA_LOCK();
3612
Benny Prijonoa1e69682007-05-11 15:14:34 +00003613 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003614
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003615 conn = offer->media[0]->conn;
3616 if (!conn)
3617 conn = offer->conn;
3618
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003619 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003620 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3621 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003622
Benny Prijono40d62b62009-08-12 17:53:47 +00003623 status = pjsua_media_channel_create_sdp(call->index,
3624 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003625 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003626 if (status != PJ_SUCCESS) {
3627 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3628 PJSUA_UNLOCK();
3629 return;
3630 }
3631
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003632 /* Check if offer's conn address is zero */
3633 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3634 pj_strcmp2(&conn->addr, "0")==0)
3635 {
3636 /* Modify address */
3637 answer->conn->addr = pj_str("0.0.0.0");
3638 }
3639
3640 /* Check if call is on-hold */
3641 if (call->local_hold) {
3642 pjmedia_sdp_attr *attr;
3643
3644 /* Remove existing directions attributes */
3645 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3646 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3647 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3648 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3649
3650 /* Keep call on-hold by setting 'sendonly' attribute.
3651 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3652 */
Benny Prijono40d62b62009-08-12 17:53:47 +00003653 attr = pjmedia_sdp_attr_create(call->inv->pool_prov, "sendonly", NULL);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003654 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3655 }
3656
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003657 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3658 if (status != PJ_SUCCESS) {
3659 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3660 PJSUA_UNLOCK();
3661 return;
3662 }
3663
3664 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003665}
3666
3667
3668/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003669 * Called to generate new offer.
3670 */
3671static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3672 pjmedia_sdp_session **offer)
3673{
3674 pjsua_call *call;
3675 pj_status_t status;
3676
3677 PJSUA_LOCK();
3678
3679 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3680
3681 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003682 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003683 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003684 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003685 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003686 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003687 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003688 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3689 call->index));
3690
Benny Prijono40d62b62009-08-12 17:53:47 +00003691 status = pjsua_media_channel_create_sdp(call->index,
3692 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003693 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003694 }
3695
3696 if (status != PJ_SUCCESS) {
3697 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3698 PJSUA_UNLOCK();
3699 return;
3700 }
3701
Benny Prijono77998ce2007-06-20 10:03:46 +00003702 PJSUA_UNLOCK();
3703}
3704
3705
3706/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003707 * Callback called by event framework when the xfer subscription state
3708 * has changed.
3709 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003710static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3711{
3712
3713 PJ_UNUSED_ARG(event);
3714
3715 /*
3716 * When subscription is accepted (got 200/OK to REFER), check if
3717 * subscription suppressed.
3718 */
3719 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3720
3721 pjsip_rx_data *rdata;
3722 pjsip_generic_string_hdr *refer_sub;
3723 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3724 pjsua_call *call;
3725
Benny Prijonoa1e69682007-05-11 15:14:34 +00003726 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003727
3728 /* Must be receipt of response message */
3729 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3730 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3731 rdata = event->body.tsx_state.src.rdata;
3732
3733 /* Find Refer-Sub header */
3734 refer_sub = (pjsip_generic_string_hdr*)
3735 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3736 &REFER_SUB, NULL);
3737
3738 /* Check if subscription is suppressed */
3739 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3740 /* Since no subscription is desired, assume that call has been
3741 * transfered successfully.
3742 */
3743 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3744 const pj_str_t ACCEPTED = { "Accepted", 8 };
3745 pj_bool_t cont = PJ_FALSE;
3746 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3747 200,
3748 &ACCEPTED,
3749 PJ_TRUE,
3750 &cont);
3751 }
3752
3753 /* Yes, subscription is suppressed.
3754 * Terminate our subscription now.
3755 */
3756 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3757 "event subcription..."));
3758 pjsip_evsub_terminate(sub, PJ_TRUE);
3759
3760 } else {
3761 /* Notify application about call transfer progress.
3762 * Initially notify with 100/Accepted status.
3763 */
3764 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3765 const pj_str_t ACCEPTED = { "Accepted", 8 };
3766 pj_bool_t cont = PJ_FALSE;
3767 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3768 100,
3769 &ACCEPTED,
3770 PJ_FALSE,
3771 &cont);
3772 }
3773 }
3774 }
3775 /*
3776 * On incoming NOTIFY, notify application about call transfer progress.
3777 */
3778 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3779 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3780 {
3781 pjsua_call *call;
3782 pjsip_msg *msg;
3783 pjsip_msg_body *body;
3784 pjsip_status_line status_line;
3785 pj_bool_t is_last;
3786 pj_bool_t cont;
3787 pj_status_t status;
3788
Benny Prijonoa1e69682007-05-11 15:14:34 +00003789 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003790
3791 /* When subscription is terminated, clear the xfer_sub member of
3792 * the inv_data.
3793 */
3794 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3795 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3796 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3797
3798 }
3799
3800 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3801 /* Application is not interested with call progress status */
3802 return;
3803 }
3804
3805 /* This better be a NOTIFY request */
3806 if (event->type == PJSIP_EVENT_TSX_STATE &&
3807 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3808 {
3809 pjsip_rx_data *rdata;
3810
3811 rdata = event->body.tsx_state.src.rdata;
3812
3813 /* Check if there's body */
3814 msg = rdata->msg_info.msg;
3815 body = msg->body;
3816 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003817 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003818 "Warning: received NOTIFY without message body"));
3819 return;
3820 }
3821
3822 /* Check for appropriate content */
3823 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3824 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3825 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003826 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003827 "Warning: received NOTIFY with non message/sipfrag "
3828 "content"));
3829 return;
3830 }
3831
3832 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003833 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003834 &status_line);
3835 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003836 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003837 "Warning: received NOTIFY with invalid "
3838 "message/sipfrag content"));
3839 return;
3840 }
3841
3842 } else {
3843 status_line.code = 500;
3844 status_line.reason = *pjsip_get_status_text(500);
3845 }
3846
3847 /* Notify application */
3848 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3849 cont = !is_last;
3850 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3851 status_line.code,
3852 &status_line.reason,
3853 is_last, &cont);
3854
3855 if (!cont) {
3856 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3857 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003858
3859 /* If the call transfer has completed but the subscription is
3860 * not terminated, terminate it now.
3861 */
3862 if (status_line.code/100 == 2 && !is_last) {
3863 pjsip_tx_data *tdata;
3864
3865 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3866 0, &tdata);
3867 if (status == PJ_SUCCESS)
3868 status = pjsip_evsub_send_request(sub, tdata);
3869 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003870 }
3871}
3872
3873
3874/*
3875 * Callback called by event framework when the xfer subscription state
3876 * has changed.
3877 */
3878static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003879{
3880
3881 PJ_UNUSED_ARG(event);
3882
3883 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003884 * When subscription is terminated, clear the xfer_sub member of
3885 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003886 */
3887 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003888 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003889
Benny Prijonoa1e69682007-05-11 15:14:34 +00003890 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003891 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003892 return;
3893
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003894 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003895 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003896
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003897 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003898 }
3899}
3900
3901
3902/*
3903 * Follow transfer (REFER) request.
3904 */
3905static void on_call_transfered( pjsip_inv_session *inv,
3906 pjsip_rx_data *rdata )
3907{
3908 pj_status_t status;
3909 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003910 pjsua_call *existing_call;
3911 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003912 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003913 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003914 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003915 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003916 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003917 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003918 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003919 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003920 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003921 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003922 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003923 pjsip_evsub *sub;
3924
Benny Prijonoa1e69682007-05-11 15:14:34 +00003925 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003926
Benny Prijono26ff9062006-02-21 23:47:00 +00003927 /* Find the Refer-To header */
3928 refer_to = (pjsip_generic_string_hdr*)
3929 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3930
3931 if (refer_to == NULL) {
3932 /* Invalid Request.
3933 * No Refer-To header!
3934 */
3935 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003936 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003937 return;
3938 }
3939
Benny Prijonoc8141a82006-08-20 09:12:19 +00003940 /* Find optional Refer-Sub header */
3941 refer_sub = (pjsip_generic_string_hdr*)
3942 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3943
3944 if (refer_sub) {
3945 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3946 no_refer_sub = PJ_TRUE;
3947 }
3948
Benny Prijono053f5222006-11-11 16:16:04 +00003949 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3950 * request.
3951 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003952 ref_by_hdr = (pjsip_hdr*)
3953 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003954 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003955
Benny Prijono9fc735d2006-05-28 14:58:12 +00003956 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003957 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003958 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3959 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3960 &refer_to->hvalue,
3961 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003962
3963 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003964 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003965 if (code >= 300) {
3966 /* Application rejects call transfer request */
3967 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3968 return;
3969 }
3970
Benny Prijono26ff9062006-02-21 23:47:00 +00003971 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3972 (int)inv->dlg->remote.info_str.slen,
3973 inv->dlg->remote.info_str.ptr,
3974 (int)refer_to->hvalue.slen,
3975 refer_to->hvalue.ptr));
3976
Benny Prijonoc8141a82006-08-20 09:12:19 +00003977 if (no_refer_sub) {
3978 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003979 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003980 */
3981 pjsip_tx_data *tdata;
3982 const pj_str_t str_false = { "false", 5};
3983 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003984
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003985 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3986 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003987 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003988 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00003989 status);
3990 return;
3991 }
Benny Prijono26ff9062006-02-21 23:47:00 +00003992
Benny Prijonoc8141a82006-08-20 09:12:19 +00003993 /* Add Refer-Sub header */
3994 hdr = (pjsip_hdr*)
3995 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
3996 &str_false);
3997 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00003998
Benny Prijono26ff9062006-02-21 23:47:00 +00003999
Benny Prijonoc8141a82006-08-20 09:12:19 +00004000 /* Send answer */
4001 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
4002 tdata);
4003 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004004 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00004005 status);
4006 return;
4007 }
4008
4009 /* Don't have subscription */
4010 sub = NULL;
4011
4012 } else {
4013 struct pjsip_evsub_user xfer_cb;
4014 pjsip_hdr hdr_list;
4015
4016 /* Init callback */
4017 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004018 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004019
4020 /* Init additional header list to be sent with REFER response */
4021 pj_list_init(&hdr_list);
4022
4023 /* Create transferee event subscription */
4024 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
4025 if (status != PJ_SUCCESS) {
4026 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
4027 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
4028 return;
4029 }
4030
4031 /* If there's Refer-Sub header and the value is "true", send back
4032 * Refer-Sub in the response with value "true" too.
4033 */
4034 if (refer_sub) {
4035 const pj_str_t str_true = { "true", 4 };
4036 pjsip_hdr *hdr;
4037
4038 hdr = (pjsip_hdr*)
4039 pjsip_generic_string_hdr_create(inv->dlg->pool,
4040 &str_refer_sub,
4041 &str_true);
4042 pj_list_push_back(&hdr_list, hdr);
4043
4044 }
4045
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004046 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00004047 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
4048
4049 /* Create initial NOTIFY request */
4050 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
4051 100, NULL, &tdata);
4052 if (status != PJ_SUCCESS) {
4053 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4054 status);
4055 return;
4056 }
4057
4058 /* Send initial NOTIFY request */
4059 status = pjsip_xfer_send_request( sub, tdata);
4060 if (status != PJ_SUCCESS) {
4061 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
4062 return;
4063 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004064 }
4065
4066 /* We're cheating here.
4067 * We need to get a null terminated string from a pj_str_t.
4068 * So grab the pointer from the hvalue and NULL terminate it, knowing
4069 * that the NULL position will be occupied by a newline.
4070 */
4071 uri = refer_to->hvalue.ptr;
4072 uri[refer_to->hvalue.slen] = '\0';
4073
Benny Prijono053f5222006-11-11 16:16:04 +00004074 /* Init msg_data */
4075 pjsua_msg_data_init(&msg_data);
4076
4077 /* If Referred-By header is present in the REFER request, copy this
4078 * to the outgoing INVITE request.
4079 */
4080 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00004081 pjsip_hdr *dup = (pjsip_hdr*)
4082 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00004083 pj_list_push_back(&msg_data.hdr_list, dup);
4084 }
4085
Benny Prijono26ff9062006-02-21 23:47:00 +00004086 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00004087 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004088 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00004089 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004090 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00004091 if (status != PJ_SUCCESS) {
4092
Benny Prijonoc8141a82006-08-20 09:12:19 +00004093 /* Notify xferer about the error (if we have subscription) */
4094 if (sub) {
4095 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
4096 500, NULL, &tdata);
4097 if (status != PJ_SUCCESS) {
4098 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4099 status);
4100 return;
4101 }
4102 status = pjsip_xfer_send_request(sub, tdata);
4103 if (status != PJ_SUCCESS) {
4104 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
4105 status);
4106 return;
4107 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004108 }
4109 return;
4110 }
4111
Benny Prijonoc8141a82006-08-20 09:12:19 +00004112 if (sub) {
4113 /* Put the server subscription in inv_data.
4114 * Subsequent state changed in pjsua_inv_on_state_changed() will be
4115 * reported back to the server subscription.
4116 */
4117 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00004118
Benny Prijonoc8141a82006-08-20 09:12:19 +00004119 /* Put the invite_data in the subscription. */
4120 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
4121 &pjsua_var.calls[new_call]);
4122 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004123}
4124
4125
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004126
Benny Prijono26ff9062006-02-21 23:47:00 +00004127/*
4128 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00004129 * session. We use this to trap:
4130 * - incoming REFER request.
4131 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00004132 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004133static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
4134 pjsip_transaction *tsx,
4135 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00004136{
Benny Prijono2285e7e2008-12-17 14:28:18 +00004137 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004138
4139 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00004140
Benny Prijono2285e7e2008-12-17 14:28:18 +00004141 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
4142
4143 if (call == NULL) {
4144 PJSUA_UNLOCK();
4145 return;
4146 }
4147
Benny Prijono19eeb6e2009-06-04 22:16:47 +00004148 if (call->inv == NULL) {
4149 /* Shouldn't happen. It happens only when we don't terminate the
4150 * server subscription caused by REFER after the call has been
4151 * transfered (and this call has been disconnected), and we
4152 * receive another REFER for this call.
4153 */
4154 PJSUA_UNLOCK();
4155 return;
4156 }
4157
Benny Prijonofeb69f42007-10-05 09:12:26 +00004158 /* Notify application callback first */
4159 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
4160 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
4161 }
4162
Benny Prijono26ff9062006-02-21 23:47:00 +00004163 if (tsx->role==PJSIP_ROLE_UAS &&
4164 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00004165 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004166 {
4167 /*
4168 * Incoming REFER request.
4169 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004170 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00004171
Benny Prijono26ff9062006-02-21 23:47:00 +00004172 }
Benny Prijonob0808372006-03-02 21:18:58 +00004173 else if (tsx->role==PJSIP_ROLE_UAS &&
4174 tsx->state==PJSIP_TSX_STATE_TRYING &&
4175 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
4176 {
4177 /*
4178 * Incoming MESSAGE request!
4179 */
4180 pjsip_rx_data *rdata;
4181 pjsip_msg *msg;
4182 pjsip_accept_hdr *accept_hdr;
4183 pj_status_t status;
4184
4185 rdata = e->body.tsx_state.src.rdata;
4186 msg = rdata->msg_info.msg;
4187
4188 /* Request MUST have message body, with Content-Type equal to
4189 * "text/plain".
4190 */
4191 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
4192
4193 pjsip_hdr hdr_list;
4194
4195 pj_list_init(&hdr_list);
4196 pj_list_push_back(&hdr_list, accept_hdr);
4197
4198 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
4199 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004200 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00004201 return;
4202 }
4203
4204 /* Respond with 200 first, so that remote doesn't retransmit in case
4205 * the UI takes too long to process the message.
4206 */
4207 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
4208
4209 /* Process MESSAGE request */
4210 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
4211 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004212
Benny Prijonob0808372006-03-02 21:18:58 +00004213 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004214 else if (tsx->role == PJSIP_ROLE_UAC &&
4215 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004216 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004217 /* Handle outgoing pager status */
4218 if (tsx->status_code >= 200) {
4219 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00004220
Benny Prijonoa1e69682007-05-11 15:14:34 +00004221 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004222 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00004223
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004224 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
4225 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
4226 &im_data->to,
4227 &im_data->body,
4228 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00004229 (pjsip_status_code)
4230 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004231 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00004232 }
Benny Prijonofccab712006-02-22 22:23:22 +00004233 }
Benny Prijono834aee32006-02-19 01:38:06 +00004234 }
Benny Prijono834aee32006-02-19 01:38:06 +00004235
Benny Prijono26ff9062006-02-21 23:47:00 +00004236
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004237 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00004238}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004239
4240
4241/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00004242static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
4243 const pjsip_uri *target,
4244 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004245{
4246 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00004247 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004248
4249 PJSUA_LOCK();
4250
4251 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00004252 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
4253 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004254 } else {
4255 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
4256 "(callback not implemented by application). Disconnecting "
4257 "call.",
4258 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00004259 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004260 }
4261
4262 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00004263
4264 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004265}
4266