blob: 5a4ab2f72f20f22bb8364e028d842313af9b657c [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 Prijono105217f2006-03-06 16:25:59 +0000104
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000105 call->index = id;
106 call->inv = NULL;
107 call->user_data = NULL;
108 call->session = NULL;
Benny Prijonoa310bd22008-06-27 21:19:44 +0000109 call->audio_idx = -1;
Benny Prijono8147f402007-11-21 14:50:07 +0000110 call->ssrc = pj_rand();
Nanang Izzuddina815ceb2008-08-26 16:51:28 +0000111 call->rtp_tx_seq = 0;
112 call->rtp_tx_ts = 0;
113 call->rtp_tx_seq_ts_set = 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000114 call->xfer_sub = NULL;
Benny Prijonoba5926a2007-05-02 11:29:37 +0000115 call->last_code = (pjsip_status_code) 0;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000116 call->conf_slot = PJSUA_INVALID_ID;
117 call->last_text.ptr = call->last_text_buf_;
118 call->last_text.slen = 0;
Benny Prijono4be63b52006-11-25 14:50:25 +0000119 call->conn_time.sec = 0;
120 call->conn_time.msec = 0;
121 call->res_time.sec = 0;
122 call->res_time.msec = 0;
Benny Prijono91a6a172007-10-31 08:59:29 +0000123 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
Nanang Izzuddin4375f902008-06-26 19:12:09 +0000124 call->rem_srtp_use = PJMEDIA_SRTP_DISABLED;
Nanang Izzuddin99d69522008-08-04 15:01:38 +0000125 call->local_hold = PJ_FALSE;
Nanang Izzuddinec919002010-11-25 09:27:06 +0000126 pj_bzero(&call->lock_codec, sizeof(call->lock_codec));
Benny Prijono105217f2006-03-06 16:25:59 +0000127}
128
129
Benny Prijono275fd682006-03-22 11:59:11 +0000130/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000131 * Init call subsystem.
Benny Prijono275fd682006-03-22 11:59:11 +0000132 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000133pj_status_t pjsua_call_subsys_init(const pjsua_config *cfg)
Benny Prijono275fd682006-03-22 11:59:11 +0000134{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000135 pjsip_inv_callback inv_cb;
136 unsigned i;
Benny Prijonoc8141a82006-08-20 09:12:19 +0000137 const pj_str_t str_norefersub = { "norefersub", 10 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000138 pj_status_t status;
Benny Prijono275fd682006-03-22 11:59:11 +0000139
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000140 /* Init calls array. */
141 for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.calls); ++i)
142 reset_call(i);
Benny Prijono275fd682006-03-22 11:59:11 +0000143
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000144 /* Copy config */
145 pjsua_config_dup(pjsua_var.pool, &pjsua_var.ua_cfg, cfg);
Benny Prijono275fd682006-03-22 11:59:11 +0000146
Benny Prijono1cd713b2009-11-11 00:33:00 +0000147 /* Verify settings */
148 if (pjsua_var.ua_cfg.max_calls >= PJSUA_MAX_CALLS) {
149 pjsua_var.ua_cfg.max_calls = PJSUA_MAX_CALLS;
150 }
151
Benny Prijono91d06b62008-09-20 12:16:56 +0000152 /* Check the route URI's and force loose route if required */
153 for (i=0; i<pjsua_var.ua_cfg.outbound_proxy_cnt; ++i) {
154 status = normalize_route_uri(pjsua_var.pool,
155 &pjsua_var.ua_cfg.outbound_proxy[i]);
156 if (status != PJ_SUCCESS)
157 return status;
158 }
159
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000160 /* Initialize invite session callback. */
Benny Prijonoac623b32006-07-03 15:19:31 +0000161 pj_bzero(&inv_cb, sizeof(inv_cb));
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000162 inv_cb.on_state_changed = &pjsua_call_on_state_changed;
163 inv_cb.on_new_session = &pjsua_call_on_forked;
164 inv_cb.on_media_update = &pjsua_call_on_media_update;
165 inv_cb.on_rx_offer = &pjsua_call_on_rx_offer;
Benny Prijono77998ce2007-06-20 10:03:46 +0000166 inv_cb.on_create_offer = &pjsua_call_on_create_offer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000167 inv_cb.on_tsx_state_changed = &pjsua_call_on_tsx_state_changed;
Benny Prijono5e51a4e2008-11-27 00:06:46 +0000168 inv_cb.on_redirected = &pjsua_call_on_redirected;
Benny Prijono275fd682006-03-22 11:59:11 +0000169
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000170 /* Initialize invite session module: */
171 status = pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb);
172 PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
173
Benny Prijonoc8141a82006-08-20 09:12:19 +0000174 /* Add "norefersub" in Supported header */
175 pjsip_endpt_add_capability(pjsua_var.endpt, NULL, PJSIP_H_SUPPORTED,
176 NULL, 1, &str_norefersub);
177
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000178 return status;
179}
180
181
182/*
183 * Start call subsystem.
184 */
185pj_status_t pjsua_call_subsys_start(void)
186{
187 /* Nothing to do */
Benny Prijono275fd682006-03-22 11:59:11 +0000188 return PJ_SUCCESS;
189}
190
191
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000192/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000193 * Get maximum number of calls configured in pjsua.
194 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000195PJ_DEF(unsigned) pjsua_call_get_max_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000196{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000197 return pjsua_var.ua_cfg.max_calls;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000198}
199
200
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000201/*
202 * Get number of currently active calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000203 */
Benny Prijono8b1889b2006-06-06 18:40:40 +0000204PJ_DEF(unsigned) pjsua_call_get_count(void)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000205{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000206 return pjsua_var.call_cnt;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000207}
208
209
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000210/*
211 * Enum calls.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000212 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000213PJ_DEF(pj_status_t) pjsua_enum_calls( pjsua_call_id ids[],
214 unsigned *count)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000215{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000216 unsigned i, c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000217
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000218 PJ_ASSERT_RETURN(ids && *count, PJ_EINVAL);
Benny Prijono9fc735d2006-05-28 14:58:12 +0000219
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000220 PJSUA_LOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000221
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000222 for (i=0, c=0; c<*count && i<pjsua_var.ua_cfg.max_calls; ++i) {
223 if (!pjsua_var.calls[i].inv)
224 continue;
225 ids[c] = i;
226 ++c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000227 }
228
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000229 *count = c;
Benny Prijono9fc735d2006-05-28 14:58:12 +0000230
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000231 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +0000232
233 return PJ_SUCCESS;
234}
235
236
Benny Prijono5773cd62008-01-19 13:01:42 +0000237/* Allocate one call id */
238static pjsua_call_id alloc_call_id(void)
239{
240 pjsua_call_id cid;
241
242#if 1
243 /* New algorithm: round-robin */
244 if (pjsua_var.next_call_id >= (int)pjsua_var.ua_cfg.max_calls ||
245 pjsua_var.next_call_id < 0)
246 {
Benny Prijono5d177b82008-02-26 10:31:28 +0000247 pjsua_var.next_call_id = 0;
Benny Prijono5773cd62008-01-19 13:01:42 +0000248 }
249
250 for (cid=pjsua_var.next_call_id;
251 cid<(int)pjsua_var.ua_cfg.max_calls;
252 ++cid)
253 {
254 if (pjsua_var.calls[cid].inv == NULL) {
255 ++pjsua_var.next_call_id;
256 return cid;
257 }
258 }
259
260 for (cid=0; cid < pjsua_var.next_call_id; ++cid) {
261 if (pjsua_var.calls[cid].inv == NULL) {
262 ++pjsua_var.next_call_id;
263 return cid;
264 }
265 }
266
267#else
268 /* Old algorithm */
269 for (cid=0; cid<(int)pjsua_var.ua_cfg.max_calls; ++cid) {
270 if (pjsua_var.calls[cid].inv == NULL)
271 return cid;
272 }
273#endif
274
275 return PJSUA_INVALID_ID;
276}
277
Benny Prijonod8179652008-01-23 20:39:07 +0000278/* Get signaling secure level.
279 * Return:
280 * 0: if signaling is not secure
281 * 1: if TLS transport is used for immediate hop
282 * 2: if end-to-end signaling is secure.
Benny Prijonod8179652008-01-23 20:39:07 +0000283 */
Benny Prijonodb844a42008-02-02 17:07:18 +0000284static int get_secure_level(pjsua_acc_id acc_id, const pj_str_t *dst_uri)
Benny Prijonod8179652008-01-23 20:39:07 +0000285{
286 const pj_str_t tls = pj_str(";transport=tls");
287 const pj_str_t sips = pj_str("sips:");
Benny Prijonodb844a42008-02-02 17:07:18 +0000288 pjsua_acc *acc = &pjsua_var.acc[acc_id];
Benny Prijonod8179652008-01-23 20:39:07 +0000289
290 if (pj_stristr(dst_uri, &sips))
291 return 2;
Benny Prijonodb844a42008-02-02 17:07:18 +0000292
293 if (!pj_list_empty(&acc->route_set)) {
294 pjsip_route_hdr *r = acc->route_set.next;
295 pjsip_uri *uri = r->name_addr.uri;
296 pjsip_sip_uri *sip_uri;
297
298 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
299 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
300 return 1;
301
302 } else {
303 if (pj_stristr(dst_uri, &tls))
304 return 1;
305 }
306
Benny Prijonod8179652008-01-23 20:39:07 +0000307 return 0;
308}
309
Benny Prijono224b4e22008-06-19 14:10:28 +0000310/*
Benny Prijonodb844a42008-02-02 17:07:18 +0000311static int call_get_secure_level(pjsua_call *call)
312{
313 if (call->inv->dlg->secure)
314 return 2;
315
316 if (!pj_list_empty(&call->inv->dlg->route_set)) {
317 pjsip_route_hdr *r = call->inv->dlg->route_set.next;
318 pjsip_uri *uri = r->name_addr.uri;
319 pjsip_sip_uri *sip_uri;
320
321 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
322 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
323 return 1;
324
325 } else {
326 pjsip_sip_uri *sip_uri;
327
328 if (PJSIP_URI_SCHEME_IS_SIPS(call->inv->dlg->target))
329 return 2;
330 if (!PJSIP_URI_SCHEME_IS_SIP(call->inv->dlg->target))
331 return 0;
332
333 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(call->inv->dlg->target);
334 if (pj_stricmp2(&sip_uri->transport_param, "tls")==0)
335 return 1;
336 }
337
338 return 0;
339}
Benny Prijono224b4e22008-06-19 14:10:28 +0000340*/
Benny Prijonodb844a42008-02-02 17:07:18 +0000341
342
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000343/*
344 * Make outgoing call to the specified URI using the specified account.
Benny Prijono9fc735d2006-05-28 14:58:12 +0000345 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000346PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
347 const pj_str_t *dest_uri,
348 unsigned options,
349 void *user_data,
350 const pjsua_msg_data *msg_data,
351 pjsua_call_id *p_call_id)
Benny Prijono84126ab2006-02-09 09:30:09 +0000352{
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000353 pj_pool_t *tmp_pool;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000354 pjsip_dialog *dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000355 pjmedia_sdp_session *offer;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000356 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000357 pjsua_acc *acc;
358 pjsua_call *call;
Benny Prijonoa1e69682007-05-11 15:14:34 +0000359 int call_id = -1;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000360 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000361 pjsip_tx_data *tdata;
362 pj_status_t status;
363
Benny Prijono9fc735d2006-05-28 14:58:12 +0000364
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000365 /* Check that account is valid */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000366 PJ_ASSERT_RETURN(acc_id>=0 || acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
Benny Prijono9fc735d2006-05-28 14:58:12 +0000367 PJ_EINVAL);
368
Benny Prijono320fa4d2006-12-07 10:09:16 +0000369 /* Check arguments */
370 PJ_ASSERT_RETURN(dest_uri, PJ_EINVAL);
371
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000372 PJSUA_LOCK();
373
Benny Prijonof798e502009-03-09 13:08:16 +0000374 /* Create sound port if none is instantiated, to check if sound device
375 * can be used. But only do this with the conference bridge, as with
376 * audio switchboard (i.e. APS-Direct), we can only open the sound
377 * device once the correct format has been known
378 */
379 if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
380 pjsua_var.null_snd==NULL && !pjsua_var.no_snd)
Nanang Izzuddin148fd392008-06-16 09:52:50 +0000381 {
382 pj_status_t status;
383
384 status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
385 if (status != PJ_SUCCESS) {
386 PJSUA_UNLOCK();
387 return status;
388 }
389 }
390
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000391 acc = &pjsua_var.acc[acc_id];
392 if (!acc->valid) {
393 pjsua_perror(THIS_FILE, "Unable to make call because account "
394 "is not valid", PJ_EINVALIDOP);
395 PJSUA_UNLOCK();
396 return PJ_EINVALIDOP;
397 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000398
Benny Prijonoa91a0032006-02-26 21:23:45 +0000399 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000400 call_id = alloc_call_id();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000401
Benny Prijono5773cd62008-01-19 13:01:42 +0000402 if (call_id == PJSUA_INVALID_ID) {
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000403 pjsua_perror(THIS_FILE, "Error making call", PJ_ETOOMANY);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000404 PJSUA_UNLOCK();
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000405 return PJ_ETOOMANY;
406 }
407
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000408 call = &pjsua_var.calls[call_id];
409
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000410 /* Associate session with account */
411 call->acc_id = acc_id;
Benny Prijonodd63b992010-10-01 02:03:42 +0000412 call->call_hold_type = acc->cfg.call_hold_type;
Nanang Izzuddin79f4f202009-10-20 13:14:40 +0000413
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000414 /* Create temporary pool */
415 tmp_pool = pjsua_pool_create("tmpcall10", 512, 256);
416
Benny Prijono320fa4d2006-12-07 10:09:16 +0000417 /* Verify that destination URI is valid before calling
418 * pjsua_acc_create_uac_contact, or otherwise there
419 * a misleading "Invalid Contact URI" error will be printed
420 * when pjsua_acc_create_uac_contact() fails.
421 */
422 if (1) {
Benny Prijono320fa4d2006-12-07 10:09:16 +0000423 pjsip_uri *uri;
424 pj_str_t dup;
425
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000426 pj_strdup_with_null(tmp_pool, &dup, dest_uri);
427 uri = pjsip_parse_uri(tmp_pool, dup.ptr, dup.slen, 0);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000428
429 if (uri == NULL) {
430 pjsua_perror(THIS_FILE, "Unable to make call",
431 PJSIP_EINVALIDREQURI);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000432 pj_pool_release(tmp_pool);
Benny Prijono320fa4d2006-12-07 10:09:16 +0000433 PJSUA_UNLOCK();
434 return PJSIP_EINVALIDREQURI;
435 }
436 }
437
Benny Prijono093d3022006-09-24 00:07:11 +0000438 PJ_LOG(4,(THIS_FILE, "Making call with acc #%d to %.*s", acc_id,
439 (int)dest_uri->slen, dest_uri->ptr));
440
Benny Prijonoe21e7842006-04-09 16:46:05 +0000441 /* Mark call start time. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000442 pj_gettimeofday(&call->start_time);
Benny Prijono84126ab2006-02-09 09:30:09 +0000443
Benny Prijonoe21e7842006-04-09 16:46:05 +0000444 /* Reset first response time */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000445 call->res_time.sec = 0;
Benny Prijonoe21e7842006-04-09 16:46:05 +0000446
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000447 /* Create suitable Contact header unless a Contact header has been
448 * set in the account.
449 */
450 if (acc->contact.slen) {
451 contact = acc->contact;
452 } else {
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000453 status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000454 acc_id, dest_uri);
455 if (status != PJ_SUCCESS) {
456 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
457 status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000458 pj_pool_release(tmp_pool);
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000459 PJSUA_UNLOCK();
460 return status;
461 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000462 }
463
Benny Prijonoe21e7842006-04-09 16:46:05 +0000464 /* Create outgoing dialog: */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000465 status = pjsip_dlg_create_uac( pjsip_ua_instance(),
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000466 &acc->cfg.id, &contact,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000467 dest_uri, dest_uri, &dlg);
Benny Prijono84126ab2006-02-09 09:30:09 +0000468 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000469 pjsua_perror(THIS_FILE, "Dialog creation failed", status);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000470 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000471 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000472 return status;
473 }
474
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000475 /* Increment the dialog's lock otherwise when invite session creation
476 * fails the dialog will be destroyed prematurely.
477 */
478 pjsip_dlg_inc_lock(dlg);
479
Benny Prijonodb844a42008-02-02 17:07:18 +0000480 /* Calculate call's secure level */
481 call->secure_level = get_secure_level(acc_id, dest_uri);
482
Benny Prijonoc97608e2007-03-23 16:34:20 +0000483 /* Init media channel */
Benny Prijonod8179652008-01-23 20:39:07 +0000484 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC,
Benny Prijono224b4e22008-06-19 14:10:28 +0000485 call->secure_level, dlg->pool,
486 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000487 if (status != PJ_SUCCESS) {
488 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
489 goto on_error;
490 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000491
Benny Prijono224b4e22008-06-19 14:10:28 +0000492 /* Create offer */
493 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000494 &offer, NULL);
Benny Prijono84126ab2006-02-09 09:30:09 +0000495 if (status != PJ_SUCCESS) {
Benny Prijono224b4e22008-06-19 14:10:28 +0000496 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000497 goto on_error;
498 }
499
500 /* Create the INVITE session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000501 options |= PJSIP_INV_SUPPORT_100REL;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000502 if (acc->cfg.require_100rel)
503 options |= PJSIP_INV_REQUIRE_100REL;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000504 if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) {
505 options |= PJSIP_INV_SUPPORT_TIMER;
506 if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
507 options |= PJSIP_INV_REQUIRE_TIMER;
508 else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
509 options |= PJSIP_INV_ALWAYS_USE_TIMER;
510 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000511
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000512 status = pjsip_inv_create_uac( dlg, offer, options, &inv);
Benny Prijono84126ab2006-02-09 09:30:09 +0000513 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000514 pjsua_perror(THIS_FILE, "Invite session creation failed", status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000515 goto on_error;
516 }
517
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000518 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000519 status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
520 if (status != PJ_SUCCESS) {
521 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
522 goto on_error;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000523 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000524
525 /* Create and associate our data in the session. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000526 call->inv = inv;
Benny Prijonoa91a0032006-02-26 21:23:45 +0000527
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000528 dlg->mod_data[pjsua_var.mod.id] = call;
529 inv->mod_data[pjsua_var.mod.id] = call;
Benny Prijono84126ab2006-02-09 09:30:09 +0000530
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000531 /* Attach user data */
532 call->user_data = user_data;
Benny Prijono84126ab2006-02-09 09:30:09 +0000533
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000534 /* If account is locked to specific transport, then lock dialog
535 * to this transport too.
536 */
537 if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
538 pjsip_tpselector tp_sel;
539
540 pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
541 pjsip_dlg_set_transport(dlg, &tp_sel);
542 }
543
Benny Prijono84126ab2006-02-09 09:30:09 +0000544 /* Set dialog Route-Set: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000545 if (!pj_list_empty(&acc->route_set))
546 pjsip_dlg_set_route_set(dlg, &acc->route_set);
Benny Prijono84126ab2006-02-09 09:30:09 +0000547
548
549 /* Set credentials: */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000550 if (acc->cred_cnt) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000551 pjsip_auth_clt_set_credentials( &dlg->auth_sess,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000552 acc->cred_cnt, acc->cred);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000553 }
Benny Prijono84126ab2006-02-09 09:30:09 +0000554
Benny Prijono48ab2b72007-11-08 09:24:30 +0000555 /* Set authentication preference */
556 pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
Benny Prijono84126ab2006-02-09 09:30:09 +0000557
558 /* Create initial INVITE: */
559
560 status = pjsip_inv_invite(inv, &tdata);
561 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000562 pjsua_perror(THIS_FILE, "Unable to create initial INVITE request",
563 status);
Benny Prijono84126ab2006-02-09 09:30:09 +0000564 goto on_error;
565 }
566
567
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000568 /* Add additional headers etc */
569
570 pjsua_process_msg_data( tdata, msg_data);
571
Benny Prijono093d3022006-09-24 00:07:11 +0000572 /* Must increment call counter now */
573 ++pjsua_var.call_cnt;
574
Benny Prijono84126ab2006-02-09 09:30:09 +0000575 /* Send initial INVITE: */
576
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +0000577 status = pjsip_inv_send_msg(inv, tdata);
Benny Prijono84126ab2006-02-09 09:30:09 +0000578 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000579 pjsua_perror(THIS_FILE, "Unable to send initial INVITE request",
580 status);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000581
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000582 /* Upon failure to send first request, the invite
Benny Prijonodc39fe82006-05-26 12:17:46 +0000583 * session would have been cleared.
584 */
585 inv = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000586 goto on_error;
587 }
588
Benny Prijono84126ab2006-02-09 09:30:09 +0000589 /* Done. */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000590
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000591 if (p_call_id)
592 *p_call_id = call_id;
593
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000594 pjsip_dlg_dec_lock(dlg);
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000595 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000596 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000597
598 return PJ_SUCCESS;
599
600
601on_error:
Benny Prijono0bd5eb92009-04-14 11:10:31 +0000602 if (dlg) {
603 /* This may destroy the dialog */
604 pjsip_dlg_dec_lock(dlg);
605 }
606
Benny Prijono1c2bf462006-03-05 11:54:02 +0000607 if (inv != NULL) {
608 pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
Benny Prijono1c2bf462006-03-05 11:54:02 +0000609 }
610
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000611 if (call_id != -1) {
612 reset_call(call_id);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000613 pjsua_media_channel_deinit(call_id);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000614 }
615
Benny Prijonoc91ed8d2008-07-13 12:24:55 +0000616 pj_pool_release(tmp_pool);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000617 PJSUA_UNLOCK();
Benny Prijono8b1889b2006-06-06 18:40:40 +0000618 return status;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000619}
620
621
Benny Prijono91a6a172007-10-31 08:59:29 +0000622/* Get the NAT type information in remote's SDP */
623static void update_remote_nat_type(pjsua_call *call,
624 const pjmedia_sdp_session *sdp)
625{
626 const pjmedia_sdp_attr *xnat;
627
628 xnat = pjmedia_sdp_attr_find2(sdp->attr_count, sdp->attr, "X-nat", NULL);
629 if (xnat) {
630 call->rem_nat_type = (pj_stun_nat_type) (xnat->value.ptr[0] - '0');
631 } else {
632 call->rem_nat_type = PJ_STUN_NAT_TYPE_UNKNOWN;
633 }
634
635 PJ_LOG(5,(THIS_FILE, "Call %d: remote NAT type is %d (%s)", call->index,
636 call->rem_nat_type, pj_stun_get_nat_name(call->rem_nat_type)));
637}
638
639
Benny Prijonodc39fe82006-05-26 12:17:46 +0000640/**
Benny Prijono84126ab2006-02-09 09:30:09 +0000641 * Handle incoming INVITE request.
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000642 * Called by pjsua_core.c
Benny Prijono84126ab2006-02-09 09:30:09 +0000643 */
Benny Prijonoa91a0032006-02-26 21:23:45 +0000644pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
Benny Prijono84126ab2006-02-09 09:30:09 +0000645{
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000646 pj_str_t contact;
Benny Prijono84126ab2006-02-09 09:30:09 +0000647 pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata);
Benny Prijono053f5222006-11-11 16:16:04 +0000648 pjsip_dialog *replaced_dlg = NULL;
Benny Prijono84126ab2006-02-09 09:30:09 +0000649 pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
650 pjsip_msg *msg = rdata->msg_info.msg;
Benny Prijono26ff9062006-02-21 23:47:00 +0000651 pjsip_tx_data *response = NULL;
652 unsigned options = 0;
Benny Prijono1c2bf462006-03-05 11:54:02 +0000653 pjsip_inv_session *inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000654 int acc_id;
655 pjsua_call *call;
656 int call_id = -1;
Benny Prijonodb844a42008-02-02 17:07:18 +0000657 int sip_err_code;
Benny Prijono1c1d7342010-08-01 09:48:51 +0000658 pjmedia_sdp_session *offer=NULL, *answer;
Benny Prijono26ff9062006-02-21 23:47:00 +0000659 pj_status_t status;
Benny Prijono84126ab2006-02-09 09:30:09 +0000660
Benny Prijono26ff9062006-02-21 23:47:00 +0000661 /* Don't want to handle anything but INVITE */
662 if (msg->line.req.method.id != PJSIP_INVITE_METHOD)
663 return PJ_FALSE;
664
665 /* Don't want to handle anything that's already associated with
666 * existing dialog or transaction.
Benny Prijono84126ab2006-02-09 09:30:09 +0000667 */
Benny Prijono26ff9062006-02-21 23:47:00 +0000668 if (dlg || tsx)
669 return PJ_FALSE;
Benny Prijono84126ab2006-02-09 09:30:09 +0000670
Benny Prijono384dab42009-10-14 01:58:04 +0000671 /* Don't want to accept the call if shutdown is in progress */
672 if (pjsua_var.thread_quit_flag) {
673 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
674 PJSIP_SC_TEMPORARILY_UNAVAILABLE, NULL,
675 NULL, NULL);
676 return PJ_TRUE;
677 }
678
Benny Prijono148c9dd2006-09-19 13:37:53 +0000679 PJSUA_LOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000680
Benny Prijono26ff9062006-02-21 23:47:00 +0000681 /* Find free call slot. */
Benny Prijono5773cd62008-01-19 13:01:42 +0000682 call_id = alloc_call_id();
Benny Prijono26ff9062006-02-21 23:47:00 +0000683
Benny Prijono5773cd62008-01-19 13:01:42 +0000684 if (call_id == PJSUA_INVALID_ID) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000685 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijono26ff9062006-02-21 23:47:00 +0000686 PJSIP_SC_BUSY_HERE, NULL,
687 NULL, NULL);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000688 PJ_LOG(2,(THIS_FILE,
689 "Unable to accept incoming call (too many calls)"));
Benny Prijono148c9dd2006-09-19 13:37:53 +0000690 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +0000691 return PJ_TRUE;
692 }
693
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000694 /* Clear call descriptor */
695 reset_call(call_id);
Benny Prijonoe21e7842006-04-09 16:46:05 +0000696
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000697 call = &pjsua_var.calls[call_id];
698
699 /* Mark call start time. */
700 pj_gettimeofday(&call->start_time);
Benny Prijono26ff9062006-02-21 23:47:00 +0000701
Benny Prijono053f5222006-11-11 16:16:04 +0000702 /* Check INVITE request for Replaces header. If Replaces header is
703 * present, the function will make sure that we can handle the request.
704 */
705 status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE,
706 &response);
707 if (status != PJ_SUCCESS) {
708 /*
709 * Something wrong with the Replaces header.
710 */
711 if (response) {
712 pjsip_response_addr res_addr;
713
714 pjsip_get_response_addr(response->pool, rdata, &res_addr);
715 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
716 NULL, NULL);
717
718 } else {
719
720 /* Respond with 500 (Internal Server Error) */
721 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
722 NULL, NULL);
723 }
724
725 PJSUA_UNLOCK();
726 return PJ_TRUE;
727 }
728
729 /* If this INVITE request contains Replaces header, notify application
730 * about the request so that application can do subsequent checking
731 * if it wants to.
732 */
733 if (replaced_dlg != NULL && pjsua_var.ua_cfg.cb.on_call_replace_request) {
734 pjsua_call *replaced_call;
735 int st_code = 200;
736 pj_str_t st_text = { "OK", 2 };
737
738 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000739 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +0000740
741 /* Notify application */
742 pjsua_var.ua_cfg.cb.on_call_replace_request(replaced_call->index,
743 rdata, &st_code, &st_text);
744
745 /* Must specify final response */
746 PJ_ASSERT_ON_FAIL(st_code >= 200, st_code = 200);
747
748 /* Check if application rejects this request. */
749 if (st_code >= 300) {
750
751 if (st_text.slen == 2)
752 st_text = *pjsip_get_status_text(st_code);
753
754 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
755 st_code, &st_text, NULL, NULL, NULL);
756 PJSUA_UNLOCK();
757 return PJ_TRUE;
758 }
759 }
760
Benny Prijonod8179652008-01-23 20:39:07 +0000761 /*
762 * Get which account is most likely to be associated with this incoming
763 * call. We need the account to find which contact URI to put for
764 * the call.
765 */
766 acc_id = call->acc_id = pjsua_acc_find_for_incoming(rdata);
Benny Prijonodd63b992010-10-01 02:03:42 +0000767 call->call_hold_type = pjsua_var.acc[acc_id].cfg.call_hold_type;
Benny Prijonod8179652008-01-23 20:39:07 +0000768
Benny Prijonodb844a42008-02-02 17:07:18 +0000769 /* Get call's secure level */
770 if (PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri))
771 call->secure_level = 2;
772 else if (PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport))
773 call->secure_level = 1;
774 else
775 call->secure_level = 0;
Benny Prijono053f5222006-11-11 16:16:04 +0000776
Benny Prijonod8179652008-01-23 20:39:07 +0000777 /* Parse SDP from incoming request */
778 if (rdata->msg_info.msg->body) {
Benny Prijono1c1d7342010-08-01 09:48:51 +0000779 pjsip_rdata_sdp_info *sdp_info;
780
781 sdp_info = pjsip_rdata_get_sdp_info(rdata);
782 offer = sdp_info->sdp;
783
784 status = sdp_info->sdp_err;
785 if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
786 status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
Benny Prijono2c484e42008-06-26 19:47:23 +0000787
Benny Prijonod8179652008-01-23 20:39:07 +0000788 if (status != PJ_SUCCESS) {
Benny Prijono617b8602008-04-07 10:10:31 +0000789 const pj_str_t reason = pj_str("Bad SDP");
Benny Prijono2c484e42008-06-26 19:47:23 +0000790 pjsip_hdr hdr_list;
791 pjsip_warning_hdr *w;
792
793 pjsua_perror(THIS_FILE, "Bad SDP in incoming INVITE",
Benny Prijono617b8602008-04-07 10:10:31 +0000794 status);
Benny Prijono2c484e42008-06-26 19:47:23 +0000795
796 w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
797 pjsip_endpt_name(pjsua_var.endpt),
798 status);
799 pj_list_init(&hdr_list);
800 pj_list_push_back(&hdr_list, w);
801
Benny Prijono224b4e22008-06-19 14:10:28 +0000802 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400,
Benny Prijono2c484e42008-06-26 19:47:23 +0000803 &reason, &hdr_list, NULL, NULL);
Benny Prijonod8179652008-01-23 20:39:07 +0000804 PJSUA_UNLOCK();
805 return PJ_TRUE;
806 }
Benny Prijono617b8602008-04-07 10:10:31 +0000807
808 /* Do quick checks on SDP before passing it to transports. More elabore
809 * checks will be done in pjsip_inv_verify_request2() below.
810 */
811 if (offer->media_count==0) {
812 const pj_str_t reason = pj_str("Missing media in SDP");
813 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 400, &reason,
814 NULL, NULL, NULL);
815 PJSUA_UNLOCK();
816 return PJ_TRUE;
817 }
818
Benny Prijonod8179652008-01-23 20:39:07 +0000819 } else {
820 offer = NULL;
821 }
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000822
Benny Prijono224b4e22008-06-19 14:10:28 +0000823 /* Init media channel */
824 status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS,
825 call->secure_level,
826 rdata->tp_info.pool, offer,
827 &sip_err_code);
828 if (status != PJ_SUCCESS) {
829 pjsua_perror(THIS_FILE, "Error initializing media channel", status);
830 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
831 sip_err_code, NULL, NULL, NULL, NULL);
832 PJSUA_UNLOCK();
833 return PJ_TRUE;
834 }
835
836 /* Create answer */
Benny Prijonod8179652008-01-23 20:39:07 +0000837 status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool,
Benny Prijono25b2ea12008-01-24 19:20:54 +0000838 offer, &answer, &sip_err_code);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000839 if (status != PJ_SUCCESS) {
Benny Prijono25b2ea12008-01-24 19:20:54 +0000840 pjsua_perror(THIS_FILE, "Error creating SDP answer", status);
Benny Prijono224b4e22008-06-19 14:10:28 +0000841 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
842 sip_err_code, NULL, NULL, NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000843 PJSUA_UNLOCK();
844 return PJ_TRUE;
845 }
846
Benny Prijono224b4e22008-06-19 14:10:28 +0000847
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000848 /* Verify that we can handle the request. */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000849 options |= PJSIP_INV_SUPPORT_100REL;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000850 options |= PJSIP_INV_SUPPORT_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000851 if (pjsua_var.acc[acc_id].cfg.require_100rel)
852 options |= PJSIP_INV_REQUIRE_100REL;
Benny Prijono07fe2302010-06-24 12:33:18 +0000853 if (pjsua_var.media_cfg.enable_ice)
854 options |= PJSIP_INV_SUPPORT_ICE;
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000855 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
856 options |= PJSIP_INV_REQUIRE_TIMER;
857 else if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
858 options |= PJSIP_INV_ALWAYS_USE_TIMER;
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000859
Benny Prijonod8179652008-01-23 20:39:07 +0000860 status = pjsip_inv_verify_request2(rdata, &options, offer, answer, NULL,
861 pjsua_var.endpt, &response);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000862 if (status != PJ_SUCCESS) {
863
864 /*
865 * No we can't handle the incoming INVITE request.
866 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000867 if (response) {
868 pjsip_response_addr res_addr;
869
870 pjsip_get_response_addr(response->pool, rdata, &res_addr);
871 pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, response,
872 NULL, NULL);
873
874 } else {
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000875 /* Respond with 500 (Internal Server Error) */
Benny Prijono224b4e22008-06-19 14:10:28 +0000876 pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata, 500, NULL,
877 NULL, NULL, NULL);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000878 }
879
Benny Prijonoc97608e2007-03-23 16:34:20 +0000880 pjsua_media_channel_deinit(call->index);
Benny Prijono1d9b9a42006-09-25 13:40:12 +0000881 PJSUA_UNLOCK();
882 return PJ_TRUE;
883 }
884
885
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000886 /* Get suitable Contact header */
Benny Prijonoddaaf6a2008-04-15 10:37:19 +0000887 if (pjsua_var.acc[acc_id].contact.slen) {
888 contact = pjsua_var.acc[acc_id].contact;
889 } else {
890 status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
891 acc_id, rdata);
892 if (status != PJ_SUCCESS) {
893 pjsua_perror(THIS_FILE, "Unable to generate Contact header",
894 status);
895 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
896 NULL, NULL);
897 pjsua_media_channel_deinit(call->index);
898 PJSUA_UNLOCK();
899 return PJ_TRUE;
900 }
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000901 }
902
Benny Prijono26ff9062006-02-21 23:47:00 +0000903 /* Create dialog: */
Benny Prijono26ff9062006-02-21 23:47:00 +0000904 status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000905 &contact, &dlg);
Benny Prijono26ff9062006-02-21 23:47:00 +0000906 if (status != PJ_SUCCESS) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000907 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
Benny Prijono26ff9062006-02-21 23:47:00 +0000908 NULL, NULL);
Benny Prijonoc97608e2007-03-23 16:34:20 +0000909 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000910 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000911 return PJ_TRUE;
912 }
913
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000914 /* Set credentials */
915 if (pjsua_var.acc[acc_id].cred_cnt) {
916 pjsip_auth_clt_set_credentials(&dlg->auth_sess,
917 pjsua_var.acc[acc_id].cred_cnt,
918 pjsua_var.acc[acc_id].cred);
919 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000920
Benny Prijono48ab2b72007-11-08 09:24:30 +0000921 /* Set preference */
922 pjsip_auth_clt_set_prefs(&dlg->auth_sess,
923 &pjsua_var.acc[acc_id].cfg.auth_pref);
924
Nanang Izzuddin742ef4b2010-09-07 09:36:15 +0000925 /* Disable Session Timers if not prefered and the incoming INVITE request
926 * did not require it.
927 */
928 if (pjsua_var.acc[acc_id].cfg.use_timer == PJSUA_SIP_TIMER_INACTIVE &&
929 (options & PJSIP_INV_REQUIRE_TIMER) == 0)
930 {
931 options &= ~(PJSIP_INV_SUPPORT_TIMER);
932 }
933
Benny Prijono26ff9062006-02-21 23:47:00 +0000934 /* Create invite session: */
Benny Prijonodcfc0ba2007-09-30 16:50:27 +0000935 status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
Benny Prijono26ff9062006-02-21 23:47:00 +0000936 if (status != PJ_SUCCESS) {
Benny Prijonoa38ada02006-07-02 14:22:35 +0000937 pjsip_hdr hdr_list;
938 pjsip_warning_hdr *w;
939
940 w = pjsip_warning_hdr_create_from_status(dlg->pool,
941 pjsip_endpt_name(pjsua_var.endpt),
942 status);
943 pj_list_init(&hdr_list);
944 pj_list_push_back(&hdr_list, w);
945
946 pjsip_dlg_respond(dlg, rdata, 500, NULL, &hdr_list, NULL);
947
948 /* Can't terminate dialog because transaction is in progress.
Benny Prijono1c2bf462006-03-05 11:54:02 +0000949 pjsip_dlg_terminate(dlg);
Benny Prijonoa38ada02006-07-02 14:22:35 +0000950 */
Benny Prijonoc97608e2007-03-23 16:34:20 +0000951 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +0000952 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +0000953 return PJ_TRUE;
954 }
955
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000956 /* Init Session Timers */
Nanang Izzuddin65add622009-08-11 16:26:20 +0000957 status = pjsip_timer_init_session(inv,
958 &pjsua_var.acc[acc_id].cfg.timer_setting);
959 if (status != PJ_SUCCESS) {
960 pjsua_perror(THIS_FILE, "Session Timer init failed", status);
961 status = pjsip_inv_end_session(inv, PJSIP_SC_INTERNAL_SERVER_ERROR,
962 NULL, &response);
963 if (status == PJ_SUCCESS && response)
964 status = pjsip_inv_send_msg(inv, response);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000965
Nanang Izzuddin65add622009-08-11 16:26:20 +0000966 pjsua_media_channel_deinit(call->index);
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000967
Nanang Izzuddin65add622009-08-11 16:26:20 +0000968 PJSUA_UNLOCK();
969 return PJ_TRUE;
Nanang Izzuddin59dffb12009-08-11 12:42:38 +0000970 }
971
Benny Prijonoea9fd392007-11-06 03:41:40 +0000972 /* Update NAT type of remote endpoint, only when there is SDP in
973 * incoming INVITE!
974 */
975 if (pjsua_var.ua_cfg.nat_type_in_sdp &&
976 pjmedia_sdp_neg_get_state(inv->neg) > PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
977 {
Benny Prijono91a6a172007-10-31 08:59:29 +0000978 const pjmedia_sdp_session *remote_sdp;
979
980 if (pjmedia_sdp_neg_get_neg_remote(inv->neg, &remote_sdp)==PJ_SUCCESS)
981 update_remote_nat_type(call, remote_sdp);
982 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000983
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000984 /* If account is locked to specific transport, then lock dialog
985 * to this transport too.
986 */
987 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
988 pjsip_tpselector tp_sel;
989
990 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
991 pjsip_dlg_set_transport(dlg, &tp_sel);
992 }
Benny Prijono26ff9062006-02-21 23:47:00 +0000993
Benny Prijono2285e7e2008-12-17 14:28:18 +0000994 /* Must answer with some response to initial INVITE. We'll do this before
995 * attaching the call to the invite session/dialog, so that the application
996 * will not get notification about this event (on another scenario, it is
997 * also possible that inv_send_msg() fails and causes the invite session to
998 * be disconnected. If we have the call attached at this time, this will
999 * cause the disconnection callback to be called before on_incoming_call()
1000 * callback is called, which is not right).
Benny Prijono64f851e2006-02-23 13:49:28 +00001001 */
Benny Prijono64f851e2006-02-23 13:49:28 +00001002 status = pjsip_inv_initial_answer(inv, rdata,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001003 100, NULL, NULL, &response);
Benny Prijono26ff9062006-02-21 23:47:00 +00001004 if (status != PJ_SUCCESS) {
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001005 if (response == NULL) {
1006 pjsua_perror(THIS_FILE, "Unable to send answer to incoming INVITE",
1007 status);
1008 pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
1009 pjsip_inv_terminate(inv, 500, PJ_FALSE);
1010 } else {
1011 pjsip_inv_send_msg(inv, response);
Nanang Izzuddin65add622009-08-11 16:26:20 +00001012 pjsip_inv_terminate(inv, response->msg->line.status.code,
Nanang Izzuddin59dffb12009-08-11 12:42:38 +00001013 PJ_FALSE);
1014 }
Benny Prijonoc97608e2007-03-23 16:34:20 +00001015 pjsua_media_channel_deinit(call->index);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001016 PJSUA_UNLOCK();
Benny Prijonoccb03fa2006-03-06 13:35:47 +00001017 return PJ_TRUE;
Benny Prijono26ff9062006-02-21 23:47:00 +00001018
1019 } else {
Benny Prijonoe8b0d3b2006-03-17 17:57:52 +00001020 status = pjsip_inv_send_msg(inv, response);
Benny Prijonoc97608e2007-03-23 16:34:20 +00001021 if (status != PJ_SUCCESS) {
Benny Prijonod2ae29d2006-02-22 15:42:31 +00001022 pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
Benny Prijono2285e7e2008-12-17 14:28:18 +00001023 PJSUA_UNLOCK();
1024 return PJ_TRUE;
Benny Prijonoc97608e2007-03-23 16:34:20 +00001025 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001026 }
1027
Benny Prijono2285e7e2008-12-17 14:28:18 +00001028 /* Create and attach pjsua_var data to the dialog: */
1029 call->inv = inv;
1030
1031 dlg->mod_data[pjsua_var.mod.id] = call;
1032 inv->mod_data[pjsua_var.mod.id] = call;
1033
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001034 ++pjsua_var.call_cnt;
Benny Prijono64f851e2006-02-23 13:49:28 +00001035
Benny Prijono105217f2006-03-06 16:25:59 +00001036
Benny Prijono053f5222006-11-11 16:16:04 +00001037 /* Check if this request should replace existing call */
1038 if (replaced_dlg) {
1039 pjsip_inv_session *replaced_inv;
1040 struct pjsua_call *replaced_call;
1041 pjsip_tx_data *tdata;
1042
1043 /* Get the invite session in the dialog */
1044 replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
1045
1046 /* Get the replaced call instance */
Benny Prijonoa1e69682007-05-11 15:14:34 +00001047 replaced_call = (pjsua_call*) replaced_dlg->mod_data[pjsua_var.mod.id];
Benny Prijono053f5222006-11-11 16:16:04 +00001048
1049 /* Notify application */
1050 if (pjsua_var.ua_cfg.cb.on_call_replaced)
1051 pjsua_var.ua_cfg.cb.on_call_replaced(replaced_call->index,
1052 call_id);
1053
1054 PJ_LOG(4,(THIS_FILE, "Answering replacement call %d with 200/OK",
1055 call_id));
1056
1057 /* Answer the new call with 200 response */
1058 status = pjsip_inv_answer(inv, 200, NULL, NULL, &tdata);
1059 if (status == PJ_SUCCESS)
1060 status = pjsip_inv_send_msg(inv, tdata);
1061
1062 if (status != PJ_SUCCESS)
1063 pjsua_perror(THIS_FILE, "Error answering session", status);
1064
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001065 /* Note that inv may be invalid if 200/OK has caused error in
1066 * starting the media.
1067 */
Benny Prijono053f5222006-11-11 16:16:04 +00001068
1069 PJ_LOG(4,(THIS_FILE, "Disconnecting replaced call %d",
1070 replaced_call->index));
1071
1072 /* Disconnect replaced invite session */
1073 status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL,
1074 &tdata);
1075 if (status == PJ_SUCCESS && tdata)
1076 status = pjsip_inv_send_msg(replaced_inv, tdata);
1077
1078 if (status != PJ_SUCCESS)
1079 pjsua_perror(THIS_FILE, "Error terminating session", status);
1080
1081
1082 } else {
1083
Benny Prijonob5388cf2007-01-04 22:45:08 +00001084 /* Notify application if on_incoming_call() is overriden,
1085 * otherwise hangup the call with 480
1086 */
1087 if (pjsua_var.ua_cfg.cb.on_incoming_call) {
Benny Prijono053f5222006-11-11 16:16:04 +00001088 pjsua_var.ua_cfg.cb.on_incoming_call(acc_id, call_id, rdata);
Benny Prijonob5388cf2007-01-04 22:45:08 +00001089 } else {
1090 pjsua_call_hangup(call_id, PJSIP_SC_TEMPORARILY_UNAVAILABLE,
1091 NULL, NULL);
1092 }
Benny Prijono053f5222006-11-11 16:16:04 +00001093 }
1094
Benny Prijono8b1889b2006-06-06 18:40:40 +00001095
Benny Prijono26ff9062006-02-21 23:47:00 +00001096 /* This INVITE request has been handled. */
Benny Prijono148c9dd2006-09-19 13:37:53 +00001097 PJSUA_UNLOCK();
Benny Prijono26ff9062006-02-21 23:47:00 +00001098 return PJ_TRUE;
Benny Prijono84126ab2006-02-09 09:30:09 +00001099}
1100
1101
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001102
1103/*
1104 * Check if the specified call has active INVITE session and the INVITE
1105 * session has not been disconnected.
1106 */
1107PJ_DEF(pj_bool_t) pjsua_call_is_active(pjsua_call_id call_id)
1108{
1109 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1110 PJ_EINVAL);
1111 return pjsua_var.calls[call_id].inv != NULL &&
1112 pjsua_var.calls[call_id].inv->state != PJSIP_INV_STATE_DISCONNECTED;
1113}
1114
1115
1116/*
1117 * Check if call has an active media session.
1118 */
1119PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
1120{
1121 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1122 PJ_EINVAL);
1123 return pjsua_var.calls[call_id].session != NULL;
1124}
1125
1126
Benny Prijonocf986c42008-09-02 11:25:07 +00001127/*
1128 * Retrieve the media session associated with this call.
1129 */
1130PJ_DEF(pjmedia_session*) pjsua_call_get_media_session(pjsua_call_id call_id)
1131{
1132 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1133 NULL);
1134 return pjsua_var.calls[call_id].session;
1135}
1136
1137
1138/*
1139 * Retrieve the media transport instance that is used for this call.
1140 */
1141PJ_DEF(pjmedia_transport*) pjsua_call_get_media_transport(pjsua_call_id cid)
1142{
1143 PJ_ASSERT_RETURN(cid>=0 && cid<(int)pjsua_var.ua_cfg.max_calls,
1144 NULL);
1145 return pjsua_var.calls[cid].med_tp;
1146}
1147
1148
Benny Prijono148c9dd2006-09-19 13:37:53 +00001149/* Acquire lock to the specified call_id */
Benny Prijono627cbb42007-09-25 20:48:49 +00001150pj_status_t acquire_call(const char *title,
Benny Prijono148c9dd2006-09-19 13:37:53 +00001151 pjsua_call_id call_id,
Benny Prijonodc752ca2006-09-22 16:55:42 +00001152 pjsua_call **p_call,
1153 pjsip_dialog **p_dlg)
Benny Prijono148c9dd2006-09-19 13:37:53 +00001154{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001155 unsigned retry;
Benny Prijonod524e822006-09-22 12:48:18 +00001156 pjsua_call *call = NULL;
1157 pj_bool_t has_pjsua_lock = PJ_FALSE;
1158 pj_status_t status = PJ_SUCCESS;
Sauw Ming844c1c92010-09-07 05:12:02 +00001159 pj_time_val time_start, timeout;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001160
Sauw Ming844c1c92010-09-07 05:12:02 +00001161 pj_gettimeofday(&time_start);
1162 timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT;
1163 pj_time_val_normalize(&timeout);
1164
1165 for (retry=0; ; ++retry) {
1166
1167 if (retry % 10 == 9) {
1168 pj_time_val dtime;
1169
1170 pj_gettimeofday(&dtime);
1171 PJ_TIME_VAL_SUB(dtime, time_start);
1172 if (!PJ_TIME_VAL_LT(dtime, timeout))
1173 break;
1174 }
Benny Prijono148c9dd2006-09-19 13:37:53 +00001175
1176 has_pjsua_lock = PJ_FALSE;
1177
1178 status = PJSUA_TRY_LOCK();
1179 if (status != PJ_SUCCESS) {
1180 pj_thread_sleep(retry/10);
1181 continue;
1182 }
1183
1184 has_pjsua_lock = PJ_TRUE;
1185 call = &pjsua_var.calls[call_id];
1186
1187 if (call->inv == NULL) {
1188 PJSUA_UNLOCK();
1189 PJ_LOG(3,(THIS_FILE, "Invalid call_id %d in %s", call_id, title));
1190 return PJSIP_ESESSIONTERMINATED;
1191 }
1192
1193 status = pjsip_dlg_try_inc_lock(call->inv->dlg);
1194 if (status != PJ_SUCCESS) {
1195 PJSUA_UNLOCK();
1196 pj_thread_sleep(retry/10);
1197 continue;
1198 }
1199
1200 PJSUA_UNLOCK();
1201
1202 break;
1203 }
1204
1205 if (status != PJ_SUCCESS) {
1206 if (has_pjsua_lock == PJ_FALSE)
1207 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire PJSUA mutex "
1208 "(possibly system has deadlocked) in %s",
1209 title));
1210 else
1211 PJ_LOG(1,(THIS_FILE, "Timed-out trying to acquire dialog mutex "
1212 "(possibly system has deadlocked) in %s",
1213 title));
1214 return PJ_ETIMEDOUT;
1215 }
1216
1217 *p_call = call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001218 *p_dlg = call->inv->dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001219
1220 return PJ_SUCCESS;
1221}
1222
1223
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001224/*
1225 * Get the conference port identification associated with the call.
1226 */
1227PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
1228{
Benny Prijono148c9dd2006-09-19 13:37:53 +00001229 pjsua_call *call;
1230 pjsua_conf_port_id port_id;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001231 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001232 pj_status_t status;
1233
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001234 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1235 PJ_EINVAL);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001236
Benny Prijonodc752ca2006-09-22 16:55:42 +00001237 status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001238 if (status != PJ_SUCCESS)
Benny Prijonod524e822006-09-22 12:48:18 +00001239 return PJSUA_INVALID_ID;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001240
1241 port_id = call->conf_slot;
1242
Benny Prijonodc752ca2006-09-22 16:55:42 +00001243 pjsip_dlg_dec_lock(dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001244
1245 return port_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001246}
1247
1248
Benny Prijono148c9dd2006-09-19 13:37:53 +00001249
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001250/*
1251 * Obtain detail information about the specified call.
1252 */
1253PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id,
1254 pjsua_call_info *info)
1255{
1256 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001257 pjsip_dialog *dlg;
Benny Prijono148c9dd2006-09-19 13:37:53 +00001258 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001259
1260 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1261 PJ_EINVAL);
1262
Benny Prijonoac623b32006-07-03 15:19:31 +00001263 pj_bzero(info, sizeof(*info));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001264
Benny Prijonodc752ca2006-09-22 16:55:42 +00001265 status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001266 if (status != PJ_SUCCESS) {
1267 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001268 }
1269
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001270 /* id and role */
1271 info->id = call_id;
1272 info->role = call->inv->role;
Benny Prijono90315512006-09-14 16:05:16 +00001273 info->acc_id = call->acc_id;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001274
1275 /* local info */
1276 info->local_info.ptr = info->buf_.local_info;
1277 pj_strncpy(&info->local_info, &call->inv->dlg->local.info_str,
1278 sizeof(info->buf_.local_info));
1279
1280 /* local contact */
1281 info->local_contact.ptr = info->buf_.local_contact;
1282 info->local_contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1283 call->inv->dlg->local.contact->uri,
1284 info->local_contact.ptr,
1285 sizeof(info->buf_.local_contact));
1286
1287 /* remote info */
1288 info->remote_info.ptr = info->buf_.remote_info;
1289 pj_strncpy(&info->remote_info, &call->inv->dlg->remote.info_str,
1290 sizeof(info->buf_.remote_info));
1291
1292 /* remote contact */
1293 if (call->inv->dlg->remote.contact) {
1294 int len;
1295 info->remote_contact.ptr = info->buf_.remote_contact;
1296 len = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
1297 call->inv->dlg->remote.contact->uri,
1298 info->remote_contact.ptr,
1299 sizeof(info->buf_.remote_contact));
1300 if (len < 0) len = 0;
1301 info->remote_contact.slen = len;
1302 } else {
1303 info->remote_contact.slen = 0;
1304 }
1305
1306 /* call id */
1307 info->call_id.ptr = info->buf_.call_id;
1308 pj_strncpy(&info->call_id, &call->inv->dlg->call_id->id,
1309 sizeof(info->buf_.call_id));
1310
1311 /* state, state_text */
1312 info->state = call->inv->state;
1313 info->state_text = pj_str((char*)pjsip_inv_state_name(info->state));
1314
1315 /* If call is disconnected, set the last_status from the cause code */
1316 if (call->inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
1317 /* last_status, last_status_text */
1318 info->last_status = call->inv->cause;
1319
1320 info->last_status_text.ptr = info->buf_.last_status_text;
1321 pj_strncpy(&info->last_status_text, &call->inv->cause_text,
1322 sizeof(info->buf_.last_status_text));
1323 } else {
1324 /* last_status, last_status_text */
1325 info->last_status = call->last_code;
1326
1327 info->last_status_text.ptr = info->buf_.last_status_text;
1328 pj_strncpy(&info->last_status_text, &call->last_text,
1329 sizeof(info->buf_.last_status_text));
1330 }
1331
1332 /* media status and dir */
1333 info->media_status = call->media_st;
1334 info->media_dir = call->media_dir;
1335
1336
1337 /* conference slot number */
1338 info->conf_slot = call->conf_slot;
1339
1340 /* calculate duration */
1341 if (info->state >= PJSIP_INV_STATE_DISCONNECTED) {
1342
1343 info->total_duration = call->dis_time;
1344 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1345
1346 if (call->conn_time.sec) {
1347 info->connect_duration = call->dis_time;
1348 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1349 }
1350
1351 } else if (info->state == PJSIP_INV_STATE_CONFIRMED) {
1352
1353 pj_gettimeofday(&info->total_duration);
1354 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1355
1356 pj_gettimeofday(&info->connect_duration);
1357 PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time);
1358
1359 } else {
1360 pj_gettimeofday(&info->total_duration);
1361 PJ_TIME_VAL_SUB(info->total_duration, call->start_time);
1362 }
1363
Benny Prijonodc752ca2006-09-22 16:55:42 +00001364 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001365
1366 return PJ_SUCCESS;
1367}
1368
Nanang Izzuddin2a1b9ee2010-06-03 10:41:32 +00001369/*
1370 * Check if call remote peer support the specified capability.
1371 */
1372PJ_DEF(pjsip_dialog_cap_status) pjsua_call_remote_has_cap(
1373 pjsua_call_id call_id,
1374 int htype,
1375 const pj_str_t *hname,
1376 const pj_str_t *token)
1377{
1378 pjsua_call *call;
1379 pjsip_dialog *dlg;
1380 pj_status_t status;
1381 pjsip_dialog_cap_status cap_status;
1382
1383 status = acquire_call("pjsua_call_peer_has_cap()", call_id, &call, &dlg);
1384 if (status != PJ_SUCCESS)
1385 return PJSIP_DIALOG_CAP_UNKNOWN;
1386
1387 cap_status = pjsip_dlg_remote_has_cap(dlg, htype, hname, token);
1388
1389 pjsip_dlg_dec_lock(dlg);
1390
1391 return cap_status;
1392}
1393
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001394
1395/*
1396 * Attach application specific data to the call.
1397 */
1398PJ_DEF(pj_status_t) pjsua_call_set_user_data( pjsua_call_id call_id,
1399 void *user_data)
1400{
1401 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1402 PJ_EINVAL);
1403 pjsua_var.calls[call_id].user_data = user_data;
1404
1405 return PJ_SUCCESS;
1406}
1407
1408
1409/*
1410 * Get user data attached to the call.
1411 */
1412PJ_DEF(void*) pjsua_call_get_user_data(pjsua_call_id call_id)
1413{
1414 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1415 NULL);
1416 return pjsua_var.calls[call_id].user_data;
1417}
1418
1419
1420/*
Benny Prijono91a6a172007-10-31 08:59:29 +00001421 * Get remote's NAT type.
1422 */
1423PJ_DEF(pj_status_t) pjsua_call_get_rem_nat_type(pjsua_call_id call_id,
1424 pj_stun_nat_type *p_type)
1425{
1426 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1427 PJ_EINVAL);
1428 PJ_ASSERT_RETURN(p_type != NULL, PJ_EINVAL);
1429
1430 *p_type = pjsua_var.calls[call_id].rem_nat_type;
1431 return PJ_SUCCESS;
1432}
1433
1434
1435/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001436 * Send response to incoming INVITE request.
1437 */
1438PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id,
1439 unsigned code,
1440 const pj_str_t *reason,
1441 const pjsua_msg_data *msg_data)
1442{
1443 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001444 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001445 pjsip_tx_data *tdata;
1446 pj_status_t status;
1447
1448 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1449 PJ_EINVAL);
1450
Benny Prijonodc752ca2006-09-22 16:55:42 +00001451 status = acquire_call("pjsua_call_answer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001452 if (status != PJ_SUCCESS)
1453 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001454
Benny Prijono2e507c22006-06-23 15:04:11 +00001455 if (call->res_time.sec == 0)
1456 pj_gettimeofday(&call->res_time);
1457
Benny Prijonoed7a5a72007-01-29 18:36:38 +00001458 if (reason && reason->slen == 0)
1459 reason = NULL;
1460
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001461 /* Create response message */
1462 status = pjsip_inv_answer(call->inv, code, reason, NULL, &tdata);
1463 if (status != PJ_SUCCESS) {
1464 pjsua_perror(THIS_FILE, "Error creating response",
1465 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001466 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001467 return status;
1468 }
1469
Benny Prijonofed7f4c2007-03-27 11:01:45 +00001470 /* Call might have been disconnected if application is answering with
1471 * 200/OK and the media failed to start.
1472 */
1473 if (call->inv == NULL) {
1474 pjsip_dlg_dec_lock(dlg);
1475 return PJSIP_ESESSIONTERMINATED;
1476 }
1477
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001478 /* Add additional headers etc */
1479 pjsua_process_msg_data( tdata, msg_data);
1480
1481 /* Send the message */
1482 status = pjsip_inv_send_msg(call->inv, tdata);
1483 if (status != PJ_SUCCESS)
1484 pjsua_perror(THIS_FILE, "Error sending response",
1485 status);
1486
Benny Prijonodc752ca2006-09-22 16:55:42 +00001487 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001488
1489 return status;
1490}
1491
1492
1493/*
1494 * Hangup call by using method that is appropriate according to the
1495 * call state.
1496 */
1497PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
1498 unsigned code,
1499 const pj_str_t *reason,
1500 const pjsua_msg_data *msg_data)
1501{
1502 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001503 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001504 pj_status_t status;
1505 pjsip_tx_data *tdata;
1506
1507
Benny Prijono148c9dd2006-09-19 13:37:53 +00001508 if (call_id<0 || call_id>=(int)pjsua_var.ua_cfg.max_calls) {
1509 PJ_LOG(1,(THIS_FILE, "pjsua_call_hangup(): invalid call id %d",
1510 call_id));
1511 }
1512
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001513 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1514 PJ_EINVAL);
1515
Benny Prijonodc752ca2006-09-22 16:55:42 +00001516 status = acquire_call("pjsua_call_hangup()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001517 if (status != PJ_SUCCESS)
1518 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001519
1520 if (code==0) {
1521 if (call->inv->state == PJSIP_INV_STATE_CONFIRMED)
1522 code = PJSIP_SC_OK;
1523 else if (call->inv->role == PJSIP_ROLE_UAS)
1524 code = PJSIP_SC_DECLINE;
1525 else
1526 code = PJSIP_SC_REQUEST_TERMINATED;
1527 }
1528
1529 status = pjsip_inv_end_session(call->inv, code, reason, &tdata);
1530 if (status != PJ_SUCCESS) {
1531 pjsua_perror(THIS_FILE,
1532 "Failed to create end session message",
1533 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001534 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001535 return status;
1536 }
1537
1538 /* pjsip_inv_end_session may return PJ_SUCCESS with NULL
1539 * as p_tdata when INVITE transaction has not been answered
1540 * with any provisional responses.
1541 */
1542 if (tdata == NULL) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001543 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001544 return PJ_SUCCESS;
1545 }
1546
1547 /* Add additional headers etc */
1548 pjsua_process_msg_data( tdata, msg_data);
1549
1550 /* Send the message */
1551 status = pjsip_inv_send_msg(call->inv, tdata);
1552 if (status != PJ_SUCCESS) {
1553 pjsua_perror(THIS_FILE,
1554 "Failed to send end session message",
1555 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001556 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001557 return status;
1558 }
1559
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00001560 /* Stop lock codec timer, if it is active */
1561 if (call->lock_codec.reinv_timer.id) {
1562 pjsip_endpt_cancel_timer(pjsua_var.endpt,
1563 &call->lock_codec.reinv_timer);
1564 call->lock_codec.reinv_timer.id = PJ_FALSE;
1565 }
1566
Benny Prijonodc752ca2006-09-22 16:55:42 +00001567 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001568
1569 return PJ_SUCCESS;
1570}
1571
1572
1573/*
Benny Prijono5e51a4e2008-11-27 00:06:46 +00001574 * Accept or reject redirection.
1575 */
1576PJ_DEF(pj_status_t) pjsua_call_process_redirect( pjsua_call_id call_id,
1577 pjsip_redirect_op cmd)
1578{
1579 pjsua_call *call;
1580 pjsip_dialog *dlg;
1581 pj_status_t status;
1582
1583 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1584 PJ_EINVAL);
1585
1586 status = acquire_call("pjsua_call_process_redirect()", call_id,
1587 &call, &dlg);
1588 if (status != PJ_SUCCESS)
1589 return status;
1590
1591 status = pjsip_inv_process_redirect(call->inv, cmd, NULL);
1592
1593 pjsip_dlg_dec_lock(dlg);
1594
1595 return status;
1596}
1597
1598
1599/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001600 * Put the specified call on hold.
1601 */
1602PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id,
1603 const pjsua_msg_data *msg_data)
1604{
1605 pjmedia_sdp_session *sdp;
1606 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001607 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001608 pjsip_tx_data *tdata;
1609 pj_status_t status;
1610
1611 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1612 PJ_EINVAL);
1613
Benny Prijonodc752ca2006-09-22 16:55:42 +00001614 status = acquire_call("pjsua_call_set_hold()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001615 if (status != PJ_SUCCESS)
1616 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001617
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001618
1619 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1620 PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001621 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001622 return PJSIP_ESESSIONSTATE;
1623 }
1624
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001625 status = create_sdp_of_call_hold(call, &sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001626 if (status != PJ_SUCCESS) {
Benny Prijonodc752ca2006-09-22 16:55:42 +00001627 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001628 return status;
1629 }
1630
1631 /* Create re-INVITE with new offer */
1632 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1633 if (status != PJ_SUCCESS) {
1634 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001635 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001636 return status;
1637 }
1638
1639 /* Add additional headers etc */
1640 pjsua_process_msg_data( tdata, msg_data);
1641
1642 /* Send the request */
1643 status = pjsip_inv_send_msg( call->inv, tdata);
1644 if (status != PJ_SUCCESS) {
1645 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001646 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001647 return status;
1648 }
1649
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001650 /* Set flag that local put the call on hold */
1651 call->local_hold = PJ_TRUE;
1652
Benny Prijonodc752ca2006-09-22 16:55:42 +00001653 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001654
1655 return PJ_SUCCESS;
1656}
1657
1658
1659/*
1660 * Send re-INVITE (to release hold).
1661 */
1662PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id,
1663 pj_bool_t unhold,
1664 const pjsua_msg_data *msg_data)
1665{
1666 pjmedia_sdp_session *sdp;
1667 pjsip_tx_data *tdata;
1668 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001669 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001670 pj_status_t status;
1671
1672
1673 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1674 PJ_EINVAL);
1675
Benny Prijonodc752ca2006-09-22 16:55:42 +00001676 status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001677 if (status != PJ_SUCCESS)
1678 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001679
1680 if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
1681 PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001682 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001683 return PJSIP_ESESSIONSTATE;
1684 }
1685
1686 /* Create SDP */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001687 if (call->local_hold && !unhold) {
1688 status = create_sdp_of_call_hold(call, &sdp);
1689 } else {
Benny Prijono40d62b62009-08-12 17:53:47 +00001690 status = pjsua_media_channel_create_sdp(call->index,
1691 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001692 NULL, &sdp, NULL);
1693 call->local_hold = PJ_FALSE;
1694 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001695 if (status != PJ_SUCCESS) {
1696 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1697 status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001698 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001699 return status;
1700 }
1701
1702 /* Create re-INVITE with new offer */
1703 status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata);
1704 if (status != PJ_SUCCESS) {
1705 pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001706 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001707 return status;
1708 }
1709
1710 /* Add additional headers etc */
1711 pjsua_process_msg_data( tdata, msg_data);
1712
1713 /* Send the request */
1714 status = pjsip_inv_send_msg( call->inv, tdata);
1715 if (status != PJ_SUCCESS) {
1716 pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001717 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001718 return status;
1719 }
1720
Benny Prijonodc752ca2006-09-22 16:55:42 +00001721 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001722
1723 return PJ_SUCCESS;
1724}
1725
1726
1727/*
Benny Prijonoc08682e2007-10-04 06:17:58 +00001728 * Send UPDATE request.
1729 */
1730PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id,
1731 unsigned options,
1732 const pjsua_msg_data *msg_data)
1733{
1734 pjmedia_sdp_session *sdp;
1735 pjsip_tx_data *tdata;
1736 pjsua_call *call;
1737 pjsip_dialog *dlg;
1738 pj_status_t status;
1739
1740 PJ_UNUSED_ARG(options);
1741
1742 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1743 PJ_EINVAL);
1744
1745 status = acquire_call("pjsua_call_update()", call_id, &call, &dlg);
1746 if (status != PJ_SUCCESS)
1747 return status;
1748
Benny Prijonoc08682e2007-10-04 06:17:58 +00001749 /* Create SDP */
Benny Prijono40d62b62009-08-12 17:53:47 +00001750 status = pjsua_media_channel_create_sdp(call->index,
1751 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00001752 NULL, &sdp, NULL);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001753 if (status != PJ_SUCCESS) {
1754 pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
1755 status);
1756 pjsip_dlg_dec_lock(dlg);
1757 return status;
1758 }
1759
Benny Prijono224b4e22008-06-19 14:10:28 +00001760 /* Create UPDATE with new offer */
Benny Prijonoc08682e2007-10-04 06:17:58 +00001761 status = pjsip_inv_update(call->inv, NULL, sdp, &tdata);
1762 if (status != PJ_SUCCESS) {
1763 pjsua_perror(THIS_FILE, "Unable to create UPDATE request", status);
1764 pjsip_dlg_dec_lock(dlg);
1765 return status;
1766 }
1767
1768 /* Add additional headers etc */
1769 pjsua_process_msg_data( tdata, msg_data);
1770
1771 /* Send the request */
1772 status = pjsip_inv_send_msg( call->inv, tdata);
1773 if (status != PJ_SUCCESS) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001774 pjsua_perror(THIS_FILE, "Unable to send UPDATE request", status);
Benny Prijonoc08682e2007-10-04 06:17:58 +00001775 pjsip_dlg_dec_lock(dlg);
1776 return status;
1777 }
1778
Nanang Izzuddin99d69522008-08-04 15:01:38 +00001779 call->local_hold = PJ_FALSE;
1780
Benny Prijonoc08682e2007-10-04 06:17:58 +00001781 pjsip_dlg_dec_lock(dlg);
1782
1783 return PJ_SUCCESS;
1784}
1785
1786
1787/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001788 * Initiate call transfer to the specified address.
1789 */
1790PJ_DEF(pj_status_t) pjsua_call_xfer( pjsua_call_id call_id,
1791 const pj_str_t *dest,
1792 const pjsua_msg_data *msg_data)
1793{
1794 pjsip_evsub *sub;
1795 pjsip_tx_data *tdata;
1796 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001797 pjsip_dialog *dlg;
Benny Prijono053f5222006-11-11 16:16:04 +00001798 pjsip_generic_string_hdr *gs_hdr;
1799 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijonod524e822006-09-22 12:48:18 +00001800 struct pjsip_evsub_user xfer_cb;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001801 pj_status_t status;
1802
1803
1804 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1805 PJ_EINVAL);
1806
Benny Prijonodc752ca2006-09-22 16:55:42 +00001807 status = acquire_call("pjsua_call_xfer()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001808 if (status != PJ_SUCCESS)
1809 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001810
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001811
Benny Prijonod524e822006-09-22 12:48:18 +00001812 /* Create xfer client subscription. */
1813 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001814 xfer_cb.on_evsub_state = &xfer_client_on_evsub_state;
Benny Prijonod524e822006-09-22 12:48:18 +00001815
1816 status = pjsip_xfer_create_uac(call->inv->dlg, &xfer_cb, &sub);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001817 if (status != PJ_SUCCESS) {
1818 pjsua_perror(THIS_FILE, "Unable to create xfer", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001819 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001820 return status;
1821 }
1822
Benny Prijono4ddad2c2006-10-18 17:16:34 +00001823 /* Associate this call with the client subscription */
1824 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, call);
1825
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001826 /*
1827 * Create REFER request.
1828 */
1829 status = pjsip_xfer_initiate(sub, dest, &tdata);
1830 if (status != PJ_SUCCESS) {
1831 pjsua_perror(THIS_FILE, "Unable to create REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001832 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001833 return status;
1834 }
1835
Benny Prijono053f5222006-11-11 16:16:04 +00001836 /* Add Referred-By header */
1837 gs_hdr = pjsip_generic_string_hdr_create(tdata->pool, &str_ref_by,
1838 &dlg->local.info_str);
1839 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gs_hdr);
1840
1841
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001842 /* Add additional headers etc */
1843 pjsua_process_msg_data( tdata, msg_data);
1844
1845 /* Send. */
1846 status = pjsip_xfer_send_request(sub, tdata);
1847 if (status != PJ_SUCCESS) {
1848 pjsua_perror(THIS_FILE, "Unable to send REFER request", status);
Benny Prijonodc752ca2006-09-22 16:55:42 +00001849 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001850 return status;
1851 }
1852
1853 /* For simplicity (that's what this program is intended to be!),
1854 * leave the original invite session as it is. More advanced application
1855 * may want to hold the INVITE, or terminate the invite, or whatever.
1856 */
1857
Benny Prijonodc752ca2006-09-22 16:55:42 +00001858 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001859
1860 return PJ_SUCCESS;
1861
1862}
1863
1864
1865/*
Benny Prijono053f5222006-11-11 16:16:04 +00001866 * Initiate attended call transfer to the specified address.
1867 */
1868PJ_DEF(pj_status_t) pjsua_call_xfer_replaces( pjsua_call_id call_id,
1869 pjsua_call_id dest_call_id,
1870 unsigned options,
1871 const pjsua_msg_data *msg_data)
1872{
1873 pjsua_call *dest_call;
1874 pjsip_dialog *dest_dlg;
Benny Prijono38fb3ea2008-01-02 08:27:03 +00001875 char str_dest_buf[PJSIP_MAX_URL_SIZE*2];
Benny Prijono053f5222006-11-11 16:16:04 +00001876 pj_str_t str_dest;
1877 int len;
1878 pjsip_uri *uri;
1879 pj_status_t status;
1880
1881
1882 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1883 PJ_EINVAL);
1884 PJ_ASSERT_RETURN(dest_call_id>=0 &&
1885 dest_call_id<(int)pjsua_var.ua_cfg.max_calls,
1886 PJ_EINVAL);
1887
1888 status = acquire_call("pjsua_call_xfer_replaces()", dest_call_id,
1889 &dest_call, &dest_dlg);
1890 if (status != PJ_SUCCESS)
1891 return status;
1892
1893 /*
1894 * Create REFER destination URI with Replaces field.
1895 */
1896
1897 /* Make sure we have sufficient buffer's length */
1898 PJ_ASSERT_RETURN( dest_dlg->remote.info_str.slen +
1899 dest_dlg->call_id->id.slen +
1900 dest_dlg->remote.info->tag.slen +
1901 dest_dlg->local.info->tag.slen + 32
Benny Prijonoa1e69682007-05-11 15:14:34 +00001902 < (long)sizeof(str_dest_buf), PJSIP_EURITOOLONG);
Benny Prijono053f5222006-11-11 16:16:04 +00001903
1904 /* Print URI */
1905 str_dest_buf[0] = '<';
1906 str_dest.slen = 1;
1907
Benny Prijonoa1e69682007-05-11 15:14:34 +00001908 uri = (pjsip_uri*) pjsip_uri_get_uri(dest_dlg->remote.info->uri);
Benny Prijono053f5222006-11-11 16:16:04 +00001909 len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri,
1910 str_dest_buf+1, sizeof(str_dest_buf)-1);
1911 if (len < 0)
1912 return PJSIP_EURITOOLONG;
1913
1914 str_dest.slen += len;
1915
1916
1917 /* Build the URI */
1918 len = pj_ansi_snprintf(str_dest_buf + str_dest.slen,
1919 sizeof(str_dest_buf) - str_dest.slen,
1920 "?%s"
1921 "Replaces=%.*s"
1922 "%%3Bto-tag%%3D%.*s"
1923 "%%3Bfrom-tag%%3D%.*s>",
1924 ((options&PJSUA_XFER_NO_REQUIRE_REPLACES) ?
1925 "" : "Require=replaces&"),
1926 (int)dest_dlg->call_id->id.slen,
1927 dest_dlg->call_id->id.ptr,
1928 (int)dest_dlg->remote.info->tag.slen,
1929 dest_dlg->remote.info->tag.ptr,
1930 (int)dest_dlg->local.info->tag.slen,
1931 dest_dlg->local.info->tag.ptr);
1932
1933 PJ_ASSERT_RETURN(len > 0 && len <= (int)sizeof(str_dest_buf)-str_dest.slen,
1934 PJSIP_EURITOOLONG);
1935
1936 str_dest.ptr = str_dest_buf;
1937 str_dest.slen += len;
1938
1939 pjsip_dlg_dec_lock(dest_dlg);
1940
1941 return pjsua_call_xfer(call_id, &str_dest, msg_data);
1942}
1943
1944
1945/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001946 * Send DTMF digits to remote using RFC 2833 payload formats.
1947 */
1948PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
1949 const pj_str_t *digits)
1950{
1951 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001952 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001953 pj_status_t status;
1954
1955 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1956 PJ_EINVAL);
1957
Benny Prijonodc752ca2006-09-22 16:55:42 +00001958 status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001959 if (status != PJ_SUCCESS)
1960 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001961
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001962 if (!call->session) {
1963 PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
Benny Prijonodc752ca2006-09-22 16:55:42 +00001964 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001965 return PJ_EINVALIDOP;
1966 }
1967
1968 status = pjmedia_session_dial_dtmf( call->session, 0, digits);
1969
Benny Prijonodc752ca2006-09-22 16:55:42 +00001970 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001971
1972 return status;
1973}
1974
1975
1976/**
1977 * Send instant messaging inside INVITE session.
1978 */
1979PJ_DEF(pj_status_t) pjsua_call_send_im( pjsua_call_id call_id,
1980 const pj_str_t *mime_type,
1981 const pj_str_t *content,
1982 const pjsua_msg_data *msg_data,
1983 void *user_data)
1984{
1985 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00001986 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00001987 const pj_str_t mime_text_plain = pj_str("text/plain");
1988 pjsip_media_type ctype;
1989 pjsua_im_data *im_data;
1990 pjsip_tx_data *tdata;
1991 pj_status_t status;
1992
1993
1994 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
1995 PJ_EINVAL);
1996
Benny Prijonodc752ca2006-09-22 16:55:42 +00001997 status = acquire_call("pjsua_call_send_im()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00001998 if (status != PJ_SUCCESS)
1999 return status;
2000
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002001 /* Set default media type if none is specified */
2002 if (mime_type == NULL) {
2003 mime_type = &mime_text_plain;
2004 }
2005
2006 /* Create request message. */
2007 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2008 -1, &tdata);
2009 if (status != PJ_SUCCESS) {
2010 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2011 goto on_return;
2012 }
2013
2014 /* Add accept header. */
2015 pjsip_msg_add_hdr( tdata->msg,
2016 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
2017
2018 /* Parse MIME type */
2019 pjsua_parse_media_type(tdata->pool, mime_type, &ctype);
2020
2021 /* Create "text/plain" message body. */
2022 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &ctype.type,
2023 &ctype.subtype, content);
2024 if (tdata->msg->body == NULL) {
2025 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
2026 pjsip_tx_data_dec_ref(tdata);
2027 goto on_return;
2028 }
2029
2030 /* Add additional headers etc */
2031 pjsua_process_msg_data( tdata, msg_data);
2032
2033 /* Create IM data and attach to the request. */
Benny Prijonoa1e69682007-05-11 15:14:34 +00002034 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002035 im_data->acc_id = call->acc_id;
2036 im_data->call_id = call_id;
2037 im_data->to = call->inv->dlg->remote.info_str;
2038 pj_strdup_with_null(tdata->pool, &im_data->body, content);
2039 im_data->user_data = user_data;
2040
2041
2042 /* Send the request. */
2043 status = pjsip_dlg_send_request( call->inv->dlg, tdata,
2044 pjsua_var.mod.id, im_data);
2045 if (status != PJ_SUCCESS) {
2046 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2047 goto on_return;
2048 }
2049
2050on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002051 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002052 return status;
2053}
2054
2055
2056/*
2057 * Send IM typing indication inside INVITE session.
2058 */
2059PJ_DEF(pj_status_t) pjsua_call_send_typing_ind( pjsua_call_id call_id,
2060 pj_bool_t is_typing,
2061 const pjsua_msg_data*msg_data)
2062{
2063 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002064 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002065 pjsip_tx_data *tdata;
2066 pj_status_t status;
2067
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002068 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2069 PJ_EINVAL);
2070
Benny Prijonodc752ca2006-09-22 16:55:42 +00002071 status = acquire_call("pjsua_call_send_typing_ind", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002072 if (status != PJ_SUCCESS)
2073 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002074
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002075 /* Create request message. */
2076 status = pjsip_dlg_create_request( call->inv->dlg, &pjsip_message_method,
2077 -1, &tdata);
2078 if (status != PJ_SUCCESS) {
2079 pjsua_perror(THIS_FILE, "Unable to create MESSAGE request", status);
2080 goto on_return;
2081 }
2082
2083 /* Create "application/im-iscomposing+xml" msg body. */
2084 tdata->msg->body = pjsip_iscomposing_create_body(tdata->pool, is_typing,
2085 NULL, NULL, -1);
2086
2087 /* Add additional headers etc */
2088 pjsua_process_msg_data( tdata, msg_data);
2089
2090 /* Send the request. */
2091 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2092 if (status != PJ_SUCCESS) {
2093 pjsua_perror(THIS_FILE, "Unable to send MESSAGE request", status);
2094 goto on_return;
2095 }
2096
2097on_return:
Benny Prijonodc752ca2006-09-22 16:55:42 +00002098 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002099 return status;
2100}
2101
2102
2103/*
Benny Prijonofeb69f42007-10-05 09:12:26 +00002104 * Send arbitrary request.
2105 */
2106PJ_DEF(pj_status_t) pjsua_call_send_request(pjsua_call_id call_id,
2107 const pj_str_t *method_str,
2108 const pjsua_msg_data *msg_data)
2109{
2110 pjsua_call *call;
2111 pjsip_dialog *dlg;
2112 pjsip_method method;
2113 pjsip_tx_data *tdata;
2114 pj_status_t status;
2115
2116 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2117 PJ_EINVAL);
2118
2119 status = acquire_call("pjsua_call_send_request", call_id, &call, &dlg);
2120 if (status != PJ_SUCCESS)
2121 return status;
2122
2123 /* Init method */
2124 pjsip_method_init_np(&method, (pj_str_t*)method_str);
2125
2126 /* Create request message. */
2127 status = pjsip_dlg_create_request( call->inv->dlg, &method, -1, &tdata);
2128 if (status != PJ_SUCCESS) {
2129 pjsua_perror(THIS_FILE, "Unable to create request", status);
2130 goto on_return;
2131 }
2132
2133 /* Add additional headers etc */
2134 pjsua_process_msg_data( tdata, msg_data);
2135
2136 /* Send the request. */
2137 status = pjsip_dlg_send_request( call->inv->dlg, tdata, -1, NULL);
2138 if (status != PJ_SUCCESS) {
2139 pjsua_perror(THIS_FILE, "Unable to send request", status);
2140 goto on_return;
2141 }
2142
2143on_return:
2144 pjsip_dlg_dec_lock(dlg);
2145 return status;
2146}
2147
2148
2149/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002150 * Terminate all calls.
2151 */
2152PJ_DEF(void) pjsua_call_hangup_all(void)
2153{
2154 unsigned i;
2155
2156 PJSUA_LOCK();
2157
2158 for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
2159 if (pjsua_var.calls[i].inv)
2160 pjsua_call_hangup(i, 0, NULL, NULL);
2161 }
2162
2163 PJSUA_UNLOCK();
2164}
2165
2166
Benny Prijono627cbb42007-09-25 20:48:49 +00002167const char *good_number(char *buf, pj_int32_t val)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002168{
2169 if (val < 1000) {
2170 pj_ansi_sprintf(buf, "%d", val);
2171 } else if (val < 1000000) {
2172 pj_ansi_sprintf(buf, "%d.%dK",
2173 val / 1000,
2174 (val % 1000) / 100);
2175 } else {
2176 pj_ansi_sprintf(buf, "%d.%02dM",
2177 val / 1000000,
2178 (val % 1000000) / 10000);
2179 }
2180
2181 return buf;
2182}
2183
2184
2185/* Dump media session */
2186static void dump_media_session(const char *indent,
2187 char *buf, unsigned maxlen,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002188 pjsua_call *call)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002189{
2190 unsigned i;
2191 char *p = buf, *end = buf+maxlen;
2192 int len;
2193 pjmedia_session_info info;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002194 pjmedia_session *session = call->session;
2195 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002196
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002197 pjmedia_transport_info_init(&tp_info);
2198
2199 pjmedia_transport_get_info(call->med_tp, &tp_info);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002200 pjmedia_session_get_info(session, &info);
2201
2202 for (i=0; i<info.stream_cnt; ++i) {
2203 pjmedia_rtcp_stat stat;
Benny Prijono5186eae2007-12-03 14:38:25 +00002204 char rem_addr_buf[80];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002205 const char *rem_addr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002206 const char *dir;
Benny Prijonoe85bc412006-07-29 20:29:24 +00002207 char last_update[64];
Benny Prijono427af7d2007-05-11 10:36:40 +00002208 char packets[32], bytes[32], ipbytes[32], avg_bps[32], avg_ipbps[32];
Benny Prijono80019eb2006-08-07 13:22:23 +00002209 pj_time_val media_duration, now;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002210
2211 pjmedia_session_get_stream_stat(session, i, &stat);
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002212 // rem_addr will contain actual address of RTP originator, instead of
2213 // remote RTP address specified by stream which is fetched from the SDP.
2214 // Please note that we are assuming only one stream per call.
2215 //rem_addr = pj_sockaddr_print(&info.stream_info[i].rem_addr,
2216 // rem_addr_buf, sizeof(rem_addr_buf), 3);
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002217 if (pj_sockaddr_has_addr(&tp_info.src_rtp_name)) {
2218 rem_addr = pj_sockaddr_print(&tp_info.src_rtp_name, rem_addr_buf,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002219 sizeof(rem_addr_buf), 3);
2220 } else {
2221 pj_ansi_snprintf(rem_addr_buf, sizeof(rem_addr_buf), "-");
Nanang Izzuddin4494a482008-09-18 12:58:33 +00002222 rem_addr = rem_addr_buf;
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002223 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002224
Benny Prijono2dbf5072010-06-23 12:38:28 +00002225 if (call->media_dir == PJMEDIA_DIR_NONE) {
2226 /* To handle when the stream that is currently being paused
2227 * (http://trac.pjsip.org/repos/ticket/1079)
2228 */
2229 dir = "inactive";
2230 } else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002231 dir = "sendonly";
2232 else if (info.stream_info[i].dir == PJMEDIA_DIR_DECODING)
2233 dir = "recvonly";
2234 else if (info.stream_info[i].dir == PJMEDIA_DIR_ENCODING_DECODING)
2235 dir = "sendrecv";
2236 else
2237 dir = "inactive";
2238
2239
2240 len = pj_ansi_snprintf(buf, end-p,
Benny Prijono5186eae2007-12-03 14:38:25 +00002241 "%s #%d %.*s @%dKHz, %s, peer=%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002242 indent, i,
Benny Prijono172cd732006-06-14 20:22:31 +00002243 (int)info.stream_info[i].fmt.encoding_name.slen,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002244 info.stream_info[i].fmt.encoding_name.ptr,
2245 info.stream_info[i].fmt.clock_rate / 1000,
2246 dir,
Benny Prijono5186eae2007-12-03 14:38:25 +00002247 rem_addr);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002248 if (len < 1 || len > end-p) {
2249 *p = '\0';
2250 return;
2251 }
2252
2253 p += len;
2254 *p++ = '\n';
2255 *p = '\0';
2256
2257 if (stat.rx.update_cnt == 0)
2258 strcpy(last_update, "never");
2259 else {
2260 pj_gettimeofday(&now);
2261 PJ_TIME_VAL_SUB(now, stat.rx.update);
2262 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2263 now.sec / 3600,
2264 (now.sec % 3600) / 60,
2265 now.sec % 60,
2266 now.msec);
2267 }
2268
Benny Prijono80019eb2006-08-07 13:22:23 +00002269 pj_gettimeofday(&media_duration);
2270 PJ_TIME_VAL_SUB(media_duration, stat.start);
2271 if (PJ_TIME_VAL_MSEC(media_duration) == 0)
2272 media_duration.msec = 1;
2273
Benny Prijono1402a4a2008-01-08 23:41:22 +00002274 /* protect against division by zero */
2275 if (stat.rx.pkt == 0)
2276 stat.rx.pkt = 1;
2277 if (stat.tx.pkt == 0)
2278 stat.tx.pkt = 1;
2279
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002280 len = pj_ansi_snprintf(p, end-p,
2281 "%s RX pt=%d, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002282 "%s total %spkt %sB (%sB +IP hdr) @avg=%sbps/%sbps\n"
Benny Prijono27c256a2008-09-08 21:31:36 +00002283 "%s pkt loss=%d (%3.1f%%), discrd=%d (%3.1f%%), dup=%d (%2.1f%%), reord=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002284 "%s (msec) min avg max last dev\n"
2285 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
Nanang Izzuddin78bec1a2010-07-15 14:45:47 +00002286 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f"
2287#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2288 "\n"
2289 "%s raw jitter : %7.3f %7.3f %7.3f %7.3f %7.3f"
2290#endif
2291#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2292 "\n"
2293 "%s IPDV : %7.3f %7.3f %7.3f %7.3f %7.3f"
2294#endif
2295 "%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002296 indent, info.stream_info[i].fmt.pt,
2297 last_update,
2298 indent,
2299 good_number(packets, stat.rx.pkt),
2300 good_number(bytes, stat.rx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002301 good_number(ipbytes, stat.rx.bytes + stat.rx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002302 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.rx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2303 good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.rx.bytes + stat.rx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002304 indent,
2305 stat.rx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002306 stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijono27c256a2008-09-08 21:31:36 +00002307 stat.rx.discard,
2308 stat.rx.discard * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002309 stat.rx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002310 stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002311 stat.rx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002312 stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002313 indent, indent,
2314 stat.rx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002315 stat.rx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002316 stat.rx.loss_period.max / 1000.0,
2317 stat.rx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002318 pj_math_stat_get_stddev(&stat.rx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002319 indent,
2320 stat.rx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002321 stat.rx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002322 stat.rx.jitter.max / 1000.0,
2323 stat.rx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002324 pj_math_stat_get_stddev(&stat.rx.jitter) / 1000.0,
Nanang Izzuddin78bec1a2010-07-15 14:45:47 +00002325#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
2326 indent,
2327 stat.rx_raw_jitter.min / 1000.0,
2328 stat.rx_raw_jitter.mean / 1000.0,
2329 stat.rx_raw_jitter.max / 1000.0,
2330 stat.rx_raw_jitter.last / 1000.0,
2331 pj_math_stat_get_stddev(&stat.rx_raw_jitter) / 1000.0,
2332#endif
2333#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
2334 indent,
2335 stat.rx_ipdv.min / 1000.0,
2336 stat.rx_ipdv.mean / 1000.0,
2337 stat.rx_ipdv.max / 1000.0,
2338 stat.rx_ipdv.last / 1000.0,
2339 pj_math_stat_get_stddev(&stat.rx_ipdv) / 1000.0,
2340#endif
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002341 ""
2342 );
2343
2344 if (len < 1 || len > end-p) {
2345 *p = '\0';
2346 return;
2347 }
2348
2349 p += len;
2350 *p++ = '\n';
2351 *p = '\0';
2352
2353 if (stat.tx.update_cnt == 0)
2354 strcpy(last_update, "never");
2355 else {
2356 pj_gettimeofday(&now);
2357 PJ_TIME_VAL_SUB(now, stat.tx.update);
2358 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2359 now.sec / 3600,
2360 (now.sec % 3600) / 60,
2361 now.sec % 60,
2362 now.msec);
2363 }
2364
2365 len = pj_ansi_snprintf(p, end-p,
2366 "%s TX pt=%d, ptime=%dms, stat last update: %s\n"
Benny Prijono427af7d2007-05-11 10:36:40 +00002367 "%s total %spkt %sB (%sB +IP hdr) @avg %sbps/%sbps\n"
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002368 "%s pkt loss=%d (%3.1f%%), dup=%d (%3.1f%%), reorder=%d (%3.1f%%)\n"
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002369 "%s (msec) min avg max last dev \n"
2370 "%s loss period: %7.3f %7.3f %7.3f %7.3f %7.3f\n"
2371 "%s jitter : %7.3f %7.3f %7.3f %7.3f %7.3f%s",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002372 indent,
2373 info.stream_info[i].tx_pt,
2374 info.stream_info[i].param->info.frm_ptime *
2375 info.stream_info[i].param->setting.frm_per_pkt,
2376 last_update,
2377
2378 indent,
2379 good_number(packets, stat.tx.pkt),
2380 good_number(bytes, stat.tx.bytes),
Benny Prijono427af7d2007-05-11 10:36:40 +00002381 good_number(ipbytes, stat.tx.bytes + stat.tx.pkt * 40),
Benny Prijonoe723b922008-01-09 22:11:24 +00002382 good_number(avg_bps, (pj_int32_t)((pj_int64_t)stat.tx.bytes * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
2383 good_number(avg_ipbps, (pj_int32_t)(((pj_int64_t)stat.tx.bytes + stat.tx.pkt * 40) * 8 * 1000 / PJ_TIME_VAL_MSEC(media_duration))),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002384
2385 indent,
2386 stat.tx.loss,
Benny Prijonof9016512006-06-29 09:41:34 +00002387 stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002388 stat.tx.dup,
Benny Prijonob12106f2006-06-29 14:45:17 +00002389 stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002390 stat.tx.reorder,
Benny Prijonob12106f2006-06-29 14:45:17 +00002391 stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002392
2393 indent, indent,
2394 stat.tx.loss_period.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002395 stat.tx.loss_period.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002396 stat.tx.loss_period.max / 1000.0,
2397 stat.tx.loss_period.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002398 pj_math_stat_get_stddev(&stat.tx.loss_period) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002399 indent,
2400 stat.tx.jitter.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002401 stat.tx.jitter.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002402 stat.tx.jitter.max / 1000.0,
2403 stat.tx.jitter.last / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002404 pj_math_stat_get_stddev(&stat.tx.jitter) / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002405 ""
2406 );
2407
2408 if (len < 1 || len > end-p) {
2409 *p = '\0';
2410 return;
2411 }
2412
2413 p += len;
2414 *p++ = '\n';
2415 *p = '\0';
2416
2417 len = pj_ansi_snprintf(p, end-p,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002418 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002419 indent,
2420 stat.rtt.min / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002421 stat.rtt.mean / 1000.0,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002422 stat.rtt.max / 1000.0,
Nanang Izzuddin2d4ee7d2008-05-17 14:54:18 +00002423 stat.rtt.last / 1000.0,
2424 pj_math_stat_get_stddev(&stat.rtt) / 1000.0
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002425 );
2426 if (len < 1 || len > end-p) {
2427 *p = '\0';
2428 return;
2429 }
2430
2431 p += len;
2432 *p++ = '\n';
2433 *p = '\0';
Nanang Izzuddin660eee82008-07-17 14:54:03 +00002434
2435#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
2436# define SAMPLES_TO_USEC(usec, samples, clock_rate) \
2437 do { \
2438 if (samples <= 4294) \
2439 usec = samples * 1000000 / clock_rate; \
2440 else { \
2441 usec = samples * 1000 / clock_rate; \
2442 usec *= 1000; \
2443 } \
2444 } while(0)
2445
2446# define PRINT_VOIP_MTC_VAL(s, v) \
2447 if (v == 127) \
2448 sprintf(s, "(na)"); \
2449 else \
2450 sprintf(s, "%d", v)
2451
2452# define VALIDATE_PRINT_BUF() \
2453 if (len < 1 || len > end-p) { *p = '\0'; return; } \
2454 p += len; *p++ = '\n'; *p = '\0'
2455
2456
2457 do {
2458 char loss[16], dup[16];
2459 char jitter[80];
2460 char toh[80];
2461 char plc[16], jba[16], jbr[16];
2462 char signal_lvl[16], noise_lvl[16], rerl[16];
2463 char r_factor[16], ext_r_factor[16], mos_lq[16], mos_cq[16];
2464 pjmedia_rtcp_xr_stat xr_stat;
2465 unsigned clock_rate;
2466
2467 if (pjmedia_session_get_stream_stat_xr(session, i, &xr_stat) !=
2468 PJ_SUCCESS)
2469 {
2470 break;
2471 }
2472
2473 clock_rate = info.stream_info[i].fmt.clock_rate;
2474
2475 len = pj_ansi_snprintf(p, end-p, "\n%s Extended reports:", indent);
2476 VALIDATE_PRINT_BUF();
2477
2478 /* Statistics Summary */
2479 len = pj_ansi_snprintf(p, end-p, "%s Statistics Summary", indent);
2480 VALIDATE_PRINT_BUF();
2481
2482 if (xr_stat.rx.stat_sum.l)
2483 sprintf(loss, "%d", xr_stat.rx.stat_sum.lost);
2484 else
2485 sprintf(loss, "(na)");
2486
2487 if (xr_stat.rx.stat_sum.d)
2488 sprintf(dup, "%d", xr_stat.rx.stat_sum.dup);
2489 else
2490 sprintf(dup, "(na)");
2491
2492 if (xr_stat.rx.stat_sum.j) {
2493 unsigned jmin, jmax, jmean, jdev;
2494
2495 SAMPLES_TO_USEC(jmin, xr_stat.rx.stat_sum.jitter.min,
2496 clock_rate);
2497 SAMPLES_TO_USEC(jmax, xr_stat.rx.stat_sum.jitter.max,
2498 clock_rate);
2499 SAMPLES_TO_USEC(jmean, xr_stat.rx.stat_sum.jitter.mean,
2500 clock_rate);
2501 SAMPLES_TO_USEC(jdev,
2502 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.jitter),
2503 clock_rate);
2504 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2505 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2506 } else
2507 sprintf(jitter, "(report not available)");
2508
2509 if (xr_stat.rx.stat_sum.t) {
2510 sprintf(toh, "%11d %11d %11d %11d",
2511 xr_stat.rx.stat_sum.toh.min,
2512 xr_stat.rx.stat_sum.toh.mean,
2513 xr_stat.rx.stat_sum.toh.max,
2514 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2515 } else
2516 sprintf(toh, "(report not available)");
2517
2518 if (xr_stat.rx.stat_sum.update.sec == 0)
2519 strcpy(last_update, "never");
2520 else {
2521 pj_gettimeofday(&now);
2522 PJ_TIME_VAL_SUB(now, xr_stat.rx.stat_sum.update);
2523 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2524 now.sec / 3600,
2525 (now.sec % 3600) / 60,
2526 now.sec % 60,
2527 now.msec);
2528 }
2529
2530 len = pj_ansi_snprintf(p, end-p,
2531 "%s RX last update: %s\n"
2532 "%s begin seq=%d, end seq=%d\n"
2533 "%s pkt loss=%s, dup=%s\n"
2534 "%s (msec) min avg max dev\n"
2535 "%s jitter : %s\n"
2536 "%s toh : %s",
2537 indent, last_update,
2538 indent,
2539 xr_stat.rx.stat_sum.begin_seq, xr_stat.rx.stat_sum.end_seq,
2540 indent, loss, dup,
2541 indent,
2542 indent, jitter,
2543 indent, toh
2544 );
2545 VALIDATE_PRINT_BUF();
2546
2547 if (xr_stat.tx.stat_sum.l)
2548 sprintf(loss, "%d", xr_stat.tx.stat_sum.lost);
2549 else
2550 sprintf(loss, "(na)");
2551
2552 if (xr_stat.tx.stat_sum.d)
2553 sprintf(dup, "%d", xr_stat.tx.stat_sum.dup);
2554 else
2555 sprintf(dup, "(na)");
2556
2557 if (xr_stat.tx.stat_sum.j) {
2558 unsigned jmin, jmax, jmean, jdev;
2559
2560 SAMPLES_TO_USEC(jmin, xr_stat.tx.stat_sum.jitter.min,
2561 clock_rate);
2562 SAMPLES_TO_USEC(jmax, xr_stat.tx.stat_sum.jitter.max,
2563 clock_rate);
2564 SAMPLES_TO_USEC(jmean, xr_stat.tx.stat_sum.jitter.mean,
2565 clock_rate);
2566 SAMPLES_TO_USEC(jdev,
2567 pj_math_stat_get_stddev(&xr_stat.tx.stat_sum.jitter),
2568 clock_rate);
2569 sprintf(jitter, "%7.3f %7.3f %7.3f %7.3f",
2570 jmin/1000.0, jmean/1000.0, jmax/1000.0, jdev/1000.0);
2571 } else
2572 sprintf(jitter, "(report not available)");
2573
2574 if (xr_stat.tx.stat_sum.t) {
2575 sprintf(toh, "%11d %11d %11d %11d",
2576 xr_stat.tx.stat_sum.toh.min,
2577 xr_stat.tx.stat_sum.toh.mean,
2578 xr_stat.tx.stat_sum.toh.max,
2579 pj_math_stat_get_stddev(&xr_stat.rx.stat_sum.toh));
2580 } else
2581 sprintf(toh, "(report not available)");
2582
2583 if (xr_stat.tx.stat_sum.update.sec == 0)
2584 strcpy(last_update, "never");
2585 else {
2586 pj_gettimeofday(&now);
2587 PJ_TIME_VAL_SUB(now, xr_stat.tx.stat_sum.update);
2588 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2589 now.sec / 3600,
2590 (now.sec % 3600) / 60,
2591 now.sec % 60,
2592 now.msec);
2593 }
2594
2595 len = pj_ansi_snprintf(p, end-p,
2596 "%s TX last update: %s\n"
2597 "%s begin seq=%d, end seq=%d\n"
2598 "%s pkt loss=%s, dup=%s\n"
2599 "%s (msec) min avg max dev\n"
2600 "%s jitter : %s\n"
2601 "%s toh : %s",
2602 indent, last_update,
2603 indent,
2604 xr_stat.tx.stat_sum.begin_seq, xr_stat.tx.stat_sum.end_seq,
2605 indent, loss, dup,
2606 indent,
2607 indent, jitter,
2608 indent, toh
2609 );
2610 VALIDATE_PRINT_BUF();
2611
2612
2613 /* VoIP Metrics */
2614 len = pj_ansi_snprintf(p, end-p, "%s VoIP Metrics", indent);
2615 VALIDATE_PRINT_BUF();
2616
2617 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.rx.voip_mtc.signal_lvl);
2618 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.rx.voip_mtc.noise_lvl);
2619 PRINT_VOIP_MTC_VAL(rerl, xr_stat.rx.voip_mtc.rerl);
2620 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.rx.voip_mtc.r_factor);
2621 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.rx.voip_mtc.ext_r_factor);
2622 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.rx.voip_mtc.mos_lq);
2623 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.rx.voip_mtc.mos_cq);
2624
2625 switch ((xr_stat.rx.voip_mtc.rx_config>>6) & 3) {
2626 case PJMEDIA_RTCP_XR_PLC_DIS:
2627 sprintf(plc, "DISABLED");
2628 break;
2629 case PJMEDIA_RTCP_XR_PLC_ENH:
2630 sprintf(plc, "ENHANCED");
2631 break;
2632 case PJMEDIA_RTCP_XR_PLC_STD:
2633 sprintf(plc, "STANDARD");
2634 break;
2635 case PJMEDIA_RTCP_XR_PLC_UNK:
2636 default:
2637 sprintf(plc, "UNKNOWN");
2638 break;
2639 }
2640
2641 switch ((xr_stat.rx.voip_mtc.rx_config>>4) & 3) {
2642 case PJMEDIA_RTCP_XR_JB_FIXED:
2643 sprintf(jba, "FIXED");
2644 break;
2645 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2646 sprintf(jba, "ADAPTIVE");
2647 break;
2648 default:
2649 sprintf(jba, "UNKNOWN");
2650 break;
2651 }
2652
2653 sprintf(jbr, "%d", xr_stat.rx.voip_mtc.rx_config & 0x0F);
2654
2655 if (xr_stat.rx.voip_mtc.update.sec == 0)
2656 strcpy(last_update, "never");
2657 else {
2658 pj_gettimeofday(&now);
2659 PJ_TIME_VAL_SUB(now, xr_stat.rx.voip_mtc.update);
2660 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2661 now.sec / 3600,
2662 (now.sec % 3600) / 60,
2663 now.sec % 60,
2664 now.msec);
2665 }
2666
2667 len = pj_ansi_snprintf(p, end-p,
2668 "%s RX last update: %s\n"
2669 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2670 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2671 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2672 "%s delay : round trip=%d%s, end system=%d%s\n"
2673 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2674 "%s quality : R factor=%s, ext R factor=%s\n"
2675 "%s MOS LQ=%s, MOS CQ=%s\n"
2676 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2677 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2678 indent,
2679 last_update,
2680 /* packets */
2681 indent,
2682 xr_stat.rx.voip_mtc.loss_rate, xr_stat.rx.voip_mtc.loss_rate*100.0/256,
2683 xr_stat.rx.voip_mtc.discard_rate, xr_stat.rx.voip_mtc.discard_rate*100.0/256,
2684 /* burst */
2685 indent,
2686 xr_stat.rx.voip_mtc.burst_den, xr_stat.rx.voip_mtc.burst_den*100.0/256,
2687 xr_stat.rx.voip_mtc.burst_dur, "ms",
2688 /* gap */
2689 indent,
2690 xr_stat.rx.voip_mtc.gap_den, xr_stat.rx.voip_mtc.gap_den*100.0/256,
2691 xr_stat.rx.voip_mtc.gap_dur, "ms",
2692 /* delay */
2693 indent,
2694 xr_stat.rx.voip_mtc.rnd_trip_delay, "ms",
2695 xr_stat.rx.voip_mtc.end_sys_delay, "ms",
2696 /* level */
2697 indent,
2698 signal_lvl, "dB",
2699 noise_lvl, "dB",
2700 rerl, "",
2701 /* quality */
2702 indent,
2703 r_factor, ext_r_factor,
2704 indent,
2705 mos_lq, mos_cq,
2706 /* config */
2707 indent,
2708 plc, jba, jbr, xr_stat.rx.voip_mtc.gmin,
2709 /* JB delay */
2710 indent,
2711 xr_stat.rx.voip_mtc.jb_nom, "ms",
2712 xr_stat.rx.voip_mtc.jb_max, "ms",
2713 xr_stat.rx.voip_mtc.jb_abs_max, "ms"
2714 );
2715 VALIDATE_PRINT_BUF();
2716
2717 PRINT_VOIP_MTC_VAL(signal_lvl, xr_stat.tx.voip_mtc.signal_lvl);
2718 PRINT_VOIP_MTC_VAL(noise_lvl, xr_stat.tx.voip_mtc.noise_lvl);
2719 PRINT_VOIP_MTC_VAL(rerl, xr_stat.tx.voip_mtc.rerl);
2720 PRINT_VOIP_MTC_VAL(r_factor, xr_stat.tx.voip_mtc.r_factor);
2721 PRINT_VOIP_MTC_VAL(ext_r_factor, xr_stat.tx.voip_mtc.ext_r_factor);
2722 PRINT_VOIP_MTC_VAL(mos_lq, xr_stat.tx.voip_mtc.mos_lq);
2723 PRINT_VOIP_MTC_VAL(mos_cq, xr_stat.tx.voip_mtc.mos_cq);
2724
2725 switch ((xr_stat.tx.voip_mtc.rx_config>>6) & 3) {
2726 case PJMEDIA_RTCP_XR_PLC_DIS:
2727 sprintf(plc, "DISABLED");
2728 break;
2729 case PJMEDIA_RTCP_XR_PLC_ENH:
2730 sprintf(plc, "ENHANCED");
2731 break;
2732 case PJMEDIA_RTCP_XR_PLC_STD:
2733 sprintf(plc, "STANDARD");
2734 break;
2735 case PJMEDIA_RTCP_XR_PLC_UNK:
2736 default:
2737 sprintf(plc, "unknown");
2738 break;
2739 }
2740
2741 switch ((xr_stat.tx.voip_mtc.rx_config>>4) & 3) {
2742 case PJMEDIA_RTCP_XR_JB_FIXED:
2743 sprintf(jba, "FIXED");
2744 break;
2745 case PJMEDIA_RTCP_XR_JB_ADAPTIVE:
2746 sprintf(jba, "ADAPTIVE");
2747 break;
2748 default:
2749 sprintf(jba, "unknown");
2750 break;
2751 }
2752
2753 sprintf(jbr, "%d", xr_stat.tx.voip_mtc.rx_config & 0x0F);
2754
2755 if (xr_stat.tx.voip_mtc.update.sec == 0)
2756 strcpy(last_update, "never");
2757 else {
2758 pj_gettimeofday(&now);
2759 PJ_TIME_VAL_SUB(now, xr_stat.tx.voip_mtc.update);
2760 sprintf(last_update, "%02ldh:%02ldm:%02ld.%03lds ago",
2761 now.sec / 3600,
2762 (now.sec % 3600) / 60,
2763 now.sec % 60,
2764 now.msec);
2765 }
2766
2767 len = pj_ansi_snprintf(p, end-p,
2768 "%s TX last update: %s\n"
2769 "%s packets : loss rate=%d (%.2f%%), discard rate=%d (%.2f%%)\n"
2770 "%s burst : density=%d (%.2f%%), duration=%d%s\n"
2771 "%s gap : density=%d (%.2f%%), duration=%d%s\n"
2772 "%s delay : round trip=%d%s, end system=%d%s\n"
2773 "%s level : signal=%s%s, noise=%s%s, RERL=%s%s\n"
2774 "%s quality : R factor=%s, ext R factor=%s\n"
2775 "%s MOS LQ=%s, MOS CQ=%s\n"
2776 "%s config : PLC=%s, JB=%s, JB rate=%s, Gmin=%d\n"
2777 "%s JB delay : cur=%d%s, max=%d%s, abs max=%d%s",
2778 indent,
2779 last_update,
2780 /* pakcets */
2781 indent,
2782 xr_stat.tx.voip_mtc.loss_rate, xr_stat.tx.voip_mtc.loss_rate*100.0/256,
2783 xr_stat.tx.voip_mtc.discard_rate, xr_stat.tx.voip_mtc.discard_rate*100.0/256,
2784 /* burst */
2785 indent,
2786 xr_stat.tx.voip_mtc.burst_den, xr_stat.tx.voip_mtc.burst_den*100.0/256,
2787 xr_stat.tx.voip_mtc.burst_dur, "ms",
2788 /* gap */
2789 indent,
2790 xr_stat.tx.voip_mtc.gap_den, xr_stat.tx.voip_mtc.gap_den*100.0/256,
2791 xr_stat.tx.voip_mtc.gap_dur, "ms",
2792 /* delay */
2793 indent,
2794 xr_stat.tx.voip_mtc.rnd_trip_delay, "ms",
2795 xr_stat.tx.voip_mtc.end_sys_delay, "ms",
2796 /* level */
2797 indent,
2798 signal_lvl, "dB",
2799 noise_lvl, "dB",
2800 rerl, "",
2801 /* quality */
2802 indent,
2803 r_factor, ext_r_factor,
2804 indent,
2805 mos_lq, mos_cq,
2806 /* config */
2807 indent,
2808 plc, jba, jbr, xr_stat.tx.voip_mtc.gmin,
2809 /* JB delay */
2810 indent,
2811 xr_stat.tx.voip_mtc.jb_nom, "ms",
2812 xr_stat.tx.voip_mtc.jb_max, "ms",
2813 xr_stat.tx.voip_mtc.jb_abs_max, "ms"
2814 );
2815 VALIDATE_PRINT_BUF();
2816
2817
2818 /* RTT delay (by receiver side) */
2819 len = pj_ansi_snprintf(p, end-p,
2820 "%s RTT (from recv) min avg max last dev",
2821 indent);
2822 VALIDATE_PRINT_BUF();
2823 len = pj_ansi_snprintf(p, end-p,
2824 "%s RTT msec : %7.3f %7.3f %7.3f %7.3f %7.3f",
2825 indent,
2826 xr_stat.rtt.min / 1000.0,
2827 xr_stat.rtt.mean / 1000.0,
2828 xr_stat.rtt.max / 1000.0,
2829 xr_stat.rtt.last / 1000.0,
2830 pj_math_stat_get_stddev(&xr_stat.rtt) / 1000.0
2831 );
2832 VALIDATE_PRINT_BUF();
2833 } while(0);
2834#endif
2835
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002836 }
2837}
2838
2839
2840/* Print call info */
Benny Prijono627cbb42007-09-25 20:48:49 +00002841void print_call(const char *title,
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002842 int call_id,
2843 char *buf, pj_size_t size)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002844{
2845 int len;
2846 pjsip_inv_session *inv = pjsua_var.calls[call_id].inv;
2847 pjsip_dialog *dlg = inv->dlg;
2848 char userinfo[128];
2849
2850 /* Dump invite sesion info. */
2851
2852 len = pjsip_hdr_print_on(dlg->remote.info, userinfo, sizeof(userinfo));
Benny Prijono329d6382009-05-29 13:04:03 +00002853 if (len < 0)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002854 pj_ansi_strcpy(userinfo, "<--uri too long-->");
2855 else
2856 userinfo[len] = '\0';
2857
2858 len = pj_ansi_snprintf(buf, size, "%s[%s] %s",
2859 title,
2860 pjsip_inv_state_name(inv->state),
2861 userinfo);
2862 if (len < 1 || len >= (int)size) {
2863 pj_ansi_strcpy(buf, "<--uri too long-->");
2864 len = 18;
2865 } else
2866 buf[len] = '\0';
2867}
2868
2869
2870/*
2871 * Dump call and media statistics to string.
2872 */
2873PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
2874 pj_bool_t with_media,
2875 char *buffer,
2876 unsigned maxlen,
2877 const char *indent)
2878{
2879 pjsua_call *call;
Benny Prijonodc752ca2006-09-22 16:55:42 +00002880 pjsip_dialog *dlg;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002881 pj_time_val duration, res_delay, con_delay;
2882 char tmp[128];
2883 char *p, *end;
Benny Prijono148c9dd2006-09-19 13:37:53 +00002884 pj_status_t status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002885 int len;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002886 pjmedia_transport_info tp_info;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002887
2888 PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
2889 PJ_EINVAL);
2890
Benny Prijonodc752ca2006-09-22 16:55:42 +00002891 status = acquire_call("pjsua_call_dump()", call_id, &call, &dlg);
Benny Prijono148c9dd2006-09-19 13:37:53 +00002892 if (status != PJ_SUCCESS)
2893 return status;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002894
2895 *buffer = '\0';
2896 p = buffer;
2897 end = buffer + maxlen;
2898 len = 0;
2899
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002900 print_call(indent, call_id, tmp, sizeof(tmp));
2901
2902 len = pj_ansi_strlen(tmp);
2903 pj_ansi_strcpy(buffer, tmp);
2904
2905 p += len;
2906 *p++ = '\r';
2907 *p++ = '\n';
2908
2909 /* Calculate call duration */
Benny Prijono4be63b52006-11-25 14:50:25 +00002910 if (call->conn_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002911 pj_gettimeofday(&duration);
2912 PJ_TIME_VAL_SUB(duration, call->conn_time);
2913 con_delay = call->conn_time;
2914 PJ_TIME_VAL_SUB(con_delay, call->start_time);
2915 } else {
2916 duration.sec = duration.msec = 0;
2917 con_delay.sec = con_delay.msec = 0;
2918 }
2919
2920 /* Calculate first response delay */
Benny Prijono4be63b52006-11-25 14:50:25 +00002921 if (call->res_time.sec != 0) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002922 res_delay = call->res_time;
2923 PJ_TIME_VAL_SUB(res_delay, call->start_time);
2924 } else {
2925 res_delay.sec = res_delay.msec = 0;
2926 }
2927
2928 /* Print duration */
2929 len = pj_ansi_snprintf(p, end-p,
2930 "%s Call time: %02dh:%02dm:%02ds, "
2931 "1st res in %d ms, conn in %dms",
2932 indent,
Benny Prijono172cd732006-06-14 20:22:31 +00002933 (int)(duration.sec / 3600),
2934 (int)((duration.sec % 3600)/60),
2935 (int)(duration.sec % 60),
2936 (int)PJ_TIME_VAL_MSEC(res_delay),
2937 (int)PJ_TIME_VAL_MSEC(con_delay));
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002938
2939 if (len > 0 && len < end-p) {
2940 p += len;
2941 *p++ = '\n';
2942 *p = '\0';
2943 }
2944
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002945 /* Get and ICE SRTP status */
Benny Prijono734fc2d2008-03-17 16:05:35 +00002946 pjmedia_transport_info_init(&tp_info);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002947 pjmedia_transport_get_info(call->med_tp, &tp_info);
2948 if (tp_info.specific_info_cnt > 0) {
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002949 unsigned i;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002950 for (i = 0; i < tp_info.specific_info_cnt; ++i) {
2951 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)
2952 {
2953 pjmedia_srtp_info *srtp_info =
2954 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer;
2955
2956 len = pj_ansi_snprintf(p, end-p,
2957 "%s SRTP status: %s Crypto-suite: %s",
2958 indent,
2959 (srtp_info->active?"Active":"Not active"),
2960 srtp_info->tx_policy.name.ptr);
2961 if (len > 0 && len < end-p) {
2962 p += len;
2963 *p++ = '\n';
2964 *p = '\0';
2965 }
Benny Prijonof5d9f1f2009-10-14 13:13:18 +00002966 } else if (tp_info.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
2967 const pjmedia_ice_transport_info *ii;
2968
2969 ii = (const pjmedia_ice_transport_info*)
2970 tp_info.spc_info[i].buffer;
2971
2972 len = pj_ansi_snprintf(p, end-p,
2973 "%s ICE role: %s, state: %s, comp_cnt: %u",
2974 indent,
2975 pj_ice_sess_role_name(ii->role),
2976 pj_ice_strans_state_name(ii->sess_state),
2977 ii->comp_cnt);
2978 if (len > 0 && len < end-p) {
2979 p += len;
2980 *p++ = '\n';
2981 *p = '\0';
2982 }
Benny Prijonoe1a5a852008-03-11 21:38:05 +00002983 }
2984 }
2985 }
2986
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002987 /* Dump session statistics */
2988 if (with_media && call->session)
Nanang Izzuddinff2b1102008-09-17 15:32:06 +00002989 dump_media_session(indent, p, end-p, call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002990
Benny Prijonodc752ca2006-09-22 16:55:42 +00002991 pjsip_dlg_dec_lock(dlg);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002992
2993 return PJ_SUCCESS;
2994}
2995
Benny Prijono1e601552010-10-20 05:31:08 +00002996/* Proto */
2997static pj_status_t perform_lock_codec(pjsua_call *call);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00002998
Benny Prijono1e601552010-10-20 05:31:08 +00002999/* Timer callback to send re-INVITE or UPDATE to lock codec */
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003000static void reinv_timer_cb(pj_timer_heap_t *th,
3001 pj_timer_entry *entry)
3002{
3003 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
3004 pjsip_dialog *dlg;
3005 pjsua_call *call;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003006 pj_status_t status;
3007
3008 PJ_UNUSED_ARG(th);
3009
3010 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
3011
3012 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
3013 if (status != PJ_SUCCESS)
3014 return;
3015
Benny Prijono1e601552010-10-20 05:31:08 +00003016 status = perform_lock_codec(call);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003017
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003018 pjsip_dlg_dec_lock(dlg);
3019}
3020
3021
3022/* Check if the specified format can be skipped in counting codecs */
3023static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
3024 const pj_str_t *fmt)
3025{
Benny Prijono1e601552010-10-20 05:31:08 +00003026 const pj_str_t STR_TEL = {"telephone-event", 15};
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003027 unsigned pt;
3028
3029 pt = pj_strtoul(fmt);
3030
3031 /* Check for comfort noise */
3032 if (pt == PJMEDIA_RTP_PT_CN)
3033 return PJ_TRUE;
3034
3035 /* Dynamic PT, check the format name */
3036 if (pt >= 96) {
3037 pjmedia_sdp_attr *a;
3038 pjmedia_sdp_rtpmap rtpmap;
3039
3040 /* Get the format name */
3041 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3042 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
3043 /* Check for telephone-event */
Benny Prijono1e601552010-10-20 05:31:08 +00003044 if (pj_stricmp(&rtpmap.enc_name, &STR_TEL)==0)
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003045 return PJ_TRUE;
3046 } else {
3047 /* Invalid SDP, should not reach here */
3048 pj_assert(!"SDP should have been validated!");
3049 return PJ_TRUE;
3050 }
3051 }
3052
3053 return PJ_FALSE;
3054}
3055
3056
Benny Prijono1e601552010-10-20 05:31:08 +00003057/* Send re-INVITE or UPDATE with new SDP offer to select only one codec
3058 * out of several codecs presented by callee in his answer.
3059 */
3060static pj_status_t perform_lock_codec(pjsua_call *call)
3061{
3062 const pj_str_t STR_UPDATE = {"UPDATE", 6};
3063 const pjmedia_sdp_session *local_sdp = NULL, *new_sdp;
Nanang Izzuddinec919002010-11-25 09:27:06 +00003064 const pjmedia_sdp_media *ref_m;
Benny Prijono1e601552010-10-20 05:31:08 +00003065 pjmedia_sdp_media *m;
3066 unsigned i, codec_cnt = 0;
3067 pj_bool_t rem_can_update;
3068 pjsip_tx_data *tdata;
3069 pj_status_t status;
3070
3071 PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE,
3072 PJ_EINVALIDOP);
3073
3074 /* Verify if another SDP negotiation is in progress, e.g: session timer
3075 * or another re-INVITE.
3076 */
3077 if (call->inv==NULL || call->inv->neg==NULL ||
3078 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
3079 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00003080 return PJMEDIA_SDPNEG_EINSTATE;
Benny Prijono1e601552010-10-20 05:31:08 +00003081 }
3082
Benny Prijono02493272010-11-17 09:15:04 +00003083 /* Don't do this if call is disconnecting! */
3084 if (call->inv->state > PJSIP_INV_STATE_CONFIRMED ||
3085 call->inv->cause >= 200)
3086 {
Nanang Izzuddinec919002010-11-25 09:27:06 +00003087 return PJ_EINVALIDOP;
Benny Prijono02493272010-11-17 09:15:04 +00003088 }
3089
Benny Prijono1e601552010-10-20 05:31:08 +00003090 /* Verify if another SDP negotiation has been completed by comparing
3091 * the SDP version.
3092 */
3093 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
3094 if (status != PJ_SUCCESS)
3095 return status;
3096 if (local_sdp->origin.version > call->lock_codec.sdp_ver)
3097 return PJMEDIA_SDP_EINVER;
3098
Nanang Izzuddinec919002010-11-25 09:27:06 +00003099 /* Verify if media is deactivated */
3100 if (call->media_st == PJSUA_CALL_MEDIA_NONE ||
3101 call->media_st == PJSUA_CALL_MEDIA_ERROR ||
3102 call->media_dir == PJMEDIA_DIR_NONE)
3103 {
3104 return PJ_EINVALIDOP;
3105 }
3106
Benny Prijono1e601552010-10-20 05:31:08 +00003107 PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec.."));
3108
3109 /* Update the new offer so it contains only a codec. Note that formats
3110 * order in the offer should have been matched to the answer, so we can
3111 * just directly update the offer without looking-up the answer.
3112 */
3113 new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp);
Benny Prijono1e601552010-10-20 05:31:08 +00003114 m = new_sdp->media[call->audio_idx];
Nanang Izzuddinec919002010-11-25 09:27:06 +00003115 ref_m = local_sdp->media[call->audio_idx];
3116 pj_assert(ref_m->desc.port);
Benny Prijono1e601552010-10-20 05:31:08 +00003117 codec_cnt = 0;
3118 i = 0;
3119 while (i < m->desc.fmt_count) {
3120 pjmedia_sdp_attr *a;
3121 pj_str_t *fmt = &m->desc.fmt[i];
3122
3123 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
3124 ++i;
3125 continue;
3126 }
3127
3128 /* Remove format */
3129 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
3130 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3131 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
3132 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
3133 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
3134 m->desc.fmt_count, i);
3135 --m->desc.fmt_count;
3136 }
3137
Nanang Izzuddinec919002010-11-25 09:27:06 +00003138 /* Last check if SDP trully needs to be updated. It is possible that OA
3139 * negotiations have completed and SDP has changed but we didn't
3140 * increase the SDP version (should not happen!).
Benny Prijono1e601552010-10-20 05:31:08 +00003141 */
Nanang Izzuddinec919002010-11-25 09:27:06 +00003142 if (ref_m->desc.fmt_count == m->desc.fmt_count)
Benny Prijono1e601552010-10-20 05:31:08 +00003143 return PJ_SUCCESS;
Benny Prijono1e601552010-10-20 05:31:08 +00003144
3145 /* Send UPDATE or re-INVITE */
3146 rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg,
3147 PJSIP_H_ALLOW,
3148 NULL, &STR_UPDATE) ==
3149 PJSIP_DIALOG_CAP_SUPPORTED;
3150 if (rem_can_update) {
3151 status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata);
3152 } else {
3153 status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata);
3154 }
3155
3156 if (status==PJ_EINVALIDOP &&
3157 ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY)
3158 {
3159 /* Ups, let's reschedule again */
3160 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
3161 pj_time_val_normalize(&delay);
3162 call->lock_codec.reinv_timer.id = PJ_TRUE;
3163 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3164 &call->lock_codec.reinv_timer, &delay);
3165 return status;
3166 } else if (status != PJ_SUCCESS) {
3167 pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec",
3168 status);
3169 return status;
3170 }
3171
3172 /* Send the UPDATE/re-INVITE request */
3173 status = pjsip_inv_send_msg(call->inv, tdata);
3174 if (status != PJ_SUCCESS) {
3175 pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec",
3176 status);
3177 return status;
3178 }
3179
3180 return status;
3181}
3182
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003183/* Check if remote answerer has given us more than one codecs. If so,
3184 * create another offer with one codec only to lock down the codec.
3185 */
3186static pj_status_t lock_codec(pjsua_call *call)
3187{
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003188 pjsip_inv_session *inv = call->inv;
Nanang Izzuddinec919002010-11-25 09:27:06 +00003189 const pjmedia_sdp_session *local_sdp, *remote_sdp;
3190 const pjmedia_sdp_media *rem_m, *loc_m;
Benny Prijono1e601552010-10-20 05:31:08 +00003191 unsigned codec_cnt=0, i;
3192 pj_time_val delay = {0, 0};
Nanang Izzuddinec919002010-11-25 09:27:06 +00003193 const pj_str_t st_update = {"UPDATE", 6};
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003194 pj_status_t status;
3195
Nanang Izzuddinec919002010-11-25 09:27:06 +00003196 /* Stop lock codec timer, if it is active */
3197 if (call->lock_codec.reinv_timer.id) {
3198 pjsip_endpt_cancel_timer(pjsua_var.endpt,
3199 &call->lock_codec.reinv_timer);
3200 call->lock_codec.reinv_timer.id = PJ_FALSE;
3201 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003202
Nanang Izzuddinec919002010-11-25 09:27:06 +00003203 /* Skip this if we are the answerer */
3204 if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
3205 return PJ_SUCCESS;
3206 }
3207
3208 /* Skip this if the media is inactive or error */
3209 if (call->media_st == PJSUA_CALL_MEDIA_NONE ||
3210 call->media_st == PJSUA_CALL_MEDIA_ERROR ||
3211 call->media_dir == PJMEDIA_DIR_NONE)
3212 {
3213 return PJ_SUCCESS;
3214 }
3215
3216 /* Delay this when the SDP negotiation done in call state EARLY and
3217 * remote does not support UPDATE method.
3218 */
3219 if (inv->state == PJSIP_INV_STATE_EARLY &&
3220 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!=
3221 PJSIP_DIALOG_CAP_SUPPORTED)
3222 {
3223 call->lock_codec.pending = PJ_TRUE;
3224 return PJ_SUCCESS;
3225 }
3226
3227 status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003228 if (status != PJ_SUCCESS)
3229 return status;
Nanang Izzuddinec919002010-11-25 09:27:06 +00003230 status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003231 if (status != PJ_SUCCESS)
3232 return status;
3233
3234 PJ_ASSERT_RETURN(call->audio_idx>=0 &&
3235 call->audio_idx < (int)remote_sdp->media_count,
3236 PJ_EINVALIDOP);
3237
3238 rem_m = remote_sdp->media[call->audio_idx];
Nanang Izzuddinec919002010-11-25 09:27:06 +00003239 loc_m = local_sdp->media[call->audio_idx];
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003240
Nanang Izzuddinec919002010-11-25 09:27:06 +00003241 /* Verify that media must be active. */
3242 pj_assert(loc_m->desc.port && rem_m->desc.port);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003243
3244 /* Count the formats in the answer. */
Nanang Izzuddinec919002010-11-25 09:27:06 +00003245 if (rem_m->desc.fmt_count==1) {
3246 codec_cnt = 1;
3247 } else {
3248 for (i=0; i<rem_m->desc.fmt_count && codec_cnt <= 1; ++i) {
3249 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[i]))
3250 ++codec_cnt;
3251 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003252 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003253 if (codec_cnt <= 1) {
3254 /* Answer contains single codec. */
Nanang Izzuddinec919002010-11-25 09:27:06 +00003255 call->lock_codec.retry_cnt = 0;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003256 return PJ_SUCCESS;
3257 }
3258
Nanang Izzuddinec919002010-11-25 09:27:06 +00003259 /* Remote keeps answering with multiple codecs, let's just give up
3260 * locking codec to avoid infinite retry loop.
3261 */
3262 if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY)
3263 return PJ_SUCCESS;
3264
Benny Prijono1e601552010-10-20 05:31:08 +00003265 PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, scheduling "
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003266 "updating media session to use only one codec.."));
3267
Benny Prijono1e601552010-10-20 05:31:08 +00003268 call->lock_codec.sdp_ver = local_sdp->origin.version;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003269
Benny Prijono1e601552010-10-20 05:31:08 +00003270 /* Can't send UPDATE or re-INVITE now, so just schedule it immediately.
3271 * See: https://trac.pjsip.org/repos/ticket/1149
3272 */
3273 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
3274 (void*)(pj_size_t)call->index,
3275 &reinv_timer_cb);
3276 pjsip_endpt_schedule_timer(pjsua_var.endpt,
3277 &call->lock_codec.reinv_timer, &delay);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003278
3279 return PJ_SUCCESS;
3280}
3281
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003282/*
Benny Prijono84126ab2006-02-09 09:30:09 +00003283 * This callback receives notification from invite session when the
3284 * session state has changed.
3285 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003286static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
3287 pjsip_event *e)
Benny Prijono84126ab2006-02-09 09:30:09 +00003288{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003289 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00003290
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003291 PJSUA_LOCK();
3292
Benny Prijonoa1e69682007-05-11 15:14:34 +00003293 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003294
3295 if (!call) {
3296 PJSUA_UNLOCK();
Benny Prijonoe21e7842006-04-09 16:46:05 +00003297 return;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003298 }
3299
Benny Prijonoe21e7842006-04-09 16:46:05 +00003300
3301 /* Get call times */
3302 switch (inv->state) {
3303 case PJSIP_INV_STATE_EARLY:
3304 case PJSIP_INV_STATE_CONNECTING:
3305 if (call->res_time.sec == 0)
3306 pj_gettimeofday(&call->res_time);
Benny Prijonoba5926a2007-05-02 11:29:37 +00003307 call->last_code = (pjsip_status_code)
3308 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003309 pj_strncpy(&call->last_text,
3310 &e->body.tsx_state.tsx->status_text,
3311 sizeof(call->last_text_buf_));
Benny Prijonoe21e7842006-04-09 16:46:05 +00003312 break;
3313 case PJSIP_INV_STATE_CONFIRMED:
3314 pj_gettimeofday(&call->conn_time);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003315
Nanang Izzuddinec919002010-11-25 09:27:06 +00003316 /* See if lock codec was pended as media update was done in the
3317 * EARLY state and remote does not support UPDATE.
3318 */
3319 if (call->lock_codec.pending) {
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003320 pj_status_t status;
3321 status = lock_codec(call);
3322 if (status != PJ_SUCCESS) {
3323 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddinec919002010-11-25 09:27:06 +00003324 }
3325 call->lock_codec.pending = PJ_FALSE;
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003326 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00003327 break;
3328 case PJSIP_INV_STATE_DISCONNECTED:
3329 pj_gettimeofday(&call->dis_time);
Benny Prijono2e507c22006-06-23 15:04:11 +00003330 if (call->res_time.sec == 0)
3331 pj_gettimeofday(&call->res_time);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003332 if (e->type == PJSIP_EVENT_TSX_STATE &&
3333 e->body.tsx_state.tsx->status_code > call->last_code)
3334 {
Benny Prijonoba5926a2007-05-02 11:29:37 +00003335 call->last_code = (pjsip_status_code)
3336 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003337 pj_strncpy(&call->last_text,
3338 &e->body.tsx_state.tsx->status_text,
3339 sizeof(call->last_text_buf_));
Benny Prijono5e51a4e2008-11-27 00:06:46 +00003340 } else {
3341 call->last_code = PJSIP_SC_REQUEST_TERMINATED;
3342 pj_strncpy(&call->last_text,
3343 pjsip_get_status_text(call->last_code),
3344 sizeof(call->last_text_buf_));
Benny Prijono8b1889b2006-06-06 18:40:40 +00003345 }
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003346
3347 /* Stop lock codec timer, if it is active */
3348 if (call->lock_codec.reinv_timer.id) {
3349 pjsip_endpt_cancel_timer(pjsua_var.endpt,
3350 &call->lock_codec.reinv_timer);
3351 call->lock_codec.reinv_timer.id = PJ_FALSE;
3352 }
Benny Prijonoe21e7842006-04-09 16:46:05 +00003353 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003354 default:
Benny Prijonoba5926a2007-05-02 11:29:37 +00003355 call->last_code = (pjsip_status_code)
3356 e->body.tsx_state.tsx->status_code;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003357 pj_strncpy(&call->last_text,
3358 &e->body.tsx_state.tsx->status_text,
3359 sizeof(call->last_text_buf_));
Benny Prijono8befd9f2006-05-13 22:46:23 +00003360 break;
Benny Prijonoe21e7842006-04-09 16:46:05 +00003361 }
3362
Benny Prijono26ff9062006-02-21 23:47:00 +00003363 /* If this is an outgoing INVITE that was created because of
3364 * REFER/transfer, send NOTIFY to transferer.
3365 */
Benny Prijonoe21e7842006-04-09 16:46:05 +00003366 if (call->xfer_sub && e->type==PJSIP_EVENT_TSX_STATE) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003367 int st_code = -1;
3368 pjsip_evsub_state ev_state = PJSIP_EVSUB_STATE_ACTIVE;
3369
3370
Benny Prijonoa91a0032006-02-26 21:23:45 +00003371 switch (call->inv->state) {
Benny Prijono26ff9062006-02-21 23:47:00 +00003372 case PJSIP_INV_STATE_NULL:
3373 case PJSIP_INV_STATE_CALLING:
3374 /* Do nothing */
3375 break;
3376
3377 case PJSIP_INV_STATE_EARLY:
3378 case PJSIP_INV_STATE_CONNECTING:
3379 st_code = e->body.tsx_state.tsx->status_code;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003380 if (call->inv->state == PJSIP_INV_STATE_CONNECTING)
3381 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3382 else
3383 ev_state = PJSIP_EVSUB_STATE_ACTIVE;
Benny Prijono26ff9062006-02-21 23:47:00 +00003384 break;
3385
Benny Prijono140beae2009-10-11 05:06:43 +00003386 case PJSIP_INV_STATE_CONFIRMED:
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003387#if 0
3388/* We don't need this, as we've terminated the subscription in
3389 * CONNECTING state.
3390 */
Benny Prijono26ff9062006-02-21 23:47:00 +00003391 /* When state is confirmed, send the final 200/OK and terminate
3392 * subscription.
3393 */
3394 st_code = e->body.tsx_state.tsx->status_code;
3395 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003396#endif
Benny Prijono140beae2009-10-11 05:06:43 +00003397 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003398
3399 case PJSIP_INV_STATE_DISCONNECTED:
3400 st_code = e->body.tsx_state.tsx->status_code;
3401 ev_state = PJSIP_EVSUB_STATE_TERMINATED;
3402 break;
Benny Prijono8befd9f2006-05-13 22:46:23 +00003403
Benny Prijono8b1889b2006-06-06 18:40:40 +00003404 case PJSIP_INV_STATE_INCOMING:
Benny Prijono8befd9f2006-05-13 22:46:23 +00003405 /* Nothing to do. Just to keep gcc from complaining about
3406 * unused enums.
3407 */
3408 break;
Benny Prijono26ff9062006-02-21 23:47:00 +00003409 }
3410
3411 if (st_code != -1) {
3412 pjsip_tx_data *tdata;
3413 pj_status_t status;
3414
Benny Prijonoa91a0032006-02-26 21:23:45 +00003415 status = pjsip_xfer_notify( call->xfer_sub,
Benny Prijono26ff9062006-02-21 23:47:00 +00003416 ev_state, st_code,
3417 NULL, &tdata);
3418 if (status != PJ_SUCCESS) {
3419 pjsua_perror(THIS_FILE, "Unable to create NOTIFY", status);
3420 } else {
Benny Prijonoa91a0032006-02-26 21:23:45 +00003421 status = pjsip_xfer_send_request(call->xfer_sub, tdata);
Benny Prijono26ff9062006-02-21 23:47:00 +00003422 if (status != PJ_SUCCESS) {
3423 pjsua_perror(THIS_FILE, "Unable to send NOTIFY", status);
3424 }
3425 }
3426 }
3427 }
3428
Benny Prijono84126ab2006-02-09 09:30:09 +00003429
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003430 if (pjsua_var.ua_cfg.cb.on_call_state)
3431 (*pjsua_var.ua_cfg.cb.on_call_state)(call->index, e);
Benny Prijonoa91a0032006-02-26 21:23:45 +00003432
3433 /* call->inv may be NULL now */
3434
Benny Prijono84126ab2006-02-09 09:30:09 +00003435 /* Destroy media session when invite session is disconnected. */
3436 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
Benny Prijono632ce712006-02-09 14:01:40 +00003437
Benny Prijonoa91a0032006-02-26 21:23:45 +00003438 pj_assert(call != NULL);
Benny Prijono632ce712006-02-09 14:01:40 +00003439
Benny Prijono275fd682006-03-22 11:59:11 +00003440 if (call)
Benny Prijonoc97608e2007-03-23 16:34:20 +00003441 pjsua_media_channel_deinit(call->index);
Benny Prijono84126ab2006-02-09 09:30:09 +00003442
Benny Prijono105217f2006-03-06 16:25:59 +00003443 /* Free call */
Benny Prijonoa91a0032006-02-26 21:23:45 +00003444 call->inv = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003445 --pjsua_var.call_cnt;
Benny Prijono4be63b52006-11-25 14:50:25 +00003446
3447 /* Reset call */
3448 reset_call(call->index);
3449
Benny Prijono84126ab2006-02-09 09:30:09 +00003450 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003451
3452 PJSUA_UNLOCK();
3453}
3454
3455/*
3456 * This callback is called by invite session framework when UAC session
3457 * has forked.
3458 */
3459static void pjsua_call_on_forked( pjsip_inv_session *inv,
3460 pjsip_event *e)
3461{
3462 PJ_UNUSED_ARG(inv);
3463 PJ_UNUSED_ARG(e);
3464
3465 PJ_TODO(HANDLE_FORKED_DIALOG);
3466}
3467
3468
3469/*
Benny Prijono3c5e28b2008-09-24 10:10:15 +00003470 * Callback from UA layer when forked dialog response is received.
3471 */
3472pjsip_dialog* on_dlg_forked(pjsip_dialog *dlg, pjsip_rx_data *res)
3473{
3474 if (dlg->uac_has_2xx &&
3475 res->msg_info.cseq->method.id == PJSIP_INVITE_METHOD &&
3476 pjsip_rdata_get_tsx(res) == NULL &&
3477 res->msg_info.msg->line.status.code/100 == 2)
3478 {
3479 pjsip_dialog *forked_dlg;
3480 pjsip_tx_data *bye;
3481 pj_status_t status;
3482
3483 /* Create forked dialog */
3484 status = pjsip_dlg_fork(dlg, res, &forked_dlg);
3485 if (status != PJ_SUCCESS)
3486 return NULL;
3487
3488 pjsip_dlg_inc_lock(forked_dlg);
3489
3490 /* Disconnect the call */
3491 status = pjsip_dlg_create_request(forked_dlg, &pjsip_bye_method,
3492 -1, &bye);
3493 if (status == PJ_SUCCESS) {
3494 status = pjsip_dlg_send_request(forked_dlg, bye, -1, NULL);
3495 }
3496
3497 pjsip_dlg_dec_lock(forked_dlg);
3498
3499 if (status != PJ_SUCCESS) {
3500 return NULL;
3501 }
3502
3503 return forked_dlg;
3504
3505 } else {
3506 return dlg;
3507 }
3508}
3509
3510/*
Benny Prijonoa38ada02006-07-02 14:22:35 +00003511 * Disconnect call upon error.
3512 */
3513static void call_disconnect( pjsip_inv_session *inv,
3514 int code )
3515{
Benny Prijono59b3aed2008-01-15 16:54:54 +00003516 pjsua_call *call;
Benny Prijonoa38ada02006-07-02 14:22:35 +00003517 pjsip_tx_data *tdata;
3518 pj_status_t status;
3519
Benny Prijono59b3aed2008-01-15 16:54:54 +00003520 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3521
Benny Prijonoa38ada02006-07-02 14:22:35 +00003522 status = pjsip_inv_end_session(inv, code, NULL, &tdata);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003523 if (status != PJ_SUCCESS)
3524 return;
3525
3526 /* Add SDP in 488 status */
Benny Prijono99639982008-03-07 14:39:52 +00003527 if (call && call->med_tp && tdata->msg->type==PJSIP_RESPONSE_MSG &&
3528 code==PJSIP_SC_NOT_ACCEPTABLE_HERE)
3529 {
Benny Prijono59b3aed2008-01-15 16:54:54 +00003530 pjmedia_sdp_session *local_sdp;
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003531 pjmedia_transport_info ti;
Benny Prijono59b3aed2008-01-15 16:54:54 +00003532
Benny Prijono734fc2d2008-03-17 16:05:35 +00003533 pjmedia_transport_info_init(&ti);
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003534 pjmedia_transport_get_info(call->med_tp, &ti);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003535 status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool,
Benny Prijonoe1a5a852008-03-11 21:38:05 +00003536 1, &ti.sock_info, &local_sdp);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003537 if (status == PJ_SUCCESS) {
3538 pjsip_create_sdp_body(tdata->pool, local_sdp,
3539 &tdata->msg->body);
3540 }
3541 }
3542
3543 pjsip_inv_send_msg(inv, tdata);
Benny Prijonoa38ada02006-07-02 14:22:35 +00003544}
3545
3546/*
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003547 * Callback to be called when SDP offer/answer negotiation has just completed
3548 * in the session. This function will start/update media if negotiation
3549 * has succeeded.
3550 */
3551static void pjsua_call_on_media_update(pjsip_inv_session *inv,
3552 pj_status_t status)
3553{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003554 pjsua_call *call;
Benny Prijono224b4e22008-06-19 14:10:28 +00003555 const pjmedia_sdp_session *local_sdp;
Benny Prijonodbce2cf2007-03-28 16:24:00 +00003556 const pjmedia_sdp_session *remote_sdp;
Nanang Izzuddinec919002010-11-25 09:27:06 +00003557 //const pj_str_t st_update = {"UPDATE", 6};
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003558
3559 PJSUA_LOCK();
3560
Benny Prijonoa1e69682007-05-11 15:14:34 +00003561 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003562
3563 if (status != PJ_SUCCESS) {
3564
3565 pjsua_perror(THIS_FILE, "SDP negotiation has failed", status);
3566
Benny Prijono2331d202008-06-26 15:46:52 +00003567 /* Do not deinitialize media since this may be a re-INVITE or
3568 * UPDATE (which in this case the media should not get affected
3569 * by the failed re-INVITE/UPDATE). The media will be shutdown
3570 * when call is disconnected anyway.
3571 */
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003572 /* Stop/destroy media, if any */
Benny Prijono2331d202008-06-26 15:46:52 +00003573 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijono1d9b9a42006-09-25 13:40:12 +00003574
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003575 /* Disconnect call if we're not in the middle of initializing an
3576 * UAS dialog and if this is not a re-INVITE
3577 */
3578 if (inv->state != PJSIP_INV_STATE_NULL &&
3579 inv->state != PJSIP_INV_STATE_CONFIRMED)
3580 {
Benny Prijono2dbed822008-02-21 10:08:27 +00003581 call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003582 }
3583
3584 PJSUA_UNLOCK();
3585 return;
3586 }
3587
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003588
3589 /* Get local and remote SDP */
Benny Prijono224b4e22008-06-19 14:10:28 +00003590 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003591 if (status != PJ_SUCCESS) {
3592 pjsua_perror(THIS_FILE,
3593 "Unable to retrieve currently active local SDP",
3594 status);
3595 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3596 PJSUA_UNLOCK();
3597 return;
3598 }
3599
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003600 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
3601 if (status != PJ_SUCCESS) {
3602 pjsua_perror(THIS_FILE,
3603 "Unable to retrieve currently active remote SDP",
3604 status);
3605 //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
3606 PJSUA_UNLOCK();
3607 return;
3608 }
3609
Benny Prijono91a6a172007-10-31 08:59:29 +00003610 /* Update remote's NAT type */
3611 if (pjsua_var.ua_cfg.nat_type_in_sdp) {
3612 update_remote_nat_type(call, remote_sdp);
3613 }
3614
3615 /* Update media channel with the new SDP */
Benny Prijonoc97608e2007-03-23 16:34:20 +00003616 status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003617 if (status != PJ_SUCCESS) {
3618 pjsua_perror(THIS_FILE, "Unable to create media session",
3619 status);
Benny Prijono59b3aed2008-01-15 16:54:54 +00003620 call_disconnect(inv, PJSIP_SC_NOT_ACCEPTABLE_HERE);
Benny Prijono2331d202008-06-26 15:46:52 +00003621 /* No need to deinitialize; media will be shutdown when call
3622 * state is disconnected anyway.
3623 */
3624 /*pjsua_media_channel_deinit(call->index);*/
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003625 PJSUA_UNLOCK();
3626 return;
3627 }
3628
Nanang Izzuddinec919002010-11-25 09:27:06 +00003629 /* Ticket #476: make sure only one codec is specified in the answer. */
3630 status = lock_codec(call);
3631 if (status != PJ_SUCCESS) {
3632 pjsua_perror(THIS_FILE, "Unable to lock codec", status);
Nanang Izzuddin93bacd02010-06-15 09:56:39 +00003633 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003634
3635 /* Call application callback, if any */
3636 if (pjsua_var.ua_cfg.cb.on_call_media_state)
3637 pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
3638
3639
3640 PJSUA_UNLOCK();
3641}
3642
3643
Benny Prijonodd63b992010-10-01 02:03:42 +00003644/* Modify SDP for call hold. */
3645static pj_status_t modify_sdp_of_call_hold(pjsua_call *call,
3646 pj_pool_t *pool,
3647 pjmedia_sdp_session *sdp)
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003648{
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003649 pjmedia_sdp_media *m;
3650
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003651 /* Call-hold is done by set the media direction to 'sendonly'
3652 * (PJMEDIA_DIR_ENCODING), except when current media direction is
3653 * 'inactive' (PJMEDIA_DIR_NONE).
3654 * (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3655 */
Benny Prijonoe0860132009-06-05 10:14:20 +00003656 /* http://trac.pjsip.org/repos/ticket/880
3657 if (call->media_dir != PJMEDIA_DIR_ENCODING) {
3658 */
Benny Prijonodd63b992010-10-01 02:03:42 +00003659 /* https://trac.pjsip.org/repos/ticket/1142:
3660 * configuration to use c=0.0.0.0 for call hold.
3661 */
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003662
3663 m = sdp->media[call->audio_idx];
3664
Benny Prijonodd63b992010-10-01 02:03:42 +00003665 if (call->call_hold_type == PJSUA_CALL_HOLD_TYPE_RFC2543) {
3666 pjmedia_sdp_conn *conn;
3667 pjmedia_sdp_attr *attr;
3668
3669 /* Get SDP media connection line */
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003670 conn = m->conn;
Benny Prijonodd63b992010-10-01 02:03:42 +00003671 if (!conn)
3672 conn = sdp->conn;
3673
3674 /* Modify address */
3675 conn->addr = pj_str("0.0.0.0");
3676
3677 /* Remove existing directions attributes */
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003678 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3679 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3680 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3681 pjmedia_sdp_media_remove_all_attr(m, "inactive");
Benny Prijonodd63b992010-10-01 02:03:42 +00003682
3683 /* Add inactive attribute */
3684 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003685 pjmedia_sdp_media_add_attr(m, attr);
Benny Prijonodd63b992010-10-01 02:03:42 +00003686
3687
3688 } else {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003689 pjmedia_sdp_attr *attr;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003690
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003691 /* Remove existing directions attributes */
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003692 pjmedia_sdp_media_remove_all_attr(m, "sendrecv");
3693 pjmedia_sdp_media_remove_all_attr(m, "sendonly");
3694 pjmedia_sdp_media_remove_all_attr(m, "recvonly");
3695 pjmedia_sdp_media_remove_all_attr(m, "inactive");
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003696
Benny Prijonoe0860132009-06-05 10:14:20 +00003697 if (call->media_dir & PJMEDIA_DIR_ENCODING) {
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003698 /* Add sendonly attribute */
3699 attr = pjmedia_sdp_attr_create(pool, "sendonly", NULL);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003700 pjmedia_sdp_media_add_attr(m, attr);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003701 } else {
3702 /* Add inactive attribute */
3703 attr = pjmedia_sdp_attr_create(pool, "inactive", NULL);
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003704 pjmedia_sdp_media_add_attr(m, attr);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003705 }
3706 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003707
Benny Prijonodd63b992010-10-01 02:03:42 +00003708 return PJ_SUCCESS;
3709}
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003710
Benny Prijonodd63b992010-10-01 02:03:42 +00003711/* Create SDP for call hold. */
3712static pj_status_t create_sdp_of_call_hold(pjsua_call *call,
3713 pjmedia_sdp_session **p_sdp)
3714{
3715 pj_status_t status;
3716 pj_pool_t *pool;
3717 pjmedia_sdp_session *sdp;
3718
3719 /* Use call's provisional pool */
3720 pool = call->inv->pool_prov;
3721
3722 /* Create new offer */
3723 status = pjsua_media_channel_create_sdp(call->index, pool, NULL, &sdp,
3724 NULL);
3725 if (status != PJ_SUCCESS) {
3726 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3727 return status;
3728 }
3729
3730 status = modify_sdp_of_call_hold(call, pool, sdp);
3731 if (status != PJ_SUCCESS)
3732 return status;
3733
3734 *p_sdp = sdp;
3735
3736 return PJ_SUCCESS;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003737}
3738
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003739/*
3740 * Called when session received new offer.
3741 */
3742static void pjsua_call_on_rx_offer(pjsip_inv_session *inv,
3743 const pjmedia_sdp_session *offer)
3744{
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003745 pjsua_call *call;
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003746 pjmedia_sdp_conn *conn = NULL;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003747 pjmedia_sdp_session *answer;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003748 pj_status_t status;
3749
3750 PJSUA_LOCK();
3751
Benny Prijonoa1e69682007-05-11 15:14:34 +00003752 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003753
Nanang Izzuddin1e952a82010-10-05 16:32:04 +00003754 if (call->audio_idx < (int)offer->media_count)
3755 conn = offer->media[call->audio_idx]->conn;
3756
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003757 if (!conn)
3758 conn = offer->conn;
3759
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003760 /* Supply candidate answer */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003761 PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
3762 call->index));
Benny Prijono667952e2007-04-02 19:27:54 +00003763
Benny Prijono40d62b62009-08-12 17:53:47 +00003764 status = pjsua_media_channel_create_sdp(call->index,
3765 call->inv->pool_prov,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003766 offer, &answer, NULL);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003767 if (status != PJ_SUCCESS) {
3768 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3769 PJSUA_UNLOCK();
3770 return;
3771 }
3772
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003773 /* Check if offer's conn address is zero */
3774 if (pj_strcmp2(&conn->addr, "0.0.0.0")==0 ||
3775 pj_strcmp2(&conn->addr, "0")==0)
3776 {
3777 /* Modify address */
3778 answer->conn->addr = pj_str("0.0.0.0");
3779 }
3780
3781 /* Check if call is on-hold */
3782 if (call->local_hold) {
Benny Prijonodd63b992010-10-01 02:03:42 +00003783 modify_sdp_of_call_hold(call, call->inv->pool_prov, answer);
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003784 }
3785
Benny Prijonoeebe9af2006-06-13 22:57:13 +00003786 status = pjsip_inv_set_sdp_answer(call->inv, answer);
3787 if (status != PJ_SUCCESS) {
3788 pjsua_perror(THIS_FILE, "Unable to set answer", status);
3789 PJSUA_UNLOCK();
3790 return;
3791 }
3792
3793 PJSUA_UNLOCK();
Benny Prijono84126ab2006-02-09 09:30:09 +00003794}
3795
3796
3797/*
Benny Prijono77998ce2007-06-20 10:03:46 +00003798 * Called to generate new offer.
3799 */
3800static void pjsua_call_on_create_offer(pjsip_inv_session *inv,
3801 pjmedia_sdp_session **offer)
3802{
3803 pjsua_call *call;
3804 pj_status_t status;
3805
3806 PJSUA_LOCK();
3807
3808 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
3809
3810 /* See if we've put call on hold. */
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003811 if (call->local_hold) {
Benny Prijono77998ce2007-06-20 10:03:46 +00003812 PJ_LOG(4,(THIS_FILE,
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003813 "Call %d: call is on-hold locally, creating call-hold SDP ",
Benny Prijono77998ce2007-06-20 10:03:46 +00003814 call->index));
Nanang Izzuddin99d69522008-08-04 15:01:38 +00003815 status = create_sdp_of_call_hold( call, offer );
Benny Prijono77998ce2007-06-20 10:03:46 +00003816 } else {
Benny Prijono77998ce2007-06-20 10:03:46 +00003817 PJ_LOG(4,(THIS_FILE, "Call %d: asked to send a new offer",
3818 call->index));
3819
Benny Prijono40d62b62009-08-12 17:53:47 +00003820 status = pjsua_media_channel_create_sdp(call->index,
3821 call->inv->pool_prov,
Benny Prijono25b2ea12008-01-24 19:20:54 +00003822 NULL, offer, NULL);
Benny Prijono77998ce2007-06-20 10:03:46 +00003823 }
3824
3825 if (status != PJ_SUCCESS) {
3826 pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
3827 PJSUA_UNLOCK();
3828 return;
3829 }
3830
Benny Prijono77998ce2007-06-20 10:03:46 +00003831 PJSUA_UNLOCK();
3832}
3833
3834
3835/*
Benny Prijono26ff9062006-02-21 23:47:00 +00003836 * Callback called by event framework when the xfer subscription state
3837 * has changed.
3838 */
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003839static void xfer_client_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
3840{
3841
3842 PJ_UNUSED_ARG(event);
3843
3844 /*
3845 * When subscription is accepted (got 200/OK to REFER), check if
3846 * subscription suppressed.
3847 */
3848 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACCEPTED) {
3849
3850 pjsip_rx_data *rdata;
3851 pjsip_generic_string_hdr *refer_sub;
3852 const pj_str_t REFER_SUB = { "Refer-Sub", 9 };
3853 pjsua_call *call;
3854
Benny Prijonoa1e69682007-05-11 15:14:34 +00003855 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003856
3857 /* Must be receipt of response message */
3858 pj_assert(event->type == PJSIP_EVENT_TSX_STATE &&
3859 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
3860 rdata = event->body.tsx_state.src.rdata;
3861
3862 /* Find Refer-Sub header */
3863 refer_sub = (pjsip_generic_string_hdr*)
3864 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg,
3865 &REFER_SUB, NULL);
3866
3867 /* Check if subscription is suppressed */
3868 if (refer_sub && pj_stricmp2(&refer_sub->hvalue, "false")==0) {
3869 /* Since no subscription is desired, assume that call has been
3870 * transfered successfully.
3871 */
3872 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3873 const pj_str_t ACCEPTED = { "Accepted", 8 };
3874 pj_bool_t cont = PJ_FALSE;
3875 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3876 200,
3877 &ACCEPTED,
3878 PJ_TRUE,
3879 &cont);
3880 }
3881
3882 /* Yes, subscription is suppressed.
3883 * Terminate our subscription now.
3884 */
3885 PJ_LOG(4,(THIS_FILE, "Xfer subscription suppressed, terminating "
3886 "event subcription..."));
3887 pjsip_evsub_terminate(sub, PJ_TRUE);
3888
3889 } else {
3890 /* Notify application about call transfer progress.
3891 * Initially notify with 100/Accepted status.
3892 */
3893 if (call && pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3894 const pj_str_t ACCEPTED = { "Accepted", 8 };
3895 pj_bool_t cont = PJ_FALSE;
3896 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3897 100,
3898 &ACCEPTED,
3899 PJ_FALSE,
3900 &cont);
3901 }
3902 }
3903 }
3904 /*
3905 * On incoming NOTIFY, notify application about call transfer progress.
3906 */
3907 else if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_ACTIVE ||
3908 pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED)
3909 {
3910 pjsua_call *call;
3911 pjsip_msg *msg;
3912 pjsip_msg_body *body;
3913 pjsip_status_line status_line;
3914 pj_bool_t is_last;
3915 pj_bool_t cont;
3916 pj_status_t status;
3917
Benny Prijonoa1e69682007-05-11 15:14:34 +00003918 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003919
3920 /* When subscription is terminated, clear the xfer_sub member of
3921 * the inv_data.
3922 */
3923 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
3924 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3925 PJ_LOG(4,(THIS_FILE, "Xfer client subscription terminated"));
3926
3927 }
3928
3929 if (!call || !event || !pjsua_var.ua_cfg.cb.on_call_transfer_status) {
3930 /* Application is not interested with call progress status */
3931 return;
3932 }
3933
3934 /* This better be a NOTIFY request */
3935 if (event->type == PJSIP_EVENT_TSX_STATE &&
3936 event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
3937 {
3938 pjsip_rx_data *rdata;
3939
3940 rdata = event->body.tsx_state.src.rdata;
3941
3942 /* Check if there's body */
3943 msg = rdata->msg_info.msg;
3944 body = msg->body;
3945 if (!body) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003946 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003947 "Warning: received NOTIFY without message body"));
3948 return;
3949 }
3950
3951 /* Check for appropriate content */
3952 if (pj_stricmp2(&body->content_type.type, "message") != 0 ||
3953 pj_stricmp2(&body->content_type.subtype, "sipfrag") != 0)
3954 {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003955 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003956 "Warning: received NOTIFY with non message/sipfrag "
3957 "content"));
3958 return;
3959 }
3960
3961 /* Try to parse the content */
Benny Prijonoa1e69682007-05-11 15:14:34 +00003962 status = pjsip_parse_status_line((char*)body->data, body->len,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003963 &status_line);
3964 if (status != PJ_SUCCESS) {
Benny Prijonoc6ff4b82009-05-12 15:55:09 +00003965 PJ_LOG(2,(THIS_FILE,
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003966 "Warning: received NOTIFY with invalid "
3967 "message/sipfrag content"));
3968 return;
3969 }
3970
3971 } else {
3972 status_line.code = 500;
3973 status_line.reason = *pjsip_get_status_text(500);
3974 }
3975
3976 /* Notify application */
3977 is_last = (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED);
3978 cont = !is_last;
3979 (*pjsua_var.ua_cfg.cb.on_call_transfer_status)(call->index,
3980 status_line.code,
3981 &status_line.reason,
3982 is_last, &cont);
3983
3984 if (!cont) {
3985 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
3986 }
Benny Prijono19eeb6e2009-06-04 22:16:47 +00003987
3988 /* If the call transfer has completed but the subscription is
3989 * not terminated, terminate it now.
3990 */
3991 if (status_line.code/100 == 2 && !is_last) {
3992 pjsip_tx_data *tdata;
3993
3994 status = pjsip_evsub_initiate(sub, &pjsip_subscribe_method,
3995 0, &tdata);
3996 if (status == PJ_SUCCESS)
3997 status = pjsip_evsub_send_request(sub, tdata);
3998 }
Benny Prijono4ddad2c2006-10-18 17:16:34 +00003999 }
4000}
4001
4002
4003/*
4004 * Callback called by event framework when the xfer subscription state
4005 * has changed.
4006 */
4007static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
Benny Prijono26ff9062006-02-21 23:47:00 +00004008{
4009
4010 PJ_UNUSED_ARG(event);
4011
4012 /*
Benny Prijonod524e822006-09-22 12:48:18 +00004013 * When subscription is terminated, clear the xfer_sub member of
4014 * the inv_data.
Benny Prijono26ff9062006-02-21 23:47:00 +00004015 */
4016 if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00004017 pjsua_call *call;
Benny Prijono26ff9062006-02-21 23:47:00 +00004018
Benny Prijonoa1e69682007-05-11 15:14:34 +00004019 call = (pjsua_call*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
Benny Prijonoa91a0032006-02-26 21:23:45 +00004020 if (!call)
Benny Prijono26ff9062006-02-21 23:47:00 +00004021 return;
4022
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004023 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
Benny Prijonoa91a0032006-02-26 21:23:45 +00004024 call->xfer_sub = NULL;
Benny Prijono26ff9062006-02-21 23:47:00 +00004025
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004026 PJ_LOG(4,(THIS_FILE, "Xfer server subscription terminated"));
Benny Prijono26ff9062006-02-21 23:47:00 +00004027 }
4028}
4029
4030
4031/*
4032 * Follow transfer (REFER) request.
4033 */
4034static void on_call_transfered( pjsip_inv_session *inv,
4035 pjsip_rx_data *rdata )
4036{
4037 pj_status_t status;
4038 pjsip_tx_data *tdata;
Benny Prijonoa91a0032006-02-26 21:23:45 +00004039 pjsua_call *existing_call;
4040 int new_call;
Benny Prijono26ff9062006-02-21 23:47:00 +00004041 const pj_str_t str_refer_to = { "Refer-To", 8};
Benny Prijonoc8141a82006-08-20 09:12:19 +00004042 const pj_str_t str_refer_sub = { "Refer-Sub", 9 };
Benny Prijono053f5222006-11-11 16:16:04 +00004043 const pj_str_t str_ref_by = { "Referred-By", 11 };
Benny Prijono26ff9062006-02-21 23:47:00 +00004044 pjsip_generic_string_hdr *refer_to;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004045 pjsip_generic_string_hdr *refer_sub;
Benny Prijono053f5222006-11-11 16:16:04 +00004046 pjsip_hdr *ref_by_hdr;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004047 pj_bool_t no_refer_sub = PJ_FALSE;
Benny Prijono26ff9062006-02-21 23:47:00 +00004048 char *uri;
Benny Prijono053f5222006-11-11 16:16:04 +00004049 pjsua_msg_data msg_data;
Benny Prijono9fc735d2006-05-28 14:58:12 +00004050 pj_str_t tmp;
Benny Prijono9fc735d2006-05-28 14:58:12 +00004051 pjsip_status_code code;
Benny Prijono26ff9062006-02-21 23:47:00 +00004052 pjsip_evsub *sub;
4053
Benny Prijonoa1e69682007-05-11 15:14:34 +00004054 existing_call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijonoa91a0032006-02-26 21:23:45 +00004055
Benny Prijono26ff9062006-02-21 23:47:00 +00004056 /* Find the Refer-To header */
4057 refer_to = (pjsip_generic_string_hdr*)
4058 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_to, NULL);
4059
4060 if (refer_to == NULL) {
4061 /* Invalid Request.
4062 * No Refer-To header!
4063 */
4064 PJ_LOG(4,(THIS_FILE, "Received REFER without Refer-To header!"));
Benny Prijonob0808372006-03-02 21:18:58 +00004065 pjsip_dlg_respond( inv->dlg, rdata, 400, NULL, NULL, NULL);
Benny Prijono26ff9062006-02-21 23:47:00 +00004066 return;
4067 }
4068
Benny Prijonoc8141a82006-08-20 09:12:19 +00004069 /* Find optional Refer-Sub header */
4070 refer_sub = (pjsip_generic_string_hdr*)
4071 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_refer_sub, NULL);
4072
4073 if (refer_sub) {
4074 if (!pj_strnicmp2(&refer_sub->hvalue, "true", 4)==0)
4075 no_refer_sub = PJ_TRUE;
4076 }
4077
Benny Prijono053f5222006-11-11 16:16:04 +00004078 /* Find optional Referred-By header (to be copied onto outgoing INVITE
4079 * request.
4080 */
Benny Prijonoa1e69682007-05-11 15:14:34 +00004081 ref_by_hdr = (pjsip_hdr*)
4082 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_ref_by,
Benny Prijono053f5222006-11-11 16:16:04 +00004083 NULL);
Benny Prijonoc8141a82006-08-20 09:12:19 +00004084
Benny Prijono9fc735d2006-05-28 14:58:12 +00004085 /* Notify callback */
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004086 code = PJSIP_SC_ACCEPTED;
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004087 if (pjsua_var.ua_cfg.cb.on_call_transfer_request)
4088 (*pjsua_var.ua_cfg.cb.on_call_transfer_request)(existing_call->index,
4089 &refer_to->hvalue,
4090 &code);
Benny Prijono9fc735d2006-05-28 14:58:12 +00004091
4092 if (code < 200)
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004093 code = PJSIP_SC_ACCEPTED;
Benny Prijono9fc735d2006-05-28 14:58:12 +00004094 if (code >= 300) {
4095 /* Application rejects call transfer request */
4096 pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL);
4097 return;
4098 }
4099
Benny Prijono26ff9062006-02-21 23:47:00 +00004100 PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s",
4101 (int)inv->dlg->remote.info_str.slen,
4102 inv->dlg->remote.info_str.ptr,
4103 (int)refer_to->hvalue.slen,
4104 refer_to->hvalue.ptr));
4105
Benny Prijonoc8141a82006-08-20 09:12:19 +00004106 if (no_refer_sub) {
4107 /*
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004108 * Always answer with 2xx.
Benny Prijonoc8141a82006-08-20 09:12:19 +00004109 */
4110 pjsip_tx_data *tdata;
4111 const pj_str_t str_false = { "false", 5};
4112 pjsip_hdr *hdr;
Benny Prijono26ff9062006-02-21 23:47:00 +00004113
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004114 status = pjsip_dlg_create_response(inv->dlg, rdata, code, NULL,
4115 &tdata);
Benny Prijonoc8141a82006-08-20 09:12:19 +00004116 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004117 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00004118 status);
4119 return;
4120 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004121
Benny Prijonoc8141a82006-08-20 09:12:19 +00004122 /* Add Refer-Sub header */
4123 hdr = (pjsip_hdr*)
4124 pjsip_generic_string_hdr_create(tdata->pool, &str_refer_sub,
4125 &str_false);
4126 pjsip_msg_add_hdr(tdata->msg, hdr);
Benny Prijono26ff9062006-02-21 23:47:00 +00004127
Benny Prijono26ff9062006-02-21 23:47:00 +00004128
Benny Prijonoc8141a82006-08-20 09:12:19 +00004129 /* Send answer */
4130 status = pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata),
4131 tdata);
4132 if (status != PJ_SUCCESS) {
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004133 pjsua_perror(THIS_FILE, "Unable to create 2xx response to REFER",
Benny Prijonoc8141a82006-08-20 09:12:19 +00004134 status);
4135 return;
4136 }
4137
4138 /* Don't have subscription */
4139 sub = NULL;
4140
4141 } else {
4142 struct pjsip_evsub_user xfer_cb;
4143 pjsip_hdr hdr_list;
4144
4145 /* Init callback */
4146 pj_bzero(&xfer_cb, sizeof(xfer_cb));
Benny Prijono4ddad2c2006-10-18 17:16:34 +00004147 xfer_cb.on_evsub_state = &xfer_server_on_evsub_state;
Benny Prijonoc8141a82006-08-20 09:12:19 +00004148
4149 /* Init additional header list to be sent with REFER response */
4150 pj_list_init(&hdr_list);
4151
4152 /* Create transferee event subscription */
4153 status = pjsip_xfer_create_uas( inv->dlg, &xfer_cb, rdata, &sub);
4154 if (status != PJ_SUCCESS) {
4155 pjsua_perror(THIS_FILE, "Unable to create xfer uas", status);
4156 pjsip_dlg_respond( inv->dlg, rdata, 500, NULL, NULL, NULL);
4157 return;
4158 }
4159
4160 /* If there's Refer-Sub header and the value is "true", send back
4161 * Refer-Sub in the response with value "true" too.
4162 */
4163 if (refer_sub) {
4164 const pj_str_t str_true = { "true", 4 };
4165 pjsip_hdr *hdr;
4166
4167 hdr = (pjsip_hdr*)
4168 pjsip_generic_string_hdr_create(inv->dlg->pool,
4169 &str_refer_sub,
4170 &str_true);
4171 pj_list_push_back(&hdr_list, hdr);
4172
4173 }
4174
Benny Prijonoc54dcb32008-04-08 23:33:15 +00004175 /* Accept the REFER request, send 2xx. */
Benny Prijonoc8141a82006-08-20 09:12:19 +00004176 pjsip_xfer_accept(sub, rdata, code, &hdr_list);
4177
4178 /* Create initial NOTIFY request */
4179 status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE,
4180 100, NULL, &tdata);
4181 if (status != PJ_SUCCESS) {
4182 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4183 status);
4184 return;
4185 }
4186
4187 /* Send initial NOTIFY request */
4188 status = pjsip_xfer_send_request( sub, tdata);
4189 if (status != PJ_SUCCESS) {
4190 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER", status);
4191 return;
4192 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004193 }
4194
4195 /* We're cheating here.
4196 * We need to get a null terminated string from a pj_str_t.
4197 * So grab the pointer from the hvalue and NULL terminate it, knowing
4198 * that the NULL position will be occupied by a newline.
4199 */
4200 uri = refer_to->hvalue.ptr;
4201 uri[refer_to->hvalue.slen] = '\0';
4202
Benny Prijono053f5222006-11-11 16:16:04 +00004203 /* Init msg_data */
4204 pjsua_msg_data_init(&msg_data);
4205
4206 /* If Referred-By header is present in the REFER request, copy this
4207 * to the outgoing INVITE request.
4208 */
4209 if (ref_by_hdr != NULL) {
Benny Prijonoa1e69682007-05-11 15:14:34 +00004210 pjsip_hdr *dup = (pjsip_hdr*)
4211 pjsip_hdr_clone(rdata->tp_info.pool, ref_by_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +00004212 pj_list_push_back(&msg_data.hdr_list, dup);
4213 }
4214
Benny Prijono26ff9062006-02-21 23:47:00 +00004215 /* Now make the outgoing call. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00004216 tmp = pj_str(uri);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004217 status = pjsua_call_make_call(existing_call->acc_id, &tmp, 0,
Benny Prijono053f5222006-11-11 16:16:04 +00004218 existing_call->user_data, &msg_data,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004219 &new_call);
Benny Prijono26ff9062006-02-21 23:47:00 +00004220 if (status != PJ_SUCCESS) {
4221
Benny Prijonoc8141a82006-08-20 09:12:19 +00004222 /* Notify xferer about the error (if we have subscription) */
4223 if (sub) {
4224 status = pjsip_xfer_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
4225 500, NULL, &tdata);
4226 if (status != PJ_SUCCESS) {
4227 pjsua_perror(THIS_FILE, "Unable to create NOTIFY to REFER",
4228 status);
4229 return;
4230 }
4231 status = pjsip_xfer_send_request(sub, tdata);
4232 if (status != PJ_SUCCESS) {
4233 pjsua_perror(THIS_FILE, "Unable to send NOTIFY to REFER",
4234 status);
4235 return;
4236 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004237 }
4238 return;
4239 }
4240
Benny Prijonoc8141a82006-08-20 09:12:19 +00004241 if (sub) {
4242 /* Put the server subscription in inv_data.
4243 * Subsequent state changed in pjsua_inv_on_state_changed() will be
4244 * reported back to the server subscription.
4245 */
4246 pjsua_var.calls[new_call].xfer_sub = sub;
Benny Prijono26ff9062006-02-21 23:47:00 +00004247
Benny Prijonoc8141a82006-08-20 09:12:19 +00004248 /* Put the invite_data in the subscription. */
4249 pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id,
4250 &pjsua_var.calls[new_call]);
4251 }
Benny Prijono26ff9062006-02-21 23:47:00 +00004252}
4253
4254
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004255
Benny Prijono26ff9062006-02-21 23:47:00 +00004256/*
4257 * This callback is called when transaction state has changed in INVITE
Benny Prijonob0808372006-03-02 21:18:58 +00004258 * session. We use this to trap:
4259 * - incoming REFER request.
4260 * - incoming MESSAGE request.
Benny Prijono26ff9062006-02-21 23:47:00 +00004261 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004262static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv,
4263 pjsip_transaction *tsx,
4264 pjsip_event *e)
Benny Prijono26ff9062006-02-21 23:47:00 +00004265{
Benny Prijono2285e7e2008-12-17 14:28:18 +00004266 pjsua_call *call;
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004267
4268 PJSUA_LOCK();
Benny Prijonoa91a0032006-02-26 21:23:45 +00004269
Benny Prijono2285e7e2008-12-17 14:28:18 +00004270 call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
4271
4272 if (call == NULL) {
4273 PJSUA_UNLOCK();
4274 return;
4275 }
4276
Benny Prijono19eeb6e2009-06-04 22:16:47 +00004277 if (call->inv == NULL) {
4278 /* Shouldn't happen. It happens only when we don't terminate the
4279 * server subscription caused by REFER after the call has been
4280 * transfered (and this call has been disconnected), and we
4281 * receive another REFER for this call.
4282 */
4283 PJSUA_UNLOCK();
4284 return;
4285 }
4286
Benny Prijonofeb69f42007-10-05 09:12:26 +00004287 /* Notify application callback first */
4288 if (pjsua_var.ua_cfg.cb.on_call_tsx_state) {
4289 (*pjsua_var.ua_cfg.cb.on_call_tsx_state)(call->index, tsx, e);
4290 }
4291
Benny Prijono26ff9062006-02-21 23:47:00 +00004292 if (tsx->role==PJSIP_ROLE_UAS &&
4293 tsx->state==PJSIP_TSX_STATE_TRYING &&
Benny Prijono1f61a8f2007-08-16 10:11:44 +00004294 pjsip_method_cmp(&tsx->method, pjsip_get_refer_method())==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004295 {
4296 /*
4297 * Incoming REFER request.
4298 */
Benny Prijonoa91a0032006-02-26 21:23:45 +00004299 on_call_transfered(call->inv, e->body.tsx_state.src.rdata);
Benny Prijonob0808372006-03-02 21:18:58 +00004300
Benny Prijono26ff9062006-02-21 23:47:00 +00004301 }
Benny Prijonob0808372006-03-02 21:18:58 +00004302 else if (tsx->role==PJSIP_ROLE_UAS &&
4303 tsx->state==PJSIP_TSX_STATE_TRYING &&
4304 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
4305 {
4306 /*
4307 * Incoming MESSAGE request!
4308 */
4309 pjsip_rx_data *rdata;
4310 pjsip_msg *msg;
4311 pjsip_accept_hdr *accept_hdr;
4312 pj_status_t status;
4313
4314 rdata = e->body.tsx_state.src.rdata;
4315 msg = rdata->msg_info.msg;
4316
4317 /* Request MUST have message body, with Content-Type equal to
4318 * "text/plain".
4319 */
4320 if (pjsua_im_accept_pager(rdata, &accept_hdr) == PJ_FALSE) {
4321
4322 pjsip_hdr hdr_list;
4323
4324 pj_list_init(&hdr_list);
4325 pj_list_push_back(&hdr_list, accept_hdr);
4326
4327 pjsip_dlg_respond( inv->dlg, rdata, PJSIP_SC_NOT_ACCEPTABLE_HERE,
4328 NULL, &hdr_list, NULL );
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004329 PJSUA_UNLOCK();
Benny Prijonob0808372006-03-02 21:18:58 +00004330 return;
4331 }
4332
4333 /* Respond with 200 first, so that remote doesn't retransmit in case
4334 * the UI takes too long to process the message.
4335 */
4336 status = pjsip_dlg_respond( inv->dlg, rdata, 200, NULL, NULL, NULL);
4337
4338 /* Process MESSAGE request */
4339 pjsua_im_process_pager(call->index, &inv->dlg->remote.info_str,
4340 &inv->dlg->local.info_str, rdata);
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004341
Benny Prijonob0808372006-03-02 21:18:58 +00004342 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004343 else if (tsx->role == PJSIP_ROLE_UAC &&
4344 pjsip_method_cmp(&tsx->method, &pjsip_message_method)==0)
Benny Prijono26ff9062006-02-21 23:47:00 +00004345 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004346 /* Handle outgoing pager status */
4347 if (tsx->status_code >= 200) {
4348 pjsua_im_data *im_data;
Benny Prijono26ff9062006-02-21 23:47:00 +00004349
Benny Prijonoa1e69682007-05-11 15:14:34 +00004350 im_data = (pjsua_im_data*) tsx->mod_data[pjsua_var.mod.id];
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004351 /* im_data can be NULL if this is typing indication */
Benny Prijono26ff9062006-02-21 23:47:00 +00004352
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004353 if (im_data && pjsua_var.ua_cfg.cb.on_pager_status) {
4354 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
4355 &im_data->to,
4356 &im_data->body,
4357 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +00004358 (pjsip_status_code)
4359 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004360 &tsx->status_text);
Benny Prijono26ff9062006-02-21 23:47:00 +00004361 }
Benny Prijonofccab712006-02-22 22:23:22 +00004362 }
Benny Prijono834aee32006-02-19 01:38:06 +00004363 }
Benny Prijono834aee32006-02-19 01:38:06 +00004364
Benny Prijono26ff9062006-02-21 23:47:00 +00004365
Benny Prijonoeebe9af2006-06-13 22:57:13 +00004366 PJSUA_UNLOCK();
Benny Prijono9fc735d2006-05-28 14:58:12 +00004367}
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004368
4369
4370/* Redirection handler */
Benny Prijono08a48b82008-11-27 12:42:07 +00004371static pjsip_redirect_op pjsua_call_on_redirected(pjsip_inv_session *inv,
4372 const pjsip_uri *target,
4373 const pjsip_event *e)
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004374{
4375 pjsua_call *call = (pjsua_call*) inv->dlg->mod_data[pjsua_var.mod.id];
Benny Prijono08a48b82008-11-27 12:42:07 +00004376 pjsip_redirect_op op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004377
4378 PJSUA_LOCK();
4379
4380 if (pjsua_var.ua_cfg.cb.on_call_redirected) {
Benny Prijono08a48b82008-11-27 12:42:07 +00004381 op = (*pjsua_var.ua_cfg.cb.on_call_redirected)(call->index,
4382 target, e);
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004383 } else {
4384 PJ_LOG(4,(THIS_FILE, "Unhandled redirection for call %d "
4385 "(callback not implemented by application). Disconnecting "
4386 "call.",
4387 call->index));
Benny Prijono08a48b82008-11-27 12:42:07 +00004388 op = PJSIP_REDIRECT_STOP;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004389 }
4390
4391 PJSUA_UNLOCK();
Benny Prijono08a48b82008-11-27 12:42:07 +00004392
4393 return op;
Benny Prijono5e51a4e2008-11-27 00:06:46 +00004394}
4395