blob: 2caaee1eea757c2aa89c7a81a6865fb222409c17 [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;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000496 if (acc->cfg.require_100rel)
497 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000498 if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) {
499 options |= PJSIP_INV_SUPPORT_TIMER;
500 if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
501 options |= PJSIP_INV_REQUIRE_TIMER;
502 else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
503 options |= PJSIP_INV_ALWAYS_USE_TIMER;
504 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000505
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000506 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000507 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000508 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000509 goto on_error;
510 }
511
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000512 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000513 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
514 if (status != PJ_SUCCESS) {
515 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
516 goto on_error;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000517 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000518
519 /* Create and associate our data in the session. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000520 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000521
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000522 dlg->mod_data[pjsua_var.mod.id] = call;
523 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000524
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000525 /* Attach user data */
526 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000527
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000528 /* If account is locked to specific transport, then lock dialog
529 * to this transport too.
530 */
531 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
532 pjsip_tpselector tp_sel;
533
534 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
535 pjsip_dlg_set_transport(dlg, &tp_sel);
536 }
537
Benny Prijono84126ab2006-02-09 09:30:09 +0000538 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000539 if (!pj_list_empty(&acc->route_set))
540 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000541
542
543 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000544 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000545 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000546 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000547 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000548
Benny Prijono48ab2b72007-11-08 09:24:30 +0000549 /* Set authentication preference */
550 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000551
552 /* Create initial INVITE: */
553
554 status = pjsip_inv_invite(inv, &tdata);
555 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000556 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
557 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000558 goto on_error;
559 }
560
561
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000562 /* Add additional headers etc */
563
564 pjsua_process_msg_data( tdata, msg_data);
565
Benny Prijono093d3022006-09-24 00:07:11 +0000566 /* Must increment call counter now */
567 ++pjsua_var.call_cnt;
568
Benny Prijono84126ab2006-02-09 09:30:09 +0000569 /* Send initial INVITE: */
570
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000571 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000572 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000573 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
574 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000575
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000576 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000577 * session would have been cleared.
578 */
579 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000580 goto on_error;
581 }
582
Benny Prijono84126ab2006-02-09 09:30:09 +0000583 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000584
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000585 if (p_call_id)
586 *p_call_id = call_id;
587
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000588 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000589 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000590 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000591
592 return PJ_SUCCESS;
593
594
595on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000596 if (dlg) {
597 /* This may destroy the dialog */
598 pjsip_dlg_dec_lock(dlg);
599 }
600
Benny Prijono1c2bf462006-03-05 11:54:02 +0000601 if (inv != NULL) {
602 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000603 }
604
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000605 if (call_id != -1) {
606 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000607 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000608 }
609
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000610 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000611 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000612 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000613}
614
615
Benny Prijono91a6a172007-10-31 08:59:29 +0000616/* Get the NAT type information in remote's SDP */
617static void update_remote_nat_type(pjsua_call *call,
618 const pjmedia_sdp_session *sdp)
619{
620 const pjmedia_sdp_attr *xnat;
621
622 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
623 if (xnat) {
624 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
625 } else {
626 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
627 }
628
629 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
630 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
631}
632
633
Benny Prijonodc39fe82006-05-26 12:17:46 +0000634/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000635 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000636 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000637 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000638pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000639{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000640 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000641 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000642 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000643 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
644 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000645 pjsip_tx_data *response = NULL;
646 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000647 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000648 int acc_id;
649 pjsua_call *call;
650 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000651 int sip_err_code;
Benny Prijono1c1d7342010-08-01 09:48:51 +0000652 pjmedia_sdp_session *offer=NULL, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000653 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000654
Benny Prijono26ff9062006-02-21 23:47:00 +0000655 /* Don't want to handle anything but INVITE */
656 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
657 return PJ_FALSE;
658
659 /* Don't want to handle anything that's already associated with
660 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000661 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000662 if (dlg || tsx)
663 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000664
Benny Prijono384dab42009-10-14 01:58:04 +0000665 /* Don't want to accept the call if shutdown is in progress */
666 if (pjsua_var.thread_quit_flag) {
667 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
668 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
669 NULL, NULL);
670 return PJ_TRUE;
671 }
672
Benny Prijono148c9dd2006-09-19 13:37:53 +0000673 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000674
Benny Prijono26ff9062006-02-21 23:47:00 +0000675 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000676 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000677
Benny Prijono5773cd62008-01-19 13:01:42 +0000678 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000679 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000680 PJSIP_SC_BUSY_HERE, NULL,
681 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000682 PJ_LOG(2,(THIS_FILE,
683 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000684 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000685 return PJ_TRUE;
686 }
687
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000688 /* Clear call descriptor */
689 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000690
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000691 call = &pjsua_var.calls[call_id];
692
693 /* Mark call start time. */
694 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000695
Benny Prijono053f5222006-11-11 16:16:04 +0000696 /* Check INVITE request for Replaces header. If Replaces header is
697 * present, the function will make sure that we can handle the request.
698 */
699 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
700 &response);
701 if (status != PJ_SUCCESS) {
702 /*
703 * Something wrong with the Replaces header.
704 */
705 if (response) {
706 pjsip_response_addr res_addr;
707
708 pjsip_get_response_addr(response->pool, rdata, &res_addr);
709 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
710 NULL, NULL);
711
712 } else {
713
714 /* Respond with 500 (Internal Server Error) */
715 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
716 NULL, NULL);
717 }
718
719 PJSUA_UNLOCK();
720 return PJ_TRUE;
721 }
722
723 /* If this INVITE request contains Replaces header, notify application
724 * about the request so that application can do subsequent checking
725 * if it wants to.
726 */
727 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
728 pjsua_call *replaced_call;
729 int st_code = 200;
730 pj_str_t st_text = { "OK", 2 };
731
732 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000733 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000734
735 /* Notify application */
736 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
737 rdata, &st_code, &st_text);
738
739 /* Must specify final response */
740 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
741
742 /* Check if application rejects this request. */
743 if (st_code >= 300) {
744
745 if (st_text.slen == 2)
746 st_text = *pjsip_get_status_text(st_code);
747
748 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
749 st_code, &st_text, NULL, NULL, NULL);
750 PJSUA_UNLOCK();
751 return PJ_TRUE;
752 }
753 }
754
Benny Prijonod8179652008-01-23 20:39:07 +0000755 /*
756 * Get which account is most likely to be associated with this incoming
757 * call. We need the account to find which contact URI to put for
758 * the call.
759 */
760 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
761
Benny Prijonodb844a42008-02-02 17:07:18 +0000762 /* Get call's secure level */
763 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
764 call->secure_level = 2;
765 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
766 call->secure_level = 1;
767 else
768 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000769
Benny Prijonod8179652008-01-23 20:39:07 +0000770 /* Parse SDP from incoming request */
771 if (rdata->msg_info.msg->body) {
Benny Prijono1c1d7342010-08-01 09:48:51 +0000772 pjsip_rdata_sdp_info *sdp_info;
773
774 sdp_info = pjsip_rdata_get_sdp_info(rdata);
775 offer = sdp_info->sdp;
776
777 status = sdp_info->sdp_err;
778 if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
779 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono2c484e42008-06-26 19:47:23 +0000780
Benny Prijonod8179652008-01-23 20:39:07 +0000781 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000782 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000783 pjsip_hdr hdr_list;
784 pjsip_warning_hdr *w;
785
786 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000787 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000788
789 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
790 pjsip_endpt_name(pjsua_var.endpt),
791 status);
792 pj_list_init(&hdr_list);
793 pj_list_push_back(&hdr_list, w);
794
Benny Prijono224b4e22008-06-19 14:10:28 +0000795 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000796 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000797 PJSUA_UNLOCK();
798 return PJ_TRUE;
799 }
Benny Prijono617b8602008-04-07 10:10:31 +0000800
801 /* Do quick checks on SDP before passing it to transports. More elabore
802 * checks will be done in pjsip_inv_verify_request2() below.
803 */
804 if (offer->media_count==0) {
805 const pj_str_t reason = pj_str("Missing media in SDP");
806 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
807 NULL, NULL, NULL);
808 PJSUA_UNLOCK();
809 return PJ_TRUE;
810 }
811
Benny Prijonod8179652008-01-23 20:39:07 +0000812 } else {
813 offer = NULL;
814 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000815
Benny Prijono224b4e22008-06-19 14:10:28 +0000816 /* Init media channel */
817 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
818 call->secure_level,
819 rdata->tp_info.pool, offer,
820 &sip_err_code);
821 if (status != PJ_SUCCESS) {
822 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
823 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
824 sip_err_code, NULL, NULL, NULL, NULL);
825 PJSUA_UNLOCK();
826 return PJ_TRUE;
827 }
828
829 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000830 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000831 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000832 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000833 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000834 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
835 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000836 PJSUA_UNLOCK();
837 return PJ_TRUE;
838 }
839
Benny Prijono224b4e22008-06-19 14:10:28 +0000840
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000841 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000842 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000843 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000844 if (pjsua_var.acc[acc_id].cfg.require_100rel)
845 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono07fe2302010-06-24 12:33:18 +0000846 if (pjsua_var.media_cfg.enable_ice)
847 options |= PJSIP_INV_SUPPORT_ICE;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000848 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
849 options |= PJSIP_INV_REQUIRE_TIMER;
850 else if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
851 options |= PJSIP_INV_ALWAYS_USE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000852
Benny Prijonod8179652008-01-23 20:39:07 +0000853 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
854 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000855 if (status != PJ_SUCCESS) {
856
857 /*
858 * No we can't handle the incoming INVITE request.
859 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000860 if (response) {
861 pjsip_response_addr res_addr;
862
863 pjsip_get_response_addr(response->pool, rdata, &res_addr);
864 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
865 NULL, NULL);
866
867 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000868 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000869 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
870 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000871 }
872
Benny Prijonoc97608e2007-03-23 16:34:20 +0000873 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000874 PJSUA_UNLOCK();
875 return PJ_TRUE;
876 }
877
878
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000879 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000880 if (pjsua_var.acc[acc_id].contact.slen) {
881 contact = pjsua_var.acc[acc_id].contact;
882 } else {
883 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
884 acc_id, rdata);
885 if (status != PJ_SUCCESS) {
886 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
887 status);
888 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
889 NULL, NULL);
890 pjsua_media_channel_deinit(call->index);
891 PJSUA_UNLOCK();
892 return PJ_TRUE;
893 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000894 }
895
Benny Prijono26ff9062006-02-21 23:47:00 +0000896 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000897 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000898 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000899 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000900 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000901 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000902 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000903 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000904 return PJ_TRUE;
905 }
906
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000907 /* Set credentials */
908 if (pjsua_var.acc[acc_id].cred_cnt) {
909 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
910 pjsua_var.acc[acc_id].cred_cnt,
911 pjsua_var.acc[acc_id].cred);
912 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000913
Benny Prijono48ab2b72007-11-08 09:24:30 +0000914 /* Set preference */
915 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
916 &pjsua_var.acc[acc_id].cfg.auth_pref);
917
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000918 /* Disable Session Timers if not prefered and the incoming INVITE request
919 * did not require it.
920 */
921 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_INACTIVE &&
922 (options & PJSIP_INV_REQUIRE_TIMER) == 0)
923 {
924 options &= ~(PJSIP_INV_SUPPORT_TIMER);
925 }
926
Benny Prijono26ff9062006-02-21 23:47:00 +0000927 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000928 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000929 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000930 pjsip_hdr hdr_list;
931 pjsip_warning_hdr *w;
932
933 w = pjsip_warning_hdr_create_from_status(dlg->pool,
934 pjsip_endpt_name(pjsua_var.endpt),
935 status);
936 pj_list_init(&hdr_list);
937 pj_list_push_back(&hdr_list, w);
938
939 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
940
941 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000942 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000943 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000944 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000945 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000946 return PJ_TRUE;
947 }
948
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000949 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000950 status = pjsip_timer_init_session(inv,
951 &pjsua_var.acc[acc_id].cfg.timer_setting);
952 if (status != PJ_SUCCESS) {
953 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
954 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
955 NULL, &response);
956 if (status == PJ_SUCCESS && response)
957 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000958
Nanang Izzuddin65add622009-08-11 16:26:20 +0000959 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000960
Nanang Izzuddin65add622009-08-11 16:26:20 +0000961 PJSUA_UNLOCK();
962 return PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000963 }
964
Benny Prijonoea9fd392007-11-06 03:41:40 +0000965 /* Update NAT type of remote endpoint, only when there is SDP in
966 * incoming INVITE!
967 */
968 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
969 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
970 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000971 const pjmedia_sdp_session *remote_sdp;
972
973 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
974 update_remote_nat_type(call, remote_sdp);
975 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000976
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000977 /* If account is locked to specific transport, then lock dialog
978 * to this transport too.
979 */
980 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
981 pjsip_tpselector tp_sel;
982
983 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
984 pjsip_dlg_set_transport(dlg, &tp_sel);
985 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000986
Benny Prijono2285e7e2008-12-17 14:28:18 +0000987 /* Must answer with some response to initial INVITE. We'll do this before
988 * attaching the call to the invite session/dialog, so that the application
989 * will not get notification about this event (on another scenario, it is
990 * also possible that inv_send_msg() fails and causes the invite session to
991 * be disconnected. If we have the call attached at this time, this will
992 * cause the disconnection callback to be called before on_incoming_call()
993 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +0000994 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000995 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000996 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000997 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000998 if (response == NULL) {
999 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
1000 status);
1001 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
1002 pjsip_inv_terminate(inv, 500, PJ_FALSE);
1003 } else {
1004 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +00001005 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001006 PJ_FALSE);
1007 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001008 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001009 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +00001010 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +00001011
1012 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001013 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001014 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +00001015 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +00001016 PJSUA_UNLOCK();
1017 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001018 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001019 }
1020
Benny Prijono2285e7e2008-12-17 14:28:18 +00001021 /* Create and attach pjsua_var data to the dialog: */
1022 call->inv = inv;
1023
1024 dlg->mod_data[pjsua_var.mod.id] = call;
1025 inv->mod_data[pjsua_var.mod.id] = call;
1026
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001027 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001028
Benny Prijono105217f2006-03-06 16:25:59 +00001029
Benny Prijono053f5222006-11-11 16:16:04 +00001030 /* Check if this request should replace existing call */
1031 if (replaced_dlg) {
1032 pjsip_inv_session *replaced_inv;
1033 struct pjsua_call *replaced_call;
1034 pjsip_tx_data *tdata;
1035
1036 /* Get the invite session in the dialog */
1037 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1038
1039 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001040 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001041
1042 /* Notify application */
1043 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1044 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1045 call_id);
1046
1047 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1048 call_id));
1049
1050 /* Answer the new call with 200 response */
1051 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1052 if (status == PJ_SUCCESS)
1053 status = pjsip_inv_send_msg(inv, tdata);
1054
1055 if (status != PJ_SUCCESS)
1056 pjsua_perror(THIS_FILE, "Error answering session", status);
1057
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001058 /* Note that inv may be invalid if 200/OK has caused error in
1059 * starting the media.
1060 */
Benny Prijono053f5222006-11-11 16:16:04 +00001061
1062 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1063 replaced_call->index));
1064
1065 /* Disconnect replaced invite session */
1066 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1067 &tdata);
1068 if (status == PJ_SUCCESS && tdata)
1069 status = pjsip_inv_send_msg(replaced_inv, tdata);
1070
1071 if (status != PJ_SUCCESS)
1072 pjsua_perror(THIS_FILE, "Error terminating session", status);
1073
1074
1075 } else {
1076
Benny Prijonob5388cf2007-01-04 22:45:08 +00001077 /* Notify application if on_incoming_call() is overriden,
1078 * otherwise hangup the call with 480
1079 */
1080 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001081 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001082 } else {
1083 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1084 NULL, NULL);
1085 }
Benny Prijono053f5222006-11-11 16:16:04 +00001086 }
1087
Benny Prijono8b1889b2006-06-06 18:40:40 +00001088
Benny Prijono26ff9062006-02-21 23:47:00 +00001089 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001090 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001091 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001092}
1093
1094
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001095
1096/*
1097 * Check if the specified call has active INVITE session and the INVITE
1098 * session has not been disconnected.
1099 */
1100PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1101{
1102 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1103 PJ_EINVAL);
1104 return pjsua_var.calls[call_id].inv != NULL &&
1105 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1106}
1107
1108
1109/*
1110 * Check if call has an active media session.
1111 */
1112PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1113{
1114 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1115 PJ_EINVAL);
1116 return pjsua_var.calls[call_id].session != NULL;
1117}
1118
1119
Benny Prijonocf986c42008-09-02 11:25:07 +00001120/*
1121 * Retrieve the media session associated with this call.
1122 */
1123PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1124{
1125 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1126 NULL);
1127 return pjsua_var.calls[call_id].session;
1128}
1129
1130
1131/*
1132 * Retrieve the media transport instance that is used for this call.
1133 */
1134PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1135{
1136 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1137 NULL);
1138 return pjsua_var.calls[cid].med_tp;
1139}
1140
1141
Benny Prijono148c9dd2006-09-19 13:37:53 +00001142/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001143pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001144 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001145 pjsua_call **p_call,
1146 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001147{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001148 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001149 pjsua_call *call = NULL;
1150 pj_bool_t has_pjsua_lock = PJ_FALSE;
1151 pj_status_t status = PJ_SUCCESS;
Sauw Ming844c1c92010-09-07 05:12:02 +00001152 pj_time_val time_start, timeout;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001153
Sauw Ming844c1c92010-09-07 05:12:02 +00001154 pj_gettimeofday(&time_start);
1155 timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT;
1156 pj_time_val_normalize(&timeout);
1157
1158 for (retry=0; ; ++retry) {
1159
1160 if (retry % 10 == 9) {
1161 pj_time_val dtime;
1162
1163 pj_gettimeofday(&dtime);
1164 PJ_TIME_VAL_SUB(dtime, time_start);
1165 if (!PJ_TIME_VAL_LT(dtime, timeout))
1166 break;
1167 }
Benny Prijono148c9dd2006-09-19 13:37:53 +00001168
1169 has_pjsua_lock = PJ_FALSE;
1170
1171 status = PJSUA_TRY_LOCK();
1172 if (status != PJ_SUCCESS) {
1173 pj_thread_sleep(retry/10);
1174 continue;
1175 }
1176
1177 has_pjsua_lock = PJ_TRUE;
1178 call = &pjsua_var.calls[call_id];
1179
1180 if (call->inv == NULL) {
1181 PJSUA_UNLOCK();
1182 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1183 return PJSIP_ESESSIONTERMINATED;
1184 }
1185
1186 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1187 if (status != PJ_SUCCESS) {
1188 PJSUA_UNLOCK();
1189 pj_thread_sleep(retry/10);
1190 continue;
1191 }
1192
1193 PJSUA_UNLOCK();
1194
1195 break;
1196 }
1197
1198 if (status != PJ_SUCCESS) {
1199 if (has_pjsua_lock == PJ_FALSE)
1200 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1201 "(possibly system has deadlocked) in %s",
1202 title));
1203 else
1204 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1205 "(possibly system has deadlocked) in %s",
1206 title));
1207 return PJ_ETIMEDOUT;
1208 }
1209
1210 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001211 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001212
1213 return PJ_SUCCESS;
1214}
1215
1216
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001217/*
1218 * Get the conference port identification associated with the call.
1219 */
1220PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1221{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001222 pjsua_call *call;
1223 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001224 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001225 pj_status_t status;
1226
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001227 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1228 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001229
Benny Prijonodc752ca2006-09-22 16:55:42 +00001230 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001231 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001232 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001233
1234 port_id = call->conf_slot;
1235
Benny Prijonodc752ca2006-09-22 16:55:42 +00001236 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001237
1238 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001239}
1240
1241
Benny Prijono148c9dd2006-09-19 13:37:53 +00001242
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001243/*
1244 * Obtain detail information about the specified call.
1245 */
1246PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1247 pjsua_call_info *info)
1248{
1249 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001250 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001251 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001252
1253 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1254 PJ_EINVAL);
1255
Benny Prijonoac623b32006-07-03 15:19:31 +00001256 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001257
Benny Prijonodc752ca2006-09-22 16:55:42 +00001258 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001259 if (status != PJ_SUCCESS) {
1260 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001261 }
1262
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001263 /* id and role */
1264 info->id = call_id;
1265 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001266 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001267
1268 /* local info */
1269 info->local_info.ptr = info->buf_.local_info;
1270 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1271 sizeof(info->buf_.local_info));
1272
1273 /* local contact */
1274 info->local_contact.ptr = info->buf_.local_contact;
1275 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1276 call->inv->dlg->local.contact->uri,
1277 info->local_contact.ptr,
1278 sizeof(info->buf_.local_contact));
1279
1280 /* remote info */
1281 info->remote_info.ptr = info->buf_.remote_info;
1282 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1283 sizeof(info->buf_.remote_info));
1284
1285 /* remote contact */
1286 if (call->inv->dlg->remote.contact) {
1287 int len;
1288 info->remote_contact.ptr = info->buf_.remote_contact;
1289 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1290 call->inv->dlg->remote.contact->uri,
1291 info->remote_contact.ptr,
1292 sizeof(info->buf_.remote_contact));
1293 if (len < 0) len = 0;
1294 info->remote_contact.slen = len;
1295 } else {
1296 info->remote_contact.slen = 0;
1297 }
1298
1299 /* call id */
1300 info->call_id.ptr = info->buf_.call_id;
1301 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1302 sizeof(info->buf_.call_id));
1303
1304 /* state, state_text */
1305 info->state = call->inv->state;
1306 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1307
1308 /* If call is disconnected, set the last_status from the cause code */
1309 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1310 /* last_status, last_status_text */
1311 info->last_status = call->inv->cause;
1312
1313 info->last_status_text.ptr = info->buf_.last_status_text;
1314 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1315 sizeof(info->buf_.last_status_text));
1316 } else {
1317 /* last_status, last_status_text */
1318 info->last_status = call->last_code;
1319
1320 info->last_status_text.ptr = info->buf_.last_status_text;
1321 pj_strncpy(&info->last_status_text, &call->last_text,
1322 sizeof(info->buf_.last_status_text));
1323 }
1324
1325 /* media status and dir */
1326 info->media_status = call->media_st;
1327 info->media_dir = call->media_dir;
1328
1329
1330 /* conference slot number */
1331 info->conf_slot = call->conf_slot;
1332
1333 /* calculate duration */
1334 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1335
1336 info->total_duration = call->dis_time;
1337 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1338
1339 if (call->conn_time.sec) {
1340 info->connect_duration = call->dis_time;
1341 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1342 }
1343
1344 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1345
1346 pj_gettimeofday(&info->total_duration);
1347 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1348
1349 pj_gettimeofday(&info->connect_duration);
1350 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1351
1352 } else {
1353 pj_gettimeofday(&info->total_duration);
1354 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1355 }
1356
Benny Prijonodc752ca2006-09-22 16:55:42 +00001357 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001358
1359 return PJ_SUCCESS;
1360}
1361
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001362/*
1363 * Check if call remote peer support the specified capability.
1364 */
1365PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1366 pjsua_call_id call_id,
1367 int htype,
1368 const pj_str_t *hname,
1369 const pj_str_t *token)
1370{
1371 pjsua_call *call;
1372 pjsip_dialog *dlg;
1373 pj_status_t status;
1374 pjsip_dialog_cap_status cap_status;
1375
1376 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1377 if (status != PJ_SUCCESS)
1378 return PJSIP_DIALOG_CAP_UNKNOWN;
1379
1380 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1381
1382 pjsip_dlg_dec_lock(dlg);
1383
1384 return cap_status;
1385}
1386
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001387
1388/*
1389 * Attach application specific data to the call.
1390 */
1391PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1392 void *user_data)
1393{
1394 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1395 PJ_EINVAL);
1396 pjsua_var.calls[call_id].user_data = user_data;
1397
1398 return PJ_SUCCESS;
1399}
1400
1401
1402/*
1403 * Get user data attached to the call.
1404 */
1405PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1406{
1407 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1408 NULL);
1409 return pjsua_var.calls[call_id].user_data;
1410}
1411
1412
1413/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001414 * Get remote's NAT type.
1415 */
1416PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1417 pj_stun_nat_type *p_type)
1418{
1419 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1420 PJ_EINVAL);
1421 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1422
1423 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1424 return PJ_SUCCESS;
1425}
1426
1427
1428/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001429 * Send response to incoming INVITE request.
1430 */
1431PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1432 unsigned code,
1433 const pj_str_t *reason,
1434 const pjsua_msg_data *msg_data)
1435{
1436 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001437 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001438 pjsip_tx_data *tdata;
1439 pj_status_t status;
1440
1441 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1442 PJ_EINVAL);
1443
Benny Prijonodc752ca2006-09-22 16:55:42 +00001444 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001445 if (status != PJ_SUCCESS)
1446 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001447
Benny Prijono2e507c22006-06-23 15:04:11 +00001448 if (call->res_time.sec == 0)
1449 pj_gettimeofday(&call->res_time);
1450
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001451 if (reason && reason->slen == 0)
1452 reason = NULL;
1453
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001454 /* Create response message */
1455 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1456 if (status != PJ_SUCCESS) {
1457 pjsua_perror(THIS_FILE, "Error creating response",
1458 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001459 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001460 return status;
1461 }
1462
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001463 /* Call might have been disconnected if application is answering with
1464 * 200/OK and the media failed to start.
1465 */
1466 if (call->inv == NULL) {
1467 pjsip_dlg_dec_lock(dlg);
1468 return PJSIP_ESESSIONTERMINATED;
1469 }
1470
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001471 /* Add additional headers etc */
1472 pjsua_process_msg_data( tdata, msg_data);
1473
1474 /* Send the message */
1475 status = pjsip_inv_send_msg(call->inv, tdata);
1476 if (status != PJ_SUCCESS)
1477 pjsua_perror(THIS_FILE, "Error sending response",
1478 status);
1479
Benny Prijonodc752ca2006-09-22 16:55:42 +00001480 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001481
1482 return status;
1483}
1484
1485
1486/*
1487 * Hangup call by using method that is appropriate according to the
1488 * call state.
1489 */
1490PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1491 unsigned code,
1492 const pj_str_t *reason,
1493 const pjsua_msg_data *msg_data)
1494{
1495 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001496 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001497 pj_status_t status;
1498 pjsip_tx_data *tdata;
1499
1500
Benny Prijono148c9dd2006-09-19 13:37:53 +00001501 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1502 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1503 call_id));
1504 }
1505
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001506 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1507 PJ_EINVAL);
1508
Benny Prijonodc752ca2006-09-22 16:55:42 +00001509 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001510 if (status != PJ_SUCCESS)
1511 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001512
1513 if (code==0) {
1514 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1515 code = PJSIP_SC_OK;
1516 else if (call->inv->role == PJSIP_ROLE_UAS)
1517 code = PJSIP_SC_DECLINE;
1518 else
1519 code = PJSIP_SC_REQUEST_TERMINATED;
1520 }
1521
1522 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1523 if (status != PJ_SUCCESS) {
1524 pjsua_perror(THIS_FILE,
1525 "Failed to create end session message",
1526 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001527 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001528 return status;
1529 }
1530
1531 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1532 * as p_tdata when INVITE transaction has not been answered
1533 * with any provisional responses.
1534 */
1535 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001536 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001537 return PJ_SUCCESS;
1538 }
1539
1540 /* Add additional headers etc */
1541 pjsua_process_msg_data( tdata, msg_data);
1542
1543 /* Send the message */
1544 status = pjsip_inv_send_msg(call->inv, tdata);
1545 if (status != PJ_SUCCESS) {
1546 pjsua_perror(THIS_FILE,
1547 "Failed to send end session message",
1548 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001549 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001550 return status;
1551 }
1552
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00001553 /* Stop lock codec timer, if it is active */
1554 if (call->lock_codec.reinv_timer.id) {
1555 pjsip_endpt_cancel_timer(pjsua_var.endpt,
1556 &call->lock_codec.reinv_timer);
1557 call->lock_codec.reinv_timer.id = PJ_FALSE;
1558 }
1559
Benny Prijonodc752ca2006-09-22 16:55:42 +00001560 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001561
1562 return PJ_SUCCESS;
1563}
1564
1565
1566/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001567 * Accept or reject redirection.
1568 */
1569PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1570 pjsip_redirect_op cmd)
1571{
1572 pjsua_call *call;
1573 pjsip_dialog *dlg;
1574 pj_status_t status;
1575
1576 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1577 PJ_EINVAL);
1578
1579 status = acquire_call("pjsua_call_process_redirect()", call_id,
1580 &call, &dlg);
1581 if (status != PJ_SUCCESS)
1582 return status;
1583
1584 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1585
1586 pjsip_dlg_dec_lock(dlg);
1587
1588 return status;
1589}
1590
1591
1592/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001593 * Put the specified call on hold.
1594 */
1595PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1596 const pjsua_msg_data *msg_data)
1597{
1598 pjmedia_sdp_session *sdp;
1599 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001600 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001601 pjsip_tx_data *tdata;
1602 pj_status_t status;
1603
1604 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1605 PJ_EINVAL);
1606
Benny Prijonodc752ca2006-09-22 16:55:42 +00001607 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001608 if (status != PJ_SUCCESS)
1609 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001610
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001611
1612 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1613 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001614 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001615 return PJSIP_ESESSIONSTATE;
1616 }
1617
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001618 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001619 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001620 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001621 return status;
1622 }
1623
1624 /* Create re-INVITE with new offer */
1625 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1626 if (status != PJ_SUCCESS) {
1627 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001628 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001629 return status;
1630 }
1631
1632 /* Add additional headers etc */
1633 pjsua_process_msg_data( tdata, msg_data);
1634
1635 /* Send the request */
1636 status = pjsip_inv_send_msg( call->inv, tdata);
1637 if (status != PJ_SUCCESS) {
1638 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001639 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001640 return status;
1641 }
1642
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001643 /* Set flag that local put the call on hold */
1644 call->local_hold = PJ_TRUE;
1645
Benny Prijonodc752ca2006-09-22 16:55:42 +00001646 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001647
1648 return PJ_SUCCESS;
1649}
1650
1651
1652/*
1653 * Send re-INVITE (to release hold).
1654 */
1655PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1656 pj_bool_t unhold,
1657 const pjsua_msg_data *msg_data)
1658{
1659 pjmedia_sdp_session *sdp;
1660 pjsip_tx_data *tdata;
1661 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001662 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001663 pj_status_t status;
1664
1665
1666 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1667 PJ_EINVAL);
1668
Benny Prijonodc752ca2006-09-22 16:55:42 +00001669 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001670 if (status != PJ_SUCCESS)
1671 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001672
1673 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1674 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001675 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001676 return PJSIP_ESESSIONSTATE;
1677 }
1678
1679 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001680 if (call->local_hold && !unhold) {
1681 status = create_sdp_of_call_hold(call, &sdp);
1682 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001683 status = pjsua_media_channel_create_sdp(call->index,
1684 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001685 NULL, &sdp, NULL);
1686 call->local_hold = PJ_FALSE;
1687 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001688 if (status != PJ_SUCCESS) {
1689 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1690 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001691 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001692 return status;
1693 }
1694
1695 /* Create re-INVITE with new offer */
1696 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1697 if (status != PJ_SUCCESS) {
1698 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001699 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001700 return status;
1701 }
1702
1703 /* Add additional headers etc */
1704 pjsua_process_msg_data( tdata, msg_data);
1705
1706 /* Send the request */
1707 status = pjsip_inv_send_msg( call->inv, tdata);
1708 if (status != PJ_SUCCESS) {
1709 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001710 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001711 return status;
1712 }
1713
Benny Prijonodc752ca2006-09-22 16:55:42 +00001714 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001715
1716 return PJ_SUCCESS;
1717}
1718
1719
1720/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001721 * Send UPDATE request.
1722 */
1723PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1724 unsigned options,
1725 const pjsua_msg_data *msg_data)
1726{
1727 pjmedia_sdp_session *sdp;
1728 pjsip_tx_data *tdata;
1729 pjsua_call *call;
1730 pjsip_dialog *dlg;
1731 pj_status_t status;
1732
1733 PJ_UNUSED_ARG(options);
1734
1735 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1736 PJ_EINVAL);
1737
1738 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1739 if (status != PJ_SUCCESS)
1740 return status;
1741
Benny Prijonoc08682e2007-10-04 06:17:58 +00001742 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001743 status = pjsua_media_channel_create_sdp(call->index,
1744 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001745 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001746 if (status != PJ_SUCCESS) {
1747 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1748 status);
1749 pjsip_dlg_dec_lock(dlg);
1750 return status;
1751 }
1752
Benny Prijono224b4e22008-06-19 14:10:28 +00001753 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001754 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1755 if (status != PJ_SUCCESS) {
1756 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1757 pjsip_dlg_dec_lock(dlg);
1758 return status;
1759 }
1760
1761 /* Add additional headers etc */
1762 pjsua_process_msg_data( tdata, msg_data);
1763
1764 /* Send the request */
1765 status = pjsip_inv_send_msg( call->inv, tdata);
1766 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001767 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001768 pjsip_dlg_dec_lock(dlg);
1769 return status;
1770 }
1771
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001772 call->local_hold = PJ_FALSE;
1773
Benny Prijonoc08682e2007-10-04 06:17:58 +00001774 pjsip_dlg_dec_lock(dlg);
1775
1776 return PJ_SUCCESS;
1777}
1778
1779
1780/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001781 * Initiate call transfer to the specified address.
1782 */
1783PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1784 const pj_str_t *dest,
1785 const pjsua_msg_data *msg_data)
1786{
1787 pjsip_evsub *sub;
1788 pjsip_tx_data *tdata;
1789 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001790 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001791 pjsip_generic_string_hdr *gs_hdr;
1792 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001793 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001794 pj_status_t status;
1795
1796
1797 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1798 PJ_EINVAL);
1799
Benny Prijonodc752ca2006-09-22 16:55:42 +00001800 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001801 if (status != PJ_SUCCESS)
1802 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001803
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001804
Benny Prijonod524e822006-09-22 12:48:18 +00001805 /* Create xfer client subscription. */
1806 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001807 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001808
1809 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001810 if (status != PJ_SUCCESS) {
1811 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001812 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001813 return status;
1814 }
1815
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001816 /* Associate this call with the client subscription */
1817 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1818
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001819 /*
1820 * Create REFER request.
1821 */
1822 status = pjsip_xfer_initiate(sub, dest, &tdata);
1823 if (status != PJ_SUCCESS) {
1824 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001825 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001826 return status;
1827 }
1828
Benny Prijono053f5222006-11-11 16:16:04 +00001829 /* Add Referred-By header */
1830 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1831 &dlg->local.info_str);
1832 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1833
1834
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001835 /* Add additional headers etc */
1836 pjsua_process_msg_data( tdata, msg_data);
1837
1838 /* Send. */
1839 status = pjsip_xfer_send_request(sub, tdata);
1840 if (status != PJ_SUCCESS) {
1841 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001842 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001843 return status;
1844 }
1845
1846 /* For simplicity (that's what this program is intended to be!),
1847 * leave the original invite session as it is. More advanced application
1848 * may want to hold the INVITE, or terminate the invite, or whatever.
1849 */
1850
Benny Prijonodc752ca2006-09-22 16:55:42 +00001851 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001852
1853 return PJ_SUCCESS;
1854
1855}
1856
1857
1858/*
Benny Prijono053f5222006-11-11 16:16:04 +00001859 * Initiate attended call transfer to the specified address.
1860 */
1861PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1862 pjsua_call_id dest_call_id,
1863 unsigned options,
1864 const pjsua_msg_data *msg_data)
1865{
1866 pjsua_call *dest_call;
1867 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001868 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001869 pj_str_t str_dest;
1870 int len;
1871 pjsip_uri *uri;
1872 pj_status_t status;
1873
1874
1875 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1876 PJ_EINVAL);
1877 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1878 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1879 PJ_EINVAL);
1880
1881 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1882 &dest_call, &dest_dlg);
1883 if (status != PJ_SUCCESS)
1884 return status;
1885
1886 /*
1887 * Create REFER destination URI with Replaces field.
1888 */
1889
1890 /* Make sure we have sufficient buffer's length */
1891 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1892 dest_dlg->call_id->id.slen +
1893 dest_dlg->remote.info->tag.slen +
1894 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001895 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001896
1897 /* Print URI */
1898 str_dest_buf[0] = '<';
1899 str_dest.slen = 1;
1900
Benny Prijonoa1e69682007-05-11 15:14:34 +00001901 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001902 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1903 str_dest_buf+1, sizeof(str_dest_buf)-1);
1904 if (len < 0)
1905 return PJSIP_EURITOOLONG;
1906
1907 str_dest.slen += len;
1908
1909
1910 /* Build the URI */
1911 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1912 sizeof(str_dest_buf) - str_dest.slen,
1913 "?%s"
1914 "Replaces=%.*s"
1915 "%%3Bto-tag%%3D%.*s"
1916 "%%3Bfrom-tag%%3D%.*s>",
1917 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1918 "" : "Require=replaces&"),
1919 (int)dest_dlg->call_id->id.slen,
1920 dest_dlg->call_id->id.ptr,
1921 (int)dest_dlg->remote.info->tag.slen,
1922 dest_dlg->remote.info->tag.ptr,
1923 (int)dest_dlg->local.info->tag.slen,
1924 dest_dlg->local.info->tag.ptr);
1925
1926 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1927 PJSIP_EURITOOLONG);
1928
1929 str_dest.ptr = str_dest_buf;
1930 str_dest.slen += len;
1931
1932 pjsip_dlg_dec_lock(dest_dlg);
1933
1934 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1935}
1936
1937
1938/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001939 * Send DTMF digits to remote using RFC 2833 payload formats.
1940 */
1941PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1942 const pj_str_t *digits)
1943{
1944 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001945 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001946 pj_status_t status;
1947
1948 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1949 PJ_EINVAL);
1950
Benny Prijonodc752ca2006-09-22 16:55:42 +00001951 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001952 if (status != PJ_SUCCESS)
1953 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001954
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001955 if (!call->session) {
1956 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001957 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001958 return PJ_EINVALIDOP;
1959 }
1960
1961 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1962
Benny Prijonodc752ca2006-09-22 16:55:42 +00001963 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001964
1965 return status;
1966}
1967
1968
1969/**
1970 * Send instant messaging inside INVITE session.
1971 */
1972PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1973 const pj_str_t *mime_type,
1974 const pj_str_t *content,
1975 const pjsua_msg_data *msg_data,
1976 void *user_data)
1977{
1978 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001979 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001980 const pj_str_t mime_text_plain = pj_str("text/plain");
1981 pjsip_media_type ctype;
1982 pjsua_im_data *im_data;
1983 pjsip_tx_data *tdata;
1984 pj_status_t status;
1985
1986
1987 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1988 PJ_EINVAL);
1989
Benny Prijonodc752ca2006-09-22 16:55:42 +00001990 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001991 if (status != PJ_SUCCESS)
1992 return status;
1993
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001994 /* Set default media type if none is specified */
1995 if (mime_type == NULL) {
1996 mime_type = &mime_text_plain;
1997 }
1998
1999 /* Create request message. */
2000 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2001 -1, &tdata);
2002 if (status != PJ_SUCCESS) {
2003 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2004 goto on_return;
2005 }
2006
2007 /* Add accept header. */
2008 pjsip_msg_add_hdr( tdata->msg,
2009 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
2010
2011 /* Parse MIME type */
2012 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
2013
2014 /* Create "text/plain" message body. */
2015 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
2016 &ctype.subtype, content);
2017 if (tdata->msg->body == NULL) {
2018 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
2019 pjsip_tx_data_dec_ref(tdata);
2020 goto on_return;
2021 }
2022
2023 /* Add additional headers etc */
2024 pjsua_process_msg_data( tdata, msg_data);
2025
2026 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002027 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002028 im_data->acc_id = call->acc_id;
2029 im_data->call_id = call_id;
2030 im_data->to = call->inv->dlg->remote.info_str;
2031 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2032 im_data->user_data = user_data;
2033
2034
2035 /* Send the request. */
2036 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2037 pjsua_var.mod.id, im_data);
2038 if (status != PJ_SUCCESS) {
2039 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2040 goto on_return;
2041 }
2042
2043on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002044 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002045 return status;
2046}
2047
2048
2049/*
2050 * Send IM typing indication inside INVITE session.
2051 */
2052PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2053 pj_bool_t is_typing,
2054 const pjsua_msg_data*msg_data)
2055{
2056 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002057 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002058 pjsip_tx_data *tdata;
2059 pj_status_t status;
2060
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002061 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2062 PJ_EINVAL);
2063
Benny Prijonodc752ca2006-09-22 16:55:42 +00002064 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002065 if (status != PJ_SUCCESS)
2066 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002067
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002068 /* Create request message. */
2069 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2070 -1, &tdata);
2071 if (status != PJ_SUCCESS) {
2072 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2073 goto on_return;
2074 }
2075
2076 /* Create "application/im-iscomposing+xml" msg body. */
2077 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2078 NULL, NULL, -1);
2079
2080 /* Add additional headers etc */
2081 pjsua_process_msg_data( tdata, msg_data);
2082
2083 /* Send the request. */
2084 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2085 if (status != PJ_SUCCESS) {
2086 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2087 goto on_return;
2088 }
2089
2090on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002091 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002092 return status;
2093}
2094
2095
2096/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002097 * Send arbitrary request.
2098 */
2099PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2100 const pj_str_t *method_str,
2101 const pjsua_msg_data *msg_data)
2102{
2103 pjsua_call *call;
2104 pjsip_dialog *dlg;
2105 pjsip_method method;
2106 pjsip_tx_data *tdata;
2107 pj_status_t status;
2108
2109 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2110 PJ_EINVAL);
2111
2112 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2113 if (status != PJ_SUCCESS)
2114 return status;
2115
2116 /* Init method */
2117 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2118
2119 /* Create request message. */
2120 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2121 if (status != PJ_SUCCESS) {
2122 pjsua_perror(THIS_FILE, "Unable to create request", status);
2123 goto on_return;
2124 }
2125
2126 /* Add additional headers etc */
2127 pjsua_process_msg_data( tdata, msg_data);
2128
2129 /* Send the request. */
2130 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2131 if (status != PJ_SUCCESS) {
2132 pjsua_perror(THIS_FILE, "Unable to send request", status);
2133 goto on_return;
2134 }
2135
2136on_return:
2137 pjsip_dlg_dec_lock(dlg);
2138 return status;
2139}
2140
2141
2142/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002143 * Terminate all calls.
2144 */
2145PJ_DEF(void) pjsua_call_hangup_all(void)
2146{
2147 unsigned i;
2148
2149 PJSUA_LOCK();
2150
2151 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2152 if (pjsua_var.calls[i].inv)
2153 pjsua_call_hangup(i, 0, NULL, NULL);
2154 }
2155
2156 PJSUA_UNLOCK();
2157}
2158
2159
Benny Prijono627cbb42007-09-25 20:48:49 +00002160const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002161{
2162 if (val < 1000) {
2163 pj_ansi_sprintf(buf, "%d", val);
2164 } else if (val < 1000000) {
2165 pj_ansi_sprintf(buf, "%d.%dK",
2166 val / 1000,
2167 (val % 1000) / 100);
2168 } else {
2169 pj_ansi_sprintf(buf, "%d.%02dM",
2170 val / 1000000,
2171 (val % 1000000) / 10000);
2172 }
2173
2174 return buf;
2175}
2176
2177
2178/* Dump media session */
2179static void dump_media_session(const char *indent,
2180 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002181 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002182{
2183 unsigned i;
2184 char *p = buf, *end = buf+maxlen;
2185 int len;
2186 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002187 pjmedia_session *session = call->session;
2188 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002189
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002190 pjmedia_transport_info_init(&tp_info);
2191
2192 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002193 pjmedia_session_get_info(session, &info);
2194
2195 for (i=0; i<info.stream_cnt; ++i) {
2196 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002197 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002198 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002199 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002200 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002201 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002202 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002203
2204 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002205 // rem_addr will contain actual address of RTP originator, instead of
2206 // remote RTP address specified by stream which is fetched from the SDP.
2207 // Please note that we are assuming only one stream per call.
2208 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2209 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002210 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2211 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002212 sizeof(rem_addr_buf), 3);
2213 } else {
2214 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002215 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002216 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002217
Benny Prijono2dbf5072010-06-23 12:38:28 +00002218 if (call->media_dir == PJMEDIA_DIR_NONE) {
2219 /* To handle when the stream that is currently being paused
2220 * (http://trac.pjsip.org/repos/ticket/1079)
2221 */
2222 dir = "inactive";
2223 } else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002224 dir = "sendonly";
2225 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2226 dir = "recvonly";
2227 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2228 dir = "sendrecv";
2229 else
2230 dir = "inactive";
2231
2232
2233 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002234 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002235 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002236 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002237 info.stream_info[i].fmt.encoding_name.ptr,
2238 info.stream_info[i].fmt.clock_rate / 1000,
2239 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002240 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002241 if (len < 1 || len > end-p) {
2242 *p = '\0';
2243 return;
2244 }
2245
2246 p += len;
2247 *p++ = '\n';
2248 *p = '\0';
2249
2250 if (stat.rx.update_cnt == 0)
2251 strcpy(last_update, "never");
2252 else {
2253 pj_gettimeofday(&now);
2254 PJ_TIME_VAL_SUB(now, stat.rx.update);
2255 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2256 now.sec / 3600,
2257 (now.sec % 3600) / 60,
2258 now.sec % 60,
2259 now.msec);
2260 }
2261
Benny Prijono80019eb2006-08-07 13:22:23 +00002262 pj_gettimeofday(&media_duration);
2263 PJ_TIME_VAL_SUB(media_duration, stat.start);
2264 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2265 media_duration.msec = 1;
2266
Benny Prijono1402a4a2008-01-08 23:41:22 +00002267 /* protect against division by zero */
2268 if (stat.rx.pkt == 0)
2269 stat.rx.pkt = 1;
2270 if (stat.tx.pkt == 0)
2271 stat.tx.pkt = 1;
2272
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002273 len = pj_ansi_snprintf(p, end-p,
2274 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002275 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002276 "%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 +00002277 "%s (msec) min avg max last dev\n"
2278 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
Nanang Izzuddin78bec1a2010-07-15 14:45:47 +00002279 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f"
2280#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2281 "\n"
2282 "%s raw jitter : %7.3f %7.3f %7.3f %7.3f %7.3f"
2283#endif
2284#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2285 "\n"
2286 "%s IPDV : %7.3f %7.3f %7.3f %7.3f %7.3f"
2287#endif
2288 "%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002289 indent, info.stream_info[i].fmt.pt,
2290 last_update,
2291 indent,
2292 good_number(packets, stat.rx.pkt),
2293 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002294 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002295 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2296 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 +00002297 indent,
2298 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002299 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002300 stat.rx.discard,
2301 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002302 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002303 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002304 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002305 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002306 indent, indent,
2307 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002308 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002309 stat.rx.loss_period.max / 1000.0,
2310 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002311 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002312 indent,
2313 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002314 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002315 stat.rx.jitter.max / 1000.0,
2316 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002317 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Nanang Izzuddin78bec1a2010-07-15 14:45:47 +00002318#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2319 indent,
2320 stat.rx_raw_jitter.min / 1000.0,
2321 stat.rx_raw_jitter.mean / 1000.0,
2322 stat.rx_raw_jitter.max / 1000.0,
2323 stat.rx_raw_jitter.last / 1000.0,
2324 pj_math_stat_get_stddev(&stat.rx_raw_jitter) / 1000.0,
2325#endif
2326#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2327 indent,
2328 stat.rx_ipdv.min / 1000.0,
2329 stat.rx_ipdv.mean / 1000.0,
2330 stat.rx_ipdv.max / 1000.0,
2331 stat.rx_ipdv.last / 1000.0,
2332 pj_math_stat_get_stddev(&stat.rx_ipdv) / 1000.0,
2333#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002334 ""
2335 );
2336
2337 if (len < 1 || len > end-p) {
2338 *p = '\0';
2339 return;
2340 }
2341
2342 p += len;
2343 *p++ = '\n';
2344 *p = '\0';
2345
2346 if (stat.tx.update_cnt == 0)
2347 strcpy(last_update, "never");
2348 else {
2349 pj_gettimeofday(&now);
2350 PJ_TIME_VAL_SUB(now, stat.tx.update);
2351 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2352 now.sec / 3600,
2353 (now.sec % 3600) / 60,
2354 now.sec % 60,
2355 now.msec);
2356 }
2357
2358 len = pj_ansi_snprintf(p, end-p,
2359 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002360 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002361 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002362 "%s (msec) min avg max last dev \n"
2363 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2364 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002365 indent,
2366 info.stream_info[i].tx_pt,
2367 info.stream_info[i].param->info.frm_ptime *
2368 info.stream_info[i].param->setting.frm_per_pkt,
2369 last_update,
2370
2371 indent,
2372 good_number(packets, stat.tx.pkt),
2373 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002374 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002375 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2376 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 +00002377
2378 indent,
2379 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002380 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002381 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002382 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002383 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002384 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002385
2386 indent, indent,
2387 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002388 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002389 stat.tx.loss_period.max / 1000.0,
2390 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002391 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002392 indent,
2393 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002394 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002395 stat.tx.jitter.max / 1000.0,
2396 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002397 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002398 ""
2399 );
2400
2401 if (len < 1 || len > end-p) {
2402 *p = '\0';
2403 return;
2404 }
2405
2406 p += len;
2407 *p++ = '\n';
2408 *p = '\0';
2409
2410 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002411 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002412 indent,
2413 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002414 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002415 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002416 stat.rtt.last / 1000.0,
2417 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002418 );
2419 if (len < 1 || len > end-p) {
2420 *p = '\0';
2421 return;
2422 }
2423
2424 p += len;
2425 *p++ = '\n';
2426 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002427
2428#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2429# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2430 do { \
2431 if (samples <= 4294) \
2432 usec = samples * 1000000 / clock_rate; \
2433 else { \
2434 usec = samples * 1000 / clock_rate; \
2435 usec *= 1000; \
2436 } \
2437 } while(0)
2438
2439# define PRINT_VOIP_MTC_VAL(s, v) \
2440 if (v == 127) \
2441 sprintf(s, "(na)"); \
2442 else \
2443 sprintf(s, "%d", v)
2444
2445# define VALIDATE_PRINT_BUF() \
2446 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2447 p += len; *p++ = '\n'; *p = '\0'
2448
2449
2450 do {
2451 char loss[16], dup[16];
2452 char jitter[80];
2453 char toh[80];
2454 char plc[16], jba[16], jbr[16];
2455 char signal_lvl[16], noise_lvl[16], rerl[16];
2456 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2457 pjmedia_rtcp_xr_stat xr_stat;
2458 unsigned clock_rate;
2459
2460 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2461 PJ_SUCCESS)
2462 {
2463 break;
2464 }
2465
2466 clock_rate = info.stream_info[i].fmt.clock_rate;
2467
2468 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2469 VALIDATE_PRINT_BUF();
2470
2471 /* Statistics Summary */
2472 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2473 VALIDATE_PRINT_BUF();
2474
2475 if (xr_stat.rx.stat_sum.l)
2476 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2477 else
2478 sprintf(loss, "(na)");
2479
2480 if (xr_stat.rx.stat_sum.d)
2481 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2482 else
2483 sprintf(dup, "(na)");
2484
2485 if (xr_stat.rx.stat_sum.j) {
2486 unsigned jmin, jmax, jmean, jdev;
2487
2488 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2489 clock_rate);
2490 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2491 clock_rate);
2492 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2493 clock_rate);
2494 SAMPLES_TO_USEC(jdev,
2495 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2496 clock_rate);
2497 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2498 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2499 } else
2500 sprintf(jitter, "(report not available)");
2501
2502 if (xr_stat.rx.stat_sum.t) {
2503 sprintf(toh, "%11d %11d %11d %11d",
2504 xr_stat.rx.stat_sum.toh.min,
2505 xr_stat.rx.stat_sum.toh.mean,
2506 xr_stat.rx.stat_sum.toh.max,
2507 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2508 } else
2509 sprintf(toh, "(report not available)");
2510
2511 if (xr_stat.rx.stat_sum.update.sec == 0)
2512 strcpy(last_update, "never");
2513 else {
2514 pj_gettimeofday(&now);
2515 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2516 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2517 now.sec / 3600,
2518 (now.sec % 3600) / 60,
2519 now.sec % 60,
2520 now.msec);
2521 }
2522
2523 len = pj_ansi_snprintf(p, end-p,
2524 "%s RX last update: %s\n"
2525 "%s begin seq=%d, end seq=%d\n"
2526 "%s pkt loss=%s, dup=%s\n"
2527 "%s (msec) min avg max dev\n"
2528 "%s jitter : %s\n"
2529 "%s toh : %s",
2530 indent, last_update,
2531 indent,
2532 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2533 indent, loss, dup,
2534 indent,
2535 indent, jitter,
2536 indent, toh
2537 );
2538 VALIDATE_PRINT_BUF();
2539
2540 if (xr_stat.tx.stat_sum.l)
2541 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2542 else
2543 sprintf(loss, "(na)");
2544
2545 if (xr_stat.tx.stat_sum.d)
2546 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2547 else
2548 sprintf(dup, "(na)");
2549
2550 if (xr_stat.tx.stat_sum.j) {
2551 unsigned jmin, jmax, jmean, jdev;
2552
2553 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2554 clock_rate);
2555 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2556 clock_rate);
2557 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2558 clock_rate);
2559 SAMPLES_TO_USEC(jdev,
2560 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2561 clock_rate);
2562 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2563 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2564 } else
2565 sprintf(jitter, "(report not available)");
2566
2567 if (xr_stat.tx.stat_sum.t) {
2568 sprintf(toh, "%11d %11d %11d %11d",
2569 xr_stat.tx.stat_sum.toh.min,
2570 xr_stat.tx.stat_sum.toh.mean,
2571 xr_stat.tx.stat_sum.toh.max,
2572 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2573 } else
2574 sprintf(toh, "(report not available)");
2575
2576 if (xr_stat.tx.stat_sum.update.sec == 0)
2577 strcpy(last_update, "never");
2578 else {
2579 pj_gettimeofday(&now);
2580 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2581 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2582 now.sec / 3600,
2583 (now.sec % 3600) / 60,
2584 now.sec % 60,
2585 now.msec);
2586 }
2587
2588 len = pj_ansi_snprintf(p, end-p,
2589 "%s TX last update: %s\n"
2590 "%s begin seq=%d, end seq=%d\n"
2591 "%s pkt loss=%s, dup=%s\n"
2592 "%s (msec) min avg max dev\n"
2593 "%s jitter : %s\n"
2594 "%s toh : %s",
2595 indent, last_update,
2596 indent,
2597 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2598 indent, loss, dup,
2599 indent,
2600 indent, jitter,
2601 indent, toh
2602 );
2603 VALIDATE_PRINT_BUF();
2604
2605
2606 /* VoIP Metrics */
2607 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2608 VALIDATE_PRINT_BUF();
2609
2610 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2611 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2612 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2613 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2614 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2615 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2616 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2617
2618 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2619 case PJMEDIA_RTCP_XR_PLC_DIS:
2620 sprintf(plc, "DISABLED");
2621 break;
2622 case PJMEDIA_RTCP_XR_PLC_ENH:
2623 sprintf(plc, "ENHANCED");
2624 break;
2625 case PJMEDIA_RTCP_XR_PLC_STD:
2626 sprintf(plc, "STANDARD");
2627 break;
2628 case PJMEDIA_RTCP_XR_PLC_UNK:
2629 default:
2630 sprintf(plc, "UNKNOWN");
2631 break;
2632 }
2633
2634 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2635 case PJMEDIA_RTCP_XR_JB_FIXED:
2636 sprintf(jba, "FIXED");
2637 break;
2638 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2639 sprintf(jba, "ADAPTIVE");
2640 break;
2641 default:
2642 sprintf(jba, "UNKNOWN");
2643 break;
2644 }
2645
2646 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2647
2648 if (xr_stat.rx.voip_mtc.update.sec == 0)
2649 strcpy(last_update, "never");
2650 else {
2651 pj_gettimeofday(&now);
2652 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2653 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2654 now.sec / 3600,
2655 (now.sec % 3600) / 60,
2656 now.sec % 60,
2657 now.msec);
2658 }
2659
2660 len = pj_ansi_snprintf(p, end-p,
2661 "%s RX last update: %s\n"
2662 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2663 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2664 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2665 "%s delay : round trip=%d%s, end system=%d%s\n"
2666 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2667 "%s quality : R factor=%s, ext R factor=%s\n"
2668 "%s MOS LQ=%s, MOS CQ=%s\n"
2669 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2670 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2671 indent,
2672 last_update,
2673 /* packets */
2674 indent,
2675 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2676 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2677 /* burst */
2678 indent,
2679 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2680 xr_stat.rx.voip_mtc.burst_dur, "ms",
2681 /* gap */
2682 indent,
2683 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2684 xr_stat.rx.voip_mtc.gap_dur, "ms",
2685 /* delay */
2686 indent,
2687 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2688 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2689 /* level */
2690 indent,
2691 signal_lvl, "dB",
2692 noise_lvl, "dB",
2693 rerl, "",
2694 /* quality */
2695 indent,
2696 r_factor, ext_r_factor,
2697 indent,
2698 mos_lq, mos_cq,
2699 /* config */
2700 indent,
2701 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2702 /* JB delay */
2703 indent,
2704 xr_stat.rx.voip_mtc.jb_nom, "ms",
2705 xr_stat.rx.voip_mtc.jb_max, "ms",
2706 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2707 );
2708 VALIDATE_PRINT_BUF();
2709
2710 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2711 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2712 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2713 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2714 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2715 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2716 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2717
2718 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2719 case PJMEDIA_RTCP_XR_PLC_DIS:
2720 sprintf(plc, "DISABLED");
2721 break;
2722 case PJMEDIA_RTCP_XR_PLC_ENH:
2723 sprintf(plc, "ENHANCED");
2724 break;
2725 case PJMEDIA_RTCP_XR_PLC_STD:
2726 sprintf(plc, "STANDARD");
2727 break;
2728 case PJMEDIA_RTCP_XR_PLC_UNK:
2729 default:
2730 sprintf(plc, "unknown");
2731 break;
2732 }
2733
2734 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2735 case PJMEDIA_RTCP_XR_JB_FIXED:
2736 sprintf(jba, "FIXED");
2737 break;
2738 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2739 sprintf(jba, "ADAPTIVE");
2740 break;
2741 default:
2742 sprintf(jba, "unknown");
2743 break;
2744 }
2745
2746 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2747
2748 if (xr_stat.tx.voip_mtc.update.sec == 0)
2749 strcpy(last_update, "never");
2750 else {
2751 pj_gettimeofday(&now);
2752 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2753 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2754 now.sec / 3600,
2755 (now.sec % 3600) / 60,
2756 now.sec % 60,
2757 now.msec);
2758 }
2759
2760 len = pj_ansi_snprintf(p, end-p,
2761 "%s TX last update: %s\n"
2762 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2763 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2764 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2765 "%s delay : round trip=%d%s, end system=%d%s\n"
2766 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2767 "%s quality : R factor=%s, ext R factor=%s\n"
2768 "%s MOS LQ=%s, MOS CQ=%s\n"
2769 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2770 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2771 indent,
2772 last_update,
2773 /* pakcets */
2774 indent,
2775 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2776 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2777 /* burst */
2778 indent,
2779 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2780 xr_stat.tx.voip_mtc.burst_dur, "ms",
2781 /* gap */
2782 indent,
2783 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2784 xr_stat.tx.voip_mtc.gap_dur, "ms",
2785 /* delay */
2786 indent,
2787 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2788 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2789 /* level */
2790 indent,
2791 signal_lvl, "dB",
2792 noise_lvl, "dB",
2793 rerl, "",
2794 /* quality */
2795 indent,
2796 r_factor, ext_r_factor,
2797 indent,
2798 mos_lq, mos_cq,
2799 /* config */
2800 indent,
2801 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2802 /* JB delay */
2803 indent,
2804 xr_stat.tx.voip_mtc.jb_nom, "ms",
2805 xr_stat.tx.voip_mtc.jb_max, "ms",
2806 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2807 );
2808 VALIDATE_PRINT_BUF();
2809
2810
2811 /* RTT delay (by receiver side) */
2812 len = pj_ansi_snprintf(p, end-p,
2813 "%s RTT (from recv) min avg max last dev",
2814 indent);
2815 VALIDATE_PRINT_BUF();
2816 len = pj_ansi_snprintf(p, end-p,
2817 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2818 indent,
2819 xr_stat.rtt.min / 1000.0,
2820 xr_stat.rtt.mean / 1000.0,
2821 xr_stat.rtt.max / 1000.0,
2822 xr_stat.rtt.last / 1000.0,
2823 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2824 );
2825 VALIDATE_PRINT_BUF();
2826 } while(0);
2827#endif
2828
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002829 }
2830}
2831
2832
2833/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002834void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002835 int call_id,
2836 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002837{
2838 int len;
2839 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2840 pjsip_dialog *dlg = inv->dlg;
2841 char userinfo[128];
2842
2843 /* Dump invite sesion info. */
2844
2845 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
Benny Prijono329d6382009-05-29 13:04:03 +00002846 if (len < 0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002847 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2848 else
2849 userinfo[len] = '\0';
2850
2851 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2852 title,
2853 pjsip_inv_state_name(inv->state),
2854 userinfo);
2855 if (len < 1 || len >= (int)size) {
2856 pj_ansi_strcpy(buf, "<--uri too long-->");
2857 len = 18;
2858 } else
2859 buf[len] = '\0';
2860}
2861
2862
2863/*
2864 * Dump call and media statistics to string.
2865 */
2866PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2867 pj_bool_t with_media,
2868 char *buffer,
2869 unsigned maxlen,
2870 const char *indent)
2871{
2872 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002873 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002874 pj_time_val duration, res_delay, con_delay;
2875 char tmp[128];
2876 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002877 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002878 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002879 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002880
2881 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2882 PJ_EINVAL);
2883
Benny Prijonodc752ca2006-09-22 16:55:42 +00002884 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002885 if (status != PJ_SUCCESS)
2886 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002887
2888 *buffer = '\0';
2889 p = buffer;
2890 end = buffer + maxlen;
2891 len = 0;
2892
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002893 print_call(indent, call_id, tmp, sizeof(tmp));
2894
2895 len = pj_ansi_strlen(tmp);
2896 pj_ansi_strcpy(buffer, tmp);
2897
2898 p += len;
2899 *p++ = '\r';
2900 *p++ = '\n';
2901
2902 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002903 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002904 pj_gettimeofday(&duration);
2905 PJ_TIME_VAL_SUB(duration, call->conn_time);
2906 con_delay = call->conn_time;
2907 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2908 } else {
2909 duration.sec = duration.msec = 0;
2910 con_delay.sec = con_delay.msec = 0;
2911 }
2912
2913 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002914 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002915 res_delay = call->res_time;
2916 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2917 } else {
2918 res_delay.sec = res_delay.msec = 0;
2919 }
2920
2921 /* Print duration */
2922 len = pj_ansi_snprintf(p, end-p,
2923 "%s Call time: %02dh:%02dm:%02ds, "
2924 "1st res in %d ms, conn in %dms",
2925 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002926 (int)(duration.sec / 3600),
2927 (int)((duration.sec % 3600)/60),
2928 (int)(duration.sec % 60),
2929 (int)PJ_TIME_VAL_MSEC(res_delay),
2930 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002931
2932 if (len > 0 && len < end-p) {
2933 p += len;
2934 *p++ = '\n';
2935 *p = '\0';
2936 }
2937
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002938 /* Get and ICE SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002939 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002940 pjmedia_transport_get_info(call->med_tp, &tp_info);
2941 if (tp_info.specific_info_cnt > 0) {
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002942 unsigned i;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002943 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2944 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2945 {
2946 pjmedia_srtp_info *srtp_info =
2947 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2948
2949 len = pj_ansi_snprintf(p, end-p,
2950 "%s SRTP status: %s Crypto-suite: %s",
2951 indent,
2952 (srtp_info->active?"Active":"Not active"),
2953 srtp_info->tx_policy.name.ptr);
2954 if (len > 0 && len < end-p) {
2955 p += len;
2956 *p++ = '\n';
2957 *p = '\0';
2958 }
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002959 } else if (tp_info.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
2960 const pjmedia_ice_transport_info *ii;
2961
2962 ii = (const pjmedia_ice_transport_info*)
2963 tp_info.spc_info[i].buffer;
2964
2965 len = pj_ansi_snprintf(p, end-p,
2966 "%s ICE role: %s, state: %s, comp_cnt: %u",
2967 indent,
2968 pj_ice_sess_role_name(ii->role),
2969 pj_ice_strans_state_name(ii->sess_state),
2970 ii->comp_cnt);
2971 if (len > 0 && len < end-p) {
2972 p += len;
2973 *p++ = '\n';
2974 *p = '\0';
2975 }
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002976 }
2977 }
2978 }
2979
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002980 /* Dump session statistics */
2981 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002982 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002983
Benny Prijonodc752ca2006-09-22 16:55:42 +00002984 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002985
2986 return PJ_SUCCESS;
2987}
2988
2989
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002990/* Timer callback to close sound device */
2991static void reinv_timer_cb(pj_timer_heap_t *th,
2992 pj_timer_entry *entry)
2993{
2994 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
2995 pjsip_dialog *dlg;
2996 pjsua_call *call;
2997 pjsip_tx_data *tdata;
2998 pj_status_t status;
2999
3000 PJ_UNUSED_ARG(th);
3001
3002 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
3003
3004 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
3005 if (status != PJ_SUCCESS)
3006 return;
3007
3008 /* Verify if another SDP negotiation is in progress, e.g: session timer
3009 * or another re-INVITE.
3010 */
3011 if (call->inv==NULL || call->inv->neg==NULL ||
3012 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
3013 {
3014 goto on_return;
3015 }
3016
3017 /* Verify if another SDP negotiation has been completed by comparing
3018 * the SDP version.
3019 */
3020 {
3021 const pjmedia_sdp_session *sdp;
3022
3023 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
3024 if (status == PJ_SUCCESS &&
3025 sdp->origin.version > call->lock_codec.new_sdp->origin.version)
3026 {
3027 goto on_return;
3028 }
3029 }
3030
3031 /* Create re-INVITE with the new offer */
3032 status = pjsip_inv_reinvite(call->inv, NULL, call->lock_codec.new_sdp,
3033 &tdata);
3034 if (status == PJ_EINVALIDOP) {
3035 /* Ups, let's reschedule again */
3036 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
3037 call->lock_codec.reinv_timer.id = PJ_TRUE;
3038 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3039 &call->lock_codec.reinv_timer, &delay);
3040 } else if (status != PJ_SUCCESS) {
3041 pjsua_perror(THIS_FILE, "Failed creating re-INVITE in lock codec",
3042 status);
3043 }
3044
3045 /* Send the UPDATE/re-INVITE request */
3046 status = pjsip_inv_send_msg(call->inv, tdata);
3047 if (status != PJ_SUCCESS) {
3048 pjsua_perror(THIS_FILE, "Failed sending re-INVITE in lock codec",
3049 status);
3050 }
3051
3052on_return:
3053 pjsip_dlg_dec_lock(dlg);
3054}
3055
3056
3057/* Check if the specified format can be skipped in counting codecs */
3058static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
3059 const pj_str_t *fmt)
3060{
3061 unsigned pt;
3062
3063 pt = pj_strtoul(fmt);
3064
3065 /* Check for comfort noise */
3066 if (pt == PJMEDIA_RTP_PT_CN)
3067 return PJ_TRUE;
3068
3069 /* Dynamic PT, check the format name */
3070 if (pt >= 96) {
3071 pjmedia_sdp_attr *a;
3072 pjmedia_sdp_rtpmap rtpmap;
3073
3074 /* Get the format name */
3075 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3076 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
3077 /* Check for telephone-event */
3078 if (pj_stricmp2(&rtpmap.enc_name, "telephone-event")==0)
3079 return PJ_TRUE;
3080 } else {
3081 /* Invalid SDP, should not reach here */
3082 pj_assert(!"SDP should have been validated!");
3083 return PJ_TRUE;
3084 }
3085 }
3086
3087 return PJ_FALSE;
3088}
3089
3090
3091/* Check if remote answerer has given us more than one codecs. If so,
3092 * create another offer with one codec only to lock down the codec.
3093 */
3094static pj_status_t lock_codec(pjsua_call *call)
3095{
3096 const pj_str_t st_update = {"UPDATE", 6};
3097 pjsip_inv_session *inv = call->inv;
3098 const pjmedia_sdp_session *local_sdp;
3099 const pjmedia_sdp_session *remote_sdp;
3100 const pjmedia_sdp_media *rem_m;
3101 pjmedia_sdp_session *new_sdp;
3102 pjmedia_sdp_media *m;
3103 pjsip_tx_data *tdata;
3104 unsigned i, codec_cnt = 0;
3105 pj_status_t status;
3106
3107 if (!pjmedia_sdp_neg_was_answer_remote(inv->neg))
3108 return PJ_SUCCESS;
3109
3110 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
3111 if (status != PJ_SUCCESS)
3112 return status;
3113 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3114 if (status != PJ_SUCCESS)
3115 return status;
3116
3117 PJ_ASSERT_RETURN(call->audio_idx>=0 &&
3118 call->audio_idx < (int)remote_sdp->media_count,
3119 PJ_EINVALIDOP);
3120
3121 rem_m = remote_sdp->media[call->audio_idx];
3122
3123 /* Check if media is disabled or only one format in the answer. */
3124 if (rem_m->desc.port==0 || rem_m->desc.fmt_count==1)
3125 return PJ_SUCCESS;
3126
3127 /* Count the formats in the answer. */
3128 for (i=0; i<rem_m->desc.fmt_count && codec_cnt <= 1; ++i) {
3129 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[i]))
3130 ++codec_cnt;
3131 }
3132
3133 if (codec_cnt <= 1) {
3134 /* Answer contains single codec. */
3135 return PJ_SUCCESS;
3136 }
3137
3138 PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, start "
3139 "updating media session to use only one codec.."));
3140
3141 /* Clone the offer */
3142 new_sdp = pjmedia_sdp_session_clone(inv->pool_prov, local_sdp);
3143 /* Note that the usage of pool_prov above is risky when locking codec
3144 * delays the re-INVITE (using timer) and there are two SDP negotiations
3145 * done before the re-INVITE.
3146 */
3147
3148 /* Update the new offer so it contains only a codec. Note that formats
3149 * order in the offer should have been matched to the answer, so we can
3150 * just directly update the offer without looking-up the answer.
3151 */
3152 m = new_sdp->media[call->audio_idx];
3153 codec_cnt = 0;
3154 i = 0;
3155 while (i < m->desc.fmt_count) {
3156 pjmedia_sdp_attr *a;
3157 pj_str_t *fmt = &m->desc.fmt[i];
3158
3159 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
3160 ++i;
3161 continue;
3162 }
3163
3164 /* Remove format */
3165 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3166 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3167 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
3168 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3169 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
3170 m->desc.fmt_count, i);
3171 --m->desc.fmt_count;
3172 }
3173
3174 /* Send new SDP offer via UPDATE or re-INVITE */
3175 if (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)==
3176 PJSIP_DIALOG_CAP_SUPPORTED)
3177 {
3178 /* Create UPDATE with the new offer */
3179 status = pjsip_inv_update(inv, NULL, new_sdp, &tdata);
3180 if (status != PJ_SUCCESS)
3181 return status;
3182
3183 } else {
3184 /* Create re-INVITE with the new offer */
3185 status = pjsip_inv_reinvite(inv, NULL, new_sdp, &tdata);
3186 if (status == PJ_EINVALIDOP) {
3187 /* Current INVITE transaction is pending, reschedule re-INVITE. */
3188 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
3189
3190 call->lock_codec.new_sdp = new_sdp;
3191 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
3192 (void*)(pj_size_t)call->index,
3193 &reinv_timer_cb);
3194 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3195 &call->lock_codec.reinv_timer, &delay);
3196 return PJ_SUCCESS;
3197
3198 } else if (status != PJ_SUCCESS)
3199 return status;
3200 }
3201
3202 /* Send the UPDATE/re-INVITE request */
3203 status = pjsip_inv_send_msg(inv, tdata);
3204 if (status != PJ_SUCCESS)
3205 return status;
3206
3207 return PJ_SUCCESS;
3208}
3209
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003210/*
Benny Prijono84126ab2006-02-09 09:30:09 +00003211 * This callback receives notification from invite session when the
3212 * session state has changed.
3213 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003214static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
3215 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00003216{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003217 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003218
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003219 PJSUA_LOCK();
3220
Benny Prijonoa1e69682007-05-11 15:14:34 +00003221 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003222
3223 if (!call) {
3224 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00003225 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003226 }
3227
Benny Prijonoe21e7842006-04-09 16:46:05 +00003228
3229 /* Get call times */
3230 switch (inv->state) {
3231 case PJSIP_INV_STATE_EARLY:
3232 case PJSIP_INV_STATE_CONNECTING:
3233 if (call->res_time.sec == 0)
3234 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00003235 call->last_code = (pjsip_status_code)
3236 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003237 pj_strncpy(&call->last_text,
3238 &e->body.tsx_state.tsx->status_text,
3239 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00003240 break;
3241 case PJSIP_INV_STATE_CONFIRMED:
3242 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003243
3244 /* Ticket #476, locking a codec in the media session. */
3245 {
3246 pj_status_t status;
3247 status = lock_codec(call);
3248 if (status != PJ_SUCCESS) {
3249 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
3250 }
3251 }
3252
Benny Prijonoe21e7842006-04-09 16:46:05 +00003253 break;
3254 case PJSIP_INV_STATE_DISCONNECTED:
3255 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00003256 if (call->res_time.sec == 0)
3257 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003258 if (e->type == PJSIP_EVENT_TSX_STATE &&
3259 e->body.tsx_state.tsx->status_code > call->last_code)
3260 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00003261 call->last_code = (pjsip_status_code)
3262 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003263 pj_strncpy(&call->last_text,
3264 &e->body.tsx_state.tsx->status_text,
3265 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003266 } else {
3267 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
3268 pj_strncpy(&call->last_text,
3269 pjsip_get_status_text(call->last_code),
3270 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00003271 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003272
3273 /* Stop lock codec timer, if it is active */
3274 if (call->lock_codec.reinv_timer.id) {
3275 pjsip_endpt_cancel_timer(pjsua_var.endpt,
3276 &call->lock_codec.reinv_timer);
3277 call->lock_codec.reinv_timer.id = PJ_FALSE;
3278 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00003279 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003280 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00003281 call->last_code = (pjsip_status_code)
3282 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003283 pj_strncpy(&call->last_text,
3284 &e->body.tsx_state.tsx->status_text,
3285 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00003286 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00003287 }
3288
Benny Prijono26ff9062006-02-21 23:47:00 +00003289 /* If this is an outgoing INVITE that was created because of
3290 * REFER/transfer, send NOTIFY to transferer.
3291 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00003292 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003293 int st_code = -1;
3294 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
3295
3296
Benny Prijonoa91a0032006-02-26 21:23:45 +00003297 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003298 case PJSIP_INV_STATE_NULL:
3299 case PJSIP_INV_STATE_CALLING:
3300 /* Do nothing */
3301 break;
3302
3303 case PJSIP_INV_STATE_EARLY:
3304 case PJSIP_INV_STATE_CONNECTING:
3305 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003306 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
3307 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3308 else
3309 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003310 break;
3311
Benny Prijono140beae2009-10-11 05:06:43 +00003312 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003313#if 0
3314/* We don't need this, as we've terminated the subscription in
3315 * CONNECTING state.
3316 */
Benny Prijono26ff9062006-02-21 23:47:00 +00003317 /* When state is confirmed, send the final 200/OK and terminate
3318 * subscription.
3319 */
3320 st_code = e->body.tsx_state.tsx->status_code;
3321 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003322#endif
Benny Prijono140beae2009-10-11 05:06:43 +00003323 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003324
3325 case PJSIP_INV_STATE_DISCONNECTED:
3326 st_code = e->body.tsx_state.tsx->status_code;
3327 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3328 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003329
Benny Prijono8b1889b2006-06-06 18:40:40 +00003330 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00003331 /* Nothing to do. Just to keep gcc from complaining about
3332 * unused enums.
3333 */
3334 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003335 }
3336
3337 if (st_code != -1) {
3338 pjsip_tx_data *tdata;
3339 pj_status_t status;
3340
Benny Prijonoa91a0032006-02-26 21:23:45 +00003341 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00003342 ev_state, st_code,
3343 NULL, &tdata);
3344 if (status != PJ_SUCCESS) {
3345 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
3346 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003347 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00003348 if (status != PJ_SUCCESS) {
3349 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
3350 }
3351 }
3352 }
3353 }
3354
Benny Prijono84126ab2006-02-09 09:30:09 +00003355
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003356 if (pjsua_var.ua_cfg.cb.on_call_state)
3357 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003358
3359 /* call->inv may be NULL now */
3360
Benny Prijono84126ab2006-02-09 09:30:09 +00003361 /* Destroy media session when invite session is disconnected. */
3362 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003363
Benny Prijonoa91a0032006-02-26 21:23:45 +00003364 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003365
Benny Prijono275fd682006-03-22 11:59:11 +00003366 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003367 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003368
Benny Prijono105217f2006-03-06 16:25:59 +00003369 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003370 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003371 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003372
3373 /* Reset call */
3374 reset_call(call->index);
3375
Benny Prijono84126ab2006-02-09 09:30:09 +00003376 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003377
3378 PJSUA_UNLOCK();
3379}
3380
3381/*
3382 * This callback is called by invite session framework when UAC session
3383 * has forked.
3384 */
3385static void pjsua_call_on_forked( pjsip_inv_session *inv,
3386 pjsip_event *e)
3387{
3388 PJ_UNUSED_ARG(inv);
3389 PJ_UNUSED_ARG(e);
3390
3391 PJ_TODO(HANDLE_FORKED_DIALOG);
3392}
3393
3394
3395/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003396 * Callback from UA layer when forked dialog response is received.
3397 */
3398pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3399{
3400 if (dlg->uac_has_2xx &&
3401 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3402 pjsip_rdata_get_tsx(res) == NULL &&
3403 res->msg_info.msg->line.status.code/100 == 2)
3404 {
3405 pjsip_dialog *forked_dlg;
3406 pjsip_tx_data *bye;
3407 pj_status_t status;
3408
3409 /* Create forked dialog */
3410 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3411 if (status != PJ_SUCCESS)
3412 return NULL;
3413
3414 pjsip_dlg_inc_lock(forked_dlg);
3415
3416 /* Disconnect the call */
3417 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3418 -1, &bye);
3419 if (status == PJ_SUCCESS) {
3420 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3421 }
3422
3423 pjsip_dlg_dec_lock(forked_dlg);
3424
3425 if (status != PJ_SUCCESS) {
3426 return NULL;
3427 }
3428
3429 return forked_dlg;
3430
3431 } else {
3432 return dlg;
3433 }
3434}
3435
3436/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003437 * Disconnect call upon error.
3438 */
3439static void call_disconnect( pjsip_inv_session *inv,
3440 int code )
3441{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003442 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003443 pjsip_tx_data *tdata;
3444 pj_status_t status;
3445
Benny Prijono59b3aed2008-01-15 16:54:54 +00003446 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3447
Benny Prijonoa38ada02006-07-02 14:22:35 +00003448 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003449 if (status != PJ_SUCCESS)
3450 return;
3451
3452 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003453 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3454 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3455 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003456 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003457 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003458
Benny Prijono734fc2d2008-03-17 16:05:35 +00003459 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003460 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003461 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003462 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003463 if (status == PJ_SUCCESS) {
3464 pjsip_create_sdp_body(tdata->pool, local_sdp,
3465 &tdata->msg->body);
3466 }
3467 }
3468
3469 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003470}
3471
3472/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003473 * Callback to be called when SDP offer/answer negotiation has just completed
3474 * in the session. This function will start/update media if negotiation
3475 * has succeeded.
3476 */
3477static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3478 pj_status_t status)
3479{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003480 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003481 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003482 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003483 const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003484
3485 PJSUA_LOCK();
3486
Benny Prijonoa1e69682007-05-11 15:14:34 +00003487 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003488
3489 if (status != PJ_SUCCESS) {
3490
3491 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3492
Benny Prijono2331d202008-06-26 15:46:52 +00003493 /* Do not deinitialize media since this may be a re-INVITE or
3494 * UPDATE (which in this case the media should not get affected
3495 * by the failed re-INVITE/UPDATE). The media will be shutdown
3496 * when call is disconnected anyway.
3497 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003498 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003499 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003500
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003501 /* Disconnect call if we're not in the middle of initializing an
3502 * UAS dialog and if this is not a re-INVITE
3503 */
3504 if (inv->state != PJSIP_INV_STATE_NULL &&
3505 inv->state != PJSIP_INV_STATE_CONFIRMED)
3506 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003507 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003508 }
3509
3510 PJSUA_UNLOCK();
3511 return;
3512 }
3513
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003514
3515 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003516 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003517 if (status != PJ_SUCCESS) {
3518 pjsua_perror(THIS_FILE,
3519 "Unable to retrieve currently active local SDP",
3520 status);
3521 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3522 PJSUA_UNLOCK();
3523 return;
3524 }
3525
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003526 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3527 if (status != PJ_SUCCESS) {
3528 pjsua_perror(THIS_FILE,
3529 "Unable to retrieve currently active remote SDP",
3530 status);
3531 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3532 PJSUA_UNLOCK();
3533 return;
3534 }
3535
Benny Prijono91a6a172007-10-31 08:59:29 +00003536 /* Update remote's NAT type */
3537 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3538 update_remote_nat_type(call, remote_sdp);
3539 }
3540
3541 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003542 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003543 if (status != PJ_SUCCESS) {
3544 pjsua_perror(THIS_FILE, "Unable to create media session",
3545 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003546 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003547 /* No need to deinitialize; media will be shutdown when call
3548 * state is disconnected anyway.
3549 */
3550 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003551 PJSUA_UNLOCK();
3552 return;
3553 }
3554
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003555 /* Ticket #476, handle the case of early media and remote support UPDATE */
3556 if (inv->state == PJSIP_INV_STATE_EARLY &&
3557 pjmedia_sdp_neg_was_answer_remote(inv->neg) &&
3558 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)==
3559 PJSIP_DIALOG_CAP_SUPPORTED)
3560 {
3561 status = lock_codec(call);
3562 if (status != PJ_SUCCESS) {
3563 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
3564 }
3565 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003566
3567 /* Call application callback, if any */
3568 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3569 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3570
3571
3572 PJSUA_UNLOCK();
3573}
3574
3575
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003576/* Create SDP for call hold. */
3577static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3578 pjmedia_sdp_session **p_answer)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003579{
3580 pj_status_t status;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003581 pj_pool_t *pool;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003582 pjmedia_sdp_session *sdp;
3583
Benny Prijono40d62b62009-08-12 17:53:47 +00003584 /* Use call's provisional pool */
3585 pool = call->inv->pool_prov;
Benny Prijonoc91ed8d2008-07-13 12:24:55 +00003586
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003587 /* Create new offer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003588 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3589 NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003590 if (status != PJ_SUCCESS) {
3591 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3592 return status;
3593 }
3594
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003595 /* Call-hold is done by set the media direction to 'sendonly'
3596 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3597 * 'inactive' (PJMEDIA_DIR_NONE).
3598 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3599 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003600 /* http://trac.pjsip.org/repos/ticket/880
3601 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3602 */
3603 if (1) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003604 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003605
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003606 /* Remove existing directions attributes */
3607 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3608 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3609 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3610 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003611
Benny Prijonoe0860132009-06-05 10:14:20 +00003612 if (call->media_dir & PJMEDIA_DIR_ENCODING) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003613 /* Add sendonly attribute */
3614 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3615 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3616 } else {
3617 /* Add inactive attribute */
3618 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3619 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3620 }
3621 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003622
3623 *p_answer = sdp;
3624
3625 return status;
3626}
3627
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003628/*
3629 * Called when session received new offer.
3630 */
3631static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3632 const pjmedia_sdp_session *offer)
3633{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003634 pjsua_call *call;
3635 pjmedia_sdp_conn *conn;
3636 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003637 pj_status_t status;
3638
3639 PJSUA_LOCK();
3640
Benny Prijonoa1e69682007-05-11 15:14:34 +00003641 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003642
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003643 conn = offer->media[0]->conn;
3644 if (!conn)
3645 conn = offer->conn;
3646
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003647 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003648 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3649 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003650
Benny Prijono40d62b62009-08-12 17:53:47 +00003651 status = pjsua_media_channel_create_sdp(call->index,
3652 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003653 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003654 if (status != PJ_SUCCESS) {
3655 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3656 PJSUA_UNLOCK();
3657 return;
3658 }
3659
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003660 /* Check if offer's conn address is zero */
3661 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3662 pj_strcmp2(&conn->addr, "0")==0)
3663 {
3664 /* Modify address */
3665 answer->conn->addr = pj_str("0.0.0.0");
3666 }
3667
3668 /* Check if call is on-hold */
3669 if (call->local_hold) {
3670 pjmedia_sdp_attr *attr;
3671
3672 /* Remove existing directions attributes */
3673 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendrecv");
3674 pjmedia_sdp_media_remove_all_attr(answer->media[0], "sendonly");
3675 pjmedia_sdp_media_remove_all_attr(answer->media[0], "recvonly");
3676 pjmedia_sdp_media_remove_all_attr(answer->media[0], "inactive");
3677
3678 /* Keep call on-hold by setting 'sendonly' attribute.
3679 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3680 */
Benny Prijono40d62b62009-08-12 17:53:47 +00003681 attr = pjmedia_sdp_attr_create(call->inv->pool_prov, "sendonly", NULL);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003682 pjmedia_sdp_media_add_attr(answer->media[0], attr);
3683 }
3684
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003685 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3686 if (status != PJ_SUCCESS) {
3687 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3688 PJSUA_UNLOCK();
3689 return;
3690 }
3691
3692 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003693}
3694
3695
3696/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003697 * Called to generate new offer.
3698 */
3699static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3700 pjmedia_sdp_session **offer)
3701{
3702 pjsua_call *call;
3703 pj_status_t status;
3704
3705 PJSUA_LOCK();
3706
3707 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3708
3709 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003710 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003711 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003712 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003713 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003714 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003715 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003716 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3717 call->index));
3718
Benny Prijono40d62b62009-08-12 17:53:47 +00003719 status = pjsua_media_channel_create_sdp(call->index,
3720 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003721 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003722 }
3723
3724 if (status != PJ_SUCCESS) {
3725 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3726 PJSUA_UNLOCK();
3727 return;
3728 }
3729
Benny Prijono77998ce2007-06-20 10:03:46 +00003730 PJSUA_UNLOCK();
3731}
3732
3733
3734/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003735 * Callback called by event framework when the xfer subscription state
3736 * has changed.
3737 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003738static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3739{
3740
3741 PJ_UNUSED_ARG(event);
3742
3743 /*
3744 * When subscription is accepted (got 200/OK to REFER), check if
3745 * subscription suppressed.
3746 */
3747 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3748
3749 pjsip_rx_data *rdata;
3750 pjsip_generic_string_hdr *refer_sub;
3751 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3752 pjsua_call *call;
3753
Benny Prijonoa1e69682007-05-11 15:14:34 +00003754 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003755
3756 /* Must be receipt of response message */
3757 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3758 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3759 rdata = event->body.tsx_state.src.rdata;
3760
3761 /* Find Refer-Sub header */
3762 refer_sub = (pjsip_generic_string_hdr*)
3763 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3764 &REFER_SUB, NULL);
3765
3766 /* Check if subscription is suppressed */
3767 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3768 /* Since no subscription is desired, assume that call has been
3769 * transfered successfully.
3770 */
3771 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3772 const pj_str_t ACCEPTED = { "Accepted", 8 };
3773 pj_bool_t cont = PJ_FALSE;
3774 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3775 200,
3776 &ACCEPTED,
3777 PJ_TRUE,
3778 &cont);
3779 }
3780
3781 /* Yes, subscription is suppressed.
3782 * Terminate our subscription now.
3783 */
3784 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3785 "event subcription..."));
3786 pjsip_evsub_terminate(sub, PJ_TRUE);
3787
3788 } else {
3789 /* Notify application about call transfer progress.
3790 * Initially notify with 100/Accepted status.
3791 */
3792 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3793 const pj_str_t ACCEPTED = { "Accepted", 8 };
3794 pj_bool_t cont = PJ_FALSE;
3795 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3796 100,
3797 &ACCEPTED,
3798 PJ_FALSE,
3799 &cont);
3800 }
3801 }
3802 }
3803 /*
3804 * On incoming NOTIFY, notify application about call transfer progress.
3805 */
3806 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3807 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3808 {
3809 pjsua_call *call;
3810 pjsip_msg *msg;
3811 pjsip_msg_body *body;
3812 pjsip_status_line status_line;
3813 pj_bool_t is_last;
3814 pj_bool_t cont;
3815 pj_status_t status;
3816
Benny Prijonoa1e69682007-05-11 15:14:34 +00003817 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003818
3819 /* When subscription is terminated, clear the xfer_sub member of
3820 * the inv_data.
3821 */
3822 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3823 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3824 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3825
3826 }
3827
3828 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3829 /* Application is not interested with call progress status */
3830 return;
3831 }
3832
3833 /* This better be a NOTIFY request */
3834 if (event->type == PJSIP_EVENT_TSX_STATE &&
3835 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3836 {
3837 pjsip_rx_data *rdata;
3838
3839 rdata = event->body.tsx_state.src.rdata;
3840
3841 /* Check if there's body */
3842 msg = rdata->msg_info.msg;
3843 body = msg->body;
3844 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003845 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003846 "Warning: received NOTIFY without message body"));
3847 return;
3848 }
3849
3850 /* Check for appropriate content */
3851 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3852 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3853 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003854 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003855 "Warning: received NOTIFY with non message/sipfrag "
3856 "content"));
3857 return;
3858 }
3859
3860 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003861 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003862 &status_line);
3863 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003864 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003865 "Warning: received NOTIFY with invalid "
3866 "message/sipfrag content"));
3867 return;
3868 }
3869
3870 } else {
3871 status_line.code = 500;
3872 status_line.reason = *pjsip_get_status_text(500);
3873 }
3874
3875 /* Notify application */
3876 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3877 cont = !is_last;
3878 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3879 status_line.code,
3880 &status_line.reason,
3881 is_last, &cont);
3882
3883 if (!cont) {
3884 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3885 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003886
3887 /* If the call transfer has completed but the subscription is
3888 * not terminated, terminate it now.
3889 */
3890 if (status_line.code/100 == 2 && !is_last) {
3891 pjsip_tx_data *tdata;
3892
3893 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3894 0, &tdata);
3895 if (status == PJ_SUCCESS)
3896 status = pjsip_evsub_send_request(sub, tdata);
3897 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003898 }
3899}
3900
3901
3902/*
3903 * Callback called by event framework when the xfer subscription state
3904 * has changed.
3905 */
3906static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003907{
3908
3909 PJ_UNUSED_ARG(event);
3910
3911 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003912 * When subscription is terminated, clear the xfer_sub member of
3913 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003914 */
3915 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003916 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003917
Benny Prijonoa1e69682007-05-11 15:14:34 +00003918 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003919 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003920 return;
3921
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003922 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003923 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003924
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003925 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003926 }
3927}
3928
3929
3930/*
3931 * Follow transfer (REFER) request.
3932 */
3933static void on_call_transfered( pjsip_inv_session *inv,
3934 pjsip_rx_data *rdata )
3935{
3936 pj_status_t status;
3937 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003938 pjsua_call *existing_call;
3939 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003940 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003941 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003942 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003943 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003944 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003945 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003946 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003947 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003948 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003949 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003950 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003951 pjsip_evsub *sub;
3952
Benny Prijonoa1e69682007-05-11 15:14:34 +00003953 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003954
Benny Prijono26ff9062006-02-21 23:47:00 +00003955 /* Find the Refer-To header */
3956 refer_to = (pjsip_generic_string_hdr*)
3957 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3958
3959 if (refer_to == NULL) {
3960 /* Invalid Request.
3961 * No Refer-To header!
3962 */
3963 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003964 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003965 return;
3966 }
3967
Benny Prijonoc8141a82006-08-20 09:12:19 +00003968 /* Find optional Refer-Sub header */
3969 refer_sub = (pjsip_generic_string_hdr*)
3970 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3971
3972 if (refer_sub) {
3973 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
3974 no_refer_sub = PJ_TRUE;
3975 }
3976
Benny Prijono053f5222006-11-11 16:16:04 +00003977 /* Find optional Referred-By header (to be copied onto outgoing INVITE
3978 * request.
3979 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003980 ref_by_hdr = (pjsip_hdr*)
3981 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00003982 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00003983
Benny Prijono9fc735d2006-05-28 14:58:12 +00003984 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003985 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003986 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
3987 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
3988 &refer_to->hvalue,
3989 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00003990
3991 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00003992 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003993 if (code >= 300) {
3994 /* Application rejects call transfer request */
3995 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
3996 return;
3997 }
3998
Benny Prijono26ff9062006-02-21 23:47:00 +00003999 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
4000 (int)inv->dlg->remote.info_str.slen,
4001 inv->dlg->remote.info_str.ptr,
4002 (int)refer_to->hvalue.slen,
4003 refer_to->hvalue.ptr));
4004
Benny Prijonoc8141a82006-08-20 09:12:19 +00004005 if (no_refer_sub) {
4006 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004007 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00004008 */
4009 pjsip_tx_data *tdata;
4010 const pj_str_t str_false = { "false", 5};
4011 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00004012
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004013 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
4014 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00004015 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004016 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00004017 status);
4018 return;
4019 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004020
Benny Prijonoc8141a82006-08-20 09:12:19 +00004021 /* Add Refer-Sub header */
4022 hdr = (pjsip_hdr*)
4023 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
4024 &str_false);
4025 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00004026
Benny Prijono26ff9062006-02-21 23:47:00 +00004027
Benny Prijonoc8141a82006-08-20 09:12:19 +00004028 /* Send answer */
4029 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
4030 tdata);
4031 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004032 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00004033 status);
4034 return;
4035 }
4036
4037 /* Don't have subscription */
4038 sub = NULL;
4039
4040 } else {
4041 struct pjsip_evsub_user xfer_cb;
4042 pjsip_hdr hdr_list;
4043
4044 /* Init callback */
4045 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004046 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004047
4048 /* Init additional header list to be sent with REFER response */
4049 pj_list_init(&hdr_list);
4050
4051 /* Create transferee event subscription */
4052 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
4053 if (status != PJ_SUCCESS) {
4054 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
4055 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
4056 return;
4057 }
4058
4059 /* If there's Refer-Sub header and the value is "true", send back
4060 * Refer-Sub in the response with value "true" too.
4061 */
4062 if (refer_sub) {
4063 const pj_str_t str_true = { "true", 4 };
4064 pjsip_hdr *hdr;
4065
4066 hdr = (pjsip_hdr*)
4067 pjsip_generic_string_hdr_create(inv->dlg->pool,
4068 &str_refer_sub,
4069 &str_true);
4070 pj_list_push_back(&hdr_list, hdr);
4071
4072 }
4073
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004074 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00004075 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
4076
4077 /* Create initial NOTIFY request */
4078 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
4079 100, NULL, &tdata);
4080 if (status != PJ_SUCCESS) {
4081 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4082 status);
4083 return;
4084 }
4085
4086 /* Send initial NOTIFY request */
4087 status = pjsip_xfer_send_request( sub, tdata);
4088 if (status != PJ_SUCCESS) {
4089 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
4090 return;
4091 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004092 }
4093
4094 /* We're cheating here.
4095 * We need to get a null terminated string from a pj_str_t.
4096 * So grab the pointer from the hvalue and NULL terminate it, knowing
4097 * that the NULL position will be occupied by a newline.
4098 */
4099 uri = refer_to->hvalue.ptr;
4100 uri[refer_to->hvalue.slen] = '\0';
4101
Benny Prijono053f5222006-11-11 16:16:04 +00004102 /* Init msg_data */
4103 pjsua_msg_data_init(&msg_data);
4104
4105 /* If Referred-By header is present in the REFER request, copy this
4106 * to the outgoing INVITE request.
4107 */
4108 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00004109 pjsip_hdr *dup = (pjsip_hdr*)
4110 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00004111 pj_list_push_back(&msg_data.hdr_list, dup);
4112 }
4113
Benny Prijono26ff9062006-02-21 23:47:00 +00004114 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00004115 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004116 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00004117 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004118 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00004119 if (status != PJ_SUCCESS) {
4120
Benny Prijonoc8141a82006-08-20 09:12:19 +00004121 /* Notify xferer about the error (if we have subscription) */
4122 if (sub) {
4123 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
4124 500, NULL, &tdata);
4125 if (status != PJ_SUCCESS) {
4126 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4127 status);
4128 return;
4129 }
4130 status = pjsip_xfer_send_request(sub, tdata);
4131 if (status != PJ_SUCCESS) {
4132 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
4133 status);
4134 return;
4135 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004136 }
4137 return;
4138 }
4139
Benny Prijonoc8141a82006-08-20 09:12:19 +00004140 if (sub) {
4141 /* Put the server subscription in inv_data.
4142 * Subsequent state changed in pjsua_inv_on_state_changed() will be
4143 * reported back to the server subscription.
4144 */
4145 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00004146
Benny Prijonoc8141a82006-08-20 09:12:19 +00004147 /* Put the invite_data in the subscription. */
4148 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
4149 &pjsua_var.calls[new_call]);
4150 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004151}
4152
4153
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004154
Benny Prijono26ff9062006-02-21 23:47:00 +00004155/*
4156 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00004157 * session. We use this to trap:
4158 * - incoming REFER request.
4159 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00004160 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004161static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
4162 pjsip_transaction *tsx,
4163 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00004164{
Benny Prijono2285e7e2008-12-17 14:28:18 +00004165 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004166
4167 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00004168
Benny Prijono2285e7e2008-12-17 14:28:18 +00004169 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
4170
4171 if (call == NULL) {
4172 PJSUA_UNLOCK();
4173 return;
4174 }
4175
Benny Prijono19eeb6e2009-06-04 22:16:47 +00004176 if (call->inv == NULL) {
4177 /* Shouldn't happen. It happens only when we don't terminate the
4178 * server subscription caused by REFER after the call has been
4179 * transfered (and this call has been disconnected), and we
4180 * receive another REFER for this call.
4181 */
4182 PJSUA_UNLOCK();
4183 return;
4184 }
4185
Benny Prijonofeb69f42007-10-05 09:12:26 +00004186 /* Notify application callback first */
4187 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
4188 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
4189 }
4190
Benny Prijono26ff9062006-02-21 23:47:00 +00004191 if (tsx->role==PJSIP_ROLE_UAS &&
4192 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00004193 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004194 {
4195 /*
4196 * Incoming REFER request.
4197 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004198 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00004199
Benny Prijono26ff9062006-02-21 23:47:00 +00004200 }
Benny Prijonob0808372006-03-02 21:18:58 +00004201 else if (tsx->role==PJSIP_ROLE_UAS &&
4202 tsx->state==PJSIP_TSX_STATE_TRYING &&
4203 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
4204 {
4205 /*
4206 * Incoming MESSAGE request!
4207 */
4208 pjsip_rx_data *rdata;
4209 pjsip_msg *msg;
4210 pjsip_accept_hdr *accept_hdr;
4211 pj_status_t status;
4212
4213 rdata = e->body.tsx_state.src.rdata;
4214 msg = rdata->msg_info.msg;
4215
4216 /* Request MUST have message body, with Content-Type equal to
4217 * "text/plain".
4218 */
4219 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
4220
4221 pjsip_hdr hdr_list;
4222
4223 pj_list_init(&hdr_list);
4224 pj_list_push_back(&hdr_list, accept_hdr);
4225
4226 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
4227 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004228 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00004229 return;
4230 }
4231
4232 /* Respond with 200 first, so that remote doesn't retransmit in case
4233 * the UI takes too long to process the message.
4234 */
4235 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
4236
4237 /* Process MESSAGE request */
4238 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
4239 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004240
Benny Prijonob0808372006-03-02 21:18:58 +00004241 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004242 else if (tsx->role == PJSIP_ROLE_UAC &&
4243 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004244 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004245 /* Handle outgoing pager status */
4246 if (tsx->status_code >= 200) {
4247 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00004248
Benny Prijonoa1e69682007-05-11 15:14:34 +00004249 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004250 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00004251
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004252 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
4253 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
4254 &im_data->to,
4255 &im_data->body,
4256 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00004257 (pjsip_status_code)
4258 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004259 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00004260 }
Benny Prijonofccab712006-02-22 22:23:22 +00004261 }
Benny Prijono834aee32006-02-19 01:38:06 +00004262 }
Benny Prijono834aee32006-02-19 01:38:06 +00004263
Benny Prijono26ff9062006-02-21 23:47:00 +00004264
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004265 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00004266}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004267
4268
4269/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00004270static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
4271 const pjsip_uri *target,
4272 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004273{
4274 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00004275 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004276
4277 PJSUA_LOCK();
4278
4279 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00004280 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
4281 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004282 } else {
4283 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
4284 "(callback not implemented by application). Disconnecting "
4285 "call.",
4286 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00004287 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004288 }
4289
4290 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00004291
4292 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004293}
4294