blob: 62c31c50f232e825fc78d9013372a5cb197898e3 [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
Benny Prijono1e601552010-10-20 05:31:08 +000032/*
33 * Max UPDATE/re-INVITE retry to lock codec
34 */
35#define LOCK_CODEC_MAX_RETRY 5
Nanang Izzuddin93bacd02010-06-15 09:56:39 +000036
Benny Prijonoeebe9af2006-06-13 22:57:13 +000037/* This callback receives notification from invite session when the
38 * session state has changed.
39 */
40static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
41 pjsip_event *e);
42
43/* This callback is called by invite session framework when UAC session
44 * has forked.
45 */
46static void pjsua_call_on_forked( pjsip_inv_session *inv,
47 pjsip_event *e);
Benny Prijono84126ab2006-02-09 09:30:09 +000048
49/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000050 * Callback to be called when SDP offer/answer negotiation has just completed
51 * in the session. This function will start/update media if negotiation
52 * has succeeded.
Benny Prijono84126ab2006-02-09 09:30:09 +000053 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000054static void pjsua_call_on_media_update(pjsip_inv_session *inv,
55 pj_status_t status);
Benny Prijono105217f2006-03-06 16:25:59 +000056
57/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000058 * Called when session received new offer.
Benny Prijono105217f2006-03-06 16:25:59 +000059 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +000060static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
61 const pjmedia_sdp_session *offer);
62
63/*
Benny Prijono77998ce2007-06-20 10:03:46 +000064 * Called to generate new offer.
65 */
66static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
67 pjmedia_sdp_session **offer);
68
69/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000070 * This callback is called when transaction state has changed in INVITE
71 * session. We use this to trap:
72 * - incoming REFER request.
73 * - incoming MESSAGE request.
74 */
75static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
76 pjsip_transaction *tsx,
77 pjsip_event *e);
78
Benny Prijono5e51a4e2008-11-27 00:06:46 +000079/*
80 * Redirection handler.
81 */
Benny Prijono08a48b82008-11-27 12:42:07 +000082static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
83 const pjsip_uri *target,
84 const pjsip_event *e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +000085
Benny Prijonoeebe9af2006-06-13 22:57:13 +000086
Nanang Izzuddin99d69522008-08-04 15:01:38 +000087/* Create SDP for call hold. */
88static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
Benny Prijonodd63b992010-10-01 02:03:42 +000089 pjmedia_sdp_session **p_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +000090
Benny Prijonod524e822006-09-22 12:48:18 +000091/*
92 * Callback called by event framework when the xfer subscription state
93 * has changed.
94 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +000095static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
96static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
97
98/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +000099 * Reset call descriptor.
100 */
101static void reset_call(pjsua_call_id id)
Benny Prijono105217f2006-03-06 16:25:59 +0000102{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000103 pjsua_call *call = &pjsua_var.calls[id];
Benny Prijono0bc99a92011-03-17 04:34:43 +0000104 unsigned i;
Benny Prijono105217f2006-03-06 16:25:59 +0000105
Benny Prijono0bc99a92011-03-17 04:34:43 +0000106 pj_bzero(call, sizeof(*call));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000107 call->index = id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000108 call->last_text.ptr = call->last_text_buf_;
Benny Prijono0bc99a92011-03-17 04:34:43 +0000109 for (i=0; i<PJ_ARRAY_SIZE(call->media); ++i) {
110 pjsua_call_media *call_med = &call->media[i];
111 call_med->ssrc = pj_rand();
112 call_med->strm.a.conf_slot = PJSUA_INVALID_ID;
113 call_med->call = call;
114 call_med->idx = i;
115 call_med->tp_auto_del = PJ_TRUE;
116 }
Benny Prijono105217f2006-03-06 16:25:59 +0000117}
118
119
Benny Prijono275fd682006-03-22 11:59:11 +0000120/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000121 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000122 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000123pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000124{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000125 pjsip_inv_callback inv_cb;
126 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000127 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000128 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000129
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000130 /* Init calls array. */
131 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
132 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000133
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000134 /* Copy config */
135 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000136
Benny Prijono1cd713b2009-11-11 00:33:00 +0000137 /* Verify settings */
138 if (pjsua_var.ua_cfg.max_calls >= PJSUA_MAX_CALLS) {
139 pjsua_var.ua_cfg.max_calls = PJSUA_MAX_CALLS;
140 }
141
Benny Prijono91d06b62008-09-20 12:16:56 +0000142 /* Check the route URI's and force loose route if required */
143 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
144 status = normalize_route_uri(pjsua_var.pool,
145 &pjsua_var.ua_cfg.outbound_proxy[i]);
146 if (status != PJ_SUCCESS)
147 return status;
148 }
149
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000150 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000151 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000152 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
153 inv_cb.on_new_session = &pjsua_call_on_forked;
154 inv_cb.on_media_update = &pjsua_call_on_media_update;
155 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000156 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000157 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000158 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000159
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000160 /* Initialize invite session module: */
161 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
162 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
163
Benny Prijonoc8141a82006-08-20 09:12:19 +0000164 /* Add "norefersub" in Supported header */
165 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
166 NULL, 1, &str_norefersub);
167
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000168 return status;
169}
170
171
172/*
173 * Start call subsystem.
174 */
175pj_status_t pjsua_call_subsys_start(void)
176{
177 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000178 return PJ_SUCCESS;
179}
180
181
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000182/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000183 * Get maximum number of calls configured in pjsua.
184 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000185PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000186{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000187 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000188}
189
190
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000191/*
192 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000193 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000194PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000195{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000196 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000197}
198
199
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000200/*
201 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000202 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000203PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
204 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000205{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000207
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000208 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000209
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000210 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000211
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000212 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
213 if (!pjsua_var.calls[i].inv)
214 continue;
215 ids[c] = i;
216 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000217 }
218
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000219 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000220
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000221 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000222
223 return PJ_SUCCESS;
224}
225
226
Benny Prijono5773cd62008-01-19 13:01:42 +0000227/* Allocate one call id */
228static pjsua_call_id alloc_call_id(void)
229{
230 pjsua_call_id cid;
231
232#if 1
233 /* New algorithm: round-robin */
234 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
235 pjsua_var.next_call_id < 0)
236 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000237 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000238 }
239
240 for (cid=pjsua_var.next_call_id;
241 cid<(int)pjsua_var.ua_cfg.max_calls;
242 ++cid)
243 {
244 if (pjsua_var.calls[cid].inv == NULL) {
245 ++pjsua_var.next_call_id;
246 return cid;
247 }
248 }
249
250 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
251 if (pjsua_var.calls[cid].inv == NULL) {
252 ++pjsua_var.next_call_id;
253 return cid;
254 }
255 }
256
257#else
258 /* Old algorithm */
259 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
260 if (pjsua_var.calls[cid].inv == NULL)
261 return cid;
262 }
263#endif
264
265 return PJSUA_INVALID_ID;
266}
267
Benny Prijonod8179652008-01-23 20:39:07 +0000268/* Get signaling secure level.
269 * Return:
270 * 0: if signaling is not secure
271 * 1: if TLS transport is used for immediate hop
272 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000273 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000274static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000275{
276 const pj_str_t tls = pj_str(";transport=tls");
277 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000278 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000279
280 if (pj_stristr(dst_uri, &sips))
281 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000282
283 if (!pj_list_empty(&acc->route_set)) {
284 pjsip_route_hdr *r = acc->route_set.next;
285 pjsip_uri *uri = r->name_addr.uri;
286 pjsip_sip_uri *sip_uri;
287
288 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
289 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
290 return 1;
291
292 } else {
293 if (pj_stristr(dst_uri, &tls))
294 return 1;
295 }
296
Benny Prijonod8179652008-01-23 20:39:07 +0000297 return 0;
298}
299
Benny Prijono224b4e22008-06-19 14:10:28 +0000300/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000301static int call_get_secure_level(pjsua_call *call)
302{
303 if (call->inv->dlg->secure)
304 return 2;
305
306 if (!pj_list_empty(&call->inv->dlg->route_set)) {
307 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
308 pjsip_uri *uri = r->name_addr.uri;
309 pjsip_sip_uri *sip_uri;
310
311 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
312 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
313 return 1;
314
315 } else {
316 pjsip_sip_uri *sip_uri;
317
318 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
319 return 2;
320 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
321 return 0;
322
323 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
324 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
325 return 1;
326 }
327
328 return 0;
329}
Benny Prijono224b4e22008-06-19 14:10:28 +0000330*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000331
332
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000333/*
334 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000335 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000336PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
337 const pj_str_t *dest_uri,
338 unsigned options,
339 void *user_data,
340 const pjsua_msg_data *msg_data,
341 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000342{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000343 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000344 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000345 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000346 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000347 pjsua_acc *acc;
348 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000349 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000350 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000351 pjsip_tx_data *tdata;
352 pj_status_t status;
353
Benny Prijono9fc735d2006-05-28 14:58:12 +0000354
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000355 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000356 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000357 PJ_EINVAL);
358
Benny Prijono320fa4d2006-12-07 10:09:16 +0000359 /* Check arguments */
360 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
361
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000362 PJSUA_LOCK();
363
Benny Prijonof798e502009-03-09 13:08:16 +0000364 /* Create sound port if none is instantiated, to check if sound device
365 * can be used. But only do this with the conference bridge, as with
366 * audio switchboard (i.e. APS-Direct), we can only open the sound
367 * device once the correct format has been known
368 */
369 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
370 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000371 {
372 pj_status_t status;
373
374 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
375 if (status != PJ_SUCCESS) {
376 PJSUA_UNLOCK();
377 return status;
378 }
379 }
380
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000381 acc = &pjsua_var.acc[acc_id];
382 if (!acc->valid) {
383 pjsua_perror(THIS_FILE, "Unable to make call because account "
384 "is not valid", PJ_EINVALIDOP);
385 PJSUA_UNLOCK();
386 return PJ_EINVALIDOP;
387 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000388
Benny Prijonoa91a0032006-02-26 21:23:45 +0000389 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000390 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000391
Benny Prijono5773cd62008-01-19 13:01:42 +0000392 if (call_id == PJSUA_INVALID_ID) {
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000393 pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000394 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000395 return PJ_ETOOMANY;
396 }
397
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000398 call = &pjsua_var.calls[call_id];
399
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000400 /* Associate session with account */
401 call->acc_id = acc_id;
Benny Prijonodd63b992010-10-01 02:03:42 +0000402 call->call_hold_type = acc->cfg.call_hold_type;
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000403
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000404 /* Create temporary pool */
405 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
406
Benny Prijono320fa4d2006-12-07 10:09:16 +0000407 /* Verify that destination URI is valid before calling
408 * pjsua_acc_create_uac_contact, or otherwise there
409 * a misleading "Invalid Contact URI" error will be printed
410 * when pjsua_acc_create_uac_contact() fails.
411 */
412 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000413 pjsip_uri *uri;
414 pj_str_t dup;
415
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000416 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
417 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000418
419 if (uri == NULL) {
420 pjsua_perror(THIS_FILE, "Unable to make call",
421 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000422 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000423 PJSUA_UNLOCK();
424 return PJSIP_EINVALIDREQURI;
425 }
426 }
427
Benny Prijono093d3022006-09-24 00:07:11 +0000428 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
429 (int)dest_uri->slen, dest_uri->ptr));
430
Benny Prijonoe21e7842006-04-09 16:46:05 +0000431 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000432 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000433
Benny Prijonoe21e7842006-04-09 16:46:05 +0000434 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000435 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000436
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000437 /* Create suitable Contact header unless a Contact header has been
438 * set in the account.
439 */
440 if (acc->contact.slen) {
441 contact = acc->contact;
442 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000443 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000444 acc_id, dest_uri);
445 if (status != PJ_SUCCESS) {
446 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
447 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000448 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000449 PJSUA_UNLOCK();
450 return status;
451 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000452 }
453
Benny Prijonoe21e7842006-04-09 16:46:05 +0000454 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000455 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000456 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000457 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000458 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000459 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000460 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000461 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000462 return status;
463 }
464
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000465 /* Increment the dialog's lock otherwise when invite session creation
466 * fails the dialog will be destroyed prematurely.
467 */
468 pjsip_dlg_inc_lock(dlg);
469
Benny Prijonodb844a42008-02-02 17:07:18 +0000470 /* Calculate call's secure level */
471 call->secure_level = get_secure_level(acc_id, dest_uri);
472
Benny Prijonoc97608e2007-03-23 16:34:20 +0000473 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000474 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000475 call->secure_level, dlg->pool,
476 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000477 if (status != PJ_SUCCESS) {
478 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
479 goto on_error;
480 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000481
Benny Prijono224b4e22008-06-19 14:10:28 +0000482 /* Create offer */
483 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000484 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000485 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000486 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000487 goto on_error;
488 }
489
490 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000491 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000492 if (acc->cfg.require_100rel)
493 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000494 if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) {
495 options |= PJSIP_INV_SUPPORT_TIMER;
496 if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
497 options |= PJSIP_INV_REQUIRE_TIMER;
498 else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
499 options |= PJSIP_INV_ALWAYS_USE_TIMER;
500 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000501
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000502 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000503 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000504 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000505 goto on_error;
506 }
507
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000508 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000509 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
510 if (status != PJ_SUCCESS) {
511 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
512 goto on_error;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000513 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000514
515 /* Create and associate our data in the session. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000516 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000517
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000518 dlg->mod_data[pjsua_var.mod.id] = call;
519 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000520
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000521 /* Attach user data */
522 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000523
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000524 /* If account is locked to specific transport, then lock dialog
525 * to this transport too.
526 */
527 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
528 pjsip_tpselector tp_sel;
529
530 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
531 pjsip_dlg_set_transport(dlg, &tp_sel);
532 }
533
Benny Prijono84126ab2006-02-09 09:30:09 +0000534 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000535 if (!pj_list_empty(&acc->route_set))
536 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000537
538
539 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000540 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000541 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000542 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000543 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000544
Benny Prijono48ab2b72007-11-08 09:24:30 +0000545 /* Set authentication preference */
546 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000547
548 /* Create initial INVITE: */
549
550 status = pjsip_inv_invite(inv, &tdata);
551 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000552 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
553 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000554 goto on_error;
555 }
556
557
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000558 /* Add additional headers etc */
559
560 pjsua_process_msg_data( tdata, msg_data);
561
Benny Prijono093d3022006-09-24 00:07:11 +0000562 /* Must increment call counter now */
563 ++pjsua_var.call_cnt;
564
Benny Prijono84126ab2006-02-09 09:30:09 +0000565 /* Send initial INVITE: */
566
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000567 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000568 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000569 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
570 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000571
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000572 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000573 * session would have been cleared.
574 */
575 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000576 goto on_error;
577 }
578
Benny Prijono84126ab2006-02-09 09:30:09 +0000579 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000580
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000581 if (p_call_id)
582 *p_call_id = call_id;
583
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000584 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000585 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000586 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000587
588 return PJ_SUCCESS;
589
590
591on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000592 if (dlg) {
593 /* This may destroy the dialog */
594 pjsip_dlg_dec_lock(dlg);
595 }
596
Benny Prijono1c2bf462006-03-05 11:54:02 +0000597 if (inv != NULL) {
598 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000599 }
600
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000601 if (call_id != -1) {
602 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000603 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000604 }
605
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000606 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000607 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000608 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000609}
610
611
Benny Prijono91a6a172007-10-31 08:59:29 +0000612/* Get the NAT type information in remote's SDP */
613static void update_remote_nat_type(pjsua_call *call,
614 const pjmedia_sdp_session *sdp)
615{
616 const pjmedia_sdp_attr *xnat;
617
618 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
619 if (xnat) {
620 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
621 } else {
622 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
623 }
624
625 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
626 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
627}
628
629
Benny Prijonodc39fe82006-05-26 12:17:46 +0000630/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000631 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000632 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000633 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000634pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000635{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000636 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000637 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000638 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000639 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
640 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000641 pjsip_tx_data *response = NULL;
642 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000643 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000644 int acc_id;
645 pjsua_call *call;
646 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000647 int sip_err_code;
Benny Prijono1c1d7342010-08-01 09:48:51 +0000648 pjmedia_sdp_session *offer=NULL, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000649 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000650
Benny Prijono26ff9062006-02-21 23:47:00 +0000651 /* Don't want to handle anything but INVITE */
652 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
653 return PJ_FALSE;
654
655 /* Don't want to handle anything that's already associated with
656 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000657 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000658 if (dlg || tsx)
659 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000660
Benny Prijono384dab42009-10-14 01:58:04 +0000661 /* Don't want to accept the call if shutdown is in progress */
662 if (pjsua_var.thread_quit_flag) {
663 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
664 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
665 NULL, NULL);
666 return PJ_TRUE;
667 }
668
Benny Prijono148c9dd2006-09-19 13:37:53 +0000669 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000670
Benny Prijono26ff9062006-02-21 23:47:00 +0000671 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000672 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000673
Benny Prijono5773cd62008-01-19 13:01:42 +0000674 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000675 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000676 PJSIP_SC_BUSY_HERE, NULL,
677 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000678 PJ_LOG(2,(THIS_FILE,
679 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000680 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000681 return PJ_TRUE;
682 }
683
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000684 /* Clear call descriptor */
685 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000686
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000687 call = &pjsua_var.calls[call_id];
688
689 /* Mark call start time. */
690 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000691
Benny Prijono053f5222006-11-11 16:16:04 +0000692 /* Check INVITE request for Replaces header. If Replaces header is
693 * present, the function will make sure that we can handle the request.
694 */
695 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
696 &response);
697 if (status != PJ_SUCCESS) {
698 /*
699 * Something wrong with the Replaces header.
700 */
701 if (response) {
702 pjsip_response_addr res_addr;
703
704 pjsip_get_response_addr(response->pool, rdata, &res_addr);
705 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
706 NULL, NULL);
707
708 } else {
709
710 /* Respond with 500 (Internal Server Error) */
711 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
712 NULL, NULL);
713 }
714
715 PJSUA_UNLOCK();
716 return PJ_TRUE;
717 }
718
719 /* If this INVITE request contains Replaces header, notify application
720 * about the request so that application can do subsequent checking
721 * if it wants to.
722 */
723 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
724 pjsua_call *replaced_call;
725 int st_code = 200;
726 pj_str_t st_text = { "OK", 2 };
727
728 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000729 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000730
731 /* Notify application */
732 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
733 rdata, &st_code, &st_text);
734
735 /* Must specify final response */
736 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
737
738 /* Check if application rejects this request. */
739 if (st_code >= 300) {
740
741 if (st_text.slen == 2)
742 st_text = *pjsip_get_status_text(st_code);
743
744 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
745 st_code, &st_text, NULL, NULL, NULL);
746 PJSUA_UNLOCK();
747 return PJ_TRUE;
748 }
749 }
750
Benny Prijonod8179652008-01-23 20:39:07 +0000751 /*
752 * Get which account is most likely to be associated with this incoming
753 * call. We need the account to find which contact URI to put for
754 * the call.
755 */
756 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonodd63b992010-10-01 02:03:42 +0000757 call->call_hold_type = pjsua_var.acc[acc_id].cfg.call_hold_type;
Benny Prijonod8179652008-01-23 20:39:07 +0000758
Benny Prijonodb844a42008-02-02 17:07:18 +0000759 /* Get call's secure level */
760 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
761 call->secure_level = 2;
762 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
763 call->secure_level = 1;
764 else
765 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000766
Benny Prijonod8179652008-01-23 20:39:07 +0000767 /* Parse SDP from incoming request */
768 if (rdata->msg_info.msg->body) {
Benny Prijono1c1d7342010-08-01 09:48:51 +0000769 pjsip_rdata_sdp_info *sdp_info;
770
771 sdp_info = pjsip_rdata_get_sdp_info(rdata);
772 offer = sdp_info->sdp;
773
774 status = sdp_info->sdp_err;
775 if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
776 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono2c484e42008-06-26 19:47:23 +0000777
Benny Prijonod8179652008-01-23 20:39:07 +0000778 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000779 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000780 pjsip_hdr hdr_list;
781 pjsip_warning_hdr *w;
782
783 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000784 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000785
786 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
787 pjsip_endpt_name(pjsua_var.endpt),
788 status);
789 pj_list_init(&hdr_list);
790 pj_list_push_back(&hdr_list, w);
791
Benny Prijono224b4e22008-06-19 14:10:28 +0000792 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000793 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000794 PJSUA_UNLOCK();
795 return PJ_TRUE;
796 }
Benny Prijono617b8602008-04-07 10:10:31 +0000797
798 /* Do quick checks on SDP before passing it to transports. More elabore
799 * checks will be done in pjsip_inv_verify_request2() below.
800 */
801 if (offer->media_count==0) {
802 const pj_str_t reason = pj_str("Missing media in SDP");
803 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
804 NULL, NULL, NULL);
805 PJSUA_UNLOCK();
806 return PJ_TRUE;
807 }
808
Benny Prijonod8179652008-01-23 20:39:07 +0000809 } else {
810 offer = NULL;
811 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000812
Benny Prijono224b4e22008-06-19 14:10:28 +0000813 /* Init media channel */
814 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
815 call->secure_level,
Benny Prijono0bc99a92011-03-17 04:34:43 +0000816 rdata->tp_info.pool,
817 offer,
Benny Prijono224b4e22008-06-19 14:10:28 +0000818 &sip_err_code);
819 if (status != PJ_SUCCESS) {
820 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
821 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
822 sip_err_code, NULL, NULL, NULL, NULL);
823 PJSUA_UNLOCK();
824 return PJ_TRUE;
825 }
826
827 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000828 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000829 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000830 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000831 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000832 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
833 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000834 PJSUA_UNLOCK();
835 return PJ_TRUE;
836 }
837
Benny Prijono224b4e22008-06-19 14:10:28 +0000838
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000839 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000840 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000841 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000842 if (pjsua_var.acc[acc_id].cfg.require_100rel)
843 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono07fe2302010-06-24 12:33:18 +0000844 if (pjsua_var.media_cfg.enable_ice)
845 options |= PJSIP_INV_SUPPORT_ICE;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000846 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
847 options |= PJSIP_INV_REQUIRE_TIMER;
848 else if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
849 options |= PJSIP_INV_ALWAYS_USE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000850
Benny Prijonod8179652008-01-23 20:39:07 +0000851 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
852 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000853 if (status != PJ_SUCCESS) {
854
855 /*
856 * No we can't handle the incoming INVITE request.
857 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000858 if (response) {
859 pjsip_response_addr res_addr;
860
861 pjsip_get_response_addr(response->pool, rdata, &res_addr);
862 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
863 NULL, NULL);
864
865 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000866 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000867 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
868 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000869 }
870
Benny Prijonoc97608e2007-03-23 16:34:20 +0000871 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000872 PJSUA_UNLOCK();
873 return PJ_TRUE;
874 }
875
876
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000877 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000878 if (pjsua_var.acc[acc_id].contact.slen) {
879 contact = pjsua_var.acc[acc_id].contact;
880 } else {
881 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
882 acc_id, rdata);
883 if (status != PJ_SUCCESS) {
884 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
885 status);
886 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
887 NULL, NULL);
888 pjsua_media_channel_deinit(call->index);
889 PJSUA_UNLOCK();
890 return PJ_TRUE;
891 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000892 }
893
Benny Prijono26ff9062006-02-21 23:47:00 +0000894 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000895 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000896 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000897 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000898 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000899 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000900 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000901 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000902 return PJ_TRUE;
903 }
904
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000905 /* Set credentials */
906 if (pjsua_var.acc[acc_id].cred_cnt) {
907 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
908 pjsua_var.acc[acc_id].cred_cnt,
909 pjsua_var.acc[acc_id].cred);
910 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000911
Benny Prijono48ab2b72007-11-08 09:24:30 +0000912 /* Set preference */
913 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
914 &pjsua_var.acc[acc_id].cfg.auth_pref);
915
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000916 /* Disable Session Timers if not prefered and the incoming INVITE request
917 * did not require it.
918 */
919 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_INACTIVE &&
920 (options & PJSIP_INV_REQUIRE_TIMER) == 0)
921 {
922 options &= ~(PJSIP_INV_SUPPORT_TIMER);
923 }
924
Benny Prijono26ff9062006-02-21 23:47:00 +0000925 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000926 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000927 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000928 pjsip_hdr hdr_list;
929 pjsip_warning_hdr *w;
930
931 w = pjsip_warning_hdr_create_from_status(dlg->pool,
932 pjsip_endpt_name(pjsua_var.endpt),
933 status);
934 pj_list_init(&hdr_list);
935 pj_list_push_back(&hdr_list, w);
936
937 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
938
939 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000940 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000941 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000942 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000943 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000944 return PJ_TRUE;
945 }
946
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000947 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000948 status = pjsip_timer_init_session(inv,
949 &pjsua_var.acc[acc_id].cfg.timer_setting);
950 if (status != PJ_SUCCESS) {
951 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
952 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
953 NULL, &response);
954 if (status == PJ_SUCCESS && response)
955 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000956
Nanang Izzuddin65add622009-08-11 16:26:20 +0000957 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000958
Nanang Izzuddin65add622009-08-11 16:26:20 +0000959 PJSUA_UNLOCK();
960 return PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000961 }
962
Benny Prijonoea9fd392007-11-06 03:41:40 +0000963 /* Update NAT type of remote endpoint, only when there is SDP in
964 * incoming INVITE!
965 */
966 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
967 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
968 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000969 const pjmedia_sdp_session *remote_sdp;
970
971 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
972 update_remote_nat_type(call, remote_sdp);
973 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000974
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000975 /* If account is locked to specific transport, then lock dialog
976 * to this transport too.
977 */
978 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
979 pjsip_tpselector tp_sel;
980
981 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
982 pjsip_dlg_set_transport(dlg, &tp_sel);
983 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000984
Benny Prijono2285e7e2008-12-17 14:28:18 +0000985 /* Must answer with some response to initial INVITE. We'll do this before
986 * attaching the call to the invite session/dialog, so that the application
987 * will not get notification about this event (on another scenario, it is
988 * also possible that inv_send_msg() fails and causes the invite session to
989 * be disconnected. If we have the call attached at this time, this will
990 * cause the disconnection callback to be called before on_incoming_call()
991 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +0000992 */
Benny Prijono64f851e2006-02-23 13:49:28 +0000993 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000994 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +0000995 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000996 if (response == NULL) {
997 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
998 status);
999 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
1000 pjsip_inv_terminate(inv, 500, PJ_FALSE);
1001 } else {
1002 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +00001003 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001004 PJ_FALSE);
1005 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001006 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001007 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +00001008 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +00001009
1010 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001011 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001012 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +00001013 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +00001014 PJSUA_UNLOCK();
1015 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001016 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001017 }
1018
Benny Prijono2285e7e2008-12-17 14:28:18 +00001019 /* Create and attach pjsua_var data to the dialog: */
1020 call->inv = inv;
1021
1022 dlg->mod_data[pjsua_var.mod.id] = call;
1023 inv->mod_data[pjsua_var.mod.id] = call;
1024
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001025 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001026
Benny Prijono105217f2006-03-06 16:25:59 +00001027
Benny Prijono053f5222006-11-11 16:16:04 +00001028 /* Check if this request should replace existing call */
1029 if (replaced_dlg) {
1030 pjsip_inv_session *replaced_inv;
1031 struct pjsua_call *replaced_call;
1032 pjsip_tx_data *tdata;
1033
1034 /* Get the invite session in the dialog */
1035 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1036
1037 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001038 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001039
1040 /* Notify application */
1041 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1042 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1043 call_id);
1044
1045 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1046 call_id));
1047
1048 /* Answer the new call with 200 response */
1049 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1050 if (status == PJ_SUCCESS)
1051 status = pjsip_inv_send_msg(inv, tdata);
1052
1053 if (status != PJ_SUCCESS)
1054 pjsua_perror(THIS_FILE, "Error answering session", status);
1055
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001056 /* Note that inv may be invalid if 200/OK has caused error in
1057 * starting the media.
1058 */
Benny Prijono053f5222006-11-11 16:16:04 +00001059
1060 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1061 replaced_call->index));
1062
1063 /* Disconnect replaced invite session */
1064 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1065 &tdata);
1066 if (status == PJ_SUCCESS && tdata)
1067 status = pjsip_inv_send_msg(replaced_inv, tdata);
1068
1069 if (status != PJ_SUCCESS)
1070 pjsua_perror(THIS_FILE, "Error terminating session", status);
1071
1072
1073 } else {
1074
Benny Prijonob5388cf2007-01-04 22:45:08 +00001075 /* Notify application if on_incoming_call() is overriden,
1076 * otherwise hangup the call with 480
1077 */
1078 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001079 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001080 } else {
1081 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1082 NULL, NULL);
1083 }
Benny Prijono053f5222006-11-11 16:16:04 +00001084 }
1085
Benny Prijono8b1889b2006-06-06 18:40:40 +00001086
Benny Prijono26ff9062006-02-21 23:47:00 +00001087 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001088 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001089 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001090}
1091
1092
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001093
1094/*
1095 * Check if the specified call has active INVITE session and the INVITE
1096 * session has not been disconnected.
1097 */
1098PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1099{
1100 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1101 PJ_EINVAL);
1102 return pjsua_var.calls[call_id].inv != NULL &&
1103 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1104}
1105
1106
1107/*
1108 * Check if call has an active media session.
1109 */
1110PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1111{
Benny Prijono0bc99a92011-03-17 04:34:43 +00001112 pjsua_call *call = &pjsua_var.calls[call_id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001113 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1114 PJ_EINVAL);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001115 return call->audio_idx >= 0 && call->media[call->audio_idx].strm.a.stream;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001116}
1117
1118
Benny Prijono0bc99a92011-03-17 04:34:43 +00001119#if DISABLED_FOR_TICKET_1185
Benny Prijonocf986c42008-09-02 11:25:07 +00001120/*
1121 * Retrieve the media session associated with this call.
1122 */
1123PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1124{
1125 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1126 NULL);
1127 return pjsua_var.calls[call_id].session;
1128}
1129
1130
1131/*
1132 * Retrieve the media transport instance that is used for this call.
1133 */
1134PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1135{
1136 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1137 NULL);
Benny Prijono0bc99a92011-03-17 04:34:43 +00001138 return pjsua_var.calls[cid].tp;
Benny Prijonocf986c42008-09-02 11:25:07 +00001139}
Benny Prijono0bc99a92011-03-17 04:34:43 +00001140#endif /* Removed in 2.0 */
Benny Prijonocf986c42008-09-02 11:25:07 +00001141
Benny Prijono148c9dd2006-09-19 13:37:53 +00001142/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001143pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001144 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001145 pjsua_call **p_call,
1146 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001147{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001148 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001149 pjsua_call *call = NULL;
1150 pj_bool_t has_pjsua_lock = PJ_FALSE;
1151 pj_status_t status = PJ_SUCCESS;
Sauw Ming844c1c92010-09-07 05:12:02 +00001152 pj_time_val time_start, timeout;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001153
Sauw Ming844c1c92010-09-07 05:12:02 +00001154 pj_gettimeofday(&time_start);
1155 timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT;
1156 pj_time_val_normalize(&timeout);
1157
1158 for (retry=0; ; ++retry) {
1159
1160 if (retry % 10 == 9) {
1161 pj_time_val dtime;
1162
1163 pj_gettimeofday(&dtime);
1164 PJ_TIME_VAL_SUB(dtime, time_start);
1165 if (!PJ_TIME_VAL_LT(dtime, timeout))
1166 break;
1167 }
Benny Prijono148c9dd2006-09-19 13:37:53 +00001168
1169 has_pjsua_lock = PJ_FALSE;
1170
1171 status = PJSUA_TRY_LOCK();
1172 if (status != PJ_SUCCESS) {
1173 pj_thread_sleep(retry/10);
1174 continue;
1175 }
1176
1177 has_pjsua_lock = PJ_TRUE;
1178 call = &pjsua_var.calls[call_id];
1179
1180 if (call->inv == NULL) {
1181 PJSUA_UNLOCK();
1182 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1183 return PJSIP_ESESSIONTERMINATED;
1184 }
1185
1186 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1187 if (status != PJ_SUCCESS) {
1188 PJSUA_UNLOCK();
1189 pj_thread_sleep(retry/10);
1190 continue;
1191 }
1192
1193 PJSUA_UNLOCK();
1194
1195 break;
1196 }
1197
1198 if (status != PJ_SUCCESS) {
1199 if (has_pjsua_lock == PJ_FALSE)
1200 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1201 "(possibly system has deadlocked) in %s",
1202 title));
1203 else
1204 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1205 "(possibly system has deadlocked) in %s",
1206 title));
1207 return PJ_ETIMEDOUT;
1208 }
1209
1210 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001211 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001212
1213 return PJ_SUCCESS;
1214}
1215
1216
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001217/*
1218 * Get the conference port identification associated with the call.
1219 */
1220PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1221{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001222 pjsua_call *call;
1223 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001224 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001225 pj_status_t status;
1226
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001227 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1228 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001229
Benny Prijonodc752ca2006-09-22 16:55:42 +00001230 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001231 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001232 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001233
Benny Prijono0bc99a92011-03-17 04:34:43 +00001234 port_id = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001235
Benny Prijonodc752ca2006-09-22 16:55:42 +00001236 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001237
1238 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001239}
1240
1241
Benny Prijono148c9dd2006-09-19 13:37:53 +00001242
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001243/*
1244 * Obtain detail information about the specified call.
1245 */
1246PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1247 pjsua_call_info *info)
1248{
1249 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001250 pjsip_dialog *dlg;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001251 unsigned mi;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001252 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001253
1254 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1255 PJ_EINVAL);
1256
Benny Prijonoac623b32006-07-03 15:19:31 +00001257 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001258
Benny Prijonodc752ca2006-09-22 16:55:42 +00001259 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001260 if (status != PJ_SUCCESS) {
1261 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001262 }
1263
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001264 /* id and role */
1265 info->id = call_id;
1266 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001267 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001268
1269 /* local info */
1270 info->local_info.ptr = info->buf_.local_info;
1271 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1272 sizeof(info->buf_.local_info));
1273
1274 /* local contact */
1275 info->local_contact.ptr = info->buf_.local_contact;
1276 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1277 call->inv->dlg->local.contact->uri,
1278 info->local_contact.ptr,
1279 sizeof(info->buf_.local_contact));
1280
1281 /* remote info */
1282 info->remote_info.ptr = info->buf_.remote_info;
1283 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1284 sizeof(info->buf_.remote_info));
1285
1286 /* remote contact */
1287 if (call->inv->dlg->remote.contact) {
1288 int len;
1289 info->remote_contact.ptr = info->buf_.remote_contact;
1290 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1291 call->inv->dlg->remote.contact->uri,
1292 info->remote_contact.ptr,
1293 sizeof(info->buf_.remote_contact));
1294 if (len < 0) len = 0;
1295 info->remote_contact.slen = len;
1296 } else {
1297 info->remote_contact.slen = 0;
1298 }
1299
1300 /* call id */
1301 info->call_id.ptr = info->buf_.call_id;
1302 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1303 sizeof(info->buf_.call_id));
1304
1305 /* state, state_text */
1306 info->state = call->inv->state;
1307 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1308
1309 /* If call is disconnected, set the last_status from the cause code */
1310 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1311 /* last_status, last_status_text */
1312 info->last_status = call->inv->cause;
1313
1314 info->last_status_text.ptr = info->buf_.last_status_text;
1315 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1316 sizeof(info->buf_.last_status_text));
1317 } else {
1318 /* last_status, last_status_text */
1319 info->last_status = call->last_code;
1320
1321 info->last_status_text.ptr = info->buf_.last_status_text;
1322 pj_strncpy(&info->last_status_text, &call->last_text,
1323 sizeof(info->buf_.last_status_text));
1324 }
1325
Benny Prijono0bc99a92011-03-17 04:34:43 +00001326 /* Build array of media status and dir */
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001327 info->media_cnt = 0;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001328 for (mi=0; mi < call->med_cnt &&
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001329 info->media_cnt < PJ_ARRAY_SIZE(info->media); ++mi)
Benny Prijono0bc99a92011-03-17 04:34:43 +00001330 {
1331 pjsua_call_media *call_med = &call->media[mi];
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001332
1333 info->media[info->media_cnt].index = mi;
1334 info->media[info->media_cnt].status = call_med->state;
1335 info->media[info->media_cnt].dir = call_med->dir;
1336 info->media[info->media_cnt].type = call_med->type;
1337
1338 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
1339 info->media[info->media_cnt].stream.audio.conf_slot =
1340 call_med->strm.a.conf_slot;
1341 } else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
1342 info->media[info->media_cnt].stream.video.capturer =
1343 call_med->strm.v.capturer;
1344 info->media[info->media_cnt].stream.video.renderer =
1345 call_med->strm.v.renderer;
1346 } else {
Benny Prijono0bc99a92011-03-17 04:34:43 +00001347 continue;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001348 }
1349 ++info->media_cnt;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001350 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001351
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00001352 if (call->audio_idx != -1) {
1353 info->media_status = call->media[call->audio_idx].state;
1354 info->media_dir = call->media[call->audio_idx].dir;
1355 info->conf_slot = call->media[call->audio_idx].strm.a.conf_slot;
Benny Prijono0bc99a92011-03-17 04:34:43 +00001356 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001357
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001358 /* calculate duration */
1359 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1360
1361 info->total_duration = call->dis_time;
1362 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1363
1364 if (call->conn_time.sec) {
1365 info->connect_duration = call->dis_time;
1366 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1367 }
1368
1369 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1370
1371 pj_gettimeofday(&info->total_duration);
1372 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1373
1374 pj_gettimeofday(&info->connect_duration);
1375 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1376
1377 } else {
1378 pj_gettimeofday(&info->total_duration);
1379 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1380 }
1381
Benny Prijonodc752ca2006-09-22 16:55:42 +00001382 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001383
1384 return PJ_SUCCESS;
1385}
1386
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001387/*
1388 * Check if call remote peer support the specified capability.
1389 */
1390PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1391 pjsua_call_id call_id,
1392 int htype,
1393 const pj_str_t *hname,
1394 const pj_str_t *token)
1395{
1396 pjsua_call *call;
1397 pjsip_dialog *dlg;
1398 pj_status_t status;
1399 pjsip_dialog_cap_status cap_status;
1400
1401 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1402 if (status != PJ_SUCCESS)
1403 return PJSIP_DIALOG_CAP_UNKNOWN;
1404
1405 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1406
1407 pjsip_dlg_dec_lock(dlg);
1408
1409 return cap_status;
1410}
1411
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001412
1413/*
1414 * Attach application specific data to the call.
1415 */
1416PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1417 void *user_data)
1418{
1419 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1420 PJ_EINVAL);
1421 pjsua_var.calls[call_id].user_data = user_data;
1422
1423 return PJ_SUCCESS;
1424}
1425
1426
1427/*
1428 * Get user data attached to the call.
1429 */
1430PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1431{
1432 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1433 NULL);
1434 return pjsua_var.calls[call_id].user_data;
1435}
1436
1437
1438/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001439 * Get remote's NAT type.
1440 */
1441PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1442 pj_stun_nat_type *p_type)
1443{
1444 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1445 PJ_EINVAL);
1446 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1447
1448 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1449 return PJ_SUCCESS;
1450}
1451
1452
1453/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001454 * Send response to incoming INVITE request.
1455 */
1456PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1457 unsigned code,
1458 const pj_str_t *reason,
1459 const pjsua_msg_data *msg_data)
1460{
1461 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001462 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001463 pjsip_tx_data *tdata;
1464 pj_status_t status;
1465
1466 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1467 PJ_EINVAL);
1468
Benny Prijonodc752ca2006-09-22 16:55:42 +00001469 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001470 if (status != PJ_SUCCESS)
1471 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001472
Benny Prijono2e507c22006-06-23 15:04:11 +00001473 if (call->res_time.sec == 0)
1474 pj_gettimeofday(&call->res_time);
1475
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001476 if (reason && reason->slen == 0)
1477 reason = NULL;
1478
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001479 /* Create response message */
1480 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1481 if (status != PJ_SUCCESS) {
1482 pjsua_perror(THIS_FILE, "Error creating response",
1483 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001484 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001485 return status;
1486 }
1487
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001488 /* Call might have been disconnected if application is answering with
1489 * 200/OK and the media failed to start.
1490 */
1491 if (call->inv == NULL) {
1492 pjsip_dlg_dec_lock(dlg);
1493 return PJSIP_ESESSIONTERMINATED;
1494 }
1495
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001496 /* Add additional headers etc */
1497 pjsua_process_msg_data( tdata, msg_data);
1498
1499 /* Send the message */
1500 status = pjsip_inv_send_msg(call->inv, tdata);
1501 if (status != PJ_SUCCESS)
1502 pjsua_perror(THIS_FILE, "Error sending response",
1503 status);
1504
Benny Prijonodc752ca2006-09-22 16:55:42 +00001505 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001506
1507 return status;
1508}
1509
1510
1511/*
1512 * Hangup call by using method that is appropriate according to the
1513 * call state.
1514 */
1515PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1516 unsigned code,
1517 const pj_str_t *reason,
1518 const pjsua_msg_data *msg_data)
1519{
1520 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001521 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001522 pj_status_t status;
1523 pjsip_tx_data *tdata;
1524
1525
Benny Prijono148c9dd2006-09-19 13:37:53 +00001526 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1527 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1528 call_id));
1529 }
1530
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001531 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1532 PJ_EINVAL);
1533
Benny Prijonodc752ca2006-09-22 16:55:42 +00001534 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001535 if (status != PJ_SUCCESS)
1536 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001537
1538 if (code==0) {
1539 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1540 code = PJSIP_SC_OK;
1541 else if (call->inv->role == PJSIP_ROLE_UAS)
1542 code = PJSIP_SC_DECLINE;
1543 else
1544 code = PJSIP_SC_REQUEST_TERMINATED;
1545 }
1546
1547 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1548 if (status != PJ_SUCCESS) {
1549 pjsua_perror(THIS_FILE,
1550 "Failed to create end session message",
1551 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001552 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001553 return status;
1554 }
1555
1556 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1557 * as p_tdata when INVITE transaction has not been answered
1558 * with any provisional responses.
1559 */
1560 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001561 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001562 return PJ_SUCCESS;
1563 }
1564
1565 /* Add additional headers etc */
1566 pjsua_process_msg_data( tdata, msg_data);
1567
1568 /* Send the message */
1569 status = pjsip_inv_send_msg(call->inv, tdata);
1570 if (status != PJ_SUCCESS) {
1571 pjsua_perror(THIS_FILE,
1572 "Failed to send end session message",
1573 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001574 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001575 return status;
1576 }
1577
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00001578 /* Stop lock codec timer, if it is active */
1579 if (call->lock_codec.reinv_timer.id) {
1580 pjsip_endpt_cancel_timer(pjsua_var.endpt,
1581 &call->lock_codec.reinv_timer);
1582 call->lock_codec.reinv_timer.id = PJ_FALSE;
1583 }
1584
Benny Prijonodc752ca2006-09-22 16:55:42 +00001585 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001586
1587 return PJ_SUCCESS;
1588}
1589
1590
1591/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001592 * Accept or reject redirection.
1593 */
1594PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1595 pjsip_redirect_op cmd)
1596{
1597 pjsua_call *call;
1598 pjsip_dialog *dlg;
1599 pj_status_t status;
1600
1601 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1602 PJ_EINVAL);
1603
1604 status = acquire_call("pjsua_call_process_redirect()", call_id,
1605 &call, &dlg);
1606 if (status != PJ_SUCCESS)
1607 return status;
1608
1609 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1610
1611 pjsip_dlg_dec_lock(dlg);
1612
1613 return status;
1614}
1615
1616
1617/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001618 * Put the specified call on hold.
1619 */
1620PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1621 const pjsua_msg_data *msg_data)
1622{
1623 pjmedia_sdp_session *sdp;
1624 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001625 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001626 pjsip_tx_data *tdata;
1627 pj_status_t status;
1628
1629 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1630 PJ_EINVAL);
1631
Benny Prijonodc752ca2006-09-22 16:55:42 +00001632 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001633 if (status != PJ_SUCCESS)
1634 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001635
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001636
1637 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1638 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001639 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001640 return PJSIP_ESESSIONSTATE;
1641 }
1642
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001643 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001644 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001645 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001646 return status;
1647 }
1648
1649 /* Create re-INVITE with new offer */
1650 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1651 if (status != PJ_SUCCESS) {
1652 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001653 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001654 return status;
1655 }
1656
1657 /* Add additional headers etc */
1658 pjsua_process_msg_data( tdata, msg_data);
1659
1660 /* Send the request */
1661 status = pjsip_inv_send_msg( call->inv, tdata);
1662 if (status != PJ_SUCCESS) {
1663 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001664 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001665 return status;
1666 }
1667
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001668 /* Set flag that local put the call on hold */
1669 call->local_hold = PJ_TRUE;
1670
Benny Prijonodc752ca2006-09-22 16:55:42 +00001671 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001672
1673 return PJ_SUCCESS;
1674}
1675
1676
1677/*
1678 * Send re-INVITE (to release hold).
1679 */
1680PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1681 pj_bool_t unhold,
1682 const pjsua_msg_data *msg_data)
1683{
1684 pjmedia_sdp_session *sdp;
1685 pjsip_tx_data *tdata;
1686 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001687 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001688 pj_status_t status;
1689
1690
1691 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1692 PJ_EINVAL);
1693
Benny Prijonodc752ca2006-09-22 16:55:42 +00001694 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001695 if (status != PJ_SUCCESS)
1696 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001697
1698 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1699 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001700 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001701 return PJSIP_ESESSIONSTATE;
1702 }
1703
1704 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001705 if (call->local_hold && !unhold) {
1706 status = create_sdp_of_call_hold(call, &sdp);
1707 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001708 status = pjsua_media_channel_create_sdp(call->index,
1709 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001710 NULL, &sdp, NULL);
1711 call->local_hold = PJ_FALSE;
1712 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001713 if (status != PJ_SUCCESS) {
1714 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1715 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001716 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001717 return status;
1718 }
1719
1720 /* Create re-INVITE with new offer */
1721 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1722 if (status != PJ_SUCCESS) {
1723 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001724 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001725 return status;
1726 }
1727
1728 /* Add additional headers etc */
1729 pjsua_process_msg_data( tdata, msg_data);
1730
1731 /* Send the request */
1732 status = pjsip_inv_send_msg( call->inv, tdata);
1733 if (status != PJ_SUCCESS) {
1734 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001735 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001736 return status;
1737 }
1738
Benny Prijonodc752ca2006-09-22 16:55:42 +00001739 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001740
1741 return PJ_SUCCESS;
1742}
1743
1744
1745/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001746 * Send UPDATE request.
1747 */
1748PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1749 unsigned options,
1750 const pjsua_msg_data *msg_data)
1751{
1752 pjmedia_sdp_session *sdp;
1753 pjsip_tx_data *tdata;
1754 pjsua_call *call;
1755 pjsip_dialog *dlg;
1756 pj_status_t status;
1757
1758 PJ_UNUSED_ARG(options);
1759
1760 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1761 PJ_EINVAL);
1762
1763 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1764 if (status != PJ_SUCCESS)
1765 return status;
1766
Benny Prijonoc08682e2007-10-04 06:17:58 +00001767 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001768 status = pjsua_media_channel_create_sdp(call->index,
1769 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001770 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001771 if (status != PJ_SUCCESS) {
1772 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1773 status);
1774 pjsip_dlg_dec_lock(dlg);
1775 return status;
1776 }
1777
Benny Prijono224b4e22008-06-19 14:10:28 +00001778 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001779 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1780 if (status != PJ_SUCCESS) {
1781 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1782 pjsip_dlg_dec_lock(dlg);
1783 return status;
1784 }
1785
1786 /* Add additional headers etc */
1787 pjsua_process_msg_data( tdata, msg_data);
1788
1789 /* Send the request */
1790 status = pjsip_inv_send_msg( call->inv, tdata);
1791 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001792 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001793 pjsip_dlg_dec_lock(dlg);
1794 return status;
1795 }
1796
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001797 call->local_hold = PJ_FALSE;
1798
Benny Prijonoc08682e2007-10-04 06:17:58 +00001799 pjsip_dlg_dec_lock(dlg);
1800
1801 return PJ_SUCCESS;
1802}
1803
1804
1805/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001806 * Initiate call transfer to the specified address.
1807 */
1808PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1809 const pj_str_t *dest,
1810 const pjsua_msg_data *msg_data)
1811{
1812 pjsip_evsub *sub;
1813 pjsip_tx_data *tdata;
1814 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001815 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001816 pjsip_generic_string_hdr *gs_hdr;
1817 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001818 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001819 pj_status_t status;
1820
1821
1822 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1823 PJ_EINVAL);
1824
Benny Prijonodc752ca2006-09-22 16:55:42 +00001825 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001826 if (status != PJ_SUCCESS)
1827 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001828
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001829
Benny Prijonod524e822006-09-22 12:48:18 +00001830 /* Create xfer client subscription. */
1831 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001832 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001833
1834 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001835 if (status != PJ_SUCCESS) {
1836 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001837 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001838 return status;
1839 }
1840
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001841 /* Associate this call with the client subscription */
1842 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1843
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001844 /*
1845 * Create REFER request.
1846 */
1847 status = pjsip_xfer_initiate(sub, dest, &tdata);
1848 if (status != PJ_SUCCESS) {
1849 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001850 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001851 return status;
1852 }
1853
Benny Prijono053f5222006-11-11 16:16:04 +00001854 /* Add Referred-By header */
1855 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1856 &dlg->local.info_str);
1857 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1858
1859
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001860 /* Add additional headers etc */
1861 pjsua_process_msg_data( tdata, msg_data);
1862
1863 /* Send. */
1864 status = pjsip_xfer_send_request(sub, tdata);
1865 if (status != PJ_SUCCESS) {
1866 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001867 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001868 return status;
1869 }
1870
1871 /* For simplicity (that's what this program is intended to be!),
1872 * leave the original invite session as it is. More advanced application
1873 * may want to hold the INVITE, or terminate the invite, or whatever.
1874 */
1875
Benny Prijonodc752ca2006-09-22 16:55:42 +00001876 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001877
1878 return PJ_SUCCESS;
1879
1880}
1881
1882
1883/*
Benny Prijono053f5222006-11-11 16:16:04 +00001884 * Initiate attended call transfer to the specified address.
1885 */
1886PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1887 pjsua_call_id dest_call_id,
1888 unsigned options,
1889 const pjsua_msg_data *msg_data)
1890{
1891 pjsua_call *dest_call;
1892 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001893 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001894 pj_str_t str_dest;
1895 int len;
1896 pjsip_uri *uri;
1897 pj_status_t status;
1898
1899
1900 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1901 PJ_EINVAL);
1902 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1903 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1904 PJ_EINVAL);
1905
1906 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1907 &dest_call, &dest_dlg);
1908 if (status != PJ_SUCCESS)
1909 return status;
1910
1911 /*
1912 * Create REFER destination URI with Replaces field.
1913 */
1914
1915 /* Make sure we have sufficient buffer's length */
1916 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1917 dest_dlg->call_id->id.slen +
1918 dest_dlg->remote.info->tag.slen +
1919 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001920 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001921
1922 /* Print URI */
1923 str_dest_buf[0] = '<';
1924 str_dest.slen = 1;
1925
Benny Prijonoa1e69682007-05-11 15:14:34 +00001926 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001927 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1928 str_dest_buf+1, sizeof(str_dest_buf)-1);
1929 if (len < 0)
1930 return PJSIP_EURITOOLONG;
1931
1932 str_dest.slen += len;
1933
1934
1935 /* Build the URI */
1936 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1937 sizeof(str_dest_buf) - str_dest.slen,
1938 "?%s"
1939 "Replaces=%.*s"
1940 "%%3Bto-tag%%3D%.*s"
1941 "%%3Bfrom-tag%%3D%.*s>",
1942 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1943 "" : "Require=replaces&"),
1944 (int)dest_dlg->call_id->id.slen,
1945 dest_dlg->call_id->id.ptr,
1946 (int)dest_dlg->remote.info->tag.slen,
1947 dest_dlg->remote.info->tag.ptr,
1948 (int)dest_dlg->local.info->tag.slen,
1949 dest_dlg->local.info->tag.ptr);
1950
1951 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1952 PJSIP_EURITOOLONG);
1953
1954 str_dest.ptr = str_dest_buf;
1955 str_dest.slen += len;
1956
1957 pjsip_dlg_dec_lock(dest_dlg);
1958
1959 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1960}
1961
1962
1963/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001964 * Send DTMF digits to remote using RFC 2833 payload formats.
1965 */
1966PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1967 const pj_str_t *digits)
1968{
1969 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001970 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001971 pj_status_t status;
1972
1973 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1974 PJ_EINVAL);
1975
Benny Prijonodc752ca2006-09-22 16:55:42 +00001976 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001977 if (status != PJ_SUCCESS)
1978 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001979
Benny Prijono0bc99a92011-03-17 04:34:43 +00001980 if (!pjsua_call_has_media(call_id)) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001981 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001982 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001983 return PJ_EINVALIDOP;
1984 }
1985
Benny Prijono0bc99a92011-03-17 04:34:43 +00001986 status = pjmedia_stream_dial_dtmf(
1987 call->media[call->audio_idx].strm.a.stream, digits);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001988
Benny Prijonodc752ca2006-09-22 16:55:42 +00001989 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001990
1991 return status;
1992}
1993
1994
1995/**
1996 * Send instant messaging inside INVITE session.
1997 */
1998PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1999 const pj_str_t *mime_type,
2000 const pj_str_t *content,
2001 const pjsua_msg_data *msg_data,
2002 void *user_data)
2003{
2004 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002005 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002006 const pj_str_t mime_text_plain = pj_str("text/plain");
2007 pjsip_media_type ctype;
2008 pjsua_im_data *im_data;
2009 pjsip_tx_data *tdata;
2010 pj_status_t status;
2011
2012
2013 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2014 PJ_EINVAL);
2015
Benny Prijonodc752ca2006-09-22 16:55:42 +00002016 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002017 if (status != PJ_SUCCESS)
2018 return status;
2019
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002020 /* Set default media type if none is specified */
2021 if (mime_type == NULL) {
2022 mime_type = &mime_text_plain;
2023 }
2024
2025 /* Create request message. */
2026 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2027 -1, &tdata);
2028 if (status != PJ_SUCCESS) {
2029 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2030 goto on_return;
2031 }
2032
2033 /* Add accept header. */
2034 pjsip_msg_add_hdr( tdata->msg,
2035 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
2036
2037 /* Parse MIME type */
2038 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
2039
2040 /* Create "text/plain" message body. */
2041 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
2042 &ctype.subtype, content);
2043 if (tdata->msg->body == NULL) {
2044 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
2045 pjsip_tx_data_dec_ref(tdata);
2046 goto on_return;
2047 }
2048
2049 /* Add additional headers etc */
2050 pjsua_process_msg_data( tdata, msg_data);
2051
2052 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002053 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002054 im_data->acc_id = call->acc_id;
2055 im_data->call_id = call_id;
2056 im_data->to = call->inv->dlg->remote.info_str;
2057 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2058 im_data->user_data = user_data;
2059
2060
2061 /* Send the request. */
2062 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2063 pjsua_var.mod.id, im_data);
2064 if (status != PJ_SUCCESS) {
2065 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2066 goto on_return;
2067 }
2068
2069on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002070 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002071 return status;
2072}
2073
2074
2075/*
2076 * Send IM typing indication inside INVITE session.
2077 */
2078PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2079 pj_bool_t is_typing,
2080 const pjsua_msg_data*msg_data)
2081{
2082 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002083 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002084 pjsip_tx_data *tdata;
2085 pj_status_t status;
2086
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002087 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2088 PJ_EINVAL);
2089
Benny Prijonodc752ca2006-09-22 16:55:42 +00002090 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002091 if (status != PJ_SUCCESS)
2092 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002093
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002094 /* Create request message. */
2095 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2096 -1, &tdata);
2097 if (status != PJ_SUCCESS) {
2098 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2099 goto on_return;
2100 }
2101
2102 /* Create "application/im-iscomposing+xml" msg body. */
2103 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2104 NULL, NULL, -1);
2105
2106 /* Add additional headers etc */
2107 pjsua_process_msg_data( tdata, msg_data);
2108
2109 /* Send the request. */
2110 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2111 if (status != PJ_SUCCESS) {
2112 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2113 goto on_return;
2114 }
2115
2116on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002117 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002118 return status;
2119}
2120
2121
2122/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002123 * Send arbitrary request.
2124 */
2125PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2126 const pj_str_t *method_str,
2127 const pjsua_msg_data *msg_data)
2128{
2129 pjsua_call *call;
2130 pjsip_dialog *dlg;
2131 pjsip_method method;
2132 pjsip_tx_data *tdata;
2133 pj_status_t status;
2134
2135 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2136 PJ_EINVAL);
2137
2138 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2139 if (status != PJ_SUCCESS)
2140 return status;
2141
2142 /* Init method */
2143 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2144
2145 /* Create request message. */
2146 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2147 if (status != PJ_SUCCESS) {
2148 pjsua_perror(THIS_FILE, "Unable to create request", status);
2149 goto on_return;
2150 }
2151
2152 /* Add additional headers etc */
2153 pjsua_process_msg_data( tdata, msg_data);
2154
2155 /* Send the request. */
2156 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2157 if (status != PJ_SUCCESS) {
2158 pjsua_perror(THIS_FILE, "Unable to send request", status);
2159 goto on_return;
2160 }
2161
2162on_return:
2163 pjsip_dlg_dec_lock(dlg);
2164 return status;
2165}
2166
2167
2168/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002169 * Terminate all calls.
2170 */
2171PJ_DEF(void) pjsua_call_hangup_all(void)
2172{
2173 unsigned i;
2174
2175 PJSUA_LOCK();
2176
2177 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2178 if (pjsua_var.calls[i].inv)
2179 pjsua_call_hangup(i, 0, NULL, NULL);
2180 }
2181
2182 PJSUA_UNLOCK();
2183}
2184
2185
Benny Prijono627cbb42007-09-25 20:48:49 +00002186const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002187{
2188 if (val < 1000) {
2189 pj_ansi_sprintf(buf, "%d", val);
2190 } else if (val < 1000000) {
2191 pj_ansi_sprintf(buf, "%d.%dK",
2192 val / 1000,
2193 (val % 1000) / 100);
2194 } else {
2195 pj_ansi_sprintf(buf, "%d.%02dM",
2196 val / 1000000,
2197 (val % 1000000) / 10000);
2198 }
2199
2200 return buf;
2201}
2202
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002203static unsigned dump_media_stat(const char *indent,
2204 char *buf, unsigned maxlen,
2205 const pjmedia_rtcp_stat *stat,
2206 const char *rx_info, const char *tx_info)
2207{
2208 char last_update[64];
2209 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
2210 pj_time_val media_duration, now;
2211 char *p = buf, *end = buf+maxlen;
2212 int len;
2213
2214 if (stat->rx.update_cnt == 0)
2215 strcpy(last_update, "never");
2216 else {
2217 pj_gettimeofday(&now);
2218 PJ_TIME_VAL_SUB(now, stat->rx.update);
2219 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2220 now.sec / 3600,
2221 (now.sec % 3600) / 60,
2222 now.sec % 60,
2223 now.msec);
2224 }
2225
2226 pj_gettimeofday(&media_duration);
2227 PJ_TIME_VAL_SUB(media_duration, stat->start);
2228 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2229 media_duration.msec = 1;
2230
2231 len = pj_ansi_snprintf(p, end-p,
2232 "%s RX %s last update:%s\n"
2233 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
2234 "%s pkt loss=%d (%3.1f%%), discrd=%d (%3.1f%%), dup=%d (%2.1f%%), reord=%d (%3.1f%%)\n"
2235 "%s (msec) min avg max last dev\n"
2236 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2237 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2238#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2239 "%s raw jitter : %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2240#endif
2241#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2242 "%s IPDV : %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2243#endif
2244 "%s",
2245 indent,
2246 rx_info? rx_info : "",
2247 last_update,
2248
2249 indent,
2250 good_number(packets, stat->rx.pkt),
2251 good_number(bytes, stat->rx.bytes),
2252 good_number(ipbytes, stat->rx.bytes + stat->rx.pkt * 40),
2253 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat->rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2254 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))),
2255 indent,
2256 stat->rx.loss,
2257 (stat->rx.loss? stat->rx.loss * 100.0 / (stat->rx.pkt + stat->rx.loss) : 0),
2258 stat->rx.discard,
2259 (stat->rx.discard? stat->rx.discard * 100.0 / (stat->rx.pkt + stat->rx.loss) : 0),
2260 stat->rx.dup,
2261 (stat->rx.dup? stat->rx.dup * 100.0 / (stat->rx.pkt + stat->rx.loss) : 0),
2262 stat->rx.reorder,
2263 (stat->rx.reorder? stat->rx.reorder * 100.0 / (stat->rx.pkt + stat->rx.loss) : 0),
2264 indent, indent,
2265 stat->rx.loss_period.min / 1000.0,
2266 stat->rx.loss_period.mean / 1000.0,
2267 stat->rx.loss_period.max / 1000.0,
2268 stat->rx.loss_period.last / 1000.0,
2269 pj_math_stat_get_stddev(&stat->rx.loss_period) / 1000.0,
2270 indent,
2271 stat->rx.jitter.min / 1000.0,
2272 stat->rx.jitter.mean / 1000.0,
2273 stat->rx.jitter.max / 1000.0,
2274 stat->rx.jitter.last / 1000.0,
2275 pj_math_stat_get_stddev(&stat->rx.jitter) / 1000.0,
2276#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2277 indent,
2278 stat->rx_raw_jitter.min / 1000.0,
2279 stat->rx_raw_jitter.mean / 1000.0,
2280 stat->rx_raw_jitter.max / 1000.0,
2281 stat->rx_raw_jitter.last / 1000.0,
2282 pj_math_stat_get_stddev(&stat->rx_raw_jitter) / 1000.0,
2283#endif
2284#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2285 indent,
2286 stat->rx_ipdv.min / 1000.0,
2287 stat->rx_ipdv.mean / 1000.0,
2288 stat->rx_ipdv.max / 1000.0,
2289 stat->rx_ipdv.last / 1000.0,
2290 pj_math_stat_get_stddev(&stat->rx_ipdv) / 1000.0,
2291#endif
2292 ""
2293 );
2294
2295 if (len < 1 || len > end-p) {
2296 *p = '\0';
2297 return (p-buf);
2298 }
2299 p += len;
2300
2301 if (stat->tx.update_cnt == 0)
2302 strcpy(last_update, "never");
2303 else {
2304 pj_gettimeofday(&now);
2305 PJ_TIME_VAL_SUB(now, stat->tx.update);
2306 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2307 now.sec / 3600,
2308 (now.sec % 3600) / 60,
2309 now.sec % 60,
2310 now.msec);
2311 }
2312
2313 len = pj_ansi_snprintf(p, end-p,
2314 "%s TX %s last update:%s\n"
2315 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
2316 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
2317 "%s (msec) min avg max last dev \n"
2318 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2319 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f\n",
2320 indent,
2321 tx_info,
2322 last_update,
2323
2324 indent,
2325 good_number(packets, stat->tx.pkt),
2326 good_number(bytes, stat->tx.bytes),
2327 good_number(ipbytes, stat->tx.bytes + stat->tx.pkt * 40),
2328 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat->tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2329 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))),
2330
2331 indent,
2332 stat->tx.loss,
2333 (stat->tx.loss? stat->tx.loss * 100.0 / (stat->tx.pkt + stat->tx.loss) : 0),
2334 stat->tx.dup,
2335 (stat->tx.dup? stat->tx.dup * 100.0 / (stat->tx.pkt + stat->tx.loss) : 0),
2336 stat->tx.reorder,
2337 (stat->tx.reorder? stat->tx.reorder * 100.0 / (stat->tx.pkt + stat->tx.loss) : 0),
2338
2339 indent, indent,
2340 stat->tx.loss_period.min / 1000.0,
2341 stat->tx.loss_period.mean / 1000.0,
2342 stat->tx.loss_period.max / 1000.0,
2343 stat->tx.loss_period.last / 1000.0,
2344 pj_math_stat_get_stddev(&stat->tx.loss_period) / 1000.0,
2345 indent,
2346 stat->tx.jitter.min / 1000.0,
2347 stat->tx.jitter.mean / 1000.0,
2348 stat->tx.jitter.max / 1000.0,
2349 stat->tx.jitter.last / 1000.0,
2350 pj_math_stat_get_stddev(&stat->tx.jitter) / 1000.0
2351 );
2352
2353 if (len < 1 || len > end-p) {
2354 *p = '\0';
2355 return (p-buf);
2356 }
2357 p += len;
2358
2359 len = pj_ansi_snprintf(p, end-p,
2360 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f\n",
2361 indent,
2362 stat->rtt.min / 1000.0,
2363 stat->rtt.mean / 1000.0,
2364 stat->rtt.max / 1000.0,
2365 stat->rtt.last / 1000.0,
2366 pj_math_stat_get_stddev(&stat->rtt) / 1000.0
2367 );
2368 if (len < 1 || len > end-p) {
2369 *p = '\0';
2370 return (p-buf);
2371 }
2372 p += len;
2373
2374 return (p-buf);
2375}
2376
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002377
2378/* Dump media session */
2379static void dump_media_session(const char *indent,
2380 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002381 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002382{
2383 unsigned i;
2384 char *p = buf, *end = buf+maxlen;
2385 int len;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002386
Benny Prijono0bc99a92011-03-17 04:34:43 +00002387 for (i=0; i<call->med_cnt; ++i) {
2388 pjsua_call_media *call_med = &call->media[i];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002389 pjmedia_rtcp_stat stat;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002390 pj_bool_t has_stat;
2391 pjmedia_transport_info tp_info;
Benny Prijono5186eae2007-12-03 14:38:25 +00002392 char rem_addr_buf[80];
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002393 char codec_info[32] = {'0'};
2394 char rx_info[80] = {'\0'};
2395 char tx_info[80] = {'\0'};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002396 const char *rem_addr;
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002397 const char *dir_str;
2398 const char *media_type_str;
2399
2400 switch (call_med->type) {
2401 case PJMEDIA_TYPE_AUDIO:
2402 media_type_str = "audio";
2403 break;
2404 case PJMEDIA_TYPE_VIDEO:
2405 media_type_str = "video";
2406 break;
2407 case PJMEDIA_TYPE_APPLICATION:
2408 media_type_str = "application";
2409 break;
2410 default:
2411 media_type_str = "unknown";
2412 break;
2413 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002414
Benny Prijono0bc99a92011-03-17 04:34:43 +00002415 /* Check if the stream is deactivated */
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002416 if (call_med->tp == NULL ||
2417 (!call_med->strm.a.stream && !call_med->strm.v.stream))
2418 {
Benny Prijono0bc99a92011-03-17 04:34:43 +00002419 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002420 "%s #%d %s deactivated\n",
Benny Prijono0bc99a92011-03-17 04:34:43 +00002421 indent, i, media_type_str);
2422 if (len < 1 || len > end-p) {
2423 *p = '\0';
2424 return;
2425 }
2426
2427 p += len;
2428 continue;
2429 }
2430
2431 pjmedia_transport_info_init(&tp_info);
2432 pjmedia_transport_get_info(call_med->tp, &tp_info);
2433
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002434 // rem_addr will contain actual address of RTP originator, instead of
2435 // remote RTP address specified by stream which is fetched from the SDP.
2436 // Please note that we are assuming only one stream per call.
2437 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2438 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002439 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2440 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002441 sizeof(rem_addr_buf), 3);
2442 } else {
2443 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002444 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002445 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002446
Benny Prijono0bc99a92011-03-17 04:34:43 +00002447 if (call_med->dir == PJMEDIA_DIR_NONE) {
Benny Prijono2dbf5072010-06-23 12:38:28 +00002448 /* To handle when the stream that is currently being paused
2449 * (http://trac.pjsip.org/repos/ticket/1079)
2450 */
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002451 dir_str = "inactive";
2452 } else if (call_med->dir == PJMEDIA_DIR_ENCODING)
2453 dir_str = "sendonly";
2454 else if (call_med->dir == PJMEDIA_DIR_DECODING)
2455 dir_str = "recvonly";
2456 else if (call_med->dir == PJMEDIA_DIR_ENCODING_DECODING)
2457 dir_str = "sendrecv";
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002458 else
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002459 dir_str = "inactive";
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002460
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002461 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
2462 pjmedia_stream *stream = call_med->strm.a.stream;
2463 pjmedia_stream_info info;
2464
2465 pjmedia_stream_get_stat(stream, &stat);
2466 has_stat = PJ_TRUE;
2467
2468 pjmedia_stream_get_info(stream, &info);
2469 pj_ansi_snprintf(codec_info, sizeof(codec_info), " %.*s @%dkHz",
Benny Prijono316f02a2011-04-07 07:53:25 +00002470 (int)info.fmt.encoding_name.slen,
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002471 info.fmt.encoding_name.ptr,
2472 info.fmt.clock_rate / 1000);
2473 pj_ansi_snprintf(rx_info, sizeof(rx_info), "pt=%d,",
2474 info.fmt.pt);
2475 pj_ansi_snprintf(tx_info, sizeof(tx_info), "pt=%d, ptime=%d,",
2476 info.tx_pt,
2477 info.param->setting.frm_per_pkt*
2478 info.param->info.frm_ptime);
2479 } else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
2480 pjmedia_vid_stream *stream = call_med->strm.v.stream;
2481 pjmedia_vid_stream_info info;
2482
2483 pjmedia_vid_stream_get_stat(stream, &stat);
2484 has_stat = PJ_TRUE;
2485
2486 pjmedia_vid_stream_get_info(stream, &info);
2487 pj_ansi_snprintf(codec_info, sizeof(codec_info), " %.*s",
Benny Prijono316f02a2011-04-07 07:53:25 +00002488 (int)info.codec_info.encoding_name.slen,
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002489 info.codec_info.encoding_name.ptr);
2490 if (call_med->dir & PJMEDIA_DIR_DECODING) {
2491 pjmedia_video_format_detail *vfd;
2492 vfd = pjmedia_format_get_video_format_detail(
2493 &info.codec_param->dec_fmt, PJ_TRUE);
2494 pj_ansi_snprintf(rx_info, sizeof(rx_info),
2495 "pt=%d, size=%dx%d, fps=%.2f,",
2496 info.rx_pt,
2497 vfd->size.w, vfd->size.h,
2498 vfd->fps.num*1.0/vfd->fps.denum);
2499 }
2500 if (call_med->dir & PJMEDIA_DIR_ENCODING) {
2501 pjmedia_video_format_detail *vfd;
2502 vfd = pjmedia_format_get_video_format_detail(
2503 &info.codec_param->enc_fmt, PJ_TRUE);
2504 pj_ansi_snprintf(tx_info, sizeof(tx_info),
2505 "pt=%d, size=%dx%d, fps=%.2f,",
2506 info.tx_pt,
2507 vfd->size.w, vfd->size.h,
2508 vfd->fps.num*1.0/vfd->fps.denum);
2509 }
2510 } else {
2511 has_stat = PJ_FALSE;
2512 }
2513
Benny Prijono0bc99a92011-03-17 04:34:43 +00002514 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002515 "%s #%d %s%s, %s, peer=%s\n",
2516 indent,
2517 call_med->idx,
2518 media_type_str,
2519 codec_info,
2520 dir_str,
Benny Prijono5186eae2007-12-03 14:38:25 +00002521 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002522 if (len < 1 || len > end-p) {
2523 *p = '\0';
2524 return;
2525 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002526 p += len;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002527
Benny Prijono029806a2011-04-13 18:07:12 +00002528 /* Get and ICE SRTP status */
2529 if (call_med->tp) {
2530 pjmedia_transport_info tp_info;
2531
2532 pjmedia_transport_info_init(&tp_info);
2533 pjmedia_transport_get_info(call_med->tp, &tp_info);
2534 if (tp_info.specific_info_cnt > 0) {
2535 unsigned j;
2536 for (j = 0; j < tp_info.specific_info_cnt; ++j) {
2537 if (tp_info.spc_info[j].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2538 {
2539 pjmedia_srtp_info *srtp_info =
2540 (pjmedia_srtp_info*) tp_info.spc_info[j].buffer;
2541
2542 len = pj_ansi_snprintf(p, end-p,
2543 " %s SRTP status: %s Crypto-suite: %s",
2544 indent,
2545 (srtp_info->active?"Active":"Not active"),
2546 srtp_info->tx_policy.name.ptr);
2547 if (len > 0 && len < end-p) {
2548 p += len;
2549 *p++ = '\n';
2550 *p = '\0';
2551 }
2552 } else if (tp_info.spc_info[j].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
2553 const pjmedia_ice_transport_info *ii;
2554
2555 ii = (const pjmedia_ice_transport_info*)
2556 tp_info.spc_info[j].buffer;
2557
2558 len = pj_ansi_snprintf(p, end-p,
2559 " %s ICE role: %s, state: %s, comp_cnt: %u",
2560 indent,
2561 pj_ice_sess_role_name(ii->role),
2562 pj_ice_strans_state_name(ii->sess_state),
2563 ii->comp_cnt);
2564 if (len > 0 && len < end-p) {
2565 p += len;
2566 *p++ = '\n';
2567 *p = '\0';
2568 }
2569 }
2570 }
2571 }
2572 }
2573
2574
Nanang Izzuddinbf26db12011-03-18 07:54:50 +00002575 if (has_stat) {
2576 len = dump_media_stat(indent, p, end-p, &stat,
2577 rx_info, tx_info);
2578 p += len;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002579 }
2580
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002581#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2582# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2583 do { \
2584 if (samples <= 4294) \
2585 usec = samples * 1000000 / clock_rate; \
2586 else { \
2587 usec = samples * 1000 / clock_rate; \
2588 usec *= 1000; \
2589 } \
2590 } while(0)
2591
2592# define PRINT_VOIP_MTC_VAL(s, v) \
2593 if (v == 127) \
2594 sprintf(s, "(na)"); \
2595 else \
2596 sprintf(s, "%d", v)
2597
2598# define VALIDATE_PRINT_BUF() \
2599 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2600 p += len; *p++ = '\n'; *p = '\0'
2601
2602
Benny Prijono9d5efa72011-04-10 23:52:05 +00002603 if (call_med->type == PJMEDIA_TYPE_AUDIO) {
2604 pjmedia_stream_info info;
2605 char last_update[64];
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002606 char loss[16], dup[16];
2607 char jitter[80];
2608 char toh[80];
2609 char plc[16], jba[16], jbr[16];
2610 char signal_lvl[16], noise_lvl[16], rerl[16];
2611 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2612 pjmedia_rtcp_xr_stat xr_stat;
2613 unsigned clock_rate;
Benny Prijono9d5efa72011-04-10 23:52:05 +00002614 pj_time_val now;
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002615
Benny Prijono9d5efa72011-04-10 23:52:05 +00002616 if (pjmedia_stream_get_stat_xr(call_med->strm.a.stream,
2617 &xr_stat) != PJ_SUCCESS)
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002618 {
Benny Prijono9d5efa72011-04-10 23:52:05 +00002619 continue;
2620 }
2621
2622 if (pjmedia_stream_get_info(call_med->strm.a.stream, &info)
2623 != PJ_SUCCESS)
2624 {
2625 continue;
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002626 }
2627
Benny Prijono0bc99a92011-03-17 04:34:43 +00002628 clock_rate = info.fmt.clock_rate;
Benny Prijono9d5efa72011-04-10 23:52:05 +00002629 pj_gettimeofday(&now);
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002630
2631 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2632 VALIDATE_PRINT_BUF();
2633
2634 /* Statistics Summary */
2635 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2636 VALIDATE_PRINT_BUF();
2637
2638 if (xr_stat.rx.stat_sum.l)
2639 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2640 else
2641 sprintf(loss, "(na)");
2642
2643 if (xr_stat.rx.stat_sum.d)
2644 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2645 else
2646 sprintf(dup, "(na)");
2647
2648 if (xr_stat.rx.stat_sum.j) {
2649 unsigned jmin, jmax, jmean, jdev;
2650
2651 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2652 clock_rate);
2653 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2654 clock_rate);
2655 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2656 clock_rate);
2657 SAMPLES_TO_USEC(jdev,
2658 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2659 clock_rate);
2660 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2661 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2662 } else
2663 sprintf(jitter, "(report not available)");
2664
2665 if (xr_stat.rx.stat_sum.t) {
2666 sprintf(toh, "%11d %11d %11d %11d",
2667 xr_stat.rx.stat_sum.toh.min,
2668 xr_stat.rx.stat_sum.toh.mean,
2669 xr_stat.rx.stat_sum.toh.max,
2670 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2671 } else
2672 sprintf(toh, "(report not available)");
2673
2674 if (xr_stat.rx.stat_sum.update.sec == 0)
2675 strcpy(last_update, "never");
2676 else {
2677 pj_gettimeofday(&now);
2678 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2679 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2680 now.sec / 3600,
2681 (now.sec % 3600) / 60,
2682 now.sec % 60,
2683 now.msec);
2684 }
2685
2686 len = pj_ansi_snprintf(p, end-p,
2687 "%s RX last update: %s\n"
2688 "%s begin seq=%d, end seq=%d\n"
2689 "%s pkt loss=%s, dup=%s\n"
2690 "%s (msec) min avg max dev\n"
2691 "%s jitter : %s\n"
2692 "%s toh : %s",
2693 indent, last_update,
2694 indent,
2695 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2696 indent, loss, dup,
2697 indent,
2698 indent, jitter,
2699 indent, toh
2700 );
2701 VALIDATE_PRINT_BUF();
2702
2703 if (xr_stat.tx.stat_sum.l)
2704 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2705 else
2706 sprintf(loss, "(na)");
2707
2708 if (xr_stat.tx.stat_sum.d)
2709 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2710 else
2711 sprintf(dup, "(na)");
2712
2713 if (xr_stat.tx.stat_sum.j) {
2714 unsigned jmin, jmax, jmean, jdev;
2715
2716 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2717 clock_rate);
2718 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2719 clock_rate);
2720 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2721 clock_rate);
2722 SAMPLES_TO_USEC(jdev,
2723 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2724 clock_rate);
2725 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2726 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2727 } else
2728 sprintf(jitter, "(report not available)");
2729
2730 if (xr_stat.tx.stat_sum.t) {
2731 sprintf(toh, "%11d %11d %11d %11d",
2732 xr_stat.tx.stat_sum.toh.min,
2733 xr_stat.tx.stat_sum.toh.mean,
2734 xr_stat.tx.stat_sum.toh.max,
2735 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2736 } else
2737 sprintf(toh, "(report not available)");
2738
2739 if (xr_stat.tx.stat_sum.update.sec == 0)
2740 strcpy(last_update, "never");
2741 else {
2742 pj_gettimeofday(&now);
2743 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2744 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2745 now.sec / 3600,
2746 (now.sec % 3600) / 60,
2747 now.sec % 60,
2748 now.msec);
2749 }
2750
2751 len = pj_ansi_snprintf(p, end-p,
2752 "%s TX last update: %s\n"
2753 "%s begin seq=%d, end seq=%d\n"
2754 "%s pkt loss=%s, dup=%s\n"
2755 "%s (msec) min avg max dev\n"
2756 "%s jitter : %s\n"
2757 "%s toh : %s",
2758 indent, last_update,
2759 indent,
2760 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2761 indent, loss, dup,
2762 indent,
2763 indent, jitter,
2764 indent, toh
2765 );
2766 VALIDATE_PRINT_BUF();
2767
2768
2769 /* VoIP Metrics */
2770 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2771 VALIDATE_PRINT_BUF();
2772
2773 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2774 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2775 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2776 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2777 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2778 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2779 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2780
2781 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2782 case PJMEDIA_RTCP_XR_PLC_DIS:
2783 sprintf(plc, "DISABLED");
2784 break;
2785 case PJMEDIA_RTCP_XR_PLC_ENH:
2786 sprintf(plc, "ENHANCED");
2787 break;
2788 case PJMEDIA_RTCP_XR_PLC_STD:
2789 sprintf(plc, "STANDARD");
2790 break;
2791 case PJMEDIA_RTCP_XR_PLC_UNK:
2792 default:
2793 sprintf(plc, "UNKNOWN");
2794 break;
2795 }
2796
2797 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2798 case PJMEDIA_RTCP_XR_JB_FIXED:
2799 sprintf(jba, "FIXED");
2800 break;
2801 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2802 sprintf(jba, "ADAPTIVE");
2803 break;
2804 default:
2805 sprintf(jba, "UNKNOWN");
2806 break;
2807 }
2808
2809 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2810
2811 if (xr_stat.rx.voip_mtc.update.sec == 0)
2812 strcpy(last_update, "never");
2813 else {
2814 pj_gettimeofday(&now);
2815 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2816 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2817 now.sec / 3600,
2818 (now.sec % 3600) / 60,
2819 now.sec % 60,
2820 now.msec);
2821 }
2822
2823 len = pj_ansi_snprintf(p, end-p,
2824 "%s RX last update: %s\n"
2825 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2826 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2827 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2828 "%s delay : round trip=%d%s, end system=%d%s\n"
2829 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2830 "%s quality : R factor=%s, ext R factor=%s\n"
2831 "%s MOS LQ=%s, MOS CQ=%s\n"
2832 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2833 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2834 indent,
2835 last_update,
2836 /* packets */
2837 indent,
2838 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2839 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2840 /* burst */
2841 indent,
2842 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2843 xr_stat.rx.voip_mtc.burst_dur, "ms",
2844 /* gap */
2845 indent,
2846 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2847 xr_stat.rx.voip_mtc.gap_dur, "ms",
2848 /* delay */
2849 indent,
2850 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2851 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2852 /* level */
2853 indent,
2854 signal_lvl, "dB",
2855 noise_lvl, "dB",
2856 rerl, "",
2857 /* quality */
2858 indent,
2859 r_factor, ext_r_factor,
2860 indent,
2861 mos_lq, mos_cq,
2862 /* config */
2863 indent,
2864 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2865 /* JB delay */
2866 indent,
2867 xr_stat.rx.voip_mtc.jb_nom, "ms",
2868 xr_stat.rx.voip_mtc.jb_max, "ms",
2869 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2870 );
2871 VALIDATE_PRINT_BUF();
2872
2873 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2874 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2875 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2876 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2877 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2878 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2879 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2880
2881 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2882 case PJMEDIA_RTCP_XR_PLC_DIS:
2883 sprintf(plc, "DISABLED");
2884 break;
2885 case PJMEDIA_RTCP_XR_PLC_ENH:
2886 sprintf(plc, "ENHANCED");
2887 break;
2888 case PJMEDIA_RTCP_XR_PLC_STD:
2889 sprintf(plc, "STANDARD");
2890 break;
2891 case PJMEDIA_RTCP_XR_PLC_UNK:
2892 default:
2893 sprintf(plc, "unknown");
2894 break;
2895 }
2896
2897 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2898 case PJMEDIA_RTCP_XR_JB_FIXED:
2899 sprintf(jba, "FIXED");
2900 break;
2901 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2902 sprintf(jba, "ADAPTIVE");
2903 break;
2904 default:
2905 sprintf(jba, "unknown");
2906 break;
2907 }
2908
2909 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2910
2911 if (xr_stat.tx.voip_mtc.update.sec == 0)
2912 strcpy(last_update, "never");
2913 else {
2914 pj_gettimeofday(&now);
2915 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2916 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2917 now.sec / 3600,
2918 (now.sec % 3600) / 60,
2919 now.sec % 60,
2920 now.msec);
2921 }
2922
2923 len = pj_ansi_snprintf(p, end-p,
2924 "%s TX last update: %s\n"
2925 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2926 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2927 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2928 "%s delay : round trip=%d%s, end system=%d%s\n"
2929 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2930 "%s quality : R factor=%s, ext R factor=%s\n"
2931 "%s MOS LQ=%s, MOS CQ=%s\n"
2932 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2933 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2934 indent,
2935 last_update,
2936 /* pakcets */
2937 indent,
2938 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2939 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2940 /* burst */
2941 indent,
2942 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2943 xr_stat.tx.voip_mtc.burst_dur, "ms",
2944 /* gap */
2945 indent,
2946 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2947 xr_stat.tx.voip_mtc.gap_dur, "ms",
2948 /* delay */
2949 indent,
2950 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2951 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2952 /* level */
2953 indent,
2954 signal_lvl, "dB",
2955 noise_lvl, "dB",
2956 rerl, "",
2957 /* quality */
2958 indent,
2959 r_factor, ext_r_factor,
2960 indent,
2961 mos_lq, mos_cq,
2962 /* config */
2963 indent,
2964 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2965 /* JB delay */
2966 indent,
2967 xr_stat.tx.voip_mtc.jb_nom, "ms",
2968 xr_stat.tx.voip_mtc.jb_max, "ms",
2969 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2970 );
2971 VALIDATE_PRINT_BUF();
2972
2973
2974 /* RTT delay (by receiver side) */
2975 len = pj_ansi_snprintf(p, end-p,
2976 "%s RTT (from recv) min avg max last dev",
2977 indent);
2978 VALIDATE_PRINT_BUF();
2979 len = pj_ansi_snprintf(p, end-p,
2980 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2981 indent,
2982 xr_stat.rtt.min / 1000.0,
2983 xr_stat.rtt.mean / 1000.0,
2984 xr_stat.rtt.max / 1000.0,
2985 xr_stat.rtt.last / 1000.0,
2986 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2987 );
2988 VALIDATE_PRINT_BUF();
Benny Prijono9d5efa72011-04-10 23:52:05 +00002989 } /* if audio */;
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002990#endif
2991
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002992 }
2993}
2994
2995
2996/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002997void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002998 int call_id,
2999 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003000{
3001 int len;
3002 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
3003 pjsip_dialog *dlg = inv->dlg;
3004 char userinfo[128];
3005
3006 /* Dump invite sesion info. */
3007
3008 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
Benny Prijono329d6382009-05-29 13:04:03 +00003009 if (len < 0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003010 pj_ansi_strcpy(userinfo, "<--uri too long-->");
3011 else
3012 userinfo[len] = '\0';
3013
3014 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
3015 title,
3016 pjsip_inv_state_name(inv->state),
3017 userinfo);
3018 if (len < 1 || len >= (int)size) {
3019 pj_ansi_strcpy(buf, "<--uri too long-->");
3020 len = 18;
3021 } else
3022 buf[len] = '\0';
3023}
3024
3025
3026/*
3027 * Dump call and media statistics to string.
3028 */
3029PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
3030 pj_bool_t with_media,
3031 char *buffer,
3032 unsigned maxlen,
3033 const char *indent)
3034{
3035 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00003036 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003037 pj_time_val duration, res_delay, con_delay;
3038 char tmp[128];
3039 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00003040 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003041 int len;
3042
3043 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
3044 PJ_EINVAL);
3045
Benny Prijonodc752ca2006-09-22 16:55:42 +00003046 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00003047 if (status != PJ_SUCCESS)
3048 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003049
3050 *buffer = '\0';
3051 p = buffer;
3052 end = buffer + maxlen;
3053 len = 0;
3054
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003055 print_call(indent, call_id, tmp, sizeof(tmp));
3056
3057 len = pj_ansi_strlen(tmp);
3058 pj_ansi_strcpy(buffer, tmp);
3059
3060 p += len;
3061 *p++ = '\r';
3062 *p++ = '\n';
3063
3064 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00003065 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003066 pj_gettimeofday(&duration);
3067 PJ_TIME_VAL_SUB(duration, call->conn_time);
3068 con_delay = call->conn_time;
3069 PJ_TIME_VAL_SUB(con_delay, call->start_time);
3070 } else {
3071 duration.sec = duration.msec = 0;
3072 con_delay.sec = con_delay.msec = 0;
3073 }
3074
3075 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00003076 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003077 res_delay = call->res_time;
3078 PJ_TIME_VAL_SUB(res_delay, call->start_time);
3079 } else {
3080 res_delay.sec = res_delay.msec = 0;
3081 }
3082
3083 /* Print duration */
3084 len = pj_ansi_snprintf(p, end-p,
3085 "%s Call time: %02dh:%02dm:%02ds, "
3086 "1st res in %d ms, conn in %dms",
3087 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00003088 (int)(duration.sec / 3600),
3089 (int)((duration.sec % 3600)/60),
3090 (int)(duration.sec % 60),
3091 (int)PJ_TIME_VAL_MSEC(res_delay),
3092 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003093
3094 if (len > 0 && len < end-p) {
3095 p += len;
3096 *p++ = '\n';
3097 *p = '\0';
3098 }
3099
3100 /* Dump session statistics */
Benny Prijono0bc99a92011-03-17 04:34:43 +00003101 if (with_media && pjsua_call_has_media(call_id))
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00003102 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003103
Benny Prijonodc752ca2006-09-22 16:55:42 +00003104 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003105
3106 return PJ_SUCCESS;
3107}
3108
Benny Prijono1e601552010-10-20 05:31:08 +00003109/* Proto */
3110static pj_status_t perform_lock_codec(pjsua_call *call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003111
Benny Prijono1e601552010-10-20 05:31:08 +00003112/* Timer callback to send re-INVITE or UPDATE to lock codec */
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003113static void reinv_timer_cb(pj_timer_heap_t *th,
3114 pj_timer_entry *entry)
3115{
3116 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
3117 pjsip_dialog *dlg;
3118 pjsua_call *call;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003119 pj_status_t status;
3120
3121 PJ_UNUSED_ARG(th);
3122
3123 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
3124
3125 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
3126 if (status != PJ_SUCCESS)
3127 return;
3128
Benny Prijono1e601552010-10-20 05:31:08 +00003129 status = perform_lock_codec(call);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003130
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003131 pjsip_dlg_dec_lock(dlg);
3132}
3133
3134
3135/* Check if the specified format can be skipped in counting codecs */
3136static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
3137 const pj_str_t *fmt)
3138{
Benny Prijono1e601552010-10-20 05:31:08 +00003139 const pj_str_t STR_TEL = {"telephone-event", 15};
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003140 unsigned pt;
3141
3142 pt = pj_strtoul(fmt);
3143
3144 /* Check for comfort noise */
3145 if (pt == PJMEDIA_RTP_PT_CN)
3146 return PJ_TRUE;
3147
3148 /* Dynamic PT, check the format name */
3149 if (pt >= 96) {
3150 pjmedia_sdp_attr *a;
3151 pjmedia_sdp_rtpmap rtpmap;
3152
3153 /* Get the format name */
3154 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3155 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
3156 /* Check for telephone-event */
Benny Prijono1e601552010-10-20 05:31:08 +00003157 if (pj_stricmp(&rtpmap.enc_name, &STR_TEL)==0)
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003158 return PJ_TRUE;
3159 } else {
3160 /* Invalid SDP, should not reach here */
3161 pj_assert(!"SDP should have been validated!");
3162 return PJ_TRUE;
3163 }
3164 }
3165
3166 return PJ_FALSE;
3167}
3168
3169
Benny Prijono1e601552010-10-20 05:31:08 +00003170/* Send re-INVITE or UPDATE with new SDP offer to select only one codec
3171 * out of several codecs presented by callee in his answer.
3172 */
3173static pj_status_t perform_lock_codec(pjsua_call *call)
3174{
3175 const pj_str_t STR_UPDATE = {"UPDATE", 6};
3176 const pjmedia_sdp_session *local_sdp = NULL, *new_sdp;
Nanang Izzuddinec919002010-11-25 09:27:06 +00003177 const pjmedia_sdp_media *ref_m;
Benny Prijono1e601552010-10-20 05:31:08 +00003178 pjmedia_sdp_media *m;
Benny Prijono0bc99a92011-03-17 04:34:43 +00003179 pjsua_call_media *call_med = &call->media[call->audio_idx];
Benny Prijono1e601552010-10-20 05:31:08 +00003180 unsigned i, codec_cnt = 0;
3181 pj_bool_t rem_can_update;
3182 pjsip_tx_data *tdata;
3183 pj_status_t status;
3184
3185 PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE,
3186 PJ_EINVALIDOP);
3187
3188 /* Verify if another SDP negotiation is in progress, e.g: session timer
3189 * or another re-INVITE.
3190 */
3191 if (call->inv==NULL || call->inv->neg==NULL ||
3192 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
3193 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00003194 return PJMEDIA_SDPNEG_EINSTATE;
Benny Prijono1e601552010-10-20 05:31:08 +00003195 }
3196
Benny Prijono02493272010-11-17 09:15:04 +00003197 /* Don't do this if call is disconnecting! */
3198 if (call->inv->state > PJSIP_INV_STATE_CONFIRMED ||
3199 call->inv->cause >= 200)
3200 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00003201 return PJ_EINVALIDOP;
Benny Prijono02493272010-11-17 09:15:04 +00003202 }
3203
Benny Prijono1e601552010-10-20 05:31:08 +00003204 /* Verify if another SDP negotiation has been completed by comparing
3205 * the SDP version.
3206 */
3207 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
3208 if (status != PJ_SUCCESS)
3209 return status;
3210 if (local_sdp->origin.version > call->lock_codec.sdp_ver)
3211 return PJMEDIA_SDP_EINVER;
3212
Nanang Izzuddinec919002010-11-25 09:27:06 +00003213 /* Verify if media is deactivated */
Benny Prijono0bc99a92011-03-17 04:34:43 +00003214 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
3215 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
3216 call_med->dir == PJMEDIA_DIR_NONE)
Nanang Izzuddinec919002010-11-25 09:27:06 +00003217 {
3218 return PJ_EINVALIDOP;
3219 }
3220
Benny Prijono1e601552010-10-20 05:31:08 +00003221 PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec.."));
3222
3223 /* Update the new offer so it contains only a codec. Note that formats
3224 * order in the offer should have been matched to the answer, so we can
3225 * just directly update the offer without looking-up the answer.
3226 */
3227 new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp);
Benny Prijono1e601552010-10-20 05:31:08 +00003228 m = new_sdp->media[call->audio_idx];
Nanang Izzuddinec919002010-11-25 09:27:06 +00003229 ref_m = local_sdp->media[call->audio_idx];
3230 pj_assert(ref_m->desc.port);
Benny Prijono1e601552010-10-20 05:31:08 +00003231 codec_cnt = 0;
3232 i = 0;
3233 while (i < m->desc.fmt_count) {
3234 pjmedia_sdp_attr *a;
3235 pj_str_t *fmt = &m->desc.fmt[i];
3236
3237 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
3238 ++i;
3239 continue;
3240 }
3241
3242 /* Remove format */
3243 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3244 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3245 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
3246 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3247 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
3248 m->desc.fmt_count, i);
3249 --m->desc.fmt_count;
3250 }
3251
Nanang Izzuddinec919002010-11-25 09:27:06 +00003252 /* Last check if SDP trully needs to be updated. It is possible that OA
3253 * negotiations have completed and SDP has changed but we didn't
3254 * increase the SDP version (should not happen!).
Benny Prijono1e601552010-10-20 05:31:08 +00003255 */
Nanang Izzuddinec919002010-11-25 09:27:06 +00003256 if (ref_m->desc.fmt_count == m->desc.fmt_count)
Benny Prijono1e601552010-10-20 05:31:08 +00003257 return PJ_SUCCESS;
Benny Prijono1e601552010-10-20 05:31:08 +00003258
3259 /* Send UPDATE or re-INVITE */
3260 rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg,
3261 PJSIP_H_ALLOW,
3262 NULL, &STR_UPDATE) ==
3263 PJSIP_DIALOG_CAP_SUPPORTED;
3264 if (rem_can_update) {
3265 status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata);
3266 } else {
3267 status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata);
3268 }
3269
3270 if (status==PJ_EINVALIDOP &&
3271 ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY)
3272 {
3273 /* Ups, let's reschedule again */
3274 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
3275 pj_time_val_normalize(&delay);
3276 call->lock_codec.reinv_timer.id = PJ_TRUE;
3277 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3278 &call->lock_codec.reinv_timer, &delay);
3279 return status;
3280 } else if (status != PJ_SUCCESS) {
3281 pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec",
3282 status);
3283 return status;
3284 }
3285
3286 /* Send the UPDATE/re-INVITE request */
3287 status = pjsip_inv_send_msg(call->inv, tdata);
3288 if (status != PJ_SUCCESS) {
3289 pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec",
3290 status);
3291 return status;
3292 }
3293
3294 return status;
3295}
3296
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003297/* Check if remote answerer has given us more than one codecs. If so,
3298 * create another offer with one codec only to lock down the codec.
3299 */
3300static pj_status_t lock_codec(pjsua_call *call)
3301{
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003302 pjsip_inv_session *inv = call->inv;
Nanang Izzuddinec919002010-11-25 09:27:06 +00003303 const pjmedia_sdp_session *local_sdp, *remote_sdp;
3304 const pjmedia_sdp_media *rem_m, *loc_m;
Benny Prijono1e601552010-10-20 05:31:08 +00003305 unsigned codec_cnt=0, i;
Benny Prijono0bc99a92011-03-17 04:34:43 +00003306 pjsua_call_media *call_med = &call->media[call->audio_idx];
Benny Prijono1e601552010-10-20 05:31:08 +00003307 pj_time_val delay = {0, 0};
Nanang Izzuddinec919002010-11-25 09:27:06 +00003308 const pj_str_t st_update = {"UPDATE", 6};
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003309 pj_status_t status;
3310
Nanang Izzuddinec919002010-11-25 09:27:06 +00003311 /* Stop lock codec timer, if it is active */
3312 if (call->lock_codec.reinv_timer.id) {
3313 pjsip_endpt_cancel_timer(pjsua_var.endpt,
3314 &call->lock_codec.reinv_timer);
3315 call->lock_codec.reinv_timer.id = PJ_FALSE;
3316 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003317
Nanang Izzuddinec919002010-11-25 09:27:06 +00003318 /* Skip this if we are the answerer */
3319 if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
3320 return PJ_SUCCESS;
3321 }
3322
3323 /* Skip this if the media is inactive or error */
Benny Prijono0bc99a92011-03-17 04:34:43 +00003324 if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
3325 call_med->state == PJSUA_CALL_MEDIA_ERROR ||
3326 call_med->dir == PJMEDIA_DIR_NONE)
Nanang Izzuddinec919002010-11-25 09:27:06 +00003327 {
3328 return PJ_SUCCESS;
3329 }
3330
3331 /* Delay this when the SDP negotiation done in call state EARLY and
3332 * remote does not support UPDATE method.
3333 */
3334 if (inv->state == PJSIP_INV_STATE_EARLY &&
3335 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!=
3336 PJSIP_DIALOG_CAP_SUPPORTED)
3337 {
3338 call->lock_codec.pending = PJ_TRUE;
3339 return PJ_SUCCESS;
3340 }
3341
3342 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003343 if (status != PJ_SUCCESS)
3344 return status;
Nanang Izzuddinec919002010-11-25 09:27:06 +00003345 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003346 if (status != PJ_SUCCESS)
3347 return status;
3348
3349 PJ_ASSERT_RETURN(call->audio_idx>=0 &&
3350 call->audio_idx < (int)remote_sdp->media_count,
3351 PJ_EINVALIDOP);
3352
3353 rem_m = remote_sdp->media[call->audio_idx];
Nanang Izzuddinec919002010-11-25 09:27:06 +00003354 loc_m = local_sdp->media[call->audio_idx];
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003355
Nanang Izzuddinec919002010-11-25 09:27:06 +00003356 /* Verify that media must be active. */
3357 pj_assert(loc_m->desc.port && rem_m->desc.port);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003358
3359 /* Count the formats in the answer. */
Nanang Izzuddinec919002010-11-25 09:27:06 +00003360 if (rem_m->desc.fmt_count==1) {
3361 codec_cnt = 1;
3362 } else {
3363 for (i=0; i<rem_m->desc.fmt_count && codec_cnt <= 1; ++i) {
3364 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[i]))
3365 ++codec_cnt;
3366 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003367 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003368 if (codec_cnt <= 1) {
3369 /* Answer contains single codec. */
Nanang Izzuddinec919002010-11-25 09:27:06 +00003370 call->lock_codec.retry_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003371 return PJ_SUCCESS;
3372 }
3373
Nanang Izzuddinec919002010-11-25 09:27:06 +00003374 /* Remote keeps answering with multiple codecs, let's just give up
3375 * locking codec to avoid infinite retry loop.
3376 */
3377 if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY)
3378 return PJ_SUCCESS;
3379
Benny Prijono1e601552010-10-20 05:31:08 +00003380 PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, scheduling "
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003381 "updating media session to use only one codec.."));
3382
Benny Prijono1e601552010-10-20 05:31:08 +00003383 call->lock_codec.sdp_ver = local_sdp->origin.version;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003384
Benny Prijono1e601552010-10-20 05:31:08 +00003385 /* Can't send UPDATE or re-INVITE now, so just schedule it immediately.
3386 * See: https://trac.pjsip.org/repos/ticket/1149
3387 */
3388 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
3389 (void*)(pj_size_t)call->index,
3390 &reinv_timer_cb);
3391 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3392 &call->lock_codec.reinv_timer, &delay);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003393
3394 return PJ_SUCCESS;
3395}
3396
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003397/*
Benny Prijono84126ab2006-02-09 09:30:09 +00003398 * This callback receives notification from invite session when the
3399 * session state has changed.
3400 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003401static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
3402 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00003403{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003404 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003405
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003406 PJSUA_LOCK();
3407
Benny Prijonoa1e69682007-05-11 15:14:34 +00003408 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003409
3410 if (!call) {
3411 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00003412 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003413 }
3414
Benny Prijonoe21e7842006-04-09 16:46:05 +00003415
3416 /* Get call times */
3417 switch (inv->state) {
3418 case PJSIP_INV_STATE_EARLY:
3419 case PJSIP_INV_STATE_CONNECTING:
3420 if (call->res_time.sec == 0)
3421 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00003422 call->last_code = (pjsip_status_code)
3423 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003424 pj_strncpy(&call->last_text,
3425 &e->body.tsx_state.tsx->status_text,
3426 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00003427 break;
3428 case PJSIP_INV_STATE_CONFIRMED:
3429 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003430
Nanang Izzuddinec919002010-11-25 09:27:06 +00003431 /* See if lock codec was pended as media update was done in the
3432 * EARLY state and remote does not support UPDATE.
3433 */
3434 if (call->lock_codec.pending) {
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003435 pj_status_t status;
3436 status = lock_codec(call);
3437 if (status != PJ_SUCCESS) {
3438 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddinec919002010-11-25 09:27:06 +00003439 }
3440 call->lock_codec.pending = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003441 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00003442 break;
3443 case PJSIP_INV_STATE_DISCONNECTED:
3444 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00003445 if (call->res_time.sec == 0)
3446 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003447 if (e->type == PJSIP_EVENT_TSX_STATE &&
3448 e->body.tsx_state.tsx->status_code > call->last_code)
3449 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00003450 call->last_code = (pjsip_status_code)
3451 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003452 pj_strncpy(&call->last_text,
3453 &e->body.tsx_state.tsx->status_text,
3454 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003455 } else {
3456 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
3457 pj_strncpy(&call->last_text,
3458 pjsip_get_status_text(call->last_code),
3459 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00003460 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003461
3462 /* Stop lock codec timer, if it is active */
3463 if (call->lock_codec.reinv_timer.id) {
3464 pjsip_endpt_cancel_timer(pjsua_var.endpt,
3465 &call->lock_codec.reinv_timer);
3466 call->lock_codec.reinv_timer.id = PJ_FALSE;
3467 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00003468 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003469 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00003470 call->last_code = (pjsip_status_code)
3471 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003472 pj_strncpy(&call->last_text,
3473 &e->body.tsx_state.tsx->status_text,
3474 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00003475 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00003476 }
3477
Benny Prijono26ff9062006-02-21 23:47:00 +00003478 /* If this is an outgoing INVITE that was created because of
3479 * REFER/transfer, send NOTIFY to transferer.
3480 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00003481 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003482 int st_code = -1;
3483 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
3484
3485
Benny Prijonoa91a0032006-02-26 21:23:45 +00003486 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003487 case PJSIP_INV_STATE_NULL:
3488 case PJSIP_INV_STATE_CALLING:
3489 /* Do nothing */
3490 break;
3491
3492 case PJSIP_INV_STATE_EARLY:
3493 case PJSIP_INV_STATE_CONNECTING:
3494 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003495 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
3496 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3497 else
3498 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003499 break;
3500
Benny Prijono140beae2009-10-11 05:06:43 +00003501 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003502#if 0
3503/* We don't need this, as we've terminated the subscription in
3504 * CONNECTING state.
3505 */
Benny Prijono26ff9062006-02-21 23:47:00 +00003506 /* When state is confirmed, send the final 200/OK and terminate
3507 * subscription.
3508 */
3509 st_code = e->body.tsx_state.tsx->status_code;
3510 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003511#endif
Benny Prijono140beae2009-10-11 05:06:43 +00003512 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003513
3514 case PJSIP_INV_STATE_DISCONNECTED:
3515 st_code = e->body.tsx_state.tsx->status_code;
3516 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3517 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003518
Benny Prijono8b1889b2006-06-06 18:40:40 +00003519 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00003520 /* Nothing to do. Just to keep gcc from complaining about
3521 * unused enums.
3522 */
3523 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003524 }
3525
3526 if (st_code != -1) {
3527 pjsip_tx_data *tdata;
3528 pj_status_t status;
3529
Benny Prijonoa91a0032006-02-26 21:23:45 +00003530 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00003531 ev_state, st_code,
3532 NULL, &tdata);
3533 if (status != PJ_SUCCESS) {
3534 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
3535 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003536 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00003537 if (status != PJ_SUCCESS) {
3538 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
3539 }
3540 }
3541 }
3542 }
3543
Benny Prijono84126ab2006-02-09 09:30:09 +00003544
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003545 if (pjsua_var.ua_cfg.cb.on_call_state)
3546 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003547
3548 /* call->inv may be NULL now */
3549
Benny Prijono84126ab2006-02-09 09:30:09 +00003550 /* Destroy media session when invite session is disconnected. */
3551 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003552
Benny Prijonoa91a0032006-02-26 21:23:45 +00003553 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003554
Benny Prijono275fd682006-03-22 11:59:11 +00003555 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003556 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003557
Benny Prijono105217f2006-03-06 16:25:59 +00003558 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003559 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003560 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003561
3562 /* Reset call */
3563 reset_call(call->index);
3564
Benny Prijono84126ab2006-02-09 09:30:09 +00003565 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003566
3567 PJSUA_UNLOCK();
3568}
3569
3570/*
3571 * This callback is called by invite session framework when UAC session
3572 * has forked.
3573 */
3574static void pjsua_call_on_forked( pjsip_inv_session *inv,
3575 pjsip_event *e)
3576{
3577 PJ_UNUSED_ARG(inv);
3578 PJ_UNUSED_ARG(e);
3579
3580 PJ_TODO(HANDLE_FORKED_DIALOG);
3581}
3582
3583
3584/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003585 * Callback from UA layer when forked dialog response is received.
3586 */
3587pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3588{
3589 if (dlg->uac_has_2xx &&
3590 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3591 pjsip_rdata_get_tsx(res) == NULL &&
3592 res->msg_info.msg->line.status.code/100 == 2)
3593 {
3594 pjsip_dialog *forked_dlg;
3595 pjsip_tx_data *bye;
3596 pj_status_t status;
3597
3598 /* Create forked dialog */
3599 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3600 if (status != PJ_SUCCESS)
3601 return NULL;
3602
3603 pjsip_dlg_inc_lock(forked_dlg);
3604
3605 /* Disconnect the call */
3606 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3607 -1, &bye);
3608 if (status == PJ_SUCCESS) {
3609 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3610 }
3611
3612 pjsip_dlg_dec_lock(forked_dlg);
3613
3614 if (status != PJ_SUCCESS) {
3615 return NULL;
3616 }
3617
3618 return forked_dlg;
3619
3620 } else {
3621 return dlg;
3622 }
3623}
3624
3625/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003626 * Disconnect call upon error.
3627 */
3628static void call_disconnect( pjsip_inv_session *inv,
3629 int code )
3630{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003631 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003632 pjsip_tx_data *tdata;
3633 pj_status_t status;
3634
Benny Prijono59b3aed2008-01-15 16:54:54 +00003635 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3636
Benny Prijonoa38ada02006-07-02 14:22:35 +00003637 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003638 if (status != PJ_SUCCESS)
3639 return;
3640
3641 /* Add SDP in 488 status */
Benny Prijono0bc99a92011-03-17 04:34:43 +00003642#if DISABLED_FOR_TICKET_1185
3643 if (call && call->tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
Benny Prijono99639982008-03-07 14:39:52 +00003644 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3645 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003646 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003647 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003648
Benny Prijono734fc2d2008-03-17 16:05:35 +00003649 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003650 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003651 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003652 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003653 if (status == PJ_SUCCESS) {
3654 pjsip_create_sdp_body(tdata->pool, local_sdp,
3655 &tdata->msg->body);
3656 }
3657 }
Benny Prijono0bc99a92011-03-17 04:34:43 +00003658#endif
Benny Prijono59b3aed2008-01-15 16:54:54 +00003659
3660 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003661}
3662
3663/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003664 * Callback to be called when SDP offer/answer negotiation has just completed
3665 * in the session. This function will start/update media if negotiation
3666 * has succeeded.
3667 */
3668static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3669 pj_status_t status)
3670{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003671 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003672 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003673 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddinec919002010-11-25 09:27:06 +00003674 //const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003675
3676 PJSUA_LOCK();
3677
Benny Prijonoa1e69682007-05-11 15:14:34 +00003678 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003679
3680 if (status != PJ_SUCCESS) {
3681
3682 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3683
Benny Prijono2331d202008-06-26 15:46:52 +00003684 /* Do not deinitialize media since this may be a re-INVITE or
3685 * UPDATE (which in this case the media should not get affected
3686 * by the failed re-INVITE/UPDATE). The media will be shutdown
3687 * when call is disconnected anyway.
3688 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003689 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003690 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003691
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003692 /* Disconnect call if we're not in the middle of initializing an
3693 * UAS dialog and if this is not a re-INVITE
3694 */
3695 if (inv->state != PJSIP_INV_STATE_NULL &&
3696 inv->state != PJSIP_INV_STATE_CONFIRMED)
3697 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003698 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003699 }
3700
3701 PJSUA_UNLOCK();
3702 return;
3703 }
3704
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003705
3706 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003707 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003708 if (status != PJ_SUCCESS) {
3709 pjsua_perror(THIS_FILE,
3710 "Unable to retrieve currently active local SDP",
3711 status);
3712 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3713 PJSUA_UNLOCK();
3714 return;
3715 }
3716
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003717 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3718 if (status != PJ_SUCCESS) {
3719 pjsua_perror(THIS_FILE,
3720 "Unable to retrieve currently active remote SDP",
3721 status);
3722 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3723 PJSUA_UNLOCK();
3724 return;
3725 }
3726
Benny Prijono91a6a172007-10-31 08:59:29 +00003727 /* Update remote's NAT type */
3728 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3729 update_remote_nat_type(call, remote_sdp);
3730 }
3731
3732 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003733 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003734 if (status != PJ_SUCCESS) {
3735 pjsua_perror(THIS_FILE, "Unable to create media session",
3736 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003737 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003738 /* No need to deinitialize; media will be shutdown when call
3739 * state is disconnected anyway.
3740 */
3741 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003742 PJSUA_UNLOCK();
3743 return;
3744 }
3745
Nanang Izzuddinec919002010-11-25 09:27:06 +00003746 /* Ticket #476: make sure only one codec is specified in the answer. */
3747 status = lock_codec(call);
3748 if (status != PJ_SUCCESS) {
3749 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003750 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003751
3752 /* Call application callback, if any */
3753 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3754 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3755
3756
3757 PJSUA_UNLOCK();
3758}
3759
3760
Benny Prijonodd63b992010-10-01 02:03:42 +00003761/* Modify SDP for call hold. */
3762static pj_status_t modify_sdp_of_call_hold(pjsua_call *call,
3763 pj_pool_t *pool,
3764 pjmedia_sdp_session *sdp)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003765{
Benny Prijono316f02a2011-04-07 07:53:25 +00003766 unsigned mi;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003767
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003768 /* Call-hold is done by set the media direction to 'sendonly'
3769 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3770 * 'inactive' (PJMEDIA_DIR_NONE).
3771 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3772 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003773 /* http://trac.pjsip.org/repos/ticket/880
Benny Prijono0bc99a92011-03-17 04:34:43 +00003774 if (call->dir != PJMEDIA_DIR_ENCODING) {
Benny Prijonoe0860132009-06-05 10:14:20 +00003775 */
Benny Prijonodd63b992010-10-01 02:03:42 +00003776 /* https://trac.pjsip.org/repos/ticket/1142:
3777 * configuration to use c=0.0.0.0 for call hold.
3778 */
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003779
Benny Prijono316f02a2011-04-07 07:53:25 +00003780 for (mi=0; mi<sdp->media_count; ++mi) {
3781 pjmedia_sdp_media *m = sdp->media[mi];
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003782
Benny Prijono316f02a2011-04-07 07:53:25 +00003783 if (call->call_hold_type == PJSUA_CALL_HOLD_TYPE_RFC2543) {
3784 pjmedia_sdp_conn *conn;
3785 pjmedia_sdp_attr *attr;
Benny Prijonodd63b992010-10-01 02:03:42 +00003786
Benny Prijono316f02a2011-04-07 07:53:25 +00003787 /* Get SDP media connection line */
3788 conn = m->conn;
3789 if (!conn)
3790 conn = sdp->conn;
Benny Prijonodd63b992010-10-01 02:03:42 +00003791
Benny Prijono316f02a2011-04-07 07:53:25 +00003792 /* Modify address */
3793 conn->addr = pj_str("0.0.0.0");
Benny Prijonodd63b992010-10-01 02:03:42 +00003794
Benny Prijono316f02a2011-04-07 07:53:25 +00003795 /* Remove existing directions attributes */
3796 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3797 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3798 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3799 pjmedia_sdp_media_remove_all_attr(m, "inactive");
Benny Prijonodd63b992010-10-01 02:03:42 +00003800
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003801 /* Add inactive attribute */
3802 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003803 pjmedia_sdp_media_add_attr(m, attr);
Benny Prijono316f02a2011-04-07 07:53:25 +00003804
3805
3806 } else {
3807 pjmedia_sdp_attr *attr;
3808
3809 /* Remove existing directions attributes */
3810 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3811 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3812 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3813 pjmedia_sdp_media_remove_all_attr(m, "inactive");
3814
3815 if (call->media[mi].dir & PJMEDIA_DIR_ENCODING) {
3816 /* Add sendonly attribute */
3817 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
3818 pjmedia_sdp_media_add_attr(m, attr);
3819 } else {
3820 /* Add inactive attribute */
3821 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
3822 pjmedia_sdp_media_add_attr(m, attr);
3823 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003824 }
3825 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003826
Benny Prijonodd63b992010-10-01 02:03:42 +00003827 return PJ_SUCCESS;
3828}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003829
Benny Prijonodd63b992010-10-01 02:03:42 +00003830/* Create SDP for call hold. */
3831static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3832 pjmedia_sdp_session **p_sdp)
3833{
3834 pj_status_t status;
3835 pj_pool_t *pool;
3836 pjmedia_sdp_session *sdp;
3837
3838 /* Use call's provisional pool */
3839 pool = call->inv->pool_prov;
3840
3841 /* Create new offer */
3842 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3843 NULL);
3844 if (status != PJ_SUCCESS) {
3845 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3846 return status;
3847 }
3848
3849 status = modify_sdp_of_call_hold(call, pool, sdp);
3850 if (status != PJ_SUCCESS)
3851 return status;
3852
3853 *p_sdp = sdp;
3854
3855 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003856}
3857
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003858/*
3859 * Called when session received new offer.
3860 */
3861static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3862 const pjmedia_sdp_session *offer)
3863{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003864 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003865 pjmedia_sdp_session *answer;
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003866 unsigned i;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003867 pj_status_t status;
3868
3869 PJSUA_LOCK();
3870
Benny Prijonoa1e69682007-05-11 15:14:34 +00003871 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003872
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003873 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003874 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3875 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003876
Benny Prijono40d62b62009-08-12 17:53:47 +00003877 status = pjsua_media_channel_create_sdp(call->index,
3878 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003879 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003880 if (status != PJ_SUCCESS) {
3881 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3882 PJSUA_UNLOCK();
3883 return;
3884 }
3885
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003886 /* Validate media count in the generated answer */
3887 pj_assert(answer->media_count == offer->media_count);
3888
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003889 /* Check if offer's conn address is zero */
Nanang Izzuddine03faad2011-04-07 14:51:28 +00003890 for (i = 0; i < answer->media_count; ++i) {
3891 pjmedia_sdp_conn *conn;
3892
3893 conn = offer->media[i]->conn;
3894 if (!conn)
3895 conn = offer->conn;
3896
3897 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3898 pj_strcmp2(&conn->addr, "0")==0)
3899 {
3900 pjmedia_sdp_conn *a_conn = answer->media[i]->conn;
3901
3902 /* Modify answer address */
3903 if (a_conn) {
3904 a_conn->addr = pj_str("0.0.0.0");
3905 } else if (answer->conn == NULL ||
3906 pj_strcmp2(&answer->conn->addr, "0.0.0.0") != 0)
3907 {
3908 a_conn = PJ_POOL_ZALLOC_T(call->inv->pool_prov,
3909 pjmedia_sdp_conn);
3910 a_conn->net_type = pj_str("IN");
3911 a_conn->addr_type = pj_str("IP4");
3912 a_conn->addr = pj_str("0.0.0.0");
3913 answer->media[i]->conn = a_conn;
3914 }
3915 }
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003916 }
3917
3918 /* Check if call is on-hold */
3919 if (call->local_hold) {
Benny Prijonodd63b992010-10-01 02:03:42 +00003920 modify_sdp_of_call_hold(call, call->inv->pool_prov, answer);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003921 }
3922
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003923 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3924 if (status != PJ_SUCCESS) {
3925 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3926 PJSUA_UNLOCK();
3927 return;
3928 }
3929
3930 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003931}
3932
3933
3934/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003935 * Called to generate new offer.
3936 */
3937static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3938 pjmedia_sdp_session **offer)
3939{
3940 pjsua_call *call;
3941 pj_status_t status;
3942
3943 PJSUA_LOCK();
3944
3945 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3946
3947 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003948 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003949 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003950 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003951 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003952 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003953 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003954 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3955 call->index));
3956
Benny Prijono40d62b62009-08-12 17:53:47 +00003957 status = pjsua_media_channel_create_sdp(call->index,
3958 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003959 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003960 }
3961
3962 if (status != PJ_SUCCESS) {
3963 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3964 PJSUA_UNLOCK();
3965 return;
3966 }
3967
Benny Prijono77998ce2007-06-20 10:03:46 +00003968 PJSUA_UNLOCK();
3969}
3970
3971
3972/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003973 * Callback called by event framework when the xfer subscription state
3974 * has changed.
3975 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003976static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3977{
3978
3979 PJ_UNUSED_ARG(event);
3980
3981 /*
3982 * When subscription is accepted (got 200/OK to REFER), check if
3983 * subscription suppressed.
3984 */
3985 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3986
3987 pjsip_rx_data *rdata;
3988 pjsip_generic_string_hdr *refer_sub;
3989 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3990 pjsua_call *call;
3991
Benny Prijonoa1e69682007-05-11 15:14:34 +00003992 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003993
3994 /* Must be receipt of response message */
3995 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3996 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3997 rdata = event->body.tsx_state.src.rdata;
3998
3999 /* Find Refer-Sub header */
4000 refer_sub = (pjsip_generic_string_hdr*)
4001 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
4002 &REFER_SUB, NULL);
4003
4004 /* Check if subscription is suppressed */
4005 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
4006 /* Since no subscription is desired, assume that call has been
4007 * transfered successfully.
4008 */
4009 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
4010 const pj_str_t ACCEPTED = { "Accepted", 8 };
4011 pj_bool_t cont = PJ_FALSE;
4012 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
4013 200,
4014 &ACCEPTED,
4015 PJ_TRUE,
4016 &cont);
4017 }
4018
4019 /* Yes, subscription is suppressed.
4020 * Terminate our subscription now.
4021 */
4022 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
4023 "event subcription..."));
4024 pjsip_evsub_terminate(sub, PJ_TRUE);
4025
4026 } else {
4027 /* Notify application about call transfer progress.
4028 * Initially notify with 100/Accepted status.
4029 */
4030 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
4031 const pj_str_t ACCEPTED = { "Accepted", 8 };
4032 pj_bool_t cont = PJ_FALSE;
4033 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
4034 100,
4035 &ACCEPTED,
4036 PJ_FALSE,
4037 &cont);
4038 }
4039 }
4040 }
4041 /*
4042 * On incoming NOTIFY, notify application about call transfer progress.
4043 */
4044 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
4045 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
4046 {
4047 pjsua_call *call;
4048 pjsip_msg *msg;
4049 pjsip_msg_body *body;
4050 pjsip_status_line status_line;
4051 pj_bool_t is_last;
4052 pj_bool_t cont;
4053 pj_status_t status;
4054
Benny Prijonoa1e69682007-05-11 15:14:34 +00004055 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004056
4057 /* When subscription is terminated, clear the xfer_sub member of
4058 * the inv_data.
4059 */
4060 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
4061 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
4062 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
4063
4064 }
4065
4066 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
4067 /* Application is not interested with call progress status */
4068 return;
4069 }
4070
4071 /* This better be a NOTIFY request */
4072 if (event->type == PJSIP_EVENT_TSX_STATE &&
4073 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
4074 {
4075 pjsip_rx_data *rdata;
4076
4077 rdata = event->body.tsx_state.src.rdata;
4078
4079 /* Check if there's body */
4080 msg = rdata->msg_info.msg;
4081 body = msg->body;
4082 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00004083 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004084 "Warning: received NOTIFY without message body"));
4085 return;
4086 }
4087
4088 /* Check for appropriate content */
4089 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
4090 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
4091 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00004092 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004093 "Warning: received NOTIFY with non message/sipfrag "
4094 "content"));
4095 return;
4096 }
4097
4098 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00004099 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004100 &status_line);
4101 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00004102 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004103 "Warning: received NOTIFY with invalid "
4104 "message/sipfrag content"));
4105 return;
4106 }
4107
4108 } else {
4109 status_line.code = 500;
4110 status_line.reason = *pjsip_get_status_text(500);
4111 }
4112
4113 /* Notify application */
4114 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
4115 cont = !is_last;
4116 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
4117 status_line.code,
4118 &status_line.reason,
4119 is_last, &cont);
4120
4121 if (!cont) {
4122 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
4123 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00004124
4125 /* If the call transfer has completed but the subscription is
4126 * not terminated, terminate it now.
4127 */
4128 if (status_line.code/100 == 2 && !is_last) {
4129 pjsip_tx_data *tdata;
4130
4131 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
4132 0, &tdata);
4133 if (status == PJ_SUCCESS)
4134 status = pjsip_evsub_send_request(sub, tdata);
4135 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004136 }
4137}
4138
4139
4140/*
4141 * Callback called by event framework when the xfer subscription state
4142 * has changed.
4143 */
4144static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00004145{
4146
4147 PJ_UNUSED_ARG(event);
4148
4149 /*
Benny Prijonod524e822006-09-22 12:48:18 +00004150 * When subscription is terminated, clear the xfer_sub member of
4151 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00004152 */
4153 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00004154 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00004155
Benny Prijonoa1e69682007-05-11 15:14:34 +00004156 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00004157 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00004158 return;
4159
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004160 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00004161 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00004162
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004163 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00004164 }
4165}
4166
4167
4168/*
4169 * Follow transfer (REFER) request.
4170 */
4171static void on_call_transfered( pjsip_inv_session *inv,
4172 pjsip_rx_data *rdata )
4173{
4174 pj_status_t status;
4175 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00004176 pjsua_call *existing_call;
4177 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00004178 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00004179 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00004180 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00004181 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004182 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00004183 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004184 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00004185 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00004186 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00004187 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00004188 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00004189 pjsip_evsub *sub;
4190
Benny Prijonoa1e69682007-05-11 15:14:34 +00004191 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00004192
Benny Prijono26ff9062006-02-21 23:47:00 +00004193 /* Find the Refer-To header */
4194 refer_to = (pjsip_generic_string_hdr*)
4195 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
4196
4197 if (refer_to == NULL) {
4198 /* Invalid Request.
4199 * No Refer-To header!
4200 */
4201 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00004202 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00004203 return;
4204 }
4205
Benny Prijonoc8141a82006-08-20 09:12:19 +00004206 /* Find optional Refer-Sub header */
4207 refer_sub = (pjsip_generic_string_hdr*)
4208 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
4209
4210 if (refer_sub) {
4211 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
4212 no_refer_sub = PJ_TRUE;
4213 }
4214
Benny Prijono053f5222006-11-11 16:16:04 +00004215 /* Find optional Referred-By header (to be copied onto outgoing INVITE
4216 * request.
4217 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00004218 ref_by_hdr = (pjsip_hdr*)
4219 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00004220 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00004221
Benny Prijono9fc735d2006-05-28 14:58:12 +00004222 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004223 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004224 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
4225 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
4226 &refer_to->hvalue,
4227 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00004228
4229 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004230 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00004231 if (code >= 300) {
4232 /* Application rejects call transfer request */
4233 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
4234 return;
4235 }
4236
Benny Prijono26ff9062006-02-21 23:47:00 +00004237 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
4238 (int)inv->dlg->remote.info_str.slen,
4239 inv->dlg->remote.info_str.ptr,
4240 (int)refer_to->hvalue.slen,
4241 refer_to->hvalue.ptr));
4242
Benny Prijonoc8141a82006-08-20 09:12:19 +00004243 if (no_refer_sub) {
4244 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004245 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00004246 */
4247 pjsip_tx_data *tdata;
4248 const pj_str_t str_false = { "false", 5};
4249 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00004250
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004251 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
4252 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00004253 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004254 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00004255 status);
4256 return;
4257 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004258
Benny Prijonoc8141a82006-08-20 09:12:19 +00004259 /* Add Refer-Sub header */
4260 hdr = (pjsip_hdr*)
4261 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
4262 &str_false);
4263 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00004264
Benny Prijono26ff9062006-02-21 23:47:00 +00004265
Benny Prijonoc8141a82006-08-20 09:12:19 +00004266 /* Send answer */
4267 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
4268 tdata);
4269 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004270 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00004271 status);
4272 return;
4273 }
4274
4275 /* Don't have subscription */
4276 sub = NULL;
4277
4278 } else {
4279 struct pjsip_evsub_user xfer_cb;
4280 pjsip_hdr hdr_list;
4281
4282 /* Init callback */
4283 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004284 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004285
4286 /* Init additional header list to be sent with REFER response */
4287 pj_list_init(&hdr_list);
4288
4289 /* Create transferee event subscription */
4290 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
4291 if (status != PJ_SUCCESS) {
4292 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
4293 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
4294 return;
4295 }
4296
4297 /* If there's Refer-Sub header and the value is "true", send back
4298 * Refer-Sub in the response with value "true" too.
4299 */
4300 if (refer_sub) {
4301 const pj_str_t str_true = { "true", 4 };
4302 pjsip_hdr *hdr;
4303
4304 hdr = (pjsip_hdr*)
4305 pjsip_generic_string_hdr_create(inv->dlg->pool,
4306 &str_refer_sub,
4307 &str_true);
4308 pj_list_push_back(&hdr_list, hdr);
4309
4310 }
4311
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004312 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00004313 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
4314
4315 /* Create initial NOTIFY request */
4316 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
4317 100, NULL, &tdata);
4318 if (status != PJ_SUCCESS) {
4319 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4320 status);
4321 return;
4322 }
4323
4324 /* Send initial NOTIFY request */
4325 status = pjsip_xfer_send_request( sub, tdata);
4326 if (status != PJ_SUCCESS) {
4327 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
4328 return;
4329 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004330 }
4331
4332 /* We're cheating here.
4333 * We need to get a null terminated string from a pj_str_t.
4334 * So grab the pointer from the hvalue and NULL terminate it, knowing
4335 * that the NULL position will be occupied by a newline.
4336 */
4337 uri = refer_to->hvalue.ptr;
4338 uri[refer_to->hvalue.slen] = '\0';
4339
Benny Prijono053f5222006-11-11 16:16:04 +00004340 /* Init msg_data */
4341 pjsua_msg_data_init(&msg_data);
4342
4343 /* If Referred-By header is present in the REFER request, copy this
4344 * to the outgoing INVITE request.
4345 */
4346 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00004347 pjsip_hdr *dup = (pjsip_hdr*)
4348 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00004349 pj_list_push_back(&msg_data.hdr_list, dup);
4350 }
4351
Benny Prijono26ff9062006-02-21 23:47:00 +00004352 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00004353 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004354 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00004355 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004356 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00004357 if (status != PJ_SUCCESS) {
4358
Benny Prijonoc8141a82006-08-20 09:12:19 +00004359 /* Notify xferer about the error (if we have subscription) */
4360 if (sub) {
4361 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
4362 500, NULL, &tdata);
4363 if (status != PJ_SUCCESS) {
4364 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4365 status);
4366 return;
4367 }
4368 status = pjsip_xfer_send_request(sub, tdata);
4369 if (status != PJ_SUCCESS) {
4370 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
4371 status);
4372 return;
4373 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004374 }
4375 return;
4376 }
4377
Benny Prijonoc8141a82006-08-20 09:12:19 +00004378 if (sub) {
4379 /* Put the server subscription in inv_data.
4380 * Subsequent state changed in pjsua_inv_on_state_changed() will be
4381 * reported back to the server subscription.
4382 */
4383 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00004384
Benny Prijonoc8141a82006-08-20 09:12:19 +00004385 /* Put the invite_data in the subscription. */
4386 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
4387 &pjsua_var.calls[new_call]);
4388 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004389}
4390
4391
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004392
Benny Prijono26ff9062006-02-21 23:47:00 +00004393/*
4394 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00004395 * session. We use this to trap:
4396 * - incoming REFER request.
4397 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00004398 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004399static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
4400 pjsip_transaction *tsx,
4401 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00004402{
Benny Prijono2285e7e2008-12-17 14:28:18 +00004403 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004404
4405 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00004406
Benny Prijono2285e7e2008-12-17 14:28:18 +00004407 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
4408
4409 if (call == NULL) {
4410 PJSUA_UNLOCK();
4411 return;
4412 }
4413
Benny Prijono19eeb6e2009-06-04 22:16:47 +00004414 if (call->inv == NULL) {
4415 /* Shouldn't happen. It happens only when we don't terminate the
4416 * server subscription caused by REFER after the call has been
4417 * transfered (and this call has been disconnected), and we
4418 * receive another REFER for this call.
4419 */
4420 PJSUA_UNLOCK();
4421 return;
4422 }
4423
Benny Prijonofeb69f42007-10-05 09:12:26 +00004424 /* Notify application callback first */
4425 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
4426 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
4427 }
4428
Benny Prijono26ff9062006-02-21 23:47:00 +00004429 if (tsx->role==PJSIP_ROLE_UAS &&
4430 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00004431 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004432 {
4433 /*
4434 * Incoming REFER request.
4435 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004436 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00004437
Benny Prijono26ff9062006-02-21 23:47:00 +00004438 }
Benny Prijonob0808372006-03-02 21:18:58 +00004439 else if (tsx->role==PJSIP_ROLE_UAS &&
4440 tsx->state==PJSIP_TSX_STATE_TRYING &&
4441 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
4442 {
4443 /*
4444 * Incoming MESSAGE request!
4445 */
4446 pjsip_rx_data *rdata;
4447 pjsip_msg *msg;
4448 pjsip_accept_hdr *accept_hdr;
4449 pj_status_t status;
4450
4451 rdata = e->body.tsx_state.src.rdata;
4452 msg = rdata->msg_info.msg;
4453
4454 /* Request MUST have message body, with Content-Type equal to
4455 * "text/plain".
4456 */
4457 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
4458
4459 pjsip_hdr hdr_list;
4460
4461 pj_list_init(&hdr_list);
4462 pj_list_push_back(&hdr_list, accept_hdr);
4463
4464 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
4465 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004466 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00004467 return;
4468 }
4469
4470 /* Respond with 200 first, so that remote doesn't retransmit in case
4471 * the UI takes too long to process the message.
4472 */
4473 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
4474
4475 /* Process MESSAGE request */
4476 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
4477 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004478
Benny Prijonob0808372006-03-02 21:18:58 +00004479 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004480 else if (tsx->role == PJSIP_ROLE_UAC &&
4481 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004482 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004483 /* Handle outgoing pager status */
4484 if (tsx->status_code >= 200) {
4485 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00004486
Benny Prijonoa1e69682007-05-11 15:14:34 +00004487 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004488 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00004489
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004490 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
4491 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
4492 &im_data->to,
4493 &im_data->body,
4494 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00004495 (pjsip_status_code)
4496 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004497 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00004498 }
Benny Prijonofccab712006-02-22 22:23:22 +00004499 }
Benny Prijono834aee32006-02-19 01:38:06 +00004500 }
Benny Prijono834aee32006-02-19 01:38:06 +00004501
Benny Prijono26ff9062006-02-21 23:47:00 +00004502
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004503 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00004504}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004505
4506
4507/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00004508static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
4509 const pjsip_uri *target,
4510 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004511{
4512 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00004513 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004514
4515 PJSUA_LOCK();
4516
4517 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00004518 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
4519 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004520 } else {
4521 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
4522 "(callback not implemented by application). Disconnecting "
4523 "call.",
4524 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00004525 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004526 }
4527
4528 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00004529
4530 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004531}
4532