blob: 216efe8354a174f12890b0a4a1a5c6392c36e5a8 [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,
Benny Prijonodd63b992010-10-01 02:03:42 +000085 pjmedia_sdp_session **p_sdp);
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;
Benny Prijonodd63b992010-10-01 02:03:42 +0000407 call->call_hold_type = acc->cfg.call_hold_type;
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000408
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000409 /* Create temporary pool */
410 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
411
Benny Prijono320fa4d2006-12-07 10:09:16 +0000412 /* Verify that destination URI is valid before calling
413 * pjsua_acc_create_uac_contact, or otherwise there
414 * a misleading "Invalid Contact URI" error will be printed
415 * when pjsua_acc_create_uac_contact() fails.
416 */
417 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000418 pjsip_uri *uri;
419 pj_str_t dup;
420
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000421 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
422 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000423
424 if (uri == NULL) {
425 pjsua_perror(THIS_FILE, "Unable to make call",
426 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000427 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000428 PJSUA_UNLOCK();
429 return PJSIP_EINVALIDREQURI;
430 }
431 }
432
Benny Prijono093d3022006-09-24 00:07:11 +0000433 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
434 (int)dest_uri->slen, dest_uri->ptr));
435
Benny Prijonoe21e7842006-04-09 16:46:05 +0000436 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000437 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000438
Benny Prijonoe21e7842006-04-09 16:46:05 +0000439 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000440 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000441
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000442 /* Create suitable Contact header unless a Contact header has been
443 * set in the account.
444 */
445 if (acc->contact.slen) {
446 contact = acc->contact;
447 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000448 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000449 acc_id, dest_uri);
450 if (status != PJ_SUCCESS) {
451 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
452 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000453 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000454 PJSUA_UNLOCK();
455 return status;
456 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000457 }
458
Benny Prijonoe21e7842006-04-09 16:46:05 +0000459 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000460 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000461 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000462 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000463 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000464 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000465 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000466 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000467 return status;
468 }
469
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000470 /* Increment the dialog's lock otherwise when invite session creation
471 * fails the dialog will be destroyed prematurely.
472 */
473 pjsip_dlg_inc_lock(dlg);
474
Benny Prijonodb844a42008-02-02 17:07:18 +0000475 /* Calculate call's secure level */
476 call->secure_level = get_secure_level(acc_id, dest_uri);
477
Benny Prijonoc97608e2007-03-23 16:34:20 +0000478 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000479 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000480 call->secure_level, dlg->pool,
481 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000482 if (status != PJ_SUCCESS) {
483 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
484 goto on_error;
485 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000486
Benny Prijono224b4e22008-06-19 14:10:28 +0000487 /* Create offer */
488 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000489 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000490 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000491 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000492 goto on_error;
493 }
494
495 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000496 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000497 if (acc->cfg.require_100rel)
498 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000499 if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) {
500 options |= PJSIP_INV_SUPPORT_TIMER;
501 if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
502 options |= PJSIP_INV_REQUIRE_TIMER;
503 else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
504 options |= PJSIP_INV_ALWAYS_USE_TIMER;
505 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000506
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000507 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000508 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000509 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000510 goto on_error;
511 }
512
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000513 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000514 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
515 if (status != PJ_SUCCESS) {
516 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
517 goto on_error;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000518 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000519
520 /* Create and associate our data in the session. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000521 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000522
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000523 dlg->mod_data[pjsua_var.mod.id] = call;
524 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000525
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000526 /* Attach user data */
527 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000528
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000529 /* If account is locked to specific transport, then lock dialog
530 * to this transport too.
531 */
532 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
533 pjsip_tpselector tp_sel;
534
535 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
536 pjsip_dlg_set_transport(dlg, &tp_sel);
537 }
538
Benny Prijono84126ab2006-02-09 09:30:09 +0000539 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000540 if (!pj_list_empty(&acc->route_set))
541 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000542
543
544 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000545 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000546 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000547 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000548 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000549
Benny Prijono48ab2b72007-11-08 09:24:30 +0000550 /* Set authentication preference */
551 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000552
553 /* Create initial INVITE: */
554
555 status = pjsip_inv_invite(inv, &tdata);
556 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000557 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
558 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000559 goto on_error;
560 }
561
562
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000563 /* Add additional headers etc */
564
565 pjsua_process_msg_data( tdata, msg_data);
566
Benny Prijono093d3022006-09-24 00:07:11 +0000567 /* Must increment call counter now */
568 ++pjsua_var.call_cnt;
569
Benny Prijono84126ab2006-02-09 09:30:09 +0000570 /* Send initial INVITE: */
571
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000572 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000573 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000574 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
575 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000576
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000577 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000578 * session would have been cleared.
579 */
580 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000581 goto on_error;
582 }
583
Benny Prijono84126ab2006-02-09 09:30:09 +0000584 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000585
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586 if (p_call_id)
587 *p_call_id = call_id;
588
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000589 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000590 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000591 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000592
593 return PJ_SUCCESS;
594
595
596on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000597 if (dlg) {
598 /* This may destroy the dialog */
599 pjsip_dlg_dec_lock(dlg);
600 }
601
Benny Prijono1c2bf462006-03-05 11:54:02 +0000602 if (inv != NULL) {
603 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000604 }
605
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000606 if (call_id != -1) {
607 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000608 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000609 }
610
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000611 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000612 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000613 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000614}
615
616
Benny Prijono91a6a172007-10-31 08:59:29 +0000617/* Get the NAT type information in remote's SDP */
618static void update_remote_nat_type(pjsua_call *call,
619 const pjmedia_sdp_session *sdp)
620{
621 const pjmedia_sdp_attr *xnat;
622
623 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
624 if (xnat) {
625 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
626 } else {
627 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
628 }
629
630 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
631 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
632}
633
634
Benny Prijonodc39fe82006-05-26 12:17:46 +0000635/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000636 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000637 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000638 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000639pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000640{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000641 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000642 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000643 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000644 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
645 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000646 pjsip_tx_data *response = NULL;
647 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000648 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000649 int acc_id;
650 pjsua_call *call;
651 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000652 int sip_err_code;
Benny Prijono1c1d7342010-08-01 09:48:51 +0000653 pjmedia_sdp_session *offer=NULL, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000654 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000655
Benny Prijono26ff9062006-02-21 23:47:00 +0000656 /* Don't want to handle anything but INVITE */
657 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
658 return PJ_FALSE;
659
660 /* Don't want to handle anything that's already associated with
661 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000662 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000663 if (dlg || tsx)
664 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000665
Benny Prijono384dab42009-10-14 01:58:04 +0000666 /* Don't want to accept the call if shutdown is in progress */
667 if (pjsua_var.thread_quit_flag) {
668 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
669 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
670 NULL, NULL);
671 return PJ_TRUE;
672 }
673
Benny Prijono148c9dd2006-09-19 13:37:53 +0000674 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000675
Benny Prijono26ff9062006-02-21 23:47:00 +0000676 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000677 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000678
Benny Prijono5773cd62008-01-19 13:01:42 +0000679 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000680 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000681 PJSIP_SC_BUSY_HERE, NULL,
682 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000683 PJ_LOG(2,(THIS_FILE,
684 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000685 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000686 return PJ_TRUE;
687 }
688
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000689 /* Clear call descriptor */
690 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000691
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000692 call = &pjsua_var.calls[call_id];
693
694 /* Mark call start time. */
695 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000696
Benny Prijono053f5222006-11-11 16:16:04 +0000697 /* Check INVITE request for Replaces header. If Replaces header is
698 * present, the function will make sure that we can handle the request.
699 */
700 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
701 &response);
702 if (status != PJ_SUCCESS) {
703 /*
704 * Something wrong with the Replaces header.
705 */
706 if (response) {
707 pjsip_response_addr res_addr;
708
709 pjsip_get_response_addr(response->pool, rdata, &res_addr);
710 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
711 NULL, NULL);
712
713 } else {
714
715 /* Respond with 500 (Internal Server Error) */
716 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
717 NULL, NULL);
718 }
719
720 PJSUA_UNLOCK();
721 return PJ_TRUE;
722 }
723
724 /* If this INVITE request contains Replaces header, notify application
725 * about the request so that application can do subsequent checking
726 * if it wants to.
727 */
728 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
729 pjsua_call *replaced_call;
730 int st_code = 200;
731 pj_str_t st_text = { "OK", 2 };
732
733 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000734 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000735
736 /* Notify application */
737 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
738 rdata, &st_code, &st_text);
739
740 /* Must specify final response */
741 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
742
743 /* Check if application rejects this request. */
744 if (st_code >= 300) {
745
746 if (st_text.slen == 2)
747 st_text = *pjsip_get_status_text(st_code);
748
749 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
750 st_code, &st_text, NULL, NULL, NULL);
751 PJSUA_UNLOCK();
752 return PJ_TRUE;
753 }
754 }
755
Benny Prijonod8179652008-01-23 20:39:07 +0000756 /*
757 * Get which account is most likely to be associated with this incoming
758 * call. We need the account to find which contact URI to put for
759 * the call.
760 */
761 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonodd63b992010-10-01 02:03:42 +0000762 call->call_hold_type = pjsua_var.acc[acc_id].cfg.call_hold_type;
Benny Prijonod8179652008-01-23 20:39:07 +0000763
Benny Prijonodb844a42008-02-02 17:07:18 +0000764 /* Get call's secure level */
765 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
766 call->secure_level = 2;
767 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
768 call->secure_level = 1;
769 else
770 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000771
Benny Prijonod8179652008-01-23 20:39:07 +0000772 /* Parse SDP from incoming request */
773 if (rdata->msg_info.msg->body) {
Benny Prijono1c1d7342010-08-01 09:48:51 +0000774 pjsip_rdata_sdp_info *sdp_info;
775
776 sdp_info = pjsip_rdata_get_sdp_info(rdata);
777 offer = sdp_info->sdp;
778
779 status = sdp_info->sdp_err;
780 if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
781 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono2c484e42008-06-26 19:47:23 +0000782
Benny Prijonod8179652008-01-23 20:39:07 +0000783 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000784 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000785 pjsip_hdr hdr_list;
786 pjsip_warning_hdr *w;
787
788 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000789 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000790
791 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
792 pjsip_endpt_name(pjsua_var.endpt),
793 status);
794 pj_list_init(&hdr_list);
795 pj_list_push_back(&hdr_list, w);
796
Benny Prijono224b4e22008-06-19 14:10:28 +0000797 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000798 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000799 PJSUA_UNLOCK();
800 return PJ_TRUE;
801 }
Benny Prijono617b8602008-04-07 10:10:31 +0000802
803 /* Do quick checks on SDP before passing it to transports. More elabore
804 * checks will be done in pjsip_inv_verify_request2() below.
805 */
806 if (offer->media_count==0) {
807 const pj_str_t reason = pj_str("Missing media in SDP");
808 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
809 NULL, NULL, NULL);
810 PJSUA_UNLOCK();
811 return PJ_TRUE;
812 }
813
Benny Prijonod8179652008-01-23 20:39:07 +0000814 } else {
815 offer = NULL;
816 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000817
Benny Prijono224b4e22008-06-19 14:10:28 +0000818 /* Init media channel */
819 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
820 call->secure_level,
821 rdata->tp_info.pool, offer,
822 &sip_err_code);
823 if (status != PJ_SUCCESS) {
824 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
825 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
826 sip_err_code, NULL, NULL, NULL, NULL);
827 PJSUA_UNLOCK();
828 return PJ_TRUE;
829 }
830
831 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000832 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000833 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000834 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000835 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000836 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
837 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000838 PJSUA_UNLOCK();
839 return PJ_TRUE;
840 }
841
Benny Prijono224b4e22008-06-19 14:10:28 +0000842
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000843 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000844 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000845 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000846 if (pjsua_var.acc[acc_id].cfg.require_100rel)
847 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono07fe2302010-06-24 12:33:18 +0000848 if (pjsua_var.media_cfg.enable_ice)
849 options |= PJSIP_INV_SUPPORT_ICE;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000850 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
851 options |= PJSIP_INV_REQUIRE_TIMER;
852 else if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
853 options |= PJSIP_INV_ALWAYS_USE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000854
Benny Prijonod8179652008-01-23 20:39:07 +0000855 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
856 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000857 if (status != PJ_SUCCESS) {
858
859 /*
860 * No we can't handle the incoming INVITE request.
861 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000862 if (response) {
863 pjsip_response_addr res_addr;
864
865 pjsip_get_response_addr(response->pool, rdata, &res_addr);
866 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
867 NULL, NULL);
868
869 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000870 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000871 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
872 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000873 }
874
Benny Prijonoc97608e2007-03-23 16:34:20 +0000875 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000876 PJSUA_UNLOCK();
877 return PJ_TRUE;
878 }
879
880
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000881 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000882 if (pjsua_var.acc[acc_id].contact.slen) {
883 contact = pjsua_var.acc[acc_id].contact;
884 } else {
885 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
886 acc_id, rdata);
887 if (status != PJ_SUCCESS) {
888 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
889 status);
890 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
891 NULL, NULL);
892 pjsua_media_channel_deinit(call->index);
893 PJSUA_UNLOCK();
894 return PJ_TRUE;
895 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000896 }
897
Benny Prijono26ff9062006-02-21 23:47:00 +0000898 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000899 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000900 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000901 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000902 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000903 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000904 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000905 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000906 return PJ_TRUE;
907 }
908
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000909 /* Set credentials */
910 if (pjsua_var.acc[acc_id].cred_cnt) {
911 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
912 pjsua_var.acc[acc_id].cred_cnt,
913 pjsua_var.acc[acc_id].cred);
914 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000915
Benny Prijono48ab2b72007-11-08 09:24:30 +0000916 /* Set preference */
917 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
918 &pjsua_var.acc[acc_id].cfg.auth_pref);
919
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000920 /* Disable Session Timers if not prefered and the incoming INVITE request
921 * did not require it.
922 */
923 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_INACTIVE &&
924 (options & PJSIP_INV_REQUIRE_TIMER) == 0)
925 {
926 options &= ~(PJSIP_INV_SUPPORT_TIMER);
927 }
928
Benny Prijono26ff9062006-02-21 23:47:00 +0000929 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000930 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000931 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000932 pjsip_hdr hdr_list;
933 pjsip_warning_hdr *w;
934
935 w = pjsip_warning_hdr_create_from_status(dlg->pool,
936 pjsip_endpt_name(pjsua_var.endpt),
937 status);
938 pj_list_init(&hdr_list);
939 pj_list_push_back(&hdr_list, w);
940
941 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
942
943 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000944 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000945 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000946 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000947 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000948 return PJ_TRUE;
949 }
950
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000951 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000952 status = pjsip_timer_init_session(inv,
953 &pjsua_var.acc[acc_id].cfg.timer_setting);
954 if (status != PJ_SUCCESS) {
955 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
956 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
957 NULL, &response);
958 if (status == PJ_SUCCESS && response)
959 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000960
Nanang Izzuddin65add622009-08-11 16:26:20 +0000961 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000962
Nanang Izzuddin65add622009-08-11 16:26:20 +0000963 PJSUA_UNLOCK();
964 return PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000965 }
966
Benny Prijonoea9fd392007-11-06 03:41:40 +0000967 /* Update NAT type of remote endpoint, only when there is SDP in
968 * incoming INVITE!
969 */
970 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
971 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
972 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000973 const pjmedia_sdp_session *remote_sdp;
974
975 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
976 update_remote_nat_type(call, remote_sdp);
977 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000978
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000979 /* If account is locked to specific transport, then lock dialog
980 * to this transport too.
981 */
982 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
983 pjsip_tpselector tp_sel;
984
985 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
986 pjsip_dlg_set_transport(dlg, &tp_sel);
987 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000988
Benny Prijono2285e7e2008-12-17 14:28:18 +0000989 /* Must answer with some response to initial INVITE. We'll do this before
990 * attaching the call to the invite session/dialog, so that the application
991 * will not get notification about this event (on another scenario, it is
992 * also possible that inv_send_msg() fails and causes the invite session to
993 * be disconnected. If we have the call attached at this time, this will
994 * cause the disconnection callback to be called before on_incoming_call()
995 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +0000996 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000997 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000998 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000999 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001000 if (response == NULL) {
1001 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
1002 status);
1003 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
1004 pjsip_inv_terminate(inv, 500, PJ_FALSE);
1005 } else {
1006 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +00001007 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001008 PJ_FALSE);
1009 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001010 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001011 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +00001012 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +00001013
1014 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001015 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001016 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +00001017 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +00001018 PJSUA_UNLOCK();
1019 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001020 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001021 }
1022
Benny Prijono2285e7e2008-12-17 14:28:18 +00001023 /* Create and attach pjsua_var data to the dialog: */
1024 call->inv = inv;
1025
1026 dlg->mod_data[pjsua_var.mod.id] = call;
1027 inv->mod_data[pjsua_var.mod.id] = call;
1028
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001029 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001030
Benny Prijono105217f2006-03-06 16:25:59 +00001031
Benny Prijono053f5222006-11-11 16:16:04 +00001032 /* Check if this request should replace existing call */
1033 if (replaced_dlg) {
1034 pjsip_inv_session *replaced_inv;
1035 struct pjsua_call *replaced_call;
1036 pjsip_tx_data *tdata;
1037
1038 /* Get the invite session in the dialog */
1039 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1040
1041 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001042 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001043
1044 /* Notify application */
1045 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1046 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1047 call_id);
1048
1049 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1050 call_id));
1051
1052 /* Answer the new call with 200 response */
1053 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1054 if (status == PJ_SUCCESS)
1055 status = pjsip_inv_send_msg(inv, tdata);
1056
1057 if (status != PJ_SUCCESS)
1058 pjsua_perror(THIS_FILE, "Error answering session", status);
1059
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001060 /* Note that inv may be invalid if 200/OK has caused error in
1061 * starting the media.
1062 */
Benny Prijono053f5222006-11-11 16:16:04 +00001063
1064 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1065 replaced_call->index));
1066
1067 /* Disconnect replaced invite session */
1068 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1069 &tdata);
1070 if (status == PJ_SUCCESS && tdata)
1071 status = pjsip_inv_send_msg(replaced_inv, tdata);
1072
1073 if (status != PJ_SUCCESS)
1074 pjsua_perror(THIS_FILE, "Error terminating session", status);
1075
1076
1077 } else {
1078
Benny Prijonob5388cf2007-01-04 22:45:08 +00001079 /* Notify application if on_incoming_call() is overriden,
1080 * otherwise hangup the call with 480
1081 */
1082 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001083 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001084 } else {
1085 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1086 NULL, NULL);
1087 }
Benny Prijono053f5222006-11-11 16:16:04 +00001088 }
1089
Benny Prijono8b1889b2006-06-06 18:40:40 +00001090
Benny Prijono26ff9062006-02-21 23:47:00 +00001091 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001092 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001093 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001094}
1095
1096
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001097
1098/*
1099 * Check if the specified call has active INVITE session and the INVITE
1100 * session has not been disconnected.
1101 */
1102PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1103{
1104 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1105 PJ_EINVAL);
1106 return pjsua_var.calls[call_id].inv != NULL &&
1107 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1108}
1109
1110
1111/*
1112 * Check if call has an active media session.
1113 */
1114PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1115{
1116 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1117 PJ_EINVAL);
1118 return pjsua_var.calls[call_id].session != NULL;
1119}
1120
1121
Benny Prijonocf986c42008-09-02 11:25:07 +00001122/*
1123 * Retrieve the media session associated with this call.
1124 */
1125PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1126{
1127 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1128 NULL);
1129 return pjsua_var.calls[call_id].session;
1130}
1131
1132
1133/*
1134 * Retrieve the media transport instance that is used for this call.
1135 */
1136PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1137{
1138 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1139 NULL);
1140 return pjsua_var.calls[cid].med_tp;
1141}
1142
1143
Benny Prijono148c9dd2006-09-19 13:37:53 +00001144/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001145pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001146 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001147 pjsua_call **p_call,
1148 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001149{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001150 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001151 pjsua_call *call = NULL;
1152 pj_bool_t has_pjsua_lock = PJ_FALSE;
1153 pj_status_t status = PJ_SUCCESS;
Sauw Ming844c1c92010-09-07 05:12:02 +00001154 pj_time_val time_start, timeout;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001155
Sauw Ming844c1c92010-09-07 05:12:02 +00001156 pj_gettimeofday(&time_start);
1157 timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT;
1158 pj_time_val_normalize(&timeout);
1159
1160 for (retry=0; ; ++retry) {
1161
1162 if (retry % 10 == 9) {
1163 pj_time_val dtime;
1164
1165 pj_gettimeofday(&dtime);
1166 PJ_TIME_VAL_SUB(dtime, time_start);
1167 if (!PJ_TIME_VAL_LT(dtime, timeout))
1168 break;
1169 }
Benny Prijono148c9dd2006-09-19 13:37:53 +00001170
1171 has_pjsua_lock = PJ_FALSE;
1172
1173 status = PJSUA_TRY_LOCK();
1174 if (status != PJ_SUCCESS) {
1175 pj_thread_sleep(retry/10);
1176 continue;
1177 }
1178
1179 has_pjsua_lock = PJ_TRUE;
1180 call = &pjsua_var.calls[call_id];
1181
1182 if (call->inv == NULL) {
1183 PJSUA_UNLOCK();
1184 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1185 return PJSIP_ESESSIONTERMINATED;
1186 }
1187
1188 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1189 if (status != PJ_SUCCESS) {
1190 PJSUA_UNLOCK();
1191 pj_thread_sleep(retry/10);
1192 continue;
1193 }
1194
1195 PJSUA_UNLOCK();
1196
1197 break;
1198 }
1199
1200 if (status != PJ_SUCCESS) {
1201 if (has_pjsua_lock == PJ_FALSE)
1202 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1203 "(possibly system has deadlocked) in %s",
1204 title));
1205 else
1206 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1207 "(possibly system has deadlocked) in %s",
1208 title));
1209 return PJ_ETIMEDOUT;
1210 }
1211
1212 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001213 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001214
1215 return PJ_SUCCESS;
1216}
1217
1218
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001219/*
1220 * Get the conference port identification associated with the call.
1221 */
1222PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1223{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001224 pjsua_call *call;
1225 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001226 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001227 pj_status_t status;
1228
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001229 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1230 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001231
Benny Prijonodc752ca2006-09-22 16:55:42 +00001232 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001233 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001234 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001235
1236 port_id = call->conf_slot;
1237
Benny Prijonodc752ca2006-09-22 16:55:42 +00001238 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001239
1240 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001241}
1242
1243
Benny Prijono148c9dd2006-09-19 13:37:53 +00001244
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001245/*
1246 * Obtain detail information about the specified call.
1247 */
1248PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1249 pjsua_call_info *info)
1250{
1251 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001252 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001253 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001254
1255 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1256 PJ_EINVAL);
1257
Benny Prijonoac623b32006-07-03 15:19:31 +00001258 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001259
Benny Prijonodc752ca2006-09-22 16:55:42 +00001260 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001261 if (status != PJ_SUCCESS) {
1262 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001263 }
1264
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001265 /* id and role */
1266 info->id = call_id;
1267 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001268 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001269
1270 /* local info */
1271 info->local_info.ptr = info->buf_.local_info;
1272 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1273 sizeof(info->buf_.local_info));
1274
1275 /* local contact */
1276 info->local_contact.ptr = info->buf_.local_contact;
1277 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1278 call->inv->dlg->local.contact->uri,
1279 info->local_contact.ptr,
1280 sizeof(info->buf_.local_contact));
1281
1282 /* remote info */
1283 info->remote_info.ptr = info->buf_.remote_info;
1284 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1285 sizeof(info->buf_.remote_info));
1286
1287 /* remote contact */
1288 if (call->inv->dlg->remote.contact) {
1289 int len;
1290 info->remote_contact.ptr = info->buf_.remote_contact;
1291 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1292 call->inv->dlg->remote.contact->uri,
1293 info->remote_contact.ptr,
1294 sizeof(info->buf_.remote_contact));
1295 if (len < 0) len = 0;
1296 info->remote_contact.slen = len;
1297 } else {
1298 info->remote_contact.slen = 0;
1299 }
1300
1301 /* call id */
1302 info->call_id.ptr = info->buf_.call_id;
1303 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1304 sizeof(info->buf_.call_id));
1305
1306 /* state, state_text */
1307 info->state = call->inv->state;
1308 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1309
1310 /* If call is disconnected, set the last_status from the cause code */
1311 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1312 /* last_status, last_status_text */
1313 info->last_status = call->inv->cause;
1314
1315 info->last_status_text.ptr = info->buf_.last_status_text;
1316 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1317 sizeof(info->buf_.last_status_text));
1318 } else {
1319 /* last_status, last_status_text */
1320 info->last_status = call->last_code;
1321
1322 info->last_status_text.ptr = info->buf_.last_status_text;
1323 pj_strncpy(&info->last_status_text, &call->last_text,
1324 sizeof(info->buf_.last_status_text));
1325 }
1326
1327 /* media status and dir */
1328 info->media_status = call->media_st;
1329 info->media_dir = call->media_dir;
1330
1331
1332 /* conference slot number */
1333 info->conf_slot = call->conf_slot;
1334
1335 /* calculate duration */
1336 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1337
1338 info->total_duration = call->dis_time;
1339 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1340
1341 if (call->conn_time.sec) {
1342 info->connect_duration = call->dis_time;
1343 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1344 }
1345
1346 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1347
1348 pj_gettimeofday(&info->total_duration);
1349 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1350
1351 pj_gettimeofday(&info->connect_duration);
1352 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1353
1354 } else {
1355 pj_gettimeofday(&info->total_duration);
1356 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1357 }
1358
Benny Prijonodc752ca2006-09-22 16:55:42 +00001359 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001360
1361 return PJ_SUCCESS;
1362}
1363
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001364/*
1365 * Check if call remote peer support the specified capability.
1366 */
1367PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1368 pjsua_call_id call_id,
1369 int htype,
1370 const pj_str_t *hname,
1371 const pj_str_t *token)
1372{
1373 pjsua_call *call;
1374 pjsip_dialog *dlg;
1375 pj_status_t status;
1376 pjsip_dialog_cap_status cap_status;
1377
1378 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1379 if (status != PJ_SUCCESS)
1380 return PJSIP_DIALOG_CAP_UNKNOWN;
1381
1382 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1383
1384 pjsip_dlg_dec_lock(dlg);
1385
1386 return cap_status;
1387}
1388
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001389
1390/*
1391 * Attach application specific data to the call.
1392 */
1393PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1394 void *user_data)
1395{
1396 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1397 PJ_EINVAL);
1398 pjsua_var.calls[call_id].user_data = user_data;
1399
1400 return PJ_SUCCESS;
1401}
1402
1403
1404/*
1405 * Get user data attached to the call.
1406 */
1407PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1408{
1409 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1410 NULL);
1411 return pjsua_var.calls[call_id].user_data;
1412}
1413
1414
1415/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001416 * Get remote's NAT type.
1417 */
1418PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1419 pj_stun_nat_type *p_type)
1420{
1421 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1422 PJ_EINVAL);
1423 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1424
1425 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1426 return PJ_SUCCESS;
1427}
1428
1429
1430/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001431 * Send response to incoming INVITE request.
1432 */
1433PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1434 unsigned code,
1435 const pj_str_t *reason,
1436 const pjsua_msg_data *msg_data)
1437{
1438 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001439 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001440 pjsip_tx_data *tdata;
1441 pj_status_t status;
1442
1443 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1444 PJ_EINVAL);
1445
Benny Prijonodc752ca2006-09-22 16:55:42 +00001446 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001447 if (status != PJ_SUCCESS)
1448 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001449
Benny Prijono2e507c22006-06-23 15:04:11 +00001450 if (call->res_time.sec == 0)
1451 pj_gettimeofday(&call->res_time);
1452
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001453 if (reason && reason->slen == 0)
1454 reason = NULL;
1455
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001456 /* Create response message */
1457 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1458 if (status != PJ_SUCCESS) {
1459 pjsua_perror(THIS_FILE, "Error creating response",
1460 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001461 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001462 return status;
1463 }
1464
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001465 /* Call might have been disconnected if application is answering with
1466 * 200/OK and the media failed to start.
1467 */
1468 if (call->inv == NULL) {
1469 pjsip_dlg_dec_lock(dlg);
1470 return PJSIP_ESESSIONTERMINATED;
1471 }
1472
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001473 /* Add additional headers etc */
1474 pjsua_process_msg_data( tdata, msg_data);
1475
1476 /* Send the message */
1477 status = pjsip_inv_send_msg(call->inv, tdata);
1478 if (status != PJ_SUCCESS)
1479 pjsua_perror(THIS_FILE, "Error sending response",
1480 status);
1481
Benny Prijonodc752ca2006-09-22 16:55:42 +00001482 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001483
1484 return status;
1485}
1486
1487
1488/*
1489 * Hangup call by using method that is appropriate according to the
1490 * call state.
1491 */
1492PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1493 unsigned code,
1494 const pj_str_t *reason,
1495 const pjsua_msg_data *msg_data)
1496{
1497 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001498 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001499 pj_status_t status;
1500 pjsip_tx_data *tdata;
1501
1502
Benny Prijono148c9dd2006-09-19 13:37:53 +00001503 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1504 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1505 call_id));
1506 }
1507
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001508 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1509 PJ_EINVAL);
1510
Benny Prijonodc752ca2006-09-22 16:55:42 +00001511 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001512 if (status != PJ_SUCCESS)
1513 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001514
1515 if (code==0) {
1516 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1517 code = PJSIP_SC_OK;
1518 else if (call->inv->role == PJSIP_ROLE_UAS)
1519 code = PJSIP_SC_DECLINE;
1520 else
1521 code = PJSIP_SC_REQUEST_TERMINATED;
1522 }
1523
1524 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1525 if (status != PJ_SUCCESS) {
1526 pjsua_perror(THIS_FILE,
1527 "Failed to create end session message",
1528 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001529 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001530 return status;
1531 }
1532
1533 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1534 * as p_tdata when INVITE transaction has not been answered
1535 * with any provisional responses.
1536 */
1537 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001538 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001539 return PJ_SUCCESS;
1540 }
1541
1542 /* Add additional headers etc */
1543 pjsua_process_msg_data( tdata, msg_data);
1544
1545 /* Send the message */
1546 status = pjsip_inv_send_msg(call->inv, tdata);
1547 if (status != PJ_SUCCESS) {
1548 pjsua_perror(THIS_FILE,
1549 "Failed to send end session message",
1550 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001551 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001552 return status;
1553 }
1554
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00001555 /* Stop lock codec timer, if it is active */
1556 if (call->lock_codec.reinv_timer.id) {
1557 pjsip_endpt_cancel_timer(pjsua_var.endpt,
1558 &call->lock_codec.reinv_timer);
1559 call->lock_codec.reinv_timer.id = PJ_FALSE;
1560 }
1561
Benny Prijonodc752ca2006-09-22 16:55:42 +00001562 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001563
1564 return PJ_SUCCESS;
1565}
1566
1567
1568/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001569 * Accept or reject redirection.
1570 */
1571PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1572 pjsip_redirect_op cmd)
1573{
1574 pjsua_call *call;
1575 pjsip_dialog *dlg;
1576 pj_status_t status;
1577
1578 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1579 PJ_EINVAL);
1580
1581 status = acquire_call("pjsua_call_process_redirect()", call_id,
1582 &call, &dlg);
1583 if (status != PJ_SUCCESS)
1584 return status;
1585
1586 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1587
1588 pjsip_dlg_dec_lock(dlg);
1589
1590 return status;
1591}
1592
1593
1594/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001595 * Put the specified call on hold.
1596 */
1597PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1598 const pjsua_msg_data *msg_data)
1599{
1600 pjmedia_sdp_session *sdp;
1601 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001602 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001603 pjsip_tx_data *tdata;
1604 pj_status_t status;
1605
1606 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1607 PJ_EINVAL);
1608
Benny Prijonodc752ca2006-09-22 16:55:42 +00001609 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001610 if (status != PJ_SUCCESS)
1611 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001612
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001613
1614 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1615 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001616 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001617 return PJSIP_ESESSIONSTATE;
1618 }
1619
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001620 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001621 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001622 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001623 return status;
1624 }
1625
1626 /* Create re-INVITE with new offer */
1627 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1628 if (status != PJ_SUCCESS) {
1629 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001630 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001631 return status;
1632 }
1633
1634 /* Add additional headers etc */
1635 pjsua_process_msg_data( tdata, msg_data);
1636
1637 /* Send the request */
1638 status = pjsip_inv_send_msg( call->inv, tdata);
1639 if (status != PJ_SUCCESS) {
1640 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001641 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001642 return status;
1643 }
1644
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001645 /* Set flag that local put the call on hold */
1646 call->local_hold = PJ_TRUE;
1647
Benny Prijonodc752ca2006-09-22 16:55:42 +00001648 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001649
1650 return PJ_SUCCESS;
1651}
1652
1653
1654/*
1655 * Send re-INVITE (to release hold).
1656 */
1657PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1658 pj_bool_t unhold,
1659 const pjsua_msg_data *msg_data)
1660{
1661 pjmedia_sdp_session *sdp;
1662 pjsip_tx_data *tdata;
1663 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001664 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001665 pj_status_t status;
1666
1667
1668 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1669 PJ_EINVAL);
1670
Benny Prijonodc752ca2006-09-22 16:55:42 +00001671 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001672 if (status != PJ_SUCCESS)
1673 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001674
1675 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1676 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001677 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001678 return PJSIP_ESESSIONSTATE;
1679 }
1680
1681 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001682 if (call->local_hold && !unhold) {
1683 status = create_sdp_of_call_hold(call, &sdp);
1684 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001685 status = pjsua_media_channel_create_sdp(call->index,
1686 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001687 NULL, &sdp, NULL);
1688 call->local_hold = PJ_FALSE;
1689 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001690 if (status != PJ_SUCCESS) {
1691 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1692 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001693 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001694 return status;
1695 }
1696
1697 /* Create re-INVITE with new offer */
1698 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1699 if (status != PJ_SUCCESS) {
1700 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001701 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001702 return status;
1703 }
1704
1705 /* Add additional headers etc */
1706 pjsua_process_msg_data( tdata, msg_data);
1707
1708 /* Send the request */
1709 status = pjsip_inv_send_msg( call->inv, tdata);
1710 if (status != PJ_SUCCESS) {
1711 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001712 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001713 return status;
1714 }
1715
Benny Prijonodc752ca2006-09-22 16:55:42 +00001716 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001717
1718 return PJ_SUCCESS;
1719}
1720
1721
1722/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001723 * Send UPDATE request.
1724 */
1725PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1726 unsigned options,
1727 const pjsua_msg_data *msg_data)
1728{
1729 pjmedia_sdp_session *sdp;
1730 pjsip_tx_data *tdata;
1731 pjsua_call *call;
1732 pjsip_dialog *dlg;
1733 pj_status_t status;
1734
1735 PJ_UNUSED_ARG(options);
1736
1737 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1738 PJ_EINVAL);
1739
1740 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1741 if (status != PJ_SUCCESS)
1742 return status;
1743
Benny Prijonoc08682e2007-10-04 06:17:58 +00001744 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001745 status = pjsua_media_channel_create_sdp(call->index,
1746 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001747 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001748 if (status != PJ_SUCCESS) {
1749 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1750 status);
1751 pjsip_dlg_dec_lock(dlg);
1752 return status;
1753 }
1754
Benny Prijono224b4e22008-06-19 14:10:28 +00001755 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001756 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1757 if (status != PJ_SUCCESS) {
1758 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1759 pjsip_dlg_dec_lock(dlg);
1760 return status;
1761 }
1762
1763 /* Add additional headers etc */
1764 pjsua_process_msg_data( tdata, msg_data);
1765
1766 /* Send the request */
1767 status = pjsip_inv_send_msg( call->inv, tdata);
1768 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001769 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001770 pjsip_dlg_dec_lock(dlg);
1771 return status;
1772 }
1773
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001774 call->local_hold = PJ_FALSE;
1775
Benny Prijonoc08682e2007-10-04 06:17:58 +00001776 pjsip_dlg_dec_lock(dlg);
1777
1778 return PJ_SUCCESS;
1779}
1780
1781
1782/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001783 * Initiate call transfer to the specified address.
1784 */
1785PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1786 const pj_str_t *dest,
1787 const pjsua_msg_data *msg_data)
1788{
1789 pjsip_evsub *sub;
1790 pjsip_tx_data *tdata;
1791 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001792 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001793 pjsip_generic_string_hdr *gs_hdr;
1794 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001795 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001796 pj_status_t status;
1797
1798
1799 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1800 PJ_EINVAL);
1801
Benny Prijonodc752ca2006-09-22 16:55:42 +00001802 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001803 if (status != PJ_SUCCESS)
1804 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001805
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001806
Benny Prijonod524e822006-09-22 12:48:18 +00001807 /* Create xfer client subscription. */
1808 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001809 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001810
1811 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001812 if (status != PJ_SUCCESS) {
1813 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001814 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001815 return status;
1816 }
1817
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001818 /* Associate this call with the client subscription */
1819 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1820
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001821 /*
1822 * Create REFER request.
1823 */
1824 status = pjsip_xfer_initiate(sub, dest, &tdata);
1825 if (status != PJ_SUCCESS) {
1826 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001827 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001828 return status;
1829 }
1830
Benny Prijono053f5222006-11-11 16:16:04 +00001831 /* Add Referred-By header */
1832 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1833 &dlg->local.info_str);
1834 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1835
1836
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001837 /* Add additional headers etc */
1838 pjsua_process_msg_data( tdata, msg_data);
1839
1840 /* Send. */
1841 status = pjsip_xfer_send_request(sub, tdata);
1842 if (status != PJ_SUCCESS) {
1843 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001844 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001845 return status;
1846 }
1847
1848 /* For simplicity (that's what this program is intended to be!),
1849 * leave the original invite session as it is. More advanced application
1850 * may want to hold the INVITE, or terminate the invite, or whatever.
1851 */
1852
Benny Prijonodc752ca2006-09-22 16:55:42 +00001853 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001854
1855 return PJ_SUCCESS;
1856
1857}
1858
1859
1860/*
Benny Prijono053f5222006-11-11 16:16:04 +00001861 * Initiate attended call transfer to the specified address.
1862 */
1863PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1864 pjsua_call_id dest_call_id,
1865 unsigned options,
1866 const pjsua_msg_data *msg_data)
1867{
1868 pjsua_call *dest_call;
1869 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001870 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001871 pj_str_t str_dest;
1872 int len;
1873 pjsip_uri *uri;
1874 pj_status_t status;
1875
1876
1877 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1878 PJ_EINVAL);
1879 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1880 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1881 PJ_EINVAL);
1882
1883 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1884 &dest_call, &dest_dlg);
1885 if (status != PJ_SUCCESS)
1886 return status;
1887
1888 /*
1889 * Create REFER destination URI with Replaces field.
1890 */
1891
1892 /* Make sure we have sufficient buffer's length */
1893 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1894 dest_dlg->call_id->id.slen +
1895 dest_dlg->remote.info->tag.slen +
1896 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001897 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001898
1899 /* Print URI */
1900 str_dest_buf[0] = '<';
1901 str_dest.slen = 1;
1902
Benny Prijonoa1e69682007-05-11 15:14:34 +00001903 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001904 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1905 str_dest_buf+1, sizeof(str_dest_buf)-1);
1906 if (len < 0)
1907 return PJSIP_EURITOOLONG;
1908
1909 str_dest.slen += len;
1910
1911
1912 /* Build the URI */
1913 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1914 sizeof(str_dest_buf) - str_dest.slen,
1915 "?%s"
1916 "Replaces=%.*s"
1917 "%%3Bto-tag%%3D%.*s"
1918 "%%3Bfrom-tag%%3D%.*s>",
1919 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1920 "" : "Require=replaces&"),
1921 (int)dest_dlg->call_id->id.slen,
1922 dest_dlg->call_id->id.ptr,
1923 (int)dest_dlg->remote.info->tag.slen,
1924 dest_dlg->remote.info->tag.ptr,
1925 (int)dest_dlg->local.info->tag.slen,
1926 dest_dlg->local.info->tag.ptr);
1927
1928 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1929 PJSIP_EURITOOLONG);
1930
1931 str_dest.ptr = str_dest_buf;
1932 str_dest.slen += len;
1933
1934 pjsip_dlg_dec_lock(dest_dlg);
1935
1936 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1937}
1938
1939
1940/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001941 * Send DTMF digits to remote using RFC 2833 payload formats.
1942 */
1943PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1944 const pj_str_t *digits)
1945{
1946 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001947 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001948 pj_status_t status;
1949
1950 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1951 PJ_EINVAL);
1952
Benny Prijonodc752ca2006-09-22 16:55:42 +00001953 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001954 if (status != PJ_SUCCESS)
1955 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001956
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001957 if (!call->session) {
1958 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001959 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001960 return PJ_EINVALIDOP;
1961 }
1962
1963 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1964
Benny Prijonodc752ca2006-09-22 16:55:42 +00001965 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001966
1967 return status;
1968}
1969
1970
1971/**
1972 * Send instant messaging inside INVITE session.
1973 */
1974PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1975 const pj_str_t *mime_type,
1976 const pj_str_t *content,
1977 const pjsua_msg_data *msg_data,
1978 void *user_data)
1979{
1980 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001981 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001982 const pj_str_t mime_text_plain = pj_str("text/plain");
1983 pjsip_media_type ctype;
1984 pjsua_im_data *im_data;
1985 pjsip_tx_data *tdata;
1986 pj_status_t status;
1987
1988
1989 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1990 PJ_EINVAL);
1991
Benny Prijonodc752ca2006-09-22 16:55:42 +00001992 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001993 if (status != PJ_SUCCESS)
1994 return status;
1995
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001996 /* Set default media type if none is specified */
1997 if (mime_type == NULL) {
1998 mime_type = &mime_text_plain;
1999 }
2000
2001 /* Create request message. */
2002 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2003 -1, &tdata);
2004 if (status != PJ_SUCCESS) {
2005 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2006 goto on_return;
2007 }
2008
2009 /* Add accept header. */
2010 pjsip_msg_add_hdr( tdata->msg,
2011 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
2012
2013 /* Parse MIME type */
2014 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
2015
2016 /* Create "text/plain" message body. */
2017 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
2018 &ctype.subtype, content);
2019 if (tdata->msg->body == NULL) {
2020 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
2021 pjsip_tx_data_dec_ref(tdata);
2022 goto on_return;
2023 }
2024
2025 /* Add additional headers etc */
2026 pjsua_process_msg_data( tdata, msg_data);
2027
2028 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002029 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002030 im_data->acc_id = call->acc_id;
2031 im_data->call_id = call_id;
2032 im_data->to = call->inv->dlg->remote.info_str;
2033 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2034 im_data->user_data = user_data;
2035
2036
2037 /* Send the request. */
2038 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2039 pjsua_var.mod.id, im_data);
2040 if (status != PJ_SUCCESS) {
2041 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2042 goto on_return;
2043 }
2044
2045on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002046 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002047 return status;
2048}
2049
2050
2051/*
2052 * Send IM typing indication inside INVITE session.
2053 */
2054PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2055 pj_bool_t is_typing,
2056 const pjsua_msg_data*msg_data)
2057{
2058 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002059 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002060 pjsip_tx_data *tdata;
2061 pj_status_t status;
2062
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002063 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2064 PJ_EINVAL);
2065
Benny Prijonodc752ca2006-09-22 16:55:42 +00002066 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002067 if (status != PJ_SUCCESS)
2068 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002069
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002070 /* Create request message. */
2071 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2072 -1, &tdata);
2073 if (status != PJ_SUCCESS) {
2074 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2075 goto on_return;
2076 }
2077
2078 /* Create "application/im-iscomposing+xml" msg body. */
2079 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2080 NULL, NULL, -1);
2081
2082 /* Add additional headers etc */
2083 pjsua_process_msg_data( tdata, msg_data);
2084
2085 /* Send the request. */
2086 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2087 if (status != PJ_SUCCESS) {
2088 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2089 goto on_return;
2090 }
2091
2092on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002093 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002094 return status;
2095}
2096
2097
2098/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002099 * Send arbitrary request.
2100 */
2101PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2102 const pj_str_t *method_str,
2103 const pjsua_msg_data *msg_data)
2104{
2105 pjsua_call *call;
2106 pjsip_dialog *dlg;
2107 pjsip_method method;
2108 pjsip_tx_data *tdata;
2109 pj_status_t status;
2110
2111 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2112 PJ_EINVAL);
2113
2114 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2115 if (status != PJ_SUCCESS)
2116 return status;
2117
2118 /* Init method */
2119 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2120
2121 /* Create request message. */
2122 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2123 if (status != PJ_SUCCESS) {
2124 pjsua_perror(THIS_FILE, "Unable to create request", status);
2125 goto on_return;
2126 }
2127
2128 /* Add additional headers etc */
2129 pjsua_process_msg_data( tdata, msg_data);
2130
2131 /* Send the request. */
2132 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2133 if (status != PJ_SUCCESS) {
2134 pjsua_perror(THIS_FILE, "Unable to send request", status);
2135 goto on_return;
2136 }
2137
2138on_return:
2139 pjsip_dlg_dec_lock(dlg);
2140 return status;
2141}
2142
2143
2144/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002145 * Terminate all calls.
2146 */
2147PJ_DEF(void) pjsua_call_hangup_all(void)
2148{
2149 unsigned i;
2150
2151 PJSUA_LOCK();
2152
2153 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2154 if (pjsua_var.calls[i].inv)
2155 pjsua_call_hangup(i, 0, NULL, NULL);
2156 }
2157
2158 PJSUA_UNLOCK();
2159}
2160
2161
Benny Prijono627cbb42007-09-25 20:48:49 +00002162const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002163{
2164 if (val < 1000) {
2165 pj_ansi_sprintf(buf, "%d", val);
2166 } else if (val < 1000000) {
2167 pj_ansi_sprintf(buf, "%d.%dK",
2168 val / 1000,
2169 (val % 1000) / 100);
2170 } else {
2171 pj_ansi_sprintf(buf, "%d.%02dM",
2172 val / 1000000,
2173 (val % 1000000) / 10000);
2174 }
2175
2176 return buf;
2177}
2178
2179
2180/* Dump media session */
2181static void dump_media_session(const char *indent,
2182 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002183 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002184{
2185 unsigned i;
2186 char *p = buf, *end = buf+maxlen;
2187 int len;
2188 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002189 pjmedia_session *session = call->session;
2190 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002191
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002192 pjmedia_transport_info_init(&tp_info);
2193
2194 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002195 pjmedia_session_get_info(session, &info);
2196
2197 for (i=0; i<info.stream_cnt; ++i) {
2198 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002199 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002200 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002201 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002202 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002203 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002204 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002205
2206 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002207 // rem_addr will contain actual address of RTP originator, instead of
2208 // remote RTP address specified by stream which is fetched from the SDP.
2209 // Please note that we are assuming only one stream per call.
2210 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2211 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002212 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2213 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002214 sizeof(rem_addr_buf), 3);
2215 } else {
2216 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002217 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002218 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002219
Benny Prijono2dbf5072010-06-23 12:38:28 +00002220 if (call->media_dir == PJMEDIA_DIR_NONE) {
2221 /* To handle when the stream that is currently being paused
2222 * (http://trac.pjsip.org/repos/ticket/1079)
2223 */
2224 dir = "inactive";
2225 } else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002226 dir = "sendonly";
2227 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2228 dir = "recvonly";
2229 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2230 dir = "sendrecv";
2231 else
2232 dir = "inactive";
2233
2234
2235 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002236 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002237 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002238 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002239 info.stream_info[i].fmt.encoding_name.ptr,
2240 info.stream_info[i].fmt.clock_rate / 1000,
2241 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002242 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002243 if (len < 1 || len > end-p) {
2244 *p = '\0';
2245 return;
2246 }
2247
2248 p += len;
2249 *p++ = '\n';
2250 *p = '\0';
2251
2252 if (stat.rx.update_cnt == 0)
2253 strcpy(last_update, "never");
2254 else {
2255 pj_gettimeofday(&now);
2256 PJ_TIME_VAL_SUB(now, stat.rx.update);
2257 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2258 now.sec / 3600,
2259 (now.sec % 3600) / 60,
2260 now.sec % 60,
2261 now.msec);
2262 }
2263
Benny Prijono80019eb2006-08-07 13:22:23 +00002264 pj_gettimeofday(&media_duration);
2265 PJ_TIME_VAL_SUB(media_duration, stat.start);
2266 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2267 media_duration.msec = 1;
2268
Benny Prijono1402a4a2008-01-08 23:41:22 +00002269 /* protect against division by zero */
2270 if (stat.rx.pkt == 0)
2271 stat.rx.pkt = 1;
2272 if (stat.tx.pkt == 0)
2273 stat.tx.pkt = 1;
2274
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002275 len = pj_ansi_snprintf(p, end-p,
2276 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002277 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002278 "%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 +00002279 "%s (msec) min avg max last dev\n"
2280 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
Nanang Izzuddin78bec1a2010-07-15 14:45:47 +00002281 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f"
2282#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2283 "\n"
2284 "%s raw jitter : %7.3f %7.3f %7.3f %7.3f %7.3f"
2285#endif
2286#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2287 "\n"
2288 "%s IPDV : %7.3f %7.3f %7.3f %7.3f %7.3f"
2289#endif
2290 "%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002291 indent, info.stream_info[i].fmt.pt,
2292 last_update,
2293 indent,
2294 good_number(packets, stat.rx.pkt),
2295 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002296 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002297 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2298 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 +00002299 indent,
2300 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002301 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002302 stat.rx.discard,
2303 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002304 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002305 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002306 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002307 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002308 indent, indent,
2309 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002310 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002311 stat.rx.loss_period.max / 1000.0,
2312 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002313 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002314 indent,
2315 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002316 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002317 stat.rx.jitter.max / 1000.0,
2318 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002319 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Nanang Izzuddin78bec1a2010-07-15 14:45:47 +00002320#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2321 indent,
2322 stat.rx_raw_jitter.min / 1000.0,
2323 stat.rx_raw_jitter.mean / 1000.0,
2324 stat.rx_raw_jitter.max / 1000.0,
2325 stat.rx_raw_jitter.last / 1000.0,
2326 pj_math_stat_get_stddev(&stat.rx_raw_jitter) / 1000.0,
2327#endif
2328#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2329 indent,
2330 stat.rx_ipdv.min / 1000.0,
2331 stat.rx_ipdv.mean / 1000.0,
2332 stat.rx_ipdv.max / 1000.0,
2333 stat.rx_ipdv.last / 1000.0,
2334 pj_math_stat_get_stddev(&stat.rx_ipdv) / 1000.0,
2335#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002336 ""
2337 );
2338
2339 if (len < 1 || len > end-p) {
2340 *p = '\0';
2341 return;
2342 }
2343
2344 p += len;
2345 *p++ = '\n';
2346 *p = '\0';
2347
2348 if (stat.tx.update_cnt == 0)
2349 strcpy(last_update, "never");
2350 else {
2351 pj_gettimeofday(&now);
2352 PJ_TIME_VAL_SUB(now, stat.tx.update);
2353 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2354 now.sec / 3600,
2355 (now.sec % 3600) / 60,
2356 now.sec % 60,
2357 now.msec);
2358 }
2359
2360 len = pj_ansi_snprintf(p, end-p,
2361 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002362 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002363 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002364 "%s (msec) min avg max last dev \n"
2365 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2366 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002367 indent,
2368 info.stream_info[i].tx_pt,
2369 info.stream_info[i].param->info.frm_ptime *
2370 info.stream_info[i].param->setting.frm_per_pkt,
2371 last_update,
2372
2373 indent,
2374 good_number(packets, stat.tx.pkt),
2375 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002376 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002377 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2378 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 +00002379
2380 indent,
2381 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002382 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002383 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002384 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002385 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002386 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002387
2388 indent, indent,
2389 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002390 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002391 stat.tx.loss_period.max / 1000.0,
2392 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002393 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002394 indent,
2395 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002396 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002397 stat.tx.jitter.max / 1000.0,
2398 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002399 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002400 ""
2401 );
2402
2403 if (len < 1 || len > end-p) {
2404 *p = '\0';
2405 return;
2406 }
2407
2408 p += len;
2409 *p++ = '\n';
2410 *p = '\0';
2411
2412 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002413 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002414 indent,
2415 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002416 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002417 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002418 stat.rtt.last / 1000.0,
2419 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002420 );
2421 if (len < 1 || len > end-p) {
2422 *p = '\0';
2423 return;
2424 }
2425
2426 p += len;
2427 *p++ = '\n';
2428 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002429
2430#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2431# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2432 do { \
2433 if (samples <= 4294) \
2434 usec = samples * 1000000 / clock_rate; \
2435 else { \
2436 usec = samples * 1000 / clock_rate; \
2437 usec *= 1000; \
2438 } \
2439 } while(0)
2440
2441# define PRINT_VOIP_MTC_VAL(s, v) \
2442 if (v == 127) \
2443 sprintf(s, "(na)"); \
2444 else \
2445 sprintf(s, "%d", v)
2446
2447# define VALIDATE_PRINT_BUF() \
2448 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2449 p += len; *p++ = '\n'; *p = '\0'
2450
2451
2452 do {
2453 char loss[16], dup[16];
2454 char jitter[80];
2455 char toh[80];
2456 char plc[16], jba[16], jbr[16];
2457 char signal_lvl[16], noise_lvl[16], rerl[16];
2458 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2459 pjmedia_rtcp_xr_stat xr_stat;
2460 unsigned clock_rate;
2461
2462 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2463 PJ_SUCCESS)
2464 {
2465 break;
2466 }
2467
2468 clock_rate = info.stream_info[i].fmt.clock_rate;
2469
2470 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2471 VALIDATE_PRINT_BUF();
2472
2473 /* Statistics Summary */
2474 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2475 VALIDATE_PRINT_BUF();
2476
2477 if (xr_stat.rx.stat_sum.l)
2478 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2479 else
2480 sprintf(loss, "(na)");
2481
2482 if (xr_stat.rx.stat_sum.d)
2483 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2484 else
2485 sprintf(dup, "(na)");
2486
2487 if (xr_stat.rx.stat_sum.j) {
2488 unsigned jmin, jmax, jmean, jdev;
2489
2490 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2491 clock_rate);
2492 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2493 clock_rate);
2494 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2495 clock_rate);
2496 SAMPLES_TO_USEC(jdev,
2497 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2498 clock_rate);
2499 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2500 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2501 } else
2502 sprintf(jitter, "(report not available)");
2503
2504 if (xr_stat.rx.stat_sum.t) {
2505 sprintf(toh, "%11d %11d %11d %11d",
2506 xr_stat.rx.stat_sum.toh.min,
2507 xr_stat.rx.stat_sum.toh.mean,
2508 xr_stat.rx.stat_sum.toh.max,
2509 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2510 } else
2511 sprintf(toh, "(report not available)");
2512
2513 if (xr_stat.rx.stat_sum.update.sec == 0)
2514 strcpy(last_update, "never");
2515 else {
2516 pj_gettimeofday(&now);
2517 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2518 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2519 now.sec / 3600,
2520 (now.sec % 3600) / 60,
2521 now.sec % 60,
2522 now.msec);
2523 }
2524
2525 len = pj_ansi_snprintf(p, end-p,
2526 "%s RX last update: %s\n"
2527 "%s begin seq=%d, end seq=%d\n"
2528 "%s pkt loss=%s, dup=%s\n"
2529 "%s (msec) min avg max dev\n"
2530 "%s jitter : %s\n"
2531 "%s toh : %s",
2532 indent, last_update,
2533 indent,
2534 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2535 indent, loss, dup,
2536 indent,
2537 indent, jitter,
2538 indent, toh
2539 );
2540 VALIDATE_PRINT_BUF();
2541
2542 if (xr_stat.tx.stat_sum.l)
2543 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2544 else
2545 sprintf(loss, "(na)");
2546
2547 if (xr_stat.tx.stat_sum.d)
2548 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2549 else
2550 sprintf(dup, "(na)");
2551
2552 if (xr_stat.tx.stat_sum.j) {
2553 unsigned jmin, jmax, jmean, jdev;
2554
2555 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2556 clock_rate);
2557 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2558 clock_rate);
2559 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2560 clock_rate);
2561 SAMPLES_TO_USEC(jdev,
2562 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2563 clock_rate);
2564 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2565 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2566 } else
2567 sprintf(jitter, "(report not available)");
2568
2569 if (xr_stat.tx.stat_sum.t) {
2570 sprintf(toh, "%11d %11d %11d %11d",
2571 xr_stat.tx.stat_sum.toh.min,
2572 xr_stat.tx.stat_sum.toh.mean,
2573 xr_stat.tx.stat_sum.toh.max,
2574 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2575 } else
2576 sprintf(toh, "(report not available)");
2577
2578 if (xr_stat.tx.stat_sum.update.sec == 0)
2579 strcpy(last_update, "never");
2580 else {
2581 pj_gettimeofday(&now);
2582 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2583 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2584 now.sec / 3600,
2585 (now.sec % 3600) / 60,
2586 now.sec % 60,
2587 now.msec);
2588 }
2589
2590 len = pj_ansi_snprintf(p, end-p,
2591 "%s TX last update: %s\n"
2592 "%s begin seq=%d, end seq=%d\n"
2593 "%s pkt loss=%s, dup=%s\n"
2594 "%s (msec) min avg max dev\n"
2595 "%s jitter : %s\n"
2596 "%s toh : %s",
2597 indent, last_update,
2598 indent,
2599 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2600 indent, loss, dup,
2601 indent,
2602 indent, jitter,
2603 indent, toh
2604 );
2605 VALIDATE_PRINT_BUF();
2606
2607
2608 /* VoIP Metrics */
2609 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2610 VALIDATE_PRINT_BUF();
2611
2612 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2613 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2614 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2615 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2616 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2617 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2618 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2619
2620 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2621 case PJMEDIA_RTCP_XR_PLC_DIS:
2622 sprintf(plc, "DISABLED");
2623 break;
2624 case PJMEDIA_RTCP_XR_PLC_ENH:
2625 sprintf(plc, "ENHANCED");
2626 break;
2627 case PJMEDIA_RTCP_XR_PLC_STD:
2628 sprintf(plc, "STANDARD");
2629 break;
2630 case PJMEDIA_RTCP_XR_PLC_UNK:
2631 default:
2632 sprintf(plc, "UNKNOWN");
2633 break;
2634 }
2635
2636 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2637 case PJMEDIA_RTCP_XR_JB_FIXED:
2638 sprintf(jba, "FIXED");
2639 break;
2640 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2641 sprintf(jba, "ADAPTIVE");
2642 break;
2643 default:
2644 sprintf(jba, "UNKNOWN");
2645 break;
2646 }
2647
2648 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2649
2650 if (xr_stat.rx.voip_mtc.update.sec == 0)
2651 strcpy(last_update, "never");
2652 else {
2653 pj_gettimeofday(&now);
2654 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2655 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2656 now.sec / 3600,
2657 (now.sec % 3600) / 60,
2658 now.sec % 60,
2659 now.msec);
2660 }
2661
2662 len = pj_ansi_snprintf(p, end-p,
2663 "%s RX last update: %s\n"
2664 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2665 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2666 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2667 "%s delay : round trip=%d%s, end system=%d%s\n"
2668 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2669 "%s quality : R factor=%s, ext R factor=%s\n"
2670 "%s MOS LQ=%s, MOS CQ=%s\n"
2671 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2672 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2673 indent,
2674 last_update,
2675 /* packets */
2676 indent,
2677 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2678 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2679 /* burst */
2680 indent,
2681 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2682 xr_stat.rx.voip_mtc.burst_dur, "ms",
2683 /* gap */
2684 indent,
2685 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2686 xr_stat.rx.voip_mtc.gap_dur, "ms",
2687 /* delay */
2688 indent,
2689 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2690 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2691 /* level */
2692 indent,
2693 signal_lvl, "dB",
2694 noise_lvl, "dB",
2695 rerl, "",
2696 /* quality */
2697 indent,
2698 r_factor, ext_r_factor,
2699 indent,
2700 mos_lq, mos_cq,
2701 /* config */
2702 indent,
2703 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2704 /* JB delay */
2705 indent,
2706 xr_stat.rx.voip_mtc.jb_nom, "ms",
2707 xr_stat.rx.voip_mtc.jb_max, "ms",
2708 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2709 );
2710 VALIDATE_PRINT_BUF();
2711
2712 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2713 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2714 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2715 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2716 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2717 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2718 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2719
2720 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2721 case PJMEDIA_RTCP_XR_PLC_DIS:
2722 sprintf(plc, "DISABLED");
2723 break;
2724 case PJMEDIA_RTCP_XR_PLC_ENH:
2725 sprintf(plc, "ENHANCED");
2726 break;
2727 case PJMEDIA_RTCP_XR_PLC_STD:
2728 sprintf(plc, "STANDARD");
2729 break;
2730 case PJMEDIA_RTCP_XR_PLC_UNK:
2731 default:
2732 sprintf(plc, "unknown");
2733 break;
2734 }
2735
2736 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2737 case PJMEDIA_RTCP_XR_JB_FIXED:
2738 sprintf(jba, "FIXED");
2739 break;
2740 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2741 sprintf(jba, "ADAPTIVE");
2742 break;
2743 default:
2744 sprintf(jba, "unknown");
2745 break;
2746 }
2747
2748 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2749
2750 if (xr_stat.tx.voip_mtc.update.sec == 0)
2751 strcpy(last_update, "never");
2752 else {
2753 pj_gettimeofday(&now);
2754 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2755 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2756 now.sec / 3600,
2757 (now.sec % 3600) / 60,
2758 now.sec % 60,
2759 now.msec);
2760 }
2761
2762 len = pj_ansi_snprintf(p, end-p,
2763 "%s TX last update: %s\n"
2764 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2765 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2766 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2767 "%s delay : round trip=%d%s, end system=%d%s\n"
2768 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2769 "%s quality : R factor=%s, ext R factor=%s\n"
2770 "%s MOS LQ=%s, MOS CQ=%s\n"
2771 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2772 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2773 indent,
2774 last_update,
2775 /* pakcets */
2776 indent,
2777 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2778 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2779 /* burst */
2780 indent,
2781 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2782 xr_stat.tx.voip_mtc.burst_dur, "ms",
2783 /* gap */
2784 indent,
2785 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2786 xr_stat.tx.voip_mtc.gap_dur, "ms",
2787 /* delay */
2788 indent,
2789 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2790 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2791 /* level */
2792 indent,
2793 signal_lvl, "dB",
2794 noise_lvl, "dB",
2795 rerl, "",
2796 /* quality */
2797 indent,
2798 r_factor, ext_r_factor,
2799 indent,
2800 mos_lq, mos_cq,
2801 /* config */
2802 indent,
2803 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2804 /* JB delay */
2805 indent,
2806 xr_stat.tx.voip_mtc.jb_nom, "ms",
2807 xr_stat.tx.voip_mtc.jb_max, "ms",
2808 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2809 );
2810 VALIDATE_PRINT_BUF();
2811
2812
2813 /* RTT delay (by receiver side) */
2814 len = pj_ansi_snprintf(p, end-p,
2815 "%s RTT (from recv) min avg max last dev",
2816 indent);
2817 VALIDATE_PRINT_BUF();
2818 len = pj_ansi_snprintf(p, end-p,
2819 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2820 indent,
2821 xr_stat.rtt.min / 1000.0,
2822 xr_stat.rtt.mean / 1000.0,
2823 xr_stat.rtt.max / 1000.0,
2824 xr_stat.rtt.last / 1000.0,
2825 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2826 );
2827 VALIDATE_PRINT_BUF();
2828 } while(0);
2829#endif
2830
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002831 }
2832}
2833
2834
2835/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002836void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002837 int call_id,
2838 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002839{
2840 int len;
2841 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2842 pjsip_dialog *dlg = inv->dlg;
2843 char userinfo[128];
2844
2845 /* Dump invite sesion info. */
2846
2847 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
Benny Prijono329d6382009-05-29 13:04:03 +00002848 if (len < 0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002849 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2850 else
2851 userinfo[len] = '\0';
2852
2853 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2854 title,
2855 pjsip_inv_state_name(inv->state),
2856 userinfo);
2857 if (len < 1 || len >= (int)size) {
2858 pj_ansi_strcpy(buf, "<--uri too long-->");
2859 len = 18;
2860 } else
2861 buf[len] = '\0';
2862}
2863
2864
2865/*
2866 * Dump call and media statistics to string.
2867 */
2868PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2869 pj_bool_t with_media,
2870 char *buffer,
2871 unsigned maxlen,
2872 const char *indent)
2873{
2874 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002875 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002876 pj_time_val duration, res_delay, con_delay;
2877 char tmp[128];
2878 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002879 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002880 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002881 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002882
2883 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2884 PJ_EINVAL);
2885
Benny Prijonodc752ca2006-09-22 16:55:42 +00002886 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002887 if (status != PJ_SUCCESS)
2888 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002889
2890 *buffer = '\0';
2891 p = buffer;
2892 end = buffer + maxlen;
2893 len = 0;
2894
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002895 print_call(indent, call_id, tmp, sizeof(tmp));
2896
2897 len = pj_ansi_strlen(tmp);
2898 pj_ansi_strcpy(buffer, tmp);
2899
2900 p += len;
2901 *p++ = '\r';
2902 *p++ = '\n';
2903
2904 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002905 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002906 pj_gettimeofday(&duration);
2907 PJ_TIME_VAL_SUB(duration, call->conn_time);
2908 con_delay = call->conn_time;
2909 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2910 } else {
2911 duration.sec = duration.msec = 0;
2912 con_delay.sec = con_delay.msec = 0;
2913 }
2914
2915 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002916 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002917 res_delay = call->res_time;
2918 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2919 } else {
2920 res_delay.sec = res_delay.msec = 0;
2921 }
2922
2923 /* Print duration */
2924 len = pj_ansi_snprintf(p, end-p,
2925 "%s Call time: %02dh:%02dm:%02ds, "
2926 "1st res in %d ms, conn in %dms",
2927 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002928 (int)(duration.sec / 3600),
2929 (int)((duration.sec % 3600)/60),
2930 (int)(duration.sec % 60),
2931 (int)PJ_TIME_VAL_MSEC(res_delay),
2932 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002933
2934 if (len > 0 && len < end-p) {
2935 p += len;
2936 *p++ = '\n';
2937 *p = '\0';
2938 }
2939
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002940 /* Get and ICE SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002941 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002942 pjmedia_transport_get_info(call->med_tp, &tp_info);
2943 if (tp_info.specific_info_cnt > 0) {
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002944 unsigned i;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002945 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2946 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2947 {
2948 pjmedia_srtp_info *srtp_info =
2949 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2950
2951 len = pj_ansi_snprintf(p, end-p,
2952 "%s SRTP status: %s Crypto-suite: %s",
2953 indent,
2954 (srtp_info->active?"Active":"Not active"),
2955 srtp_info->tx_policy.name.ptr);
2956 if (len > 0 && len < end-p) {
2957 p += len;
2958 *p++ = '\n';
2959 *p = '\0';
2960 }
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002961 } else if (tp_info.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
2962 const pjmedia_ice_transport_info *ii;
2963
2964 ii = (const pjmedia_ice_transport_info*)
2965 tp_info.spc_info[i].buffer;
2966
2967 len = pj_ansi_snprintf(p, end-p,
2968 "%s ICE role: %s, state: %s, comp_cnt: %u",
2969 indent,
2970 pj_ice_sess_role_name(ii->role),
2971 pj_ice_strans_state_name(ii->sess_state),
2972 ii->comp_cnt);
2973 if (len > 0 && len < end-p) {
2974 p += len;
2975 *p++ = '\n';
2976 *p = '\0';
2977 }
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002978 }
2979 }
2980 }
2981
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002982 /* Dump session statistics */
2983 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002984 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002985
Benny Prijonodc752ca2006-09-22 16:55:42 +00002986 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002987
2988 return PJ_SUCCESS;
2989}
2990
2991
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00002992/* Timer callback to close sound device */
2993static void reinv_timer_cb(pj_timer_heap_t *th,
2994 pj_timer_entry *entry)
2995{
2996 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
2997 pjsip_dialog *dlg;
2998 pjsua_call *call;
2999 pjsip_tx_data *tdata;
3000 pj_status_t status;
3001
3002 PJ_UNUSED_ARG(th);
3003
3004 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
3005
3006 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
3007 if (status != PJ_SUCCESS)
3008 return;
3009
3010 /* Verify if another SDP negotiation is in progress, e.g: session timer
3011 * or another re-INVITE.
3012 */
3013 if (call->inv==NULL || call->inv->neg==NULL ||
3014 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
3015 {
3016 goto on_return;
3017 }
3018
3019 /* Verify if another SDP negotiation has been completed by comparing
3020 * the SDP version.
3021 */
3022 {
3023 const pjmedia_sdp_session *sdp;
3024
3025 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
3026 if (status == PJ_SUCCESS &&
3027 sdp->origin.version > call->lock_codec.new_sdp->origin.version)
3028 {
3029 goto on_return;
3030 }
3031 }
3032
3033 /* Create re-INVITE with the new offer */
3034 status = pjsip_inv_reinvite(call->inv, NULL, call->lock_codec.new_sdp,
3035 &tdata);
3036 if (status == PJ_EINVALIDOP) {
3037 /* Ups, let's reschedule again */
3038 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
3039 call->lock_codec.reinv_timer.id = PJ_TRUE;
3040 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3041 &call->lock_codec.reinv_timer, &delay);
3042 } else if (status != PJ_SUCCESS) {
3043 pjsua_perror(THIS_FILE, "Failed creating re-INVITE in lock codec",
3044 status);
3045 }
3046
3047 /* Send the UPDATE/re-INVITE request */
3048 status = pjsip_inv_send_msg(call->inv, tdata);
3049 if (status != PJ_SUCCESS) {
3050 pjsua_perror(THIS_FILE, "Failed sending re-INVITE in lock codec",
3051 status);
3052 }
3053
3054on_return:
3055 pjsip_dlg_dec_lock(dlg);
3056}
3057
3058
3059/* Check if the specified format can be skipped in counting codecs */
3060static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
3061 const pj_str_t *fmt)
3062{
3063 unsigned pt;
3064
3065 pt = pj_strtoul(fmt);
3066
3067 /* Check for comfort noise */
3068 if (pt == PJMEDIA_RTP_PT_CN)
3069 return PJ_TRUE;
3070
3071 /* Dynamic PT, check the format name */
3072 if (pt >= 96) {
3073 pjmedia_sdp_attr *a;
3074 pjmedia_sdp_rtpmap rtpmap;
3075
3076 /* Get the format name */
3077 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3078 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
3079 /* Check for telephone-event */
3080 if (pj_stricmp2(&rtpmap.enc_name, "telephone-event")==0)
3081 return PJ_TRUE;
3082 } else {
3083 /* Invalid SDP, should not reach here */
3084 pj_assert(!"SDP should have been validated!");
3085 return PJ_TRUE;
3086 }
3087 }
3088
3089 return PJ_FALSE;
3090}
3091
3092
3093/* Check if remote answerer has given us more than one codecs. If so,
3094 * create another offer with one codec only to lock down the codec.
3095 */
3096static pj_status_t lock_codec(pjsua_call *call)
3097{
3098 const pj_str_t st_update = {"UPDATE", 6};
3099 pjsip_inv_session *inv = call->inv;
3100 const pjmedia_sdp_session *local_sdp;
3101 const pjmedia_sdp_session *remote_sdp;
3102 const pjmedia_sdp_media *rem_m;
3103 pjmedia_sdp_session *new_sdp;
3104 pjmedia_sdp_media *m;
3105 pjsip_tx_data *tdata;
3106 unsigned i, codec_cnt = 0;
3107 pj_status_t status;
3108
3109 if (!pjmedia_sdp_neg_was_answer_remote(inv->neg))
3110 return PJ_SUCCESS;
3111
3112 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
3113 if (status != PJ_SUCCESS)
3114 return status;
3115 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3116 if (status != PJ_SUCCESS)
3117 return status;
3118
3119 PJ_ASSERT_RETURN(call->audio_idx>=0 &&
3120 call->audio_idx < (int)remote_sdp->media_count,
3121 PJ_EINVALIDOP);
3122
3123 rem_m = remote_sdp->media[call->audio_idx];
3124
3125 /* Check if media is disabled or only one format in the answer. */
3126 if (rem_m->desc.port==0 || rem_m->desc.fmt_count==1)
3127 return PJ_SUCCESS;
3128
3129 /* Count the formats in the answer. */
3130 for (i=0; i<rem_m->desc.fmt_count && codec_cnt <= 1; ++i) {
3131 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[i]))
3132 ++codec_cnt;
3133 }
3134
3135 if (codec_cnt <= 1) {
3136 /* Answer contains single codec. */
3137 return PJ_SUCCESS;
3138 }
3139
3140 PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, start "
3141 "updating media session to use only one codec.."));
3142
3143 /* Clone the offer */
3144 new_sdp = pjmedia_sdp_session_clone(inv->pool_prov, local_sdp);
3145 /* Note that the usage of pool_prov above is risky when locking codec
3146 * delays the re-INVITE (using timer) and there are two SDP negotiations
3147 * done before the re-INVITE.
3148 */
3149
3150 /* Update the new offer so it contains only a codec. Note that formats
3151 * order in the offer should have been matched to the answer, so we can
3152 * just directly update the offer without looking-up the answer.
3153 */
3154 m = new_sdp->media[call->audio_idx];
3155 codec_cnt = 0;
3156 i = 0;
3157 while (i < m->desc.fmt_count) {
3158 pjmedia_sdp_attr *a;
3159 pj_str_t *fmt = &m->desc.fmt[i];
3160
3161 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
3162 ++i;
3163 continue;
3164 }
3165
3166 /* Remove format */
3167 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3168 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3169 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
3170 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3171 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
3172 m->desc.fmt_count, i);
3173 --m->desc.fmt_count;
3174 }
3175
3176 /* Send new SDP offer via UPDATE or re-INVITE */
3177 if (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)==
3178 PJSIP_DIALOG_CAP_SUPPORTED)
3179 {
3180 /* Create UPDATE with the new offer */
3181 status = pjsip_inv_update(inv, NULL, new_sdp, &tdata);
3182 if (status != PJ_SUCCESS)
3183 return status;
3184
3185 } else {
3186 /* Create re-INVITE with the new offer */
3187 status = pjsip_inv_reinvite(inv, NULL, new_sdp, &tdata);
3188 if (status == PJ_EINVALIDOP) {
3189 /* Current INVITE transaction is pending, reschedule re-INVITE. */
3190 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
3191
3192 call->lock_codec.new_sdp = new_sdp;
3193 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
3194 (void*)(pj_size_t)call->index,
3195 &reinv_timer_cb);
3196 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3197 &call->lock_codec.reinv_timer, &delay);
3198 return PJ_SUCCESS;
3199
3200 } else if (status != PJ_SUCCESS)
3201 return status;
3202 }
3203
3204 /* Send the UPDATE/re-INVITE request */
3205 status = pjsip_inv_send_msg(inv, tdata);
3206 if (status != PJ_SUCCESS)
3207 return status;
3208
3209 return PJ_SUCCESS;
3210}
3211
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003212/*
Benny Prijono84126ab2006-02-09 09:30:09 +00003213 * This callback receives notification from invite session when the
3214 * session state has changed.
3215 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003216static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
3217 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00003218{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003219 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003220
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003221 PJSUA_LOCK();
3222
Benny Prijonoa1e69682007-05-11 15:14:34 +00003223 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003224
3225 if (!call) {
3226 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00003227 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003228 }
3229
Benny Prijonoe21e7842006-04-09 16:46:05 +00003230
3231 /* Get call times */
3232 switch (inv->state) {
3233 case PJSIP_INV_STATE_EARLY:
3234 case PJSIP_INV_STATE_CONNECTING:
3235 if (call->res_time.sec == 0)
3236 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00003237 call->last_code = (pjsip_status_code)
3238 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003239 pj_strncpy(&call->last_text,
3240 &e->body.tsx_state.tsx->status_text,
3241 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00003242 break;
3243 case PJSIP_INV_STATE_CONFIRMED:
3244 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003245
3246 /* Ticket #476, locking a codec in the media session. */
3247 {
3248 pj_status_t status;
3249 status = lock_codec(call);
3250 if (status != PJ_SUCCESS) {
3251 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
3252 }
3253 }
3254
Benny Prijonoe21e7842006-04-09 16:46:05 +00003255 break;
3256 case PJSIP_INV_STATE_DISCONNECTED:
3257 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00003258 if (call->res_time.sec == 0)
3259 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003260 if (e->type == PJSIP_EVENT_TSX_STATE &&
3261 e->body.tsx_state.tsx->status_code > call->last_code)
3262 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00003263 call->last_code = (pjsip_status_code)
3264 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003265 pj_strncpy(&call->last_text,
3266 &e->body.tsx_state.tsx->status_text,
3267 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003268 } else {
3269 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
3270 pj_strncpy(&call->last_text,
3271 pjsip_get_status_text(call->last_code),
3272 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00003273 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003274
3275 /* Stop lock codec timer, if it is active */
3276 if (call->lock_codec.reinv_timer.id) {
3277 pjsip_endpt_cancel_timer(pjsua_var.endpt,
3278 &call->lock_codec.reinv_timer);
3279 call->lock_codec.reinv_timer.id = PJ_FALSE;
3280 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00003281 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003282 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00003283 call->last_code = (pjsip_status_code)
3284 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003285 pj_strncpy(&call->last_text,
3286 &e->body.tsx_state.tsx->status_text,
3287 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00003288 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00003289 }
3290
Benny Prijono26ff9062006-02-21 23:47:00 +00003291 /* If this is an outgoing INVITE that was created because of
3292 * REFER/transfer, send NOTIFY to transferer.
3293 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00003294 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003295 int st_code = -1;
3296 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
3297
3298
Benny Prijonoa91a0032006-02-26 21:23:45 +00003299 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003300 case PJSIP_INV_STATE_NULL:
3301 case PJSIP_INV_STATE_CALLING:
3302 /* Do nothing */
3303 break;
3304
3305 case PJSIP_INV_STATE_EARLY:
3306 case PJSIP_INV_STATE_CONNECTING:
3307 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003308 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
3309 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3310 else
3311 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003312 break;
3313
Benny Prijono140beae2009-10-11 05:06:43 +00003314 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003315#if 0
3316/* We don't need this, as we've terminated the subscription in
3317 * CONNECTING state.
3318 */
Benny Prijono26ff9062006-02-21 23:47:00 +00003319 /* When state is confirmed, send the final 200/OK and terminate
3320 * subscription.
3321 */
3322 st_code = e->body.tsx_state.tsx->status_code;
3323 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003324#endif
Benny Prijono140beae2009-10-11 05:06:43 +00003325 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003326
3327 case PJSIP_INV_STATE_DISCONNECTED:
3328 st_code = e->body.tsx_state.tsx->status_code;
3329 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3330 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003331
Benny Prijono8b1889b2006-06-06 18:40:40 +00003332 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00003333 /* Nothing to do. Just to keep gcc from complaining about
3334 * unused enums.
3335 */
3336 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003337 }
3338
3339 if (st_code != -1) {
3340 pjsip_tx_data *tdata;
3341 pj_status_t status;
3342
Benny Prijonoa91a0032006-02-26 21:23:45 +00003343 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00003344 ev_state, st_code,
3345 NULL, &tdata);
3346 if (status != PJ_SUCCESS) {
3347 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
3348 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003349 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00003350 if (status != PJ_SUCCESS) {
3351 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
3352 }
3353 }
3354 }
3355 }
3356
Benny Prijono84126ab2006-02-09 09:30:09 +00003357
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003358 if (pjsua_var.ua_cfg.cb.on_call_state)
3359 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003360
3361 /* call->inv may be NULL now */
3362
Benny Prijono84126ab2006-02-09 09:30:09 +00003363 /* Destroy media session when invite session is disconnected. */
3364 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003365
Benny Prijonoa91a0032006-02-26 21:23:45 +00003366 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003367
Benny Prijono275fd682006-03-22 11:59:11 +00003368 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003369 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003370
Benny Prijono105217f2006-03-06 16:25:59 +00003371 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003372 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003373 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003374
3375 /* Reset call */
3376 reset_call(call->index);
3377
Benny Prijono84126ab2006-02-09 09:30:09 +00003378 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003379
3380 PJSUA_UNLOCK();
3381}
3382
3383/*
3384 * This callback is called by invite session framework when UAC session
3385 * has forked.
3386 */
3387static void pjsua_call_on_forked( pjsip_inv_session *inv,
3388 pjsip_event *e)
3389{
3390 PJ_UNUSED_ARG(inv);
3391 PJ_UNUSED_ARG(e);
3392
3393 PJ_TODO(HANDLE_FORKED_DIALOG);
3394}
3395
3396
3397/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003398 * Callback from UA layer when forked dialog response is received.
3399 */
3400pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3401{
3402 if (dlg->uac_has_2xx &&
3403 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3404 pjsip_rdata_get_tsx(res) == NULL &&
3405 res->msg_info.msg->line.status.code/100 == 2)
3406 {
3407 pjsip_dialog *forked_dlg;
3408 pjsip_tx_data *bye;
3409 pj_status_t status;
3410
3411 /* Create forked dialog */
3412 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3413 if (status != PJ_SUCCESS)
3414 return NULL;
3415
3416 pjsip_dlg_inc_lock(forked_dlg);
3417
3418 /* Disconnect the call */
3419 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3420 -1, &bye);
3421 if (status == PJ_SUCCESS) {
3422 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3423 }
3424
3425 pjsip_dlg_dec_lock(forked_dlg);
3426
3427 if (status != PJ_SUCCESS) {
3428 return NULL;
3429 }
3430
3431 return forked_dlg;
3432
3433 } else {
3434 return dlg;
3435 }
3436}
3437
3438/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003439 * Disconnect call upon error.
3440 */
3441static void call_disconnect( pjsip_inv_session *inv,
3442 int code )
3443{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003444 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003445 pjsip_tx_data *tdata;
3446 pj_status_t status;
3447
Benny Prijono59b3aed2008-01-15 16:54:54 +00003448 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3449
Benny Prijonoa38ada02006-07-02 14:22:35 +00003450 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003451 if (status != PJ_SUCCESS)
3452 return;
3453
3454 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003455 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3456 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3457 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003458 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003459 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003460
Benny Prijono734fc2d2008-03-17 16:05:35 +00003461 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003462 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003463 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003464 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003465 if (status == PJ_SUCCESS) {
3466 pjsip_create_sdp_body(tdata->pool, local_sdp,
3467 &tdata->msg->body);
3468 }
3469 }
3470
3471 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003472}
3473
3474/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003475 * Callback to be called when SDP offer/answer negotiation has just completed
3476 * in the session. This function will start/update media if negotiation
3477 * has succeeded.
3478 */
3479static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3480 pj_status_t status)
3481{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003482 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003483 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003484 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003485 const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003486
3487 PJSUA_LOCK();
3488
Benny Prijonoa1e69682007-05-11 15:14:34 +00003489 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003490
3491 if (status != PJ_SUCCESS) {
3492
3493 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3494
Benny Prijono2331d202008-06-26 15:46:52 +00003495 /* Do not deinitialize media since this may be a re-INVITE or
3496 * UPDATE (which in this case the media should not get affected
3497 * by the failed re-INVITE/UPDATE). The media will be shutdown
3498 * when call is disconnected anyway.
3499 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003500 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003501 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003502
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003503 /* Disconnect call if we're not in the middle of initializing an
3504 * UAS dialog and if this is not a re-INVITE
3505 */
3506 if (inv->state != PJSIP_INV_STATE_NULL &&
3507 inv->state != PJSIP_INV_STATE_CONFIRMED)
3508 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003509 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003510 }
3511
3512 PJSUA_UNLOCK();
3513 return;
3514 }
3515
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003516
3517 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003518 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003519 if (status != PJ_SUCCESS) {
3520 pjsua_perror(THIS_FILE,
3521 "Unable to retrieve currently active local SDP",
3522 status);
3523 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3524 PJSUA_UNLOCK();
3525 return;
3526 }
3527
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003528 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3529 if (status != PJ_SUCCESS) {
3530 pjsua_perror(THIS_FILE,
3531 "Unable to retrieve currently active remote SDP",
3532 status);
3533 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3534 PJSUA_UNLOCK();
3535 return;
3536 }
3537
Benny Prijono91a6a172007-10-31 08:59:29 +00003538 /* Update remote's NAT type */
3539 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3540 update_remote_nat_type(call, remote_sdp);
3541 }
3542
3543 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003544 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003545 if (status != PJ_SUCCESS) {
3546 pjsua_perror(THIS_FILE, "Unable to create media session",
3547 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003548 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003549 /* No need to deinitialize; media will be shutdown when call
3550 * state is disconnected anyway.
3551 */
3552 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003553 PJSUA_UNLOCK();
3554 return;
3555 }
3556
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003557 /* Ticket #476, handle the case of early media and remote support UPDATE */
3558 if (inv->state == PJSIP_INV_STATE_EARLY &&
3559 pjmedia_sdp_neg_was_answer_remote(inv->neg) &&
3560 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)==
3561 PJSIP_DIALOG_CAP_SUPPORTED)
3562 {
3563 status = lock_codec(call);
3564 if (status != PJ_SUCCESS) {
3565 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
3566 }
3567 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003568
3569 /* Call application callback, if any */
3570 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3571 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3572
3573
3574 PJSUA_UNLOCK();
3575}
3576
3577
Benny Prijonodd63b992010-10-01 02:03:42 +00003578/* Modify SDP for call hold. */
3579static pj_status_t modify_sdp_of_call_hold(pjsua_call *call,
3580 pj_pool_t *pool,
3581 pjmedia_sdp_session *sdp)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003582{
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003583 /* Call-hold is done by set the media direction to 'sendonly'
3584 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3585 * 'inactive' (PJMEDIA_DIR_NONE).
3586 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3587 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003588 /* http://trac.pjsip.org/repos/ticket/880
3589 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3590 */
Benny Prijonodd63b992010-10-01 02:03:42 +00003591 /* https://trac.pjsip.org/repos/ticket/1142:
3592 * configuration to use c=0.0.0.0 for call hold.
3593 */
3594 if (call->call_hold_type == PJSUA_CALL_HOLD_TYPE_RFC2543) {
3595 pjmedia_sdp_conn *conn;
3596 pjmedia_sdp_attr *attr;
3597
3598 /* Get SDP media connection line */
3599 conn = sdp->media[0]->conn;
3600 if (!conn)
3601 conn = sdp->conn;
3602
3603 /* Modify address */
3604 conn->addr = pj_str("0.0.0.0");
3605
3606 /* 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");
3611
3612 /* Add inactive attribute */
3613 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3614 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3615
3616
3617 } else {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003618 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003619
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003620 /* Remove existing directions attributes */
3621 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendrecv");
3622 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "sendonly");
3623 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "recvonly");
3624 pjmedia_sdp_media_remove_all_attr(sdp->media[0], "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003625
Benny Prijonoe0860132009-06-05 10:14:20 +00003626 if (call->media_dir & PJMEDIA_DIR_ENCODING) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003627 /* Add sendonly attribute */
3628 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3629 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3630 } else {
3631 /* Add inactive attribute */
3632 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3633 pjmedia_sdp_media_add_attr(sdp->media[0], attr);
3634 }
3635 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003636
Benny Prijonodd63b992010-10-01 02:03:42 +00003637 return PJ_SUCCESS;
3638}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003639
Benny Prijonodd63b992010-10-01 02:03:42 +00003640/* Create SDP for call hold. */
3641static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3642 pjmedia_sdp_session **p_sdp)
3643{
3644 pj_status_t status;
3645 pj_pool_t *pool;
3646 pjmedia_sdp_session *sdp;
3647
3648 /* Use call's provisional pool */
3649 pool = call->inv->pool_prov;
3650
3651 /* Create new offer */
3652 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3653 NULL);
3654 if (status != PJ_SUCCESS) {
3655 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3656 return status;
3657 }
3658
3659 status = modify_sdp_of_call_hold(call, pool, sdp);
3660 if (status != PJ_SUCCESS)
3661 return status;
3662
3663 *p_sdp = sdp;
3664
3665 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003666}
3667
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003668/*
3669 * Called when session received new offer.
3670 */
3671static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3672 const pjmedia_sdp_session *offer)
3673{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003674 pjsua_call *call;
3675 pjmedia_sdp_conn *conn;
3676 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003677 pj_status_t status;
3678
3679 PJSUA_LOCK();
3680
Benny Prijonoa1e69682007-05-11 15:14:34 +00003681 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003682
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003683 conn = offer->media[0]->conn;
3684 if (!conn)
3685 conn = offer->conn;
3686
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003687 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003688 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3689 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003690
Benny Prijono40d62b62009-08-12 17:53:47 +00003691 status = pjsua_media_channel_create_sdp(call->index,
3692 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003693 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003694 if (status != PJ_SUCCESS) {
3695 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3696 PJSUA_UNLOCK();
3697 return;
3698 }
3699
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003700 /* Check if offer's conn address is zero */
3701 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3702 pj_strcmp2(&conn->addr, "0")==0)
3703 {
3704 /* Modify address */
3705 answer->conn->addr = pj_str("0.0.0.0");
3706 }
3707
3708 /* Check if call is on-hold */
3709 if (call->local_hold) {
Benny Prijonodd63b992010-10-01 02:03:42 +00003710 modify_sdp_of_call_hold(call, call->inv->pool_prov, answer);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003711 }
3712
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003713 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3714 if (status != PJ_SUCCESS) {
3715 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3716 PJSUA_UNLOCK();
3717 return;
3718 }
3719
3720 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003721}
3722
3723
3724/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003725 * Called to generate new offer.
3726 */
3727static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3728 pjmedia_sdp_session **offer)
3729{
3730 pjsua_call *call;
3731 pj_status_t status;
3732
3733 PJSUA_LOCK();
3734
3735 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3736
3737 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003738 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003739 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003740 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003741 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003742 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003743 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003744 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3745 call->index));
3746
Benny Prijono40d62b62009-08-12 17:53:47 +00003747 status = pjsua_media_channel_create_sdp(call->index,
3748 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003749 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003750 }
3751
3752 if (status != PJ_SUCCESS) {
3753 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3754 PJSUA_UNLOCK();
3755 return;
3756 }
3757
Benny Prijono77998ce2007-06-20 10:03:46 +00003758 PJSUA_UNLOCK();
3759}
3760
3761
3762/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003763 * Callback called by event framework when the xfer subscription state
3764 * has changed.
3765 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003766static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3767{
3768
3769 PJ_UNUSED_ARG(event);
3770
3771 /*
3772 * When subscription is accepted (got 200/OK to REFER), check if
3773 * subscription suppressed.
3774 */
3775 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3776
3777 pjsip_rx_data *rdata;
3778 pjsip_generic_string_hdr *refer_sub;
3779 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3780 pjsua_call *call;
3781
Benny Prijonoa1e69682007-05-11 15:14:34 +00003782 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003783
3784 /* Must be receipt of response message */
3785 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3786 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3787 rdata = event->body.tsx_state.src.rdata;
3788
3789 /* Find Refer-Sub header */
3790 refer_sub = (pjsip_generic_string_hdr*)
3791 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3792 &REFER_SUB, NULL);
3793
3794 /* Check if subscription is suppressed */
3795 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3796 /* Since no subscription is desired, assume that call has been
3797 * transfered successfully.
3798 */
3799 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3800 const pj_str_t ACCEPTED = { "Accepted", 8 };
3801 pj_bool_t cont = PJ_FALSE;
3802 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3803 200,
3804 &ACCEPTED,
3805 PJ_TRUE,
3806 &cont);
3807 }
3808
3809 /* Yes, subscription is suppressed.
3810 * Terminate our subscription now.
3811 */
3812 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3813 "event subcription..."));
3814 pjsip_evsub_terminate(sub, PJ_TRUE);
3815
3816 } else {
3817 /* Notify application about call transfer progress.
3818 * Initially notify with 100/Accepted status.
3819 */
3820 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3821 const pj_str_t ACCEPTED = { "Accepted", 8 };
3822 pj_bool_t cont = PJ_FALSE;
3823 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3824 100,
3825 &ACCEPTED,
3826 PJ_FALSE,
3827 &cont);
3828 }
3829 }
3830 }
3831 /*
3832 * On incoming NOTIFY, notify application about call transfer progress.
3833 */
3834 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3835 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3836 {
3837 pjsua_call *call;
3838 pjsip_msg *msg;
3839 pjsip_msg_body *body;
3840 pjsip_status_line status_line;
3841 pj_bool_t is_last;
3842 pj_bool_t cont;
3843 pj_status_t status;
3844
Benny Prijonoa1e69682007-05-11 15:14:34 +00003845 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003846
3847 /* When subscription is terminated, clear the xfer_sub member of
3848 * the inv_data.
3849 */
3850 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3851 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3852 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3853
3854 }
3855
3856 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3857 /* Application is not interested with call progress status */
3858 return;
3859 }
3860
3861 /* This better be a NOTIFY request */
3862 if (event->type == PJSIP_EVENT_TSX_STATE &&
3863 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3864 {
3865 pjsip_rx_data *rdata;
3866
3867 rdata = event->body.tsx_state.src.rdata;
3868
3869 /* Check if there's body */
3870 msg = rdata->msg_info.msg;
3871 body = msg->body;
3872 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003873 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003874 "Warning: received NOTIFY without message body"));
3875 return;
3876 }
3877
3878 /* Check for appropriate content */
3879 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3880 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3881 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003882 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003883 "Warning: received NOTIFY with non message/sipfrag "
3884 "content"));
3885 return;
3886 }
3887
3888 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003889 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003890 &status_line);
3891 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003892 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003893 "Warning: received NOTIFY with invalid "
3894 "message/sipfrag content"));
3895 return;
3896 }
3897
3898 } else {
3899 status_line.code = 500;
3900 status_line.reason = *pjsip_get_status_text(500);
3901 }
3902
3903 /* Notify application */
3904 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3905 cont = !is_last;
3906 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3907 status_line.code,
3908 &status_line.reason,
3909 is_last, &cont);
3910
3911 if (!cont) {
3912 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3913 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003914
3915 /* If the call transfer has completed but the subscription is
3916 * not terminated, terminate it now.
3917 */
3918 if (status_line.code/100 == 2 && !is_last) {
3919 pjsip_tx_data *tdata;
3920
3921 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3922 0, &tdata);
3923 if (status == PJ_SUCCESS)
3924 status = pjsip_evsub_send_request(sub, tdata);
3925 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003926 }
3927}
3928
3929
3930/*
3931 * Callback called by event framework when the xfer subscription state
3932 * has changed.
3933 */
3934static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00003935{
3936
3937 PJ_UNUSED_ARG(event);
3938
3939 /*
Benny Prijonod524e822006-09-22 12:48:18 +00003940 * When subscription is terminated, clear the xfer_sub member of
3941 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00003942 */
3943 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003944 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003945
Benny Prijonoa1e69682007-05-11 15:14:34 +00003946 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003947 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00003948 return;
3949
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003950 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003951 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00003952
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003953 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00003954 }
3955}
3956
3957
3958/*
3959 * Follow transfer (REFER) request.
3960 */
3961static void on_call_transfered( pjsip_inv_session *inv,
3962 pjsip_rx_data *rdata )
3963{
3964 pj_status_t status;
3965 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00003966 pjsua_call *existing_call;
3967 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003968 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00003969 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00003970 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00003971 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003972 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00003973 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00003974 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003975 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00003976 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003977 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00003978 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00003979 pjsip_evsub *sub;
3980
Benny Prijonoa1e69682007-05-11 15:14:34 +00003981 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00003982
Benny Prijono26ff9062006-02-21 23:47:00 +00003983 /* Find the Refer-To header */
3984 refer_to = (pjsip_generic_string_hdr*)
3985 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
3986
3987 if (refer_to == NULL) {
3988 /* Invalid Request.
3989 * No Refer-To header!
3990 */
3991 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00003992 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00003993 return;
3994 }
3995
Benny Prijonoc8141a82006-08-20 09:12:19 +00003996 /* Find optional Refer-Sub header */
3997 refer_sub = (pjsip_generic_string_hdr*)
3998 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
3999
4000 if (refer_sub) {
4001 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
4002 no_refer_sub = PJ_TRUE;
4003 }
4004
Benny Prijono053f5222006-11-11 16:16:04 +00004005 /* Find optional Referred-By header (to be copied onto outgoing INVITE
4006 * request.
4007 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00004008 ref_by_hdr = (pjsip_hdr*)
4009 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00004010 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00004011
Benny Prijono9fc735d2006-05-28 14:58:12 +00004012 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004013 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004014 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
4015 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
4016 &refer_to->hvalue,
4017 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00004018
4019 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004020 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00004021 if (code >= 300) {
4022 /* Application rejects call transfer request */
4023 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
4024 return;
4025 }
4026
Benny Prijono26ff9062006-02-21 23:47:00 +00004027 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
4028 (int)inv->dlg->remote.info_str.slen,
4029 inv->dlg->remote.info_str.ptr,
4030 (int)refer_to->hvalue.slen,
4031 refer_to->hvalue.ptr));
4032
Benny Prijonoc8141a82006-08-20 09:12:19 +00004033 if (no_refer_sub) {
4034 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004035 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00004036 */
4037 pjsip_tx_data *tdata;
4038 const pj_str_t str_false = { "false", 5};
4039 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00004040
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004041 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
4042 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00004043 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004044 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00004045 status);
4046 return;
4047 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004048
Benny Prijonoc8141a82006-08-20 09:12:19 +00004049 /* Add Refer-Sub header */
4050 hdr = (pjsip_hdr*)
4051 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
4052 &str_false);
4053 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00004054
Benny Prijono26ff9062006-02-21 23:47:00 +00004055
Benny Prijonoc8141a82006-08-20 09:12:19 +00004056 /* Send answer */
4057 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
4058 tdata);
4059 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004060 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00004061 status);
4062 return;
4063 }
4064
4065 /* Don't have subscription */
4066 sub = NULL;
4067
4068 } else {
4069 struct pjsip_evsub_user xfer_cb;
4070 pjsip_hdr hdr_list;
4071
4072 /* Init callback */
4073 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004074 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004075
4076 /* Init additional header list to be sent with REFER response */
4077 pj_list_init(&hdr_list);
4078
4079 /* Create transferee event subscription */
4080 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
4081 if (status != PJ_SUCCESS) {
4082 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
4083 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
4084 return;
4085 }
4086
4087 /* If there's Refer-Sub header and the value is "true", send back
4088 * Refer-Sub in the response with value "true" too.
4089 */
4090 if (refer_sub) {
4091 const pj_str_t str_true = { "true", 4 };
4092 pjsip_hdr *hdr;
4093
4094 hdr = (pjsip_hdr*)
4095 pjsip_generic_string_hdr_create(inv->dlg->pool,
4096 &str_refer_sub,
4097 &str_true);
4098 pj_list_push_back(&hdr_list, hdr);
4099
4100 }
4101
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004102 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00004103 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
4104
4105 /* Create initial NOTIFY request */
4106 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
4107 100, NULL, &tdata);
4108 if (status != PJ_SUCCESS) {
4109 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4110 status);
4111 return;
4112 }
4113
4114 /* Send initial NOTIFY request */
4115 status = pjsip_xfer_send_request( sub, tdata);
4116 if (status != PJ_SUCCESS) {
4117 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
4118 return;
4119 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004120 }
4121
4122 /* We're cheating here.
4123 * We need to get a null terminated string from a pj_str_t.
4124 * So grab the pointer from the hvalue and NULL terminate it, knowing
4125 * that the NULL position will be occupied by a newline.
4126 */
4127 uri = refer_to->hvalue.ptr;
4128 uri[refer_to->hvalue.slen] = '\0';
4129
Benny Prijono053f5222006-11-11 16:16:04 +00004130 /* Init msg_data */
4131 pjsua_msg_data_init(&msg_data);
4132
4133 /* If Referred-By header is present in the REFER request, copy this
4134 * to the outgoing INVITE request.
4135 */
4136 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00004137 pjsip_hdr *dup = (pjsip_hdr*)
4138 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00004139 pj_list_push_back(&msg_data.hdr_list, dup);
4140 }
4141
Benny Prijono26ff9062006-02-21 23:47:00 +00004142 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00004143 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004144 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00004145 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004146 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00004147 if (status != PJ_SUCCESS) {
4148
Benny Prijonoc8141a82006-08-20 09:12:19 +00004149 /* Notify xferer about the error (if we have subscription) */
4150 if (sub) {
4151 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
4152 500, NULL, &tdata);
4153 if (status != PJ_SUCCESS) {
4154 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4155 status);
4156 return;
4157 }
4158 status = pjsip_xfer_send_request(sub, tdata);
4159 if (status != PJ_SUCCESS) {
4160 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
4161 status);
4162 return;
4163 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004164 }
4165 return;
4166 }
4167
Benny Prijonoc8141a82006-08-20 09:12:19 +00004168 if (sub) {
4169 /* Put the server subscription in inv_data.
4170 * Subsequent state changed in pjsua_inv_on_state_changed() will be
4171 * reported back to the server subscription.
4172 */
4173 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00004174
Benny Prijonoc8141a82006-08-20 09:12:19 +00004175 /* Put the invite_data in the subscription. */
4176 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
4177 &pjsua_var.calls[new_call]);
4178 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004179}
4180
4181
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004182
Benny Prijono26ff9062006-02-21 23:47:00 +00004183/*
4184 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00004185 * session. We use this to trap:
4186 * - incoming REFER request.
4187 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00004188 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004189static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
4190 pjsip_transaction *tsx,
4191 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00004192{
Benny Prijono2285e7e2008-12-17 14:28:18 +00004193 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004194
4195 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00004196
Benny Prijono2285e7e2008-12-17 14:28:18 +00004197 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
4198
4199 if (call == NULL) {
4200 PJSUA_UNLOCK();
4201 return;
4202 }
4203
Benny Prijono19eeb6e2009-06-04 22:16:47 +00004204 if (call->inv == NULL) {
4205 /* Shouldn't happen. It happens only when we don't terminate the
4206 * server subscription caused by REFER after the call has been
4207 * transfered (and this call has been disconnected), and we
4208 * receive another REFER for this call.
4209 */
4210 PJSUA_UNLOCK();
4211 return;
4212 }
4213
Benny Prijonofeb69f42007-10-05 09:12:26 +00004214 /* Notify application callback first */
4215 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
4216 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
4217 }
4218
Benny Prijono26ff9062006-02-21 23:47:00 +00004219 if (tsx->role==PJSIP_ROLE_UAS &&
4220 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00004221 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004222 {
4223 /*
4224 * Incoming REFER request.
4225 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004226 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00004227
Benny Prijono26ff9062006-02-21 23:47:00 +00004228 }
Benny Prijonob0808372006-03-02 21:18:58 +00004229 else if (tsx->role==PJSIP_ROLE_UAS &&
4230 tsx->state==PJSIP_TSX_STATE_TRYING &&
4231 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
4232 {
4233 /*
4234 * Incoming MESSAGE request!
4235 */
4236 pjsip_rx_data *rdata;
4237 pjsip_msg *msg;
4238 pjsip_accept_hdr *accept_hdr;
4239 pj_status_t status;
4240
4241 rdata = e->body.tsx_state.src.rdata;
4242 msg = rdata->msg_info.msg;
4243
4244 /* Request MUST have message body, with Content-Type equal to
4245 * "text/plain".
4246 */
4247 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
4248
4249 pjsip_hdr hdr_list;
4250
4251 pj_list_init(&hdr_list);
4252 pj_list_push_back(&hdr_list, accept_hdr);
4253
4254 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
4255 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004256 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00004257 return;
4258 }
4259
4260 /* Respond with 200 first, so that remote doesn't retransmit in case
4261 * the UI takes too long to process the message.
4262 */
4263 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
4264
4265 /* Process MESSAGE request */
4266 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
4267 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004268
Benny Prijonob0808372006-03-02 21:18:58 +00004269 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004270 else if (tsx->role == PJSIP_ROLE_UAC &&
4271 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004272 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004273 /* Handle outgoing pager status */
4274 if (tsx->status_code >= 200) {
4275 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00004276
Benny Prijonoa1e69682007-05-11 15:14:34 +00004277 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004278 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00004279
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004280 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
4281 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
4282 &im_data->to,
4283 &im_data->body,
4284 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00004285 (pjsip_status_code)
4286 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004287 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00004288 }
Benny Prijonofccab712006-02-22 22:23:22 +00004289 }
Benny Prijono834aee32006-02-19 01:38:06 +00004290 }
Benny Prijono834aee32006-02-19 01:38:06 +00004291
Benny Prijono26ff9062006-02-21 23:47:00 +00004292
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004293 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00004294}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004295
4296
4297/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00004298static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
4299 const pjsip_uri *target,
4300 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004301{
4302 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00004303 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004304
4305 PJSUA_LOCK();
4306
4307 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00004308 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
4309 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004310 } else {
4311 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
4312 "(callback not implemented by application). Disconnecting "
4313 "call.",
4314 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00004315 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004316 }
4317
4318 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00004319
4320 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004321}
4322