blob: ed5eb100321602c2d8b92ecb92374902425d1049 [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{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001133 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001134 pjsua_call *call = NULL;
1135 pj_bool_t has_pjsua_lock = PJ_FALSE;
1136 pj_status_t status = PJ_SUCCESS;
Sauw Ming844c1c92010-09-07 05:12:02 +00001137 pj_time_val time_start, timeout;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001138
Sauw Ming844c1c92010-09-07 05:12:02 +00001139 pj_gettimeofday(&time_start);
1140 timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT;
1141 pj_time_val_normalize(&timeout);
1142
1143 for (retry=0; ; ++retry) {
1144
1145 if (retry % 10 == 9) {
1146 pj_time_val dtime;
1147
1148 pj_gettimeofday(&dtime);
1149 PJ_TIME_VAL_SUB(dtime, time_start);
1150 if (!PJ_TIME_VAL_LT(dtime, timeout))
1151 break;
1152 }
Benny Prijono148c9dd2006-09-19 13:37:53 +00001153
1154 has_pjsua_lock = PJ_FALSE;
1155
1156 status = PJSUA_TRY_LOCK();
1157 if (status != PJ_SUCCESS) {
1158 pj_thread_sleep(retry/10);
1159 continue;
1160 }
1161
1162 has_pjsua_lock = PJ_TRUE;
1163 call = &pjsua_var.calls[call_id];
1164
1165 if (call->inv == NULL) {
1166 PJSUA_UNLOCK();
1167 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1168 return PJSIP_ESESSIONTERMINATED;
1169 }
1170
1171 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1172 if (status != PJ_SUCCESS) {
1173 PJSUA_UNLOCK();
1174 pj_thread_sleep(retry/10);
1175 continue;
1176 }
1177
1178 PJSUA_UNLOCK();
1179
1180 break;
1181 }
1182
1183 if (status != PJ_SUCCESS) {
1184 if (has_pjsua_lock == PJ_FALSE)
1185 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1186 "(possibly system has deadlocked) in %s",
1187 title));
1188 else
1189 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1190 "(possibly system has deadlocked) in %s",
1191 title));
1192 return PJ_ETIMEDOUT;
1193 }
1194
1195 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001196 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001197
1198 return PJ_SUCCESS;
1199}
1200
1201
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001202/*
1203 * Get the conference port identification associated with the call.
1204 */
1205PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1206{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001207 pjsua_call *call;
1208 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001209 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001210 pj_status_t status;
1211
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001212 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1213 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001214
Benny Prijonodc752ca2006-09-22 16:55:42 +00001215 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001216 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001217 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001218
1219 port_id = call->conf_slot;
1220
Benny Prijonodc752ca2006-09-22 16:55:42 +00001221 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001222
1223 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001224}
1225
1226
Benny Prijono148c9dd2006-09-19 13:37:53 +00001227
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001228/*
1229 * Obtain detail information about the specified call.
1230 */
1231PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1232 pjsua_call_info *info)
1233{
1234 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001235 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001236 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001237
1238 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1239 PJ_EINVAL);
1240
Benny Prijonoac623b32006-07-03 15:19:31 +00001241 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001242
Benny Prijonodc752ca2006-09-22 16:55:42 +00001243 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001244 if (status != PJ_SUCCESS) {
1245 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001246 }
1247
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001248 /* id and role */
1249 info->id = call_id;
1250 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001251 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001252
1253 /* local info */
1254 info->local_info.ptr = info->buf_.local_info;
1255 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1256 sizeof(info->buf_.local_info));
1257
1258 /* local contact */
1259 info->local_contact.ptr = info->buf_.local_contact;
1260 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1261 call->inv->dlg->local.contact->uri,
1262 info->local_contact.ptr,
1263 sizeof(info->buf_.local_contact));
1264
1265 /* remote info */
1266 info->remote_info.ptr = info->buf_.remote_info;
1267 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1268 sizeof(info->buf_.remote_info));
1269
1270 /* remote contact */
1271 if (call->inv->dlg->remote.contact) {
1272 int len;
1273 info->remote_contact.ptr = info->buf_.remote_contact;
1274 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1275 call->inv->dlg->remote.contact->uri,
1276 info->remote_contact.ptr,
1277 sizeof(info->buf_.remote_contact));
1278 if (len < 0) len = 0;
1279 info->remote_contact.slen = len;
1280 } else {
1281 info->remote_contact.slen = 0;
1282 }
1283
1284 /* call id */
1285 info->call_id.ptr = info->buf_.call_id;
1286 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1287 sizeof(info->buf_.call_id));
1288
1289 /* state, state_text */
1290 info->state = call->inv->state;
1291 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1292
1293 /* If call is disconnected, set the last_status from the cause code */
1294 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1295 /* last_status, last_status_text */
1296 info->last_status = call->inv->cause;
1297
1298 info->last_status_text.ptr = info->buf_.last_status_text;
1299 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1300 sizeof(info->buf_.last_status_text));
1301 } else {
1302 /* last_status, last_status_text */
1303 info->last_status = call->last_code;
1304
1305 info->last_status_text.ptr = info->buf_.last_status_text;
1306 pj_strncpy(&info->last_status_text, &call->last_text,
1307 sizeof(info->buf_.last_status_text));
1308 }
1309
1310 /* media status and dir */
1311 info->media_status = call->media_st;
1312 info->media_dir = call->media_dir;
1313
1314
1315 /* conference slot number */
1316 info->conf_slot = call->conf_slot;
1317
1318 /* calculate duration */
1319 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1320
1321 info->total_duration = call->dis_time;
1322 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1323
1324 if (call->conn_time.sec) {
1325 info->connect_duration = call->dis_time;
1326 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1327 }
1328
1329 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1330
1331 pj_gettimeofday(&info->total_duration);
1332 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1333
1334 pj_gettimeofday(&info->connect_duration);
1335 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1336
1337 } else {
1338 pj_gettimeofday(&info->total_duration);
1339 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1340 }
1341
Benny Prijonodc752ca2006-09-22 16:55:42 +00001342 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001343
1344 return PJ_SUCCESS;
1345}
1346
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001347/*
1348 * Check if call remote peer support the specified capability.
1349 */
1350PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1351 pjsua_call_id call_id,
1352 int htype,
1353 const pj_str_t *hname,
1354 const pj_str_t *token)
1355{
1356 pjsua_call *call;
1357 pjsip_dialog *dlg;
1358 pj_status_t status;
1359 pjsip_dialog_cap_status cap_status;
1360
1361 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1362 if (status != PJ_SUCCESS)
1363 return PJSIP_DIALOG_CAP_UNKNOWN;
1364
1365 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1366
1367 pjsip_dlg_dec_lock(dlg);
1368
1369 return cap_status;
1370}
1371
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001372
1373/*
1374 * Attach application specific data to the call.
1375 */
1376PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1377 void *user_data)
1378{
1379 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1380 PJ_EINVAL);
1381 pjsua_var.calls[call_id].user_data = user_data;
1382
1383 return PJ_SUCCESS;
1384}
1385
1386
1387/*
1388 * Get user data attached to the call.
1389 */
1390PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1391{
1392 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1393 NULL);
1394 return pjsua_var.calls[call_id].user_data;
1395}
1396
1397
1398/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001399 * Get remote's NAT type.
1400 */
1401PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1402 pj_stun_nat_type *p_type)
1403{
1404 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1405 PJ_EINVAL);
1406 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1407
1408 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1409 return PJ_SUCCESS;
1410}
1411
1412
1413/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001414 * Send response to incoming INVITE request.
1415 */
1416PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1417 unsigned code,
1418 const pj_str_t *reason,
1419 const pjsua_msg_data *msg_data)
1420{
1421 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001422 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001423 pjsip_tx_data *tdata;
1424 pj_status_t status;
1425
1426 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1427 PJ_EINVAL);
1428
Benny Prijonodc752ca2006-09-22 16:55:42 +00001429 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001430 if (status != PJ_SUCCESS)
1431 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001432
Benny Prijono2e507c22006-06-23 15:04:11 +00001433 if (call->res_time.sec == 0)
1434 pj_gettimeofday(&call->res_time);
1435
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001436 if (reason && reason->slen == 0)
1437 reason = NULL;
1438
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001439 /* Create response message */
1440 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1441 if (status != PJ_SUCCESS) {
1442 pjsua_perror(THIS_FILE, "Error creating response",
1443 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001444 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001445 return status;
1446 }
1447
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001448 /* Call might have been disconnected if application is answering with
1449 * 200/OK and the media failed to start.
1450 */
1451 if (call->inv == NULL) {
1452 pjsip_dlg_dec_lock(dlg);
1453 return PJSIP_ESESSIONTERMINATED;
1454 }
1455
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001456 /* Add additional headers etc */
1457 pjsua_process_msg_data( tdata, msg_data);
1458
1459 /* Send the message */
1460 status = pjsip_inv_send_msg(call->inv, tdata);
1461 if (status != PJ_SUCCESS)
1462 pjsua_perror(THIS_FILE, "Error sending response",
1463 status);
1464
Benny Prijonodc752ca2006-09-22 16:55:42 +00001465 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001466
1467 return status;
1468}
1469
1470
1471/*
1472 * Hangup call by using method that is appropriate according to the
1473 * call state.
1474 */
1475PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1476 unsigned code,
1477 const pj_str_t *reason,
1478 const pjsua_msg_data *msg_data)
1479{
1480 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001481 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001482 pj_status_t status;
1483 pjsip_tx_data *tdata;
1484
1485
Benny Prijono148c9dd2006-09-19 13:37:53 +00001486 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1487 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1488 call_id));
1489 }
1490
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001491 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1492 PJ_EINVAL);
1493
Benny Prijonodc752ca2006-09-22 16:55:42 +00001494 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001495 if (status != PJ_SUCCESS)
1496 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001497
1498 if (code==0) {
1499 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1500 code = PJSIP_SC_OK;
1501 else if (call->inv->role == PJSIP_ROLE_UAS)
1502 code = PJSIP_SC_DECLINE;
1503 else
1504 code = PJSIP_SC_REQUEST_TERMINATED;
1505 }
1506
1507 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1508 if (status != PJ_SUCCESS) {
1509 pjsua_perror(THIS_FILE,
1510 "Failed to create end session message",
1511 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001512 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001513 return status;
1514 }
1515
1516 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1517 * as p_tdata when INVITE transaction has not been answered
1518 * with any provisional responses.
1519 */
1520 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001521 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001522 return PJ_SUCCESS;
1523 }
1524
1525 /* Add additional headers etc */
1526 pjsua_process_msg_data( tdata, msg_data);
1527
1528 /* Send the message */
1529 status = pjsip_inv_send_msg(call->inv, tdata);
1530 if (status != PJ_SUCCESS) {
1531 pjsua_perror(THIS_FILE,
1532 "Failed to send end session message",
1533 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001534 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001535 return status;
1536 }
1537
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00001538 /* Stop lock codec timer, if it is active */
1539 if (call->lock_codec.reinv_timer.id) {
1540 pjsip_endpt_cancel_timer(pjsua_var.endpt,
1541 &call->lock_codec.reinv_timer);
1542 call->lock_codec.reinv_timer.id = PJ_FALSE;
1543 }
1544
Benny Prijonodc752ca2006-09-22 16:55:42 +00001545 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001546
1547 return PJ_SUCCESS;
1548}
1549
1550
1551/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001552 * Accept or reject redirection.
1553 */
1554PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1555 pjsip_redirect_op cmd)
1556{
1557 pjsua_call *call;
1558 pjsip_dialog *dlg;
1559 pj_status_t status;
1560
1561 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1562 PJ_EINVAL);
1563
1564 status = acquire_call("pjsua_call_process_redirect()", call_id,
1565 &call, &dlg);
1566 if (status != PJ_SUCCESS)
1567 return status;
1568
1569 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1570
1571 pjsip_dlg_dec_lock(dlg);
1572
1573 return status;
1574}
1575
1576
1577/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001578 * Put the specified call on hold.
1579 */
1580PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1581 const pjsua_msg_data *msg_data)
1582{
1583 pjmedia_sdp_session *sdp;
1584 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001585 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001586 pjsip_tx_data *tdata;
1587 pj_status_t status;
1588
1589 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1590 PJ_EINVAL);
1591
Benny Prijonodc752ca2006-09-22 16:55:42 +00001592 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001593 if (status != PJ_SUCCESS)
1594 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001595
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001596
1597 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1598 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001599 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001600 return PJSIP_ESESSIONSTATE;
1601 }
1602
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001603 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001604 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001605 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001606 return status;
1607 }
1608
1609 /* Create re-INVITE with new offer */
1610 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1611 if (status != PJ_SUCCESS) {
1612 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001613 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001614 return status;
1615 }
1616
1617 /* Add additional headers etc */
1618 pjsua_process_msg_data( tdata, msg_data);
1619
1620 /* Send the request */
1621 status = pjsip_inv_send_msg( call->inv, tdata);
1622 if (status != PJ_SUCCESS) {
1623 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001624 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001625 return status;
1626 }
1627
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001628 /* Set flag that local put the call on hold */
1629 call->local_hold = PJ_TRUE;
1630
Benny Prijonodc752ca2006-09-22 16:55:42 +00001631 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001632
1633 return PJ_SUCCESS;
1634}
1635
1636
1637/*
1638 * Send re-INVITE (to release hold).
1639 */
1640PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1641 pj_bool_t unhold,
1642 const pjsua_msg_data *msg_data)
1643{
1644 pjmedia_sdp_session *sdp;
1645 pjsip_tx_data *tdata;
1646 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001647 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001648 pj_status_t status;
1649
1650
1651 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1652 PJ_EINVAL);
1653
Benny Prijonodc752ca2006-09-22 16:55:42 +00001654 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001655 if (status != PJ_SUCCESS)
1656 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001657
1658 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1659 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001660 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001661 return PJSIP_ESESSIONSTATE;
1662 }
1663
1664 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001665 if (call->local_hold && !unhold) {
1666 status = create_sdp_of_call_hold(call, &sdp);
1667 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001668 status = pjsua_media_channel_create_sdp(call->index,
1669 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001670 NULL, &sdp, NULL);
1671 call->local_hold = PJ_FALSE;
1672 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001673 if (status != PJ_SUCCESS) {
1674 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1675 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001676 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001677 return status;
1678 }
1679
1680 /* Create re-INVITE with new offer */
1681 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1682 if (status != PJ_SUCCESS) {
1683 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001684 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001685 return status;
1686 }
1687
1688 /* Add additional headers etc */
1689 pjsua_process_msg_data( tdata, msg_data);
1690
1691 /* Send the request */
1692 status = pjsip_inv_send_msg( call->inv, tdata);
1693 if (status != PJ_SUCCESS) {
1694 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001695 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001696 return status;
1697 }
1698
Benny Prijonodc752ca2006-09-22 16:55:42 +00001699 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001700
1701 return PJ_SUCCESS;
1702}
1703
1704
1705/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001706 * Send UPDATE request.
1707 */
1708PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1709 unsigned options,
1710 const pjsua_msg_data *msg_data)
1711{
1712 pjmedia_sdp_session *sdp;
1713 pjsip_tx_data *tdata;
1714 pjsua_call *call;
1715 pjsip_dialog *dlg;
1716 pj_status_t status;
1717
1718 PJ_UNUSED_ARG(options);
1719
1720 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1721 PJ_EINVAL);
1722
1723 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1724 if (status != PJ_SUCCESS)
1725 return status;
1726
Benny Prijonoc08682e2007-10-04 06:17:58 +00001727 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001728 status = pjsua_media_channel_create_sdp(call->index,
1729 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001730 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001731 if (status != PJ_SUCCESS) {
1732 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1733 status);
1734 pjsip_dlg_dec_lock(dlg);
1735 return status;
1736 }
1737
Benny Prijono224b4e22008-06-19 14:10:28 +00001738 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001739 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1740 if (status != PJ_SUCCESS) {
1741 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1742 pjsip_dlg_dec_lock(dlg);
1743 return status;
1744 }
1745
1746 /* Add additional headers etc */
1747 pjsua_process_msg_data( tdata, msg_data);
1748
1749 /* Send the request */
1750 status = pjsip_inv_send_msg( call->inv, tdata);
1751 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001752 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001753 pjsip_dlg_dec_lock(dlg);
1754 return status;
1755 }
1756
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001757 call->local_hold = PJ_FALSE;
1758
Benny Prijonoc08682e2007-10-04 06:17:58 +00001759 pjsip_dlg_dec_lock(dlg);
1760
1761 return PJ_SUCCESS;
1762}
1763
1764
1765/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001766 * Initiate call transfer to the specified address.
1767 */
1768PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1769 const pj_str_t *dest,
1770 const pjsua_msg_data *msg_data)
1771{
1772 pjsip_evsub *sub;
1773 pjsip_tx_data *tdata;
1774 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001775 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001776 pjsip_generic_string_hdr *gs_hdr;
1777 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001778 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001779 pj_status_t status;
1780
1781
1782 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1783 PJ_EINVAL);
1784
Benny Prijonodc752ca2006-09-22 16:55:42 +00001785 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001786 if (status != PJ_SUCCESS)
1787 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001788
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001789
Benny Prijonod524e822006-09-22 12:48:18 +00001790 /* Create xfer client subscription. */
1791 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001792 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001793
1794 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001795 if (status != PJ_SUCCESS) {
1796 pjsua_perror(THIS_FILE, "Unable to create xfer", 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 Prijono4ddad2c2006-10-18 17:16:34 +00001801 /* Associate this call with the client subscription */
1802 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1803
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001804 /*
1805 * Create REFER request.
1806 */
1807 status = pjsip_xfer_initiate(sub, dest, &tdata);
1808 if (status != PJ_SUCCESS) {
1809 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001810 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001811 return status;
1812 }
1813
Benny Prijono053f5222006-11-11 16:16:04 +00001814 /* Add Referred-By header */
1815 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1816 &dlg->local.info_str);
1817 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1818
1819
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001820 /* Add additional headers etc */
1821 pjsua_process_msg_data( tdata, msg_data);
1822
1823 /* Send. */
1824 status = pjsip_xfer_send_request(sub, tdata);
1825 if (status != PJ_SUCCESS) {
1826 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001827 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001828 return status;
1829 }
1830
1831 /* For simplicity (that's what this program is intended to be!),
1832 * leave the original invite session as it is. More advanced application
1833 * may want to hold the INVITE, or terminate the invite, or whatever.
1834 */
1835
Benny Prijonodc752ca2006-09-22 16:55:42 +00001836 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001837
1838 return PJ_SUCCESS;
1839
1840}
1841
1842
1843/*
Benny Prijono053f5222006-11-11 16:16:04 +00001844 * Initiate attended call transfer to the specified address.
1845 */
1846PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1847 pjsua_call_id dest_call_id,
1848 unsigned options,
1849 const pjsua_msg_data *msg_data)
1850{
1851 pjsua_call *dest_call;
1852 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001853 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001854 pj_str_t str_dest;
1855 int len;
1856 pjsip_uri *uri;
1857 pj_status_t status;
1858
1859
1860 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1861 PJ_EINVAL);
1862 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1863 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1864 PJ_EINVAL);
1865
1866 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1867 &dest_call, &dest_dlg);
1868 if (status != PJ_SUCCESS)
1869 return status;
1870
1871 /*
1872 * Create REFER destination URI with Replaces field.
1873 */
1874
1875 /* Make sure we have sufficient buffer's length */
1876 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1877 dest_dlg->call_id->id.slen +
1878 dest_dlg->remote.info->tag.slen +
1879 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001880 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001881
1882 /* Print URI */
1883 str_dest_buf[0] = '<';
1884 str_dest.slen = 1;
1885
Benny Prijonoa1e69682007-05-11 15:14:34 +00001886 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001887 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1888 str_dest_buf+1, sizeof(str_dest_buf)-1);
1889 if (len < 0)
1890 return PJSIP_EURITOOLONG;
1891
1892 str_dest.slen += len;
1893
1894
1895 /* Build the URI */
1896 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1897 sizeof(str_dest_buf) - str_dest.slen,
1898 "?%s"
1899 "Replaces=%.*s"
1900 "%%3Bto-tag%%3D%.*s"
1901 "%%3Bfrom-tag%%3D%.*s>",
1902 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1903 "" : "Require=replaces&"),
1904 (int)dest_dlg->call_id->id.slen,
1905 dest_dlg->call_id->id.ptr,
1906 (int)dest_dlg->remote.info->tag.slen,
1907 dest_dlg->remote.info->tag.ptr,
1908 (int)dest_dlg->local.info->tag.slen,
1909 dest_dlg->local.info->tag.ptr);
1910
1911 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1912 PJSIP_EURITOOLONG);
1913
1914 str_dest.ptr = str_dest_buf;
1915 str_dest.slen += len;
1916
1917 pjsip_dlg_dec_lock(dest_dlg);
1918
1919 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1920}
1921
1922
1923/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001924 * Send DTMF digits to remote using RFC 2833 payload formats.
1925 */
1926PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1927 const pj_str_t *digits)
1928{
1929 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001930 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001931 pj_status_t status;
1932
1933 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1934 PJ_EINVAL);
1935
Benny Prijonodc752ca2006-09-22 16:55:42 +00001936 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001937 if (status != PJ_SUCCESS)
1938 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001939
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001940 if (!call->session) {
1941 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001942 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001943 return PJ_EINVALIDOP;
1944 }
1945
1946 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1947
Benny Prijonodc752ca2006-09-22 16:55:42 +00001948 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001949
1950 return status;
1951}
1952
1953
1954/**
1955 * Send instant messaging inside INVITE session.
1956 */
1957PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1958 const pj_str_t *mime_type,
1959 const pj_str_t *content,
1960 const pjsua_msg_data *msg_data,
1961 void *user_data)
1962{
1963 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001964 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001965 const pj_str_t mime_text_plain = pj_str("text/plain");
1966 pjsip_media_type ctype;
1967 pjsua_im_data *im_data;
1968 pjsip_tx_data *tdata;
1969 pj_status_t status;
1970
1971
1972 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1973 PJ_EINVAL);
1974
Benny Prijonodc752ca2006-09-22 16:55:42 +00001975 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001976 if (status != PJ_SUCCESS)
1977 return status;
1978
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001979 /* Set default media type if none is specified */
1980 if (mime_type == NULL) {
1981 mime_type = &mime_text_plain;
1982 }
1983
1984 /* Create request message. */
1985 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
1986 -1, &tdata);
1987 if (status != PJ_SUCCESS) {
1988 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
1989 goto on_return;
1990 }
1991
1992 /* Add accept header. */
1993 pjsip_msg_add_hdr( tdata->msg,
1994 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
1995
1996 /* Parse MIME type */
1997 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
1998
1999 /* Create "text/plain" message body. */
2000 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
2001 &ctype.subtype, content);
2002 if (tdata->msg->body == NULL) {
2003 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
2004 pjsip_tx_data_dec_ref(tdata);
2005 goto on_return;
2006 }
2007
2008 /* Add additional headers etc */
2009 pjsua_process_msg_data( tdata, msg_data);
2010
2011 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002012 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002013 im_data->acc_id = call->acc_id;
2014 im_data->call_id = call_id;
2015 im_data->to = call->inv->dlg->remote.info_str;
2016 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2017 im_data->user_data = user_data;
2018
2019
2020 /* Send the request. */
2021 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2022 pjsua_var.mod.id, im_data);
2023 if (status != PJ_SUCCESS) {
2024 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2025 goto on_return;
2026 }
2027
2028on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002029 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002030 return status;
2031}
2032
2033
2034/*
2035 * Send IM typing indication inside INVITE session.
2036 */
2037PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2038 pj_bool_t is_typing,
2039 const pjsua_msg_data*msg_data)
2040{
2041 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002042 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002043 pjsip_tx_data *tdata;
2044 pj_status_t status;
2045
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002046 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2047 PJ_EINVAL);
2048
Benny Prijonodc752ca2006-09-22 16:55:42 +00002049 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002050 if (status != PJ_SUCCESS)
2051 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002052
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002053 /* Create request message. */
2054 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2055 -1, &tdata);
2056 if (status != PJ_SUCCESS) {
2057 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2058 goto on_return;
2059 }
2060
2061 /* Create "application/im-iscomposing+xml" msg body. */
2062 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2063 NULL, NULL, -1);
2064
2065 /* Add additional headers etc */
2066 pjsua_process_msg_data( tdata, msg_data);
2067
2068 /* Send the request. */
2069 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2070 if (status != PJ_SUCCESS) {
2071 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2072 goto on_return;
2073 }
2074
2075on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002076 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002077 return status;
2078}
2079
2080
2081/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002082 * Send arbitrary request.
2083 */
2084PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2085 const pj_str_t *method_str,
2086 const pjsua_msg_data *msg_data)
2087{
2088 pjsua_call *call;
2089 pjsip_dialog *dlg;
2090 pjsip_method method;
2091 pjsip_tx_data *tdata;
2092 pj_status_t status;
2093
2094 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2095 PJ_EINVAL);
2096
2097 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2098 if (status != PJ_SUCCESS)
2099 return status;
2100
2101 /* Init method */
2102 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2103
2104 /* Create request message. */
2105 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2106 if (status != PJ_SUCCESS) {
2107 pjsua_perror(THIS_FILE, "Unable to create request", status);
2108 goto on_return;
2109 }
2110
2111 /* Add additional headers etc */
2112 pjsua_process_msg_data( tdata, msg_data);
2113
2114 /* Send the request. */
2115 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2116 if (status != PJ_SUCCESS) {
2117 pjsua_perror(THIS_FILE, "Unable to send request", status);
2118 goto on_return;
2119 }
2120
2121on_return:
2122 pjsip_dlg_dec_lock(dlg);
2123 return status;
2124}
2125
2126
2127/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002128 * Terminate all calls.
2129 */
2130PJ_DEF(void) pjsua_call_hangup_all(void)
2131{
2132 unsigned i;
2133
2134 PJSUA_LOCK();
2135
2136 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2137 if (pjsua_var.calls[i].inv)
2138 pjsua_call_hangup(i, 0, NULL, NULL);
2139 }
2140
2141 PJSUA_UNLOCK();
2142}
2143
2144
Benny Prijono627cbb42007-09-25 20:48:49 +00002145const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002146{
2147 if (val < 1000) {
2148 pj_ansi_sprintf(buf, "%d", val);
2149 } else if (val < 1000000) {
2150 pj_ansi_sprintf(buf, "%d.%dK",
2151 val / 1000,
2152 (val % 1000) / 100);
2153 } else {
2154 pj_ansi_sprintf(buf, "%d.%02dM",
2155 val / 1000000,
2156 (val % 1000000) / 10000);
2157 }
2158
2159 return buf;
2160}
2161
2162
2163/* Dump media session */
2164static void dump_media_session(const char *indent,
2165 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002166 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002167{
2168 unsigned i;
2169 char *p = buf, *end = buf+maxlen;
2170 int len;
2171 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002172 pjmedia_session *session = call->session;
2173 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002174
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002175 pjmedia_transport_info_init(&tp_info);
2176
2177 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002178 pjmedia_session_get_info(session, &info);
2179
2180 for (i=0; i<info.stream_cnt; ++i) {
2181 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002182 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002183 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002184 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002185 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002186 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002187 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002188
2189 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002190 // rem_addr will contain actual address of RTP originator, instead of
2191 // remote RTP address specified by stream which is fetched from the SDP.
2192 // Please note that we are assuming only one stream per call.
2193 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2194 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002195 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2196 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002197 sizeof(rem_addr_buf), 3);
2198 } else {
2199 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002200 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002201 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002202
Benny Prijono2dbf5072010-06-23 12:38:28 +00002203 if (call->media_dir == PJMEDIA_DIR_NONE) {
2204 /* To handle when the stream that is currently being paused
2205 * (http://trac.pjsip.org/repos/ticket/1079)
2206 */
2207 dir = "inactive";
2208 } else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002209 dir = "sendonly";
2210 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2211 dir = "recvonly";
2212 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2213 dir = "sendrecv";
2214 else
2215 dir = "inactive";
2216
2217
2218 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002219 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002220 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002221 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002222 info.stream_info[i].fmt.encoding_name.ptr,
2223 info.stream_info[i].fmt.clock_rate / 1000,
2224 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002225 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002226 if (len < 1 || len > end-p) {
2227 *p = '\0';
2228 return;
2229 }
2230
2231 p += len;
2232 *p++ = '\n';
2233 *p = '\0';
2234
2235 if (stat.rx.update_cnt == 0)
2236 strcpy(last_update, "never");
2237 else {
2238 pj_gettimeofday(&now);
2239 PJ_TIME_VAL_SUB(now, stat.rx.update);
2240 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2241 now.sec / 3600,
2242 (now.sec % 3600) / 60,
2243 now.sec % 60,
2244 now.msec);
2245 }
2246
Benny Prijono80019eb2006-08-07 13:22:23 +00002247 pj_gettimeofday(&media_duration);
2248 PJ_TIME_VAL_SUB(media_duration, stat.start);
2249 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2250 media_duration.msec = 1;
2251
Benny Prijono1402a4a2008-01-08 23:41:22 +00002252 /* protect against division by zero */
2253 if (stat.rx.pkt == 0)
2254 stat.rx.pkt = 1;
2255 if (stat.tx.pkt == 0)
2256 stat.tx.pkt = 1;
2257
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002258 len = pj_ansi_snprintf(p, end-p,
2259 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002260 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002261 "%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 +00002262 "%s (msec) min avg max last dev\n"
2263 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
Nanang Izzuddin78bec1a2010-07-15 14:45:47 +00002264 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f"
2265#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2266 "\n"
2267 "%s raw jitter : %7.3f %7.3f %7.3f %7.3f %7.3f"
2268#endif
2269#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2270 "\n"
2271 "%s IPDV : %7.3f %7.3f %7.3f %7.3f %7.3f"
2272#endif
2273 "%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002274 indent, info.stream_info[i].fmt.pt,
2275 last_update,
2276 indent,
2277 good_number(packets, stat.rx.pkt),
2278 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002279 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002280 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2281 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 +00002282 indent,
2283 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002284 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002285 stat.rx.discard,
2286 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002287 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002288 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002289 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002290 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002291 indent, indent,
2292 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002293 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002294 stat.rx.loss_period.max / 1000.0,
2295 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002296 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002297 indent,
2298 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002299 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002300 stat.rx.jitter.max / 1000.0,
2301 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002302 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Nanang Izzuddin78bec1a2010-07-15 14:45:47 +00002303#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2304 indent,
2305 stat.rx_raw_jitter.min / 1000.0,
2306 stat.rx_raw_jitter.mean / 1000.0,
2307 stat.rx_raw_jitter.max / 1000.0,
2308 stat.rx_raw_jitter.last / 1000.0,
2309 pj_math_stat_get_stddev(&stat.rx_raw_jitter) / 1000.0,
2310#endif
2311#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2312 indent,
2313 stat.rx_ipdv.min / 1000.0,
2314 stat.rx_ipdv.mean / 1000.0,
2315 stat.rx_ipdv.max / 1000.0,
2316 stat.rx_ipdv.last / 1000.0,
2317 pj_math_stat_get_stddev(&stat.rx_ipdv) / 1000.0,
2318#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002319 ""
2320 );
2321
2322 if (len < 1 || len > end-p) {
2323 *p = '\0';
2324 return;
2325 }
2326
2327 p += len;
2328 *p++ = '\n';
2329 *p = '\0';
2330
2331 if (stat.tx.update_cnt == 0)
2332 strcpy(last_update, "never");
2333 else {
2334 pj_gettimeofday(&now);
2335 PJ_TIME_VAL_SUB(now, stat.tx.update);
2336 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2337 now.sec / 3600,
2338 (now.sec % 3600) / 60,
2339 now.sec % 60,
2340 now.msec);
2341 }
2342
2343 len = pj_ansi_snprintf(p, end-p,
2344 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002345 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002346 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002347 "%s (msec) min avg max last dev \n"
2348 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2349 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002350 indent,
2351 info.stream_info[i].tx_pt,
2352 info.stream_info[i].param->info.frm_ptime *
2353 info.stream_info[i].param->setting.frm_per_pkt,
2354 last_update,
2355
2356 indent,
2357 good_number(packets, stat.tx.pkt),
2358 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002359 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002360 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2361 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 +00002362
2363 indent,
2364 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002365 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002366 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002367 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002368 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002369 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002370
2371 indent, indent,
2372 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002373 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002374 stat.tx.loss_period.max / 1000.0,
2375 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002376 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002377 indent,
2378 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002379 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002380 stat.tx.jitter.max / 1000.0,
2381 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002382 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002383 ""
2384 );
2385
2386 if (len < 1 || len > end-p) {
2387 *p = '\0';
2388 return;
2389 }
2390
2391 p += len;
2392 *p++ = '\n';
2393 *p = '\0';
2394
2395 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002396 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002397 indent,
2398 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002399 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002400 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002401 stat.rtt.last / 1000.0,
2402 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002403 );
2404 if (len < 1 || len > end-p) {
2405 *p = '\0';
2406 return;
2407 }
2408
2409 p += len;
2410 *p++ = '\n';
2411 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002412
2413#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2414# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2415 do { \
2416 if (samples <= 4294) \
2417 usec = samples * 1000000 / clock_rate; \
2418 else { \
2419 usec = samples * 1000 / clock_rate; \
2420 usec *= 1000; \
2421 } \
2422 } while(0)
2423
2424# define PRINT_VOIP_MTC_VAL(s, v) \
2425 if (v == 127) \
2426 sprintf(s, "(na)"); \
2427 else \
2428 sprintf(s, "%d", v)
2429
2430# define VALIDATE_PRINT_BUF() \
2431 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2432 p += len; *p++ = '\n'; *p = '\0'
2433
2434
2435 do {
2436 char loss[16], dup[16];
2437 char jitter[80];
2438 char toh[80];
2439 char plc[16], jba[16], jbr[16];
2440 char signal_lvl[16], noise_lvl[16], rerl[16];
2441 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2442 pjmedia_rtcp_xr_stat xr_stat;
2443 unsigned clock_rate;
2444
2445 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2446 PJ_SUCCESS)
2447 {
2448 break;
2449 }
2450
2451 clock_rate = info.stream_info[i].fmt.clock_rate;
2452
2453 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2454 VALIDATE_PRINT_BUF();
2455
2456 /* Statistics Summary */
2457 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2458 VALIDATE_PRINT_BUF();
2459
2460 if (xr_stat.rx.stat_sum.l)
2461 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2462 else
2463 sprintf(loss, "(na)");
2464
2465 if (xr_stat.rx.stat_sum.d)
2466 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2467 else
2468 sprintf(dup, "(na)");
2469
2470 if (xr_stat.rx.stat_sum.j) {
2471 unsigned jmin, jmax, jmean, jdev;
2472
2473 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2474 clock_rate);
2475 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2476 clock_rate);
2477 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2478 clock_rate);
2479 SAMPLES_TO_USEC(jdev,
2480 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2481 clock_rate);
2482 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2483 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2484 } else
2485 sprintf(jitter, "(report not available)");
2486
2487 if (xr_stat.rx.stat_sum.t) {
2488 sprintf(toh, "%11d %11d %11d %11d",
2489 xr_stat.rx.stat_sum.toh.min,
2490 xr_stat.rx.stat_sum.toh.mean,
2491 xr_stat.rx.stat_sum.toh.max,
2492 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2493 } else
2494 sprintf(toh, "(report not available)");
2495
2496 if (xr_stat.rx.stat_sum.update.sec == 0)
2497 strcpy(last_update, "never");
2498 else {
2499 pj_gettimeofday(&now);
2500 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2501 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2502 now.sec / 3600,
2503 (now.sec % 3600) / 60,
2504 now.sec % 60,
2505 now.msec);
2506 }
2507
2508 len = pj_ansi_snprintf(p, end-p,
2509 "%s RX last update: %s\n"
2510 "%s begin seq=%d, end seq=%d\n"
2511 "%s pkt loss=%s, dup=%s\n"
2512 "%s (msec) min avg max dev\n"
2513 "%s jitter : %s\n"
2514 "%s toh : %s",
2515 indent, last_update,
2516 indent,
2517 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2518 indent, loss, dup,
2519 indent,
2520 indent, jitter,
2521 indent, toh
2522 );
2523 VALIDATE_PRINT_BUF();
2524
2525 if (xr_stat.tx.stat_sum.l)
2526 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2527 else
2528 sprintf(loss, "(na)");
2529
2530 if (xr_stat.tx.stat_sum.d)
2531 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2532 else
2533 sprintf(dup, "(na)");
2534
2535 if (xr_stat.tx.stat_sum.j) {
2536 unsigned jmin, jmax, jmean, jdev;
2537
2538 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2539 clock_rate);
2540 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2541 clock_rate);
2542 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2543 clock_rate);
2544 SAMPLES_TO_USEC(jdev,
2545 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2546 clock_rate);
2547 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2548 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2549 } else
2550 sprintf(jitter, "(report not available)");
2551
2552 if (xr_stat.tx.stat_sum.t) {
2553 sprintf(toh, "%11d %11d %11d %11d",
2554 xr_stat.tx.stat_sum.toh.min,
2555 xr_stat.tx.stat_sum.toh.mean,
2556 xr_stat.tx.stat_sum.toh.max,
2557 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2558 } else
2559 sprintf(toh, "(report not available)");
2560
2561 if (xr_stat.tx.stat_sum.update.sec == 0)
2562 strcpy(last_update, "never");
2563 else {
2564 pj_gettimeofday(&now);
2565 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2566 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2567 now.sec / 3600,
2568 (now.sec % 3600) / 60,
2569 now.sec % 60,
2570 now.msec);
2571 }
2572
2573 len = pj_ansi_snprintf(p, end-p,
2574 "%s TX last update: %s\n"
2575 "%s begin seq=%d, end seq=%d\n"
2576 "%s pkt loss=%s, dup=%s\n"
2577 "%s (msec) min avg max dev\n"
2578 "%s jitter : %s\n"
2579 "%s toh : %s",
2580 indent, last_update,
2581 indent,
2582 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2583 indent, loss, dup,
2584 indent,
2585 indent, jitter,
2586 indent, toh
2587 );
2588 VALIDATE_PRINT_BUF();
2589
2590
2591 /* VoIP Metrics */
2592 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2593 VALIDATE_PRINT_BUF();
2594
2595 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2596 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2597 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2598 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2599 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2600 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2601 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2602
2603 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2604 case PJMEDIA_RTCP_XR_PLC_DIS:
2605 sprintf(plc, "DISABLED");
2606 break;
2607 case PJMEDIA_RTCP_XR_PLC_ENH:
2608 sprintf(plc, "ENHANCED");
2609 break;
2610 case PJMEDIA_RTCP_XR_PLC_STD:
2611 sprintf(plc, "STANDARD");
2612 break;
2613 case PJMEDIA_RTCP_XR_PLC_UNK:
2614 default:
2615 sprintf(plc, "UNKNOWN");
2616 break;
2617 }
2618
2619 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2620 case PJMEDIA_RTCP_XR_JB_FIXED:
2621 sprintf(jba, "FIXED");
2622 break;
2623 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2624 sprintf(jba, "ADAPTIVE");
2625 break;
2626 default:
2627 sprintf(jba, "UNKNOWN");
2628 break;
2629 }
2630
2631 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2632
2633 if (xr_stat.rx.voip_mtc.update.sec == 0)
2634 strcpy(last_update, "never");
2635 else {
2636 pj_gettimeofday(&now);
2637 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2638 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2639 now.sec / 3600,
2640 (now.sec % 3600) / 60,
2641 now.sec % 60,
2642 now.msec);
2643 }
2644
2645 len = pj_ansi_snprintf(p, end-p,
2646 "%s RX last update: %s\n"
2647 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2648 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2649 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2650 "%s delay : round trip=%d%s, end system=%d%s\n"
2651 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2652 "%s quality : R factor=%s, ext R factor=%s\n"
2653 "%s MOS LQ=%s, MOS CQ=%s\n"
2654 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2655 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2656 indent,
2657 last_update,
2658 /* packets */
2659 indent,
2660 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2661 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2662 /* burst */
2663 indent,
2664 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2665 xr_stat.rx.voip_mtc.burst_dur, "ms",
2666 /* gap */
2667 indent,
2668 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2669 xr_stat.rx.voip_mtc.gap_dur, "ms",
2670 /* delay */
2671 indent,
2672 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2673 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2674 /* level */
2675 indent,
2676 signal_lvl, "dB",
2677 noise_lvl, "dB",
2678 rerl, "",
2679 /* quality */
2680 indent,
2681 r_factor, ext_r_factor,
2682 indent,
2683 mos_lq, mos_cq,
2684 /* config */
2685 indent,
2686 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2687 /* JB delay */
2688 indent,
2689 xr_stat.rx.voip_mtc.jb_nom, "ms",
2690 xr_stat.rx.voip_mtc.jb_max, "ms",
2691 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2692 );
2693 VALIDATE_PRINT_BUF();
2694
2695 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2696 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2697 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2698 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2699 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2700 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2701 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2702
2703 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2704 case PJMEDIA_RTCP_XR_PLC_DIS:
2705 sprintf(plc, "DISABLED");
2706 break;
2707 case PJMEDIA_RTCP_XR_PLC_ENH:
2708 sprintf(plc, "ENHANCED");
2709 break;
2710 case PJMEDIA_RTCP_XR_PLC_STD:
2711 sprintf(plc, "STANDARD");
2712 break;
2713 case PJMEDIA_RTCP_XR_PLC_UNK:
2714 default:
2715 sprintf(plc, "unknown");
2716 break;
2717 }
2718
2719 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2720 case PJMEDIA_RTCP_XR_JB_FIXED:
2721 sprintf(jba, "FIXED");
2722 break;
2723 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2724 sprintf(jba, "ADAPTIVE");
2725 break;
2726 default:
2727 sprintf(jba, "unknown");
2728 break;
2729 }
2730
2731 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2732
2733 if (xr_stat.tx.voip_mtc.update.sec == 0)
2734 strcpy(last_update, "never");
2735 else {
2736 pj_gettimeofday(&now);
2737 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2738 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2739 now.sec / 3600,
2740 (now.sec % 3600) / 60,
2741 now.sec % 60,
2742 now.msec);
2743 }
2744
2745 len = pj_ansi_snprintf(p, end-p,
2746 "%s TX last update: %s\n"
2747 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2748 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2749 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2750 "%s delay : round trip=%d%s, end system=%d%s\n"
2751 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2752 "%s quality : R factor=%s, ext R factor=%s\n"
2753 "%s MOS LQ=%s, MOS CQ=%s\n"
2754 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2755 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2756 indent,
2757 last_update,
2758 /* pakcets */
2759 indent,
2760 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2761 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2762 /* burst */
2763 indent,
2764 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2765 xr_stat.tx.voip_mtc.burst_dur, "ms",
2766 /* gap */
2767 indent,
2768 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2769 xr_stat.tx.voip_mtc.gap_dur, "ms",
2770 /* delay */
2771 indent,
2772 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2773 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2774 /* level */
2775 indent,
2776 signal_lvl, "dB",
2777 noise_lvl, "dB",
2778 rerl, "",
2779 /* quality */
2780 indent,
2781 r_factor, ext_r_factor,
2782 indent,
2783 mos_lq, mos_cq,
2784 /* config */
2785 indent,
2786 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2787 /* JB delay */
2788 indent,
2789 xr_stat.tx.voip_mtc.jb_nom, "ms",
2790 xr_stat.tx.voip_mtc.jb_max, "ms",
2791 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2792 );
2793 VALIDATE_PRINT_BUF();
2794
2795
2796 /* RTT delay (by receiver side) */
2797 len = pj_ansi_snprintf(p, end-p,
2798 "%s RTT (from recv) min avg max last dev",
2799 indent);
2800 VALIDATE_PRINT_BUF();
2801 len = pj_ansi_snprintf(p, end-p,
2802 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2803 indent,
2804 xr_stat.rtt.min / 1000.0,
2805 xr_stat.rtt.mean / 1000.0,
2806 xr_stat.rtt.max / 1000.0,
2807 xr_stat.rtt.last / 1000.0,
2808 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2809 );
2810 VALIDATE_PRINT_BUF();
2811 } while(0);
2812#endif
2813
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002814 }
2815}
2816
2817
2818/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002819void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002820 int call_id,
2821 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002822{
2823 int len;
2824 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2825 pjsip_dialog *dlg = inv->dlg;
2826 char userinfo[128];
2827
2828 /* Dump invite sesion info. */
2829
2830 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
Benny Prijono329d6382009-05-29 13:04:03 +00002831 if (len < 0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002832 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2833 else
2834 userinfo[len] = '\0';
2835
2836 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2837 title,
2838 pjsip_inv_state_name(inv->state),
2839 userinfo);
2840 if (len < 1 || len >= (int)size) {
2841 pj_ansi_strcpy(buf, "<--uri too long-->");
2842 len = 18;
2843 } else
2844 buf[len] = '\0';
2845}
2846
2847
2848/*
2849 * Dump call and media statistics to string.
2850 */
2851PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2852 pj_bool_t with_media,
2853 char *buffer,
2854 unsigned maxlen,
2855 const char *indent)
2856{
2857 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002858 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002859 pj_time_val duration, res_delay, con_delay;
2860 char tmp[128];
2861 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002862 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002863 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002864 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002865
2866 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2867 PJ_EINVAL);
2868
Benny Prijonodc752ca2006-09-22 16:55:42 +00002869 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002870 if (status != PJ_SUCCESS)
2871 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002872
2873 *buffer = '\0';
2874 p = buffer;
2875 end = buffer + maxlen;
2876 len = 0;
2877
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002878 print_call(indent, call_id, tmp, sizeof(tmp));
2879
2880 len = pj_ansi_strlen(tmp);
2881 pj_ansi_strcpy(buffer, tmp);
2882
2883 p += len;
2884 *p++ = '\r';
2885 *p++ = '\n';
2886
2887 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002888 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002889 pj_gettimeofday(&duration);
2890 PJ_TIME_VAL_SUB(duration, call->conn_time);
2891 con_delay = call->conn_time;
2892 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2893 } else {
2894 duration.sec = duration.msec = 0;
2895 con_delay.sec = con_delay.msec = 0;
2896 }
2897
2898 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002899 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002900 res_delay = call->res_time;
2901 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2902 } else {
2903 res_delay.sec = res_delay.msec = 0;
2904 }
2905
2906 /* Print duration */
2907 len = pj_ansi_snprintf(p, end-p,
2908 "%s Call time: %02dh:%02dm:%02ds, "
2909 "1st res in %d ms, conn in %dms",
2910 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002911 (int)(duration.sec / 3600),
2912 (int)((duration.sec % 3600)/60),
2913 (int)(duration.sec % 60),
2914 (int)PJ_TIME_VAL_MSEC(res_delay),
2915 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002916
2917 if (len > 0 && len < end-p) {
2918 p += len;
2919 *p++ = '\n';
2920 *p = '\0';
2921 }
2922
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002923 /* Get and ICE SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002924 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002925 pjmedia_transport_get_info(call->med_tp, &tp_info);
2926 if (tp_info.specific_info_cnt > 0) {
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002927 unsigned i;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002928 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2929 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2930 {
2931 pjmedia_srtp_info *srtp_info =
2932 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2933
2934 len = pj_ansi_snprintf(p, end-p,
2935 "%s SRTP status: %s Crypto-suite: %s",
2936 indent,
2937 (srtp_info->active?"Active":"Not active"),
2938 srtp_info->tx_policy.name.ptr);
2939 if (len > 0 && len < end-p) {
2940 p += len;
2941 *p++ = '\n';
2942 *p = '\0';
2943 }
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002944 } else if (tp_info.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
2945 const pjmedia_ice_transport_info *ii;
2946
2947 ii = (const pjmedia_ice_transport_info*)
2948 tp_info.spc_info[i].buffer;
2949
2950 len = pj_ansi_snprintf(p, end-p,
2951 "%s ICE role: %s, state: %s, comp_cnt: %u",
2952 indent,
2953 pj_ice_sess_role_name(ii->role),
2954 pj_ice_strans_state_name(ii->sess_state),
2955 ii->comp_cnt);
2956 if (len > 0 && len < end-p) {
2957 p += len;
2958 *p++ = '\n';
2959 *p = '\0';
2960 }
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002961 }
2962 }
2963 }
2964
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002965 /* Dump session statistics */
2966 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002967 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002968
Benny Prijonodc752ca2006-09-22 16:55:42 +00002969 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002970
2971 return PJ_SUCCESS;
2972}
2973
2974
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002975/* Timer callback to close sound device */
2976static void reinv_timer_cb(pj_timer_heap_t *th,
2977 pj_timer_entry *entry)
2978{
2979 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
2980 pjsip_dialog *dlg;
2981 pjsua_call *call;
2982 pjsip_tx_data *tdata;
2983 pj_status_t status;
2984
2985 PJ_UNUSED_ARG(th);
2986
2987 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
2988
2989 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
2990 if (status != PJ_SUCCESS)
2991 return;
2992
2993 /* Verify if another SDP negotiation is in progress, e.g: session timer
2994 * or another re-INVITE.
2995 */
2996 if (call->inv==NULL || call->inv->neg==NULL ||
2997 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
2998 {
2999 goto on_return;
3000 }
3001
3002 /* Verify if another SDP negotiation has been completed by comparing
3003 * the SDP version.
3004 */
3005 {
3006 const pjmedia_sdp_session *sdp;
3007
3008 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
3009 if (status == PJ_SUCCESS &&
3010 sdp->origin.version > call->lock_codec.new_sdp->origin.version)
3011 {
3012 goto on_return;
3013 }
3014 }
3015
3016 /* Create re-INVITE with the new offer */
3017 status = pjsip_inv_reinvite(call->inv, NULL, call->lock_codec.new_sdp,
3018 &tdata);
3019 if (status == PJ_EINVALIDOP) {
3020 /* Ups, let's reschedule again */
3021 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
3022 call->lock_codec.reinv_timer.id = PJ_TRUE;
3023 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3024 &call->lock_codec.reinv_timer, &delay);
3025 } else if (status != PJ_SUCCESS) {
3026 pjsua_perror(THIS_FILE, "Failed creating re-INVITE in lock codec",
3027 status);
3028 }
3029
3030 /* Send the UPDATE/re-INVITE request */
3031 status = pjsip_inv_send_msg(call->inv, tdata);
3032 if (status != PJ_SUCCESS) {
3033 pjsua_perror(THIS_FILE, "Failed sending re-INVITE in lock codec",
3034 status);
3035 }
3036
3037on_return:
3038 pjsip_dlg_dec_lock(dlg);
3039}
3040
3041
3042/* Check if the specified format can be skipped in counting codecs */
3043static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
3044 const pj_str_t *fmt)
3045{
3046 unsigned pt;
3047
3048 pt = pj_strtoul(fmt);
3049
3050 /* Check for comfort noise */
3051 if (pt == PJMEDIA_RTP_PT_CN)
3052 return PJ_TRUE;
3053
3054 /* Dynamic PT, check the format name */
3055 if (pt >= 96) {
3056 pjmedia_sdp_attr *a;
3057 pjmedia_sdp_rtpmap rtpmap;
3058
3059 /* Get the format name */
3060 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3061 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
3062 /* Check for telephone-event */
3063 if (pj_stricmp2(&rtpmap.enc_name, "telephone-event")==0)
3064 return PJ_TRUE;
3065 } else {
3066 /* Invalid SDP, should not reach here */
3067 pj_assert(!"SDP should have been validated!");
3068 return PJ_TRUE;
3069 }
3070 }
3071
3072 return PJ_FALSE;
3073}
3074
3075
3076/* Check if remote answerer has given us more than one codecs. If so,
3077 * create another offer with one codec only to lock down the codec.
3078 */
3079static pj_status_t lock_codec(pjsua_call *call)
3080{
3081 const pj_str_t st_update = {"UPDATE", 6};
3082 pjsip_inv_session *inv = call->inv;
3083 const pjmedia_sdp_session *local_sdp;
3084 const pjmedia_sdp_session *remote_sdp;
3085 const pjmedia_sdp_media *rem_m;
3086 pjmedia_sdp_session *new_sdp;
3087 pjmedia_sdp_media *m;
3088 pjsip_tx_data *tdata;
3089 unsigned i, codec_cnt = 0;
3090 pj_status_t status;
3091
3092 if (!pjmedia_sdp_neg_was_answer_remote(inv->neg))
3093 return PJ_SUCCESS;
3094
3095 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
3096 if (status != PJ_SUCCESS)
3097 return status;
3098 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3099 if (status != PJ_SUCCESS)
3100 return status;
3101
3102 PJ_ASSERT_RETURN(call->audio_idx>=0 &&
3103 call->audio_idx < (int)remote_sdp->media_count,
3104 PJ_EINVALIDOP);
3105
3106 rem_m = remote_sdp->media[call->audio_idx];
3107
3108 /* Check if media is disabled or only one format in the answer. */
3109 if (rem_m->desc.port==0 || rem_m->desc.fmt_count==1)
3110 return PJ_SUCCESS;
3111
3112 /* Count the formats in the answer. */
3113 for (i=0; i<rem_m->desc.fmt_count && codec_cnt <= 1; ++i) {
3114 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[i]))
3115 ++codec_cnt;
3116 }
3117
3118 if (codec_cnt <= 1) {
3119 /* Answer contains single codec. */
3120 return PJ_SUCCESS;
3121 }
3122
3123 PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, start "
3124 "updating media session to use only one codec.."));
3125
3126 /* Clone the offer */
3127 new_sdp = pjmedia_sdp_session_clone(inv->pool_prov, local_sdp);
3128 /* Note that the usage of pool_prov above is risky when locking codec
3129 * delays the re-INVITE (using timer) and there are two SDP negotiations
3130 * done before the re-INVITE.
3131 */
3132
3133 /* Update the new offer so it contains only a codec. Note that formats
3134 * order in the offer should have been matched to the answer, so we can
3135 * just directly update the offer without looking-up the answer.
3136 */
3137 m = new_sdp->media[call->audio_idx];
3138 codec_cnt = 0;
3139 i = 0;
3140 while (i < m->desc.fmt_count) {
3141 pjmedia_sdp_attr *a;
3142 pj_str_t *fmt = &m->desc.fmt[i];
3143
3144 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
3145 ++i;
3146 continue;
3147 }
3148
3149 /* Remove format */
3150 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3151 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3152 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
3153 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3154 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
3155 m->desc.fmt_count, i);
3156 --m->desc.fmt_count;
3157 }
3158
3159 /* Send new SDP offer via UPDATE or re-INVITE */
3160 if (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)==
3161 PJSIP_DIALOG_CAP_SUPPORTED)
3162 {
3163 /* Create UPDATE with the new offer */
3164 status = pjsip_inv_update(inv, NULL, new_sdp, &tdata);
3165 if (status != PJ_SUCCESS)
3166 return status;
3167
3168 } else {
3169 /* Create re-INVITE with the new offer */
3170 status = pjsip_inv_reinvite(inv, NULL, new_sdp, &tdata);
3171 if (status == PJ_EINVALIDOP) {
3172 /* Current INVITE transaction is pending, reschedule re-INVITE. */
3173 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
3174
3175 call->lock_codec.new_sdp = new_sdp;
3176 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
3177 (void*)(pj_size_t)call->index,
3178 &reinv_timer_cb);
3179 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3180 &call->lock_codec.reinv_timer, &delay);
3181 return PJ_SUCCESS;
3182
3183 } else if (status != PJ_SUCCESS)
3184 return status;
3185 }
3186
3187 /* Send the UPDATE/re-INVITE request */
3188 status = pjsip_inv_send_msg(inv, tdata);
3189 if (status != PJ_SUCCESS)
3190 return status;
3191
3192 return PJ_SUCCESS;
3193}
3194
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003195/*
Benny Prijono84126ab2006-02-09 09:30:09 +00003196 * This callback receives notification from invite session when the
3197 * session state has changed.
3198 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003199static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
3200 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00003201{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003202 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003203
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003204 PJSUA_LOCK();
3205
Benny Prijonoa1e69682007-05-11 15:14:34 +00003206 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003207
3208 if (!call) {
3209 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00003210 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003211 }
3212
Benny Prijonoe21e7842006-04-09 16:46:05 +00003213
3214 /* Get call times */
3215 switch (inv->state) {
3216 case PJSIP_INV_STATE_EARLY:
3217 case PJSIP_INV_STATE_CONNECTING:
3218 if (call->res_time.sec == 0)
3219 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00003220 call->last_code = (pjsip_status_code)
3221 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003222 pj_strncpy(&call->last_text,
3223 &e->body.tsx_state.tsx->status_text,
3224 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00003225 break;
3226 case PJSIP_INV_STATE_CONFIRMED:
3227 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003228
3229 /* Ticket #476, locking a codec in the media session. */
3230 {
3231 pj_status_t status;
3232 status = lock_codec(call);
3233 if (status != PJ_SUCCESS) {
3234 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
3235 }
3236 }
3237
Benny Prijonoe21e7842006-04-09 16:46:05 +00003238 break;
3239 case PJSIP_INV_STATE_DISCONNECTED:
3240 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00003241 if (call->res_time.sec == 0)
3242 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003243 if (e->type == PJSIP_EVENT_TSX_STATE &&
3244 e->body.tsx_state.tsx->status_code > call->last_code)
3245 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00003246 call->last_code = (pjsip_status_code)
3247 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003248 pj_strncpy(&call->last_text,
3249 &e->body.tsx_state.tsx->status_text,
3250 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003251 } else {
3252 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
3253 pj_strncpy(&call->last_text,
3254 pjsip_get_status_text(call->last_code),
3255 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00003256 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003257
3258 /* Stop lock codec timer, if it is active */
3259 if (call->lock_codec.reinv_timer.id) {
3260 pjsip_endpt_cancel_timer(pjsua_var.endpt,
3261 &call->lock_codec.reinv_timer);
3262 call->lock_codec.reinv_timer.id = PJ_FALSE;
3263 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00003264 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003265 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00003266 call->last_code = (pjsip_status_code)
3267 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003268 pj_strncpy(&call->last_text,
3269 &e->body.tsx_state.tsx->status_text,
3270 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00003271 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00003272 }
3273
Benny Prijono26ff9062006-02-21 23:47:00 +00003274 /* If this is an outgoing INVITE that was created because of
3275 * REFER/transfer, send NOTIFY to transferer.
3276 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00003277 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003278 int st_code = -1;
3279 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
3280
3281
Benny Prijonoa91a0032006-02-26 21:23:45 +00003282 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003283 case PJSIP_INV_STATE_NULL:
3284 case PJSIP_INV_STATE_CALLING:
3285 /* Do nothing */
3286 break;
3287
3288 case PJSIP_INV_STATE_EARLY:
3289 case PJSIP_INV_STATE_CONNECTING:
3290 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003291 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
3292 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3293 else
3294 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003295 break;
3296
Benny Prijono140beae2009-10-11 05:06:43 +00003297 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003298#if 0
3299/* We don't need this, as we've terminated the subscription in
3300 * CONNECTING state.
3301 */
Benny Prijono26ff9062006-02-21 23:47:00 +00003302 /* When state is confirmed, send the final 200/OK and terminate
3303 * subscription.
3304 */
3305 st_code = e->body.tsx_state.tsx->status_code;
3306 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003307#endif
Benny Prijono140beae2009-10-11 05:06:43 +00003308 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003309
3310 case PJSIP_INV_STATE_DISCONNECTED:
3311 st_code = e->body.tsx_state.tsx->status_code;
3312 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3313 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003314
Benny Prijono8b1889b2006-06-06 18:40:40 +00003315 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00003316 /* Nothing to do. Just to keep gcc from complaining about
3317 * unused enums.
3318 */
3319 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003320 }
3321
3322 if (st_code != -1) {
3323 pjsip_tx_data *tdata;
3324 pj_status_t status;
3325
Benny Prijonoa91a0032006-02-26 21:23:45 +00003326 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00003327 ev_state, st_code,
3328 NULL, &tdata);
3329 if (status != PJ_SUCCESS) {
3330 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
3331 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003332 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00003333 if (status != PJ_SUCCESS) {
3334 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
3335 }
3336 }
3337 }
3338 }
3339
Benny Prijono84126ab2006-02-09 09:30:09 +00003340
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003341 if (pjsua_var.ua_cfg.cb.on_call_state)
3342 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003343
3344 /* call->inv may be NULL now */
3345
Benny Prijono84126ab2006-02-09 09:30:09 +00003346 /* Destroy media session when invite session is disconnected. */
3347 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003348
Benny Prijonoa91a0032006-02-26 21:23:45 +00003349 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003350
Benny Prijono275fd682006-03-22 11:59:11 +00003351 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003352 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003353
Benny Prijono105217f2006-03-06 16:25:59 +00003354 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003355 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003356 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003357
3358 /* Reset call */
3359 reset_call(call->index);
3360
Benny Prijono84126ab2006-02-09 09:30:09 +00003361 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003362
3363 PJSUA_UNLOCK();
3364}
3365
3366/*
3367 * This callback is called by invite session framework when UAC session
3368 * has forked.
3369 */
3370static void pjsua_call_on_forked( pjsip_inv_session *inv,
3371 pjsip_event *e)
3372{
3373 PJ_UNUSED_ARG(inv);
3374 PJ_UNUSED_ARG(e);
3375
3376 PJ_TODO(HANDLE_FORKED_DIALOG);
3377}
3378
3379
3380/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003381 * Callback from UA layer when forked dialog response is received.
3382 */
3383pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3384{
3385 if (dlg->uac_has_2xx &&
3386 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3387 pjsip_rdata_get_tsx(res) == NULL &&
3388 res->msg_info.msg->line.status.code/100 == 2)
3389 {
3390 pjsip_dialog *forked_dlg;
3391 pjsip_tx_data *bye;
3392 pj_status_t status;
3393
3394 /* Create forked dialog */
3395 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3396 if (status != PJ_SUCCESS)
3397 return NULL;
3398
3399 pjsip_dlg_inc_lock(forked_dlg);
3400
3401 /* Disconnect the call */
3402 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3403 -1, &bye);
3404 if (status == PJ_SUCCESS) {
3405 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3406 }
3407
3408 pjsip_dlg_dec_lock(forked_dlg);
3409
3410 if (status != PJ_SUCCESS) {
3411 return NULL;
3412 }
3413
3414 return forked_dlg;
3415
3416 } else {
3417 return dlg;
3418 }
3419}
3420
3421/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003422 * Disconnect call upon error.
3423 */
3424static void call_disconnect( pjsip_inv_session *inv,
3425 int code )
3426{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003427 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003428 pjsip_tx_data *tdata;
3429 pj_status_t status;
3430
Benny Prijono59b3aed2008-01-15 16:54:54 +00003431 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3432
Benny Prijonoa38ada02006-07-02 14:22:35 +00003433 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003434 if (status != PJ_SUCCESS)
3435 return;
3436
3437 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003438 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3439 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3440 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003441 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003442 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003443
Benny Prijono734fc2d2008-03-17 16:05:35 +00003444 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003445 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003446 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003447 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003448 if (status == PJ_SUCCESS) {
3449 pjsip_create_sdp_body(tdata->pool, local_sdp,
3450 &tdata->msg->body);
3451 }
3452 }
3453
3454 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003455}
3456
3457/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003458 * Callback to be called when SDP offer/answer negotiation has just completed
3459 * in the session. This function will start/update media if negotiation
3460 * has succeeded.
3461 */
3462static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3463 pj_status_t status)
3464{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003465 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003466 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003467 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003468 const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003469
3470 PJSUA_LOCK();
3471
Benny Prijonoa1e69682007-05-11 15:14:34 +00003472 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003473
3474 if (status != PJ_SUCCESS) {
3475
3476 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3477
Benny Prijono2331d202008-06-26 15:46:52 +00003478 /* Do not deinitialize media since this may be a re-INVITE or
3479 * UPDATE (which in this case the media should not get affected
3480 * by the failed re-INVITE/UPDATE). The media will be shutdown
3481 * when call is disconnected anyway.
3482 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003483 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003484 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003485
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003486 /* Disconnect call if we're not in the middle of initializing an
3487 * UAS dialog and if this is not a re-INVITE
3488 */
3489 if (inv->state != PJSIP_INV_STATE_NULL &&
3490 inv->state != PJSIP_INV_STATE_CONFIRMED)
3491 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003492 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003493 }
3494
3495 PJSUA_UNLOCK();
3496 return;
3497 }
3498
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003499
3500 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003501 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003502 if (status != PJ_SUCCESS) {
3503 pjsua_perror(THIS_FILE,
3504 "Unable to retrieve currently active local SDP",
3505 status);
3506 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3507 PJSUA_UNLOCK();
3508 return;
3509 }
3510
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003511 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3512 if (status != PJ_SUCCESS) {
3513 pjsua_perror(THIS_FILE,
3514 "Unable to retrieve currently active remote SDP",
3515 status);
3516 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3517 PJSUA_UNLOCK();
3518 return;
3519 }
3520
Benny Prijono91a6a172007-10-31 08:59:29 +00003521 /* Update remote's NAT type */
3522 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3523 update_remote_nat_type(call, remote_sdp);
3524 }
3525
3526 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003527 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003528 if (status != PJ_SUCCESS) {
3529 pjsua_perror(THIS_FILE, "Unable to create media session",
3530 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003531 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003532 /* No need to deinitialize; media will be shutdown when call
3533 * state is disconnected anyway.
3534 */
3535 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003536 PJSUA_UNLOCK();
3537 return;
3538 }
3539
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003540 /* Ticket #476, handle the case of early media and remote support UPDATE */
3541 if (inv->state == PJSIP_INV_STATE_EARLY &&
3542 pjmedia_sdp_neg_was_answer_remote(inv->neg) &&
3543 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)==
3544 PJSIP_DIALOG_CAP_SUPPORTED)
3545 {
3546 status = lock_codec(call);
3547 if (status != PJ_SUCCESS) {
3548 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
3549 }
3550 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003551
3552 /* Call application callback, if any */
3553 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3554 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3555
3556
3557 PJSUA_UNLOCK();
3558}
3559
3560
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003561/* Create SDP for call hold. */
3562static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3563 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003564{
3565 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003566 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003567 pjmedia_sdp_session *sdp;
3568
Benny Prijono40d62b62009-08-12 17:53:47 +00003569 /* Use call's provisional pool */
3570 pool = call->inv->pool_prov;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003571
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003572 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003573 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3574 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003575 if (status != PJ_SUCCESS) {
3576 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3577 return status;
3578 }
3579
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003580 /* Call-hold is done by set the media direction to 'sendonly'
3581 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3582 * 'inactive' (PJMEDIA_DIR_NONE).
3583 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3584 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003585 /* http://trac.pjsip.org/repos/ticket/880
3586 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3587 */
3588 if (1) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003589 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003590
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003591 /* Remove existing directions attributes */
3592 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3593 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3594 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3595 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003596
Benny Prijonoe0860132009-06-05 10:14:20 +00003597 if (call->media_dir & PJMEDIA_DIR_ENCODING) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003598 /* Add sendonly attribute */
3599 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3600 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3601 } else {
3602 /* Add inactive attribute */
3603 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3604 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3605 }
3606 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003607
3608 *p_answer = sdp;
3609
3610 return status;
3611}
3612
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003613/*
3614 * Called when session received new offer.
3615 */
3616static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3617 const pjmedia_sdp_session *offer)
3618{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003619 pjsua_call *call;
3620 pjmedia_sdp_conn *conn;
3621 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003622 pj_status_t status;
3623
3624 PJSUA_LOCK();
3625
Benny Prijonoa1e69682007-05-11 15:14:34 +00003626 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003627
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003628 conn = offer->media[0]->conn;
3629 if (!conn)
3630 conn = offer->conn;
3631
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003632 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003633 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3634 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003635
Benny Prijono40d62b62009-08-12 17:53:47 +00003636 status = pjsua_media_channel_create_sdp(call->index,
3637 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003638 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003639 if (status != PJ_SUCCESS) {
3640 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3641 PJSUA_UNLOCK();
3642 return;
3643 }
3644
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003645 /* Check if offer's conn address is zero */
3646 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3647 pj_strcmp2(&conn->addr, "0")==0)
3648 {
3649 /* Modify address */
3650 answer->conn->addr = pj_str("0.0.0.0");
3651 }
3652
3653 /* Check if call is on-hold */
3654 if (call->local_hold) {
3655 pjmedia_sdp_attr *attr;
3656
3657 /* Remove existing directions attributes */
3658 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3659 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3660 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3661 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3662
3663 /* Keep call on-hold by setting 'sendonly' attribute.
3664 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3665 */
Benny Prijono40d62b62009-08-12 17:53:47 +00003666 attr = pjmedia_sdp_attr_create(call->inv->pool_prov, "sendonly", NULL);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003667 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3668 }
3669
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003670 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3671 if (status != PJ_SUCCESS) {
3672 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3673 PJSUA_UNLOCK();
3674 return;
3675 }
3676
3677 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003678}
3679
3680
3681/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003682 * Called to generate new offer.
3683 */
3684static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3685 pjmedia_sdp_session **offer)
3686{
3687 pjsua_call *call;
3688 pj_status_t status;
3689
3690 PJSUA_LOCK();
3691
3692 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3693
3694 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003695 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003696 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003697 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003698 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003699 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003700 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003701 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3702 call->index));
3703
Benny Prijono40d62b62009-08-12 17:53:47 +00003704 status = pjsua_media_channel_create_sdp(call->index,
3705 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003706 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003707 }
3708
3709 if (status != PJ_SUCCESS) {
3710 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3711 PJSUA_UNLOCK();
3712 return;
3713 }
3714
Benny Prijono77998ce2007-06-20 10:03:46 +00003715 PJSUA_UNLOCK();
3716}
3717
3718
3719/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003720 * Callback called by event framework when the xfer subscription state
3721 * has changed.
3722 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003723static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3724{
3725
3726 PJ_UNUSED_ARG(event);
3727
3728 /*
3729 * When subscription is accepted (got 200/OK to REFER), check if
3730 * subscription suppressed.
3731 */
3732 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3733
3734 pjsip_rx_data *rdata;
3735 pjsip_generic_string_hdr *refer_sub;
3736 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3737 pjsua_call *call;
3738
Benny Prijonoa1e69682007-05-11 15:14:34 +00003739 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003740
3741 /* Must be receipt of response message */
3742 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3743 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3744 rdata = event->body.tsx_state.src.rdata;
3745
3746 /* Find Refer-Sub header */
3747 refer_sub = (pjsip_generic_string_hdr*)
3748 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3749 &REFER_SUB, NULL);
3750
3751 /* Check if subscription is suppressed */
3752 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3753 /* Since no subscription is desired, assume that call has been
3754 * transfered successfully.
3755 */
3756 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3757 const pj_str_t ACCEPTED = { "Accepted", 8 };
3758 pj_bool_t cont = PJ_FALSE;
3759 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3760 200,
3761 &ACCEPTED,
3762 PJ_TRUE,
3763 &cont);
3764 }
3765
3766 /* Yes, subscription is suppressed.
3767 * Terminate our subscription now.
3768 */
3769 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3770 "event subcription..."));
3771 pjsip_evsub_terminate(sub, PJ_TRUE);
3772
3773 } else {
3774 /* Notify application about call transfer progress.
3775 * Initially notify with 100/Accepted status.
3776 */
3777 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3778 const pj_str_t ACCEPTED = { "Accepted", 8 };
3779 pj_bool_t cont = PJ_FALSE;
3780 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3781 100,
3782 &ACCEPTED,
3783 PJ_FALSE,
3784 &cont);
3785 }
3786 }
3787 }
3788 /*
3789 * On incoming NOTIFY, notify application about call transfer progress.
3790 */
3791 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3792 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3793 {
3794 pjsua_call *call;
3795 pjsip_msg *msg;
3796 pjsip_msg_body *body;
3797 pjsip_status_line status_line;
3798 pj_bool_t is_last;
3799 pj_bool_t cont;
3800 pj_status_t status;
3801
Benny Prijonoa1e69682007-05-11 15:14:34 +00003802 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003803
3804 /* When subscription is terminated, clear the xfer_sub member of
3805 * the inv_data.
3806 */
3807 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3808 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3809 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3810
3811 }
3812
3813 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3814 /* Application is not interested with call progress status */
3815 return;
3816 }
3817
3818 /* This better be a NOTIFY request */
3819 if (event->type == PJSIP_EVENT_TSX_STATE &&
3820 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3821 {
3822 pjsip_rx_data *rdata;
3823
3824 rdata = event->body.tsx_state.src.rdata;
3825
3826 /* Check if there's body */
3827 msg = rdata->msg_info.msg;
3828 body = msg->body;
3829 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003830 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003831 "Warning: received NOTIFY without message body"));
3832 return;
3833 }
3834
3835 /* Check for appropriate content */
3836 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3837 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3838 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003839 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003840 "Warning: received NOTIFY with non message/sipfrag "
3841 "content"));
3842 return;
3843 }
3844
3845 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003846 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003847 &status_line);
3848 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003849 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003850 "Warning: received NOTIFY with invalid "
3851 "message/sipfrag content"));
3852 return;
3853 }
3854
3855 } else {
3856 status_line.code = 500;
3857 status_line.reason = *pjsip_get_status_text(500);
3858 }
3859
3860 /* Notify application */
3861 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3862 cont = !is_last;
3863 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3864 status_line.code,
3865 &status_line.reason,
3866 is_last, &cont);
3867
3868 if (!cont) {
3869 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3870 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003871
3872 /* If the call transfer has completed but the subscription is
3873 * not terminated, terminate it now.
3874 */
3875 if (status_line.code/100 == 2 && !is_last) {
3876 pjsip_tx_data *tdata;
3877
3878 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3879 0, &tdata);
3880 if (status == PJ_SUCCESS)
3881 status = pjsip_evsub_send_request(sub, tdata);
3882 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003883 }
3884}
3885
3886
3887/*
3888 * Callback called by event framework when the xfer subscription state
3889 * has changed.
3890 */
3891static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003892{
3893
3894 PJ_UNUSED_ARG(event);
3895
3896 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003897 * When subscription is terminated, clear the xfer_sub member of
3898 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003899 */
3900 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003901 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003902
Benny Prijonoa1e69682007-05-11 15:14:34 +00003903 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003904 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003905 return;
3906
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003907 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003908 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003909
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003910 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003911 }
3912}
3913
3914
3915/*
3916 * Follow transfer (REFER) request.
3917 */
3918static void on_call_transfered( pjsip_inv_session *inv,
3919 pjsip_rx_data *rdata )
3920{
3921 pj_status_t status;
3922 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003923 pjsua_call *existing_call;
3924 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003925 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003926 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003927 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003928 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003929 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003930 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003931 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003932 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003933 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003934 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003935 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003936 pjsip_evsub *sub;
3937
Benny Prijonoa1e69682007-05-11 15:14:34 +00003938 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003939
Benny Prijono26ff9062006-02-21 23:47:00 +00003940 /* Find the Refer-To header */
3941 refer_to = (pjsip_generic_string_hdr*)
3942 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3943
3944 if (refer_to == NULL) {
3945 /* Invalid Request.
3946 * No Refer-To header!
3947 */
3948 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003949 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003950 return;
3951 }
3952
Benny Prijonoc8141a82006-08-20 09:12:19 +00003953 /* Find optional Refer-Sub header */
3954 refer_sub = (pjsip_generic_string_hdr*)
3955 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3956
3957 if (refer_sub) {
3958 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3959 no_refer_sub = PJ_TRUE;
3960 }
3961
Benny Prijono053f5222006-11-11 16:16:04 +00003962 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3963 * request.
3964 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003965 ref_by_hdr = (pjsip_hdr*)
3966 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003967 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003968
Benny Prijono9fc735d2006-05-28 14:58:12 +00003969 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003970 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003971 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3972 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3973 &refer_to->hvalue,
3974 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003975
3976 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003977 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003978 if (code >= 300) {
3979 /* Application rejects call transfer request */
3980 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3981 return;
3982 }
3983
Benny Prijono26ff9062006-02-21 23:47:00 +00003984 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
3985 (int)inv->dlg->remote.info_str.slen,
3986 inv->dlg->remote.info_str.ptr,
3987 (int)refer_to->hvalue.slen,
3988 refer_to->hvalue.ptr));
3989
Benny Prijonoc8141a82006-08-20 09:12:19 +00003990 if (no_refer_sub) {
3991 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003992 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00003993 */
3994 pjsip_tx_data *tdata;
3995 const pj_str_t str_false = { "false", 5};
3996 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00003997
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003998 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
3999 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00004000 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004001 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00004002 status);
4003 return;
4004 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004005
Benny Prijonoc8141a82006-08-20 09:12:19 +00004006 /* Add Refer-Sub header */
4007 hdr = (pjsip_hdr*)
4008 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
4009 &str_false);
4010 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00004011
Benny Prijono26ff9062006-02-21 23:47:00 +00004012
Benny Prijonoc8141a82006-08-20 09:12:19 +00004013 /* Send answer */
4014 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
4015 tdata);
4016 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004017 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00004018 status);
4019 return;
4020 }
4021
4022 /* Don't have subscription */
4023 sub = NULL;
4024
4025 } else {
4026 struct pjsip_evsub_user xfer_cb;
4027 pjsip_hdr hdr_list;
4028
4029 /* Init callback */
4030 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004031 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004032
4033 /* Init additional header list to be sent with REFER response */
4034 pj_list_init(&hdr_list);
4035
4036 /* Create transferee event subscription */
4037 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
4038 if (status != PJ_SUCCESS) {
4039 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
4040 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
4041 return;
4042 }
4043
4044 /* If there's Refer-Sub header and the value is "true", send back
4045 * Refer-Sub in the response with value "true" too.
4046 */
4047 if (refer_sub) {
4048 const pj_str_t str_true = { "true", 4 };
4049 pjsip_hdr *hdr;
4050
4051 hdr = (pjsip_hdr*)
4052 pjsip_generic_string_hdr_create(inv->dlg->pool,
4053 &str_refer_sub,
4054 &str_true);
4055 pj_list_push_back(&hdr_list, hdr);
4056
4057 }
4058
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004059 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00004060 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
4061
4062 /* Create initial NOTIFY request */
4063 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
4064 100, NULL, &tdata);
4065 if (status != PJ_SUCCESS) {
4066 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4067 status);
4068 return;
4069 }
4070
4071 /* Send initial NOTIFY request */
4072 status = pjsip_xfer_send_request( sub, tdata);
4073 if (status != PJ_SUCCESS) {
4074 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
4075 return;
4076 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004077 }
4078
4079 /* We're cheating here.
4080 * We need to get a null terminated string from a pj_str_t.
4081 * So grab the pointer from the hvalue and NULL terminate it, knowing
4082 * that the NULL position will be occupied by a newline.
4083 */
4084 uri = refer_to->hvalue.ptr;
4085 uri[refer_to->hvalue.slen] = '\0';
4086
Benny Prijono053f5222006-11-11 16:16:04 +00004087 /* Init msg_data */
4088 pjsua_msg_data_init(&msg_data);
4089
4090 /* If Referred-By header is present in the REFER request, copy this
4091 * to the outgoing INVITE request.
4092 */
4093 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00004094 pjsip_hdr *dup = (pjsip_hdr*)
4095 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00004096 pj_list_push_back(&msg_data.hdr_list, dup);
4097 }
4098
Benny Prijono26ff9062006-02-21 23:47:00 +00004099 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00004100 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004101 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00004102 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004103 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00004104 if (status != PJ_SUCCESS) {
4105
Benny Prijonoc8141a82006-08-20 09:12:19 +00004106 /* Notify xferer about the error (if we have subscription) */
4107 if (sub) {
4108 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
4109 500, NULL, &tdata);
4110 if (status != PJ_SUCCESS) {
4111 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4112 status);
4113 return;
4114 }
4115 status = pjsip_xfer_send_request(sub, tdata);
4116 if (status != PJ_SUCCESS) {
4117 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
4118 status);
4119 return;
4120 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004121 }
4122 return;
4123 }
4124
Benny Prijonoc8141a82006-08-20 09:12:19 +00004125 if (sub) {
4126 /* Put the server subscription in inv_data.
4127 * Subsequent state changed in pjsua_inv_on_state_changed() will be
4128 * reported back to the server subscription.
4129 */
4130 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00004131
Benny Prijonoc8141a82006-08-20 09:12:19 +00004132 /* Put the invite_data in the subscription. */
4133 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
4134 &pjsua_var.calls[new_call]);
4135 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004136}
4137
4138
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004139
Benny Prijono26ff9062006-02-21 23:47:00 +00004140/*
4141 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00004142 * session. We use this to trap:
4143 * - incoming REFER request.
4144 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00004145 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004146static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
4147 pjsip_transaction *tsx,
4148 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00004149{
Benny Prijono2285e7e2008-12-17 14:28:18 +00004150 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004151
4152 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00004153
Benny Prijono2285e7e2008-12-17 14:28:18 +00004154 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
4155
4156 if (call == NULL) {
4157 PJSUA_UNLOCK();
4158 return;
4159 }
4160
Benny Prijono19eeb6e2009-06-04 22:16:47 +00004161 if (call->inv == NULL) {
4162 /* Shouldn't happen. It happens only when we don't terminate the
4163 * server subscription caused by REFER after the call has been
4164 * transfered (and this call has been disconnected), and we
4165 * receive another REFER for this call.
4166 */
4167 PJSUA_UNLOCK();
4168 return;
4169 }
4170
Benny Prijonofeb69f42007-10-05 09:12:26 +00004171 /* Notify application callback first */
4172 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
4173 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
4174 }
4175
Benny Prijono26ff9062006-02-21 23:47:00 +00004176 if (tsx->role==PJSIP_ROLE_UAS &&
4177 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00004178 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004179 {
4180 /*
4181 * Incoming REFER request.
4182 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004183 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00004184
Benny Prijono26ff9062006-02-21 23:47:00 +00004185 }
Benny Prijonob0808372006-03-02 21:18:58 +00004186 else if (tsx->role==PJSIP_ROLE_UAS &&
4187 tsx->state==PJSIP_TSX_STATE_TRYING &&
4188 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
4189 {
4190 /*
4191 * Incoming MESSAGE request!
4192 */
4193 pjsip_rx_data *rdata;
4194 pjsip_msg *msg;
4195 pjsip_accept_hdr *accept_hdr;
4196 pj_status_t status;
4197
4198 rdata = e->body.tsx_state.src.rdata;
4199 msg = rdata->msg_info.msg;
4200
4201 /* Request MUST have message body, with Content-Type equal to
4202 * "text/plain".
4203 */
4204 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
4205
4206 pjsip_hdr hdr_list;
4207
4208 pj_list_init(&hdr_list);
4209 pj_list_push_back(&hdr_list, accept_hdr);
4210
4211 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
4212 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004213 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00004214 return;
4215 }
4216
4217 /* Respond with 200 first, so that remote doesn't retransmit in case
4218 * the UI takes too long to process the message.
4219 */
4220 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
4221
4222 /* Process MESSAGE request */
4223 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
4224 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004225
Benny Prijonob0808372006-03-02 21:18:58 +00004226 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004227 else if (tsx->role == PJSIP_ROLE_UAC &&
4228 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004229 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004230 /* Handle outgoing pager status */
4231 if (tsx->status_code >= 200) {
4232 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00004233
Benny Prijonoa1e69682007-05-11 15:14:34 +00004234 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004235 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00004236
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004237 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
4238 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
4239 &im_data->to,
4240 &im_data->body,
4241 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00004242 (pjsip_status_code)
4243 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004244 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00004245 }
Benny Prijonofccab712006-02-22 22:23:22 +00004246 }
Benny Prijono834aee32006-02-19 01:38:06 +00004247 }
Benny Prijono834aee32006-02-19 01:38:06 +00004248
Benny Prijono26ff9062006-02-21 23:47:00 +00004249
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004250 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00004251}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004252
4253
4254/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00004255static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
4256 const pjsip_uri *target,
4257 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004258{
4259 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00004260 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004261
4262 PJSUA_LOCK();
4263
4264 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00004265 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
4266 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004267 } else {
4268 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
4269 "(callback not implemented by application). Disconnecting "
4270 "call.",
4271 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00004272 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004273 }
4274
4275 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00004276
4277 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004278}
4279