blob: dba563ce8b94e81249640cf5264e6b1701e24d40 [file] [log] [blame]
Benny Prijono268ca612006-02-07 12:34:11 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
Benny Prijono4f9f64e2006-02-27 00:00:30 +000019#include <pjsua-lib/pjsua.h>
Benny Prijonodc39fe82006-05-26 12:17:46 +000020#include "pjsua_imp.h"
Benny Prijono268ca612006-02-07 12:34:11 +000021
Benny Prijono84126ab2006-02-09 09:30:09 +000022/*
23 * pjsua_core.c
24 *
25 * Core application functionalities.
26 */
Benny Prijono268ca612006-02-07 12:34:11 +000027
Benny Prijono84126ab2006-02-09 09:30:09 +000028#define THIS_FILE "pjsua_core.c"
Benny Prijono268ca612006-02-07 12:34:11 +000029
30
Benny Prijono95196582006-02-09 00:13:40 +000031/*
Benny Prijono84126ab2006-02-09 09:30:09 +000032 * Global variable.
33 */
34struct pjsua pjsua;
35
36
37/*
38 * Default local URI, if none is specified in cmd-line
Benny Prijono95196582006-02-09 00:13:40 +000039 */
Benny Prijonoccf95622006-02-07 18:48:01 +000040#define PJSUA_LOCAL_URI "<sip:user@127.0.0.1>"
Benny Prijono268ca612006-02-07 12:34:11 +000041
Benny Prijono268ca612006-02-07 12:34:11 +000042
Benny Prijono268ca612006-02-07 12:34:11 +000043
44/*
45 * Init default application parameters.
46 */
Benny Prijonodc39fe82006-05-26 12:17:46 +000047PJ_DEF(void) pjsua_default_config(pjsua_config *cfg)
Benny Prijono268ca612006-02-07 12:34:11 +000048{
Benny Prijonoa91a0032006-02-26 21:23:45 +000049 unsigned i;
50
Benny Prijonodc39fe82006-05-26 12:17:46 +000051 pj_memset(cfg, 0, sizeof(pjsua_config));
Benny Prijono268ca612006-02-07 12:34:11 +000052
Benny Prijonodc39fe82006-05-26 12:17:46 +000053 cfg->thread_cnt = 1;
54 cfg->udp_port = 5060;
55 cfg->start_rtp_port = 4000;
56 cfg->max_calls = 4;
57 cfg->conf_ports = 0;
Benny Prijono268ca612006-02-07 12:34:11 +000058
Benny Prijonofa137ca2006-03-20 17:42:37 +000059#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
60 pjsua.clock_rate = 44100;
Benny Prijonofa137ca2006-03-20 17:42:37 +000061#endif
Benny Prijonodc39fe82006-05-26 12:17:46 +000062
63 cfg->complexity = 10;
64 cfg->quality = 10;
65
66 cfg->auto_answer = 100;
67 cfg->uas_duration = 3600;
68
69 /* Default logging settings: */
70 cfg->log_level = 5;
71 cfg->app_log_level = 4;
72 cfg->log_decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
73 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
74
75
76 /* Also init logging settings in pjsua.config, because log
77 * may be written before pjsua_init() is called.
78 */
79 pjsua.config.log_level = 5;
80 pjsua.config.app_log_level = 4;
Benny Prijono1c2bf462006-03-05 11:54:02 +000081
Benny Prijono08e0d062006-03-04 14:52:44 +000082
Benny Prijonoa91a0032006-02-26 21:23:45 +000083 /* Init accounts: */
Benny Prijonoa91a0032006-02-26 21:23:45 +000084 for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
Benny Prijonodc39fe82006-05-26 12:17:46 +000085 cfg->acc_config[i].reg_timeout = 55;
Benny Prijonoa91a0032006-02-26 21:23:45 +000086 }
Benny Prijono268ca612006-02-07 12:34:11 +000087}
88
89
Benny Prijonodc39fe82006-05-26 12:17:46 +000090#define strncpy_with_null(dst,src,len) \
91do { \
92 strncpy(dst, src, len); \
93 dst[len-1] = '\0'; \
94} while (0)
95
96
97
98PJ_DEF(pj_status_t) pjsua_test_config( const pjsua_config *cfg,
99 char *errmsg,
100 int len)
101{
102 unsigned i;
103
104 /* If UDP port is zero, then sip_host and sip_port must be specified */
105 if (cfg->udp_port == 0) {
106 if (cfg->sip_host.slen==0 || cfg->sip_port==0) {
107 strncpy_with_null(errmsg,
108 "sip_host and sip_port must be specified",
109 len);
110 return -1;
111 }
112 }
113
114 if (cfg->max_calls < 1) {
115 strncpy_with_null(errmsg,
116 "max_calls needs to be at least 1",
117 len);
118 return -1;
119 }
120
121 /* STUN */
122 if (cfg->stun_srv1.slen || cfg->stun_port1 || cfg->stun_port2 ||
123 cfg->stun_srv2.slen)
124 {
125 if (cfg->stun_port1 == 0) {
126 strncpy_with_null(errmsg, "stun_port1 required", len);
127 return -1;
128 }
129 if (cfg->stun_srv1.slen == 0) {
130 strncpy_with_null(errmsg, "stun_srv1 required", len);
131 return -1;
132 }
133 if (cfg->stun_port2 == 0) {
134 strncpy_with_null(errmsg, "stun_port2 required", len);
135 return -1;
136 }
137 if (cfg->stun_srv2.slen == 0) {
138 strncpy_with_null(errmsg, "stun_srv2 required", len);
139 return -1;
140 }
141 }
142
143 /* Verify accounts */
144 for (i=0; i<cfg->acc_cnt; ++i) {
145 const pjsua_acc_config *acc_cfg = &cfg->acc_config[i];
146 unsigned j;
147
148 if (acc_cfg->id.slen == 0) {
149 strncpy_with_null(errmsg, "missing account ID", len);
150 return -1;
151 }
152
153 if (acc_cfg->id.slen == 0) {
154 strncpy_with_null(errmsg, "missing registrar URI", len);
155 return -1;
156 }
157
158 if (acc_cfg->reg_timeout == 0) {
159 strncpy_with_null(errmsg, "missing registration timeout", len);
160 return -1;
161 }
162
163
164 for (j=0; j<acc_cfg->cred_count; ++j) {
165
166 if (acc_cfg->cred_info[j].scheme.slen == 0) {
167 strncpy_with_null(errmsg, "missing auth scheme in account",
168 len);
169 return -1;
170 }
171
172 if (acc_cfg->cred_info[j].realm.slen == 0) {
173 strncpy_with_null(errmsg, "missing realm in account", len);
174 return -1;
175 }
176
177 if (acc_cfg->cred_info[j].username.slen == 0) {
178 strncpy_with_null(errmsg, "missing username in account", len);
179 return -1;
180 }
181
182 }
183 }
184
185 return PJ_SUCCESS;
186}
187
Benny Prijono268ca612006-02-07 12:34:11 +0000188
189/*
190 * Handler for receiving incoming requests.
191 *
192 * This handler serves multiple purposes:
193 * - it receives requests outside dialogs.
194 * - it receives requests inside dialogs, when the requests are
195 * unhandled by other dialog usages. Example of these
196 * requests are: MESSAGE.
197 */
198static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
199{
Benny Prijono38998232006-02-08 22:44:25 +0000200
Benny Prijono84126ab2006-02-09 09:30:09 +0000201 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000202
Benny Prijonoa91a0032006-02-26 21:23:45 +0000203 return pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000204 }
205
Benny Prijono268ca612006-02-07 12:34:11 +0000206 return PJ_FALSE;
207}
208
209
210/*
211 * Handler for receiving incoming responses.
212 *
213 * This handler serves multiple purposes:
214 * - it receives strayed responses (i.e. outside any dialog and
215 * outside any transactions).
216 * - it receives responses coming to a transaction, when pjsua
217 * module is set as transaction user for the transaction.
218 * - it receives responses inside a dialog, when these responses
219 * are unhandled by other dialog usages.
220 */
221static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
222{
223 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000224 return PJ_FALSE;
225}
226
227
Benny Prijono834aee32006-02-19 01:38:06 +0000228static int PJ_THREAD_FUNC pjsua_poll(void *arg)
Benny Prijono268ca612006-02-07 12:34:11 +0000229{
Benny Prijonof8baa872006-02-27 23:54:23 +0000230 pj_status_t last_err = 0;
231
Benny Prijono268ca612006-02-07 12:34:11 +0000232 PJ_UNUSED_ARG(arg);
233
Benny Prijono834aee32006-02-19 01:38:06 +0000234 do {
Benny Prijono268ca612006-02-07 12:34:11 +0000235 pj_time_val timeout = { 0, 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000236 pj_status_t status;
237
238 status = pjsip_endpt_handle_events (pjsua.endpt, &timeout);
Benny Prijono080a2c42006-03-30 20:55:20 +0000239 if (status != PJ_SUCCESS && status != last_err) {
Benny Prijonof8baa872006-02-27 23:54:23 +0000240 last_err = status;
241 pjsua_perror(THIS_FILE, "handle_events() returned error", status);
242 }
Benny Prijono834aee32006-02-19 01:38:06 +0000243 } while (!pjsua.quit_flag);
Benny Prijono268ca612006-02-07 12:34:11 +0000244
245 return 0;
246}
247
Benny Prijonodc39fe82006-05-26 12:17:46 +0000248
249
250#define pjsua_has_stun() (pjsua.config.stun_port1 && \
251 pjsua.config.stun_port2)
252
253
Benny Prijono268ca612006-02-07 12:34:11 +0000254/*
Benny Prijonodc39fe82006-05-26 12:17:46 +0000255 * Create and initialize SIP socket (and possibly resolve public
256 * address via STUN, depending on config).
Benny Prijono268ca612006-02-07 12:34:11 +0000257 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000258static pj_status_t create_sip_udp_sock(int port,
259 pj_sock_t *p_sock,
260 pj_sockaddr_in *p_pub_addr)
Benny Prijono268ca612006-02-07 12:34:11 +0000261{
Benny Prijonodc39fe82006-05-26 12:17:46 +0000262 pj_sock_t sock;
Benny Prijono268ca612006-02-07 12:34:11 +0000263 pj_status_t status;
264
Benny Prijonodc39fe82006-05-26 12:17:46 +0000265 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
266 if (status != PJ_SUCCESS) {
267 pjsua_perror(THIS_FILE, "socket() error", status);
268 return status;
269 }
Benny Prijono268ca612006-02-07 12:34:11 +0000270
Benny Prijonodc39fe82006-05-26 12:17:46 +0000271 status = pj_sock_bind_in(sock, 0, (pj_uint16_t)port);
272 if (status != PJ_SUCCESS) {
273 pjsua_perror(THIS_FILE, "bind() error", status);
274 pj_sock_close(sock);
275 return status;
276 }
Benny Prijono268ca612006-02-07 12:34:11 +0000277
Benny Prijonodc39fe82006-05-26 12:17:46 +0000278 if (pjsua_has_stun()) {
279 status = pj_stun_get_mapped_addr(&pjsua.cp.factory, 1, &sock,
280 &pjsua.config.stun_srv1,
281 pjsua.config.stun_port1,
282 &pjsua.config.stun_srv2,
283 pjsua.config.stun_port2,
284 p_pub_addr);
285 if (status != PJ_SUCCESS) {
286 pjsua_perror(THIS_FILE, "STUN resolve error", status);
287 pj_sock_close(sock);
288 return status;
289 }
290
291 } else {
292
293 const pj_str_t *hostname = pj_gethostname();
294 struct pj_hostent he;
295
296 status = pj_gethostbyname(hostname, &he);
297 if (status != PJ_SUCCESS) {
298 pjsua_perror(THIS_FILE, "Unable to resolve local host", status);
299 pj_sock_close(sock);
300 return status;
301 }
302
303 pj_memset(p_pub_addr, 0, sizeof(pj_sockaddr_in));
304 p_pub_addr->sin_family = PJ_AF_INET;
305 p_pub_addr->sin_port = pj_htons((pj_uint16_t)port);
306 p_pub_addr->sin_addr = *(pj_in_addr*)he.h_addr;
307 }
308
309 *p_sock = sock;
310 return PJ_SUCCESS;
311}
312
313
314/*
315 * Create RTP and RTCP socket pair, and possibly resolve their public
316 * address via STUN.
317 */
318static pj_status_t create_rtp_rtcp_sock(pjmedia_sock_info *skinfo)
319{
320 enum {
321 RTP_RETRY = 100
322 };
323 int i;
324 static pj_uint16_t rtp_port;
325 pj_sockaddr_in mapped_addr[2];
326 pj_status_t status = PJ_SUCCESS;
327 pj_sock_t sock[2];
328
329 if (rtp_port == 0)
330 rtp_port = (pj_uint16_t)pjsua.config.start_rtp_port;
331
332 for (i=0; i<2; ++i)
333 sock[i] = PJ_INVALID_SOCKET;
334
335
336 /* Loop retry to bind RTP and RTCP sockets. */
337 for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) {
338
339 /* Create and bind RTP socket. */
340 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[0]);
341 if (status != PJ_SUCCESS) {
342 pjsua_perror(THIS_FILE, "socket() error", status);
343 return status;
344 }
345
346 status = pj_sock_bind_in(sock[0], 0, rtp_port);
347 if (status != PJ_SUCCESS) {
348 pj_sock_close(sock[0]);
349 sock[0] = PJ_INVALID_SOCKET;
350 continue;
351 }
352
353 /* Create and bind RTCP socket. */
354 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[1]);
355 if (status != PJ_SUCCESS) {
356 pjsua_perror(THIS_FILE, "socket() error", status);
357 pj_sock_close(sock[0]);
358 return status;
359 }
360
361 status = pj_sock_bind_in(sock[1], 0, (pj_uint16_t)(rtp_port+1));
362 if (status != PJ_SUCCESS) {
363 pj_sock_close(sock[0]);
364 sock[0] = PJ_INVALID_SOCKET;
365
366 pj_sock_close(sock[1]);
367 sock[1] = PJ_INVALID_SOCKET;
368 continue;
369 }
370
371 /*
372 * If we're configured to use STUN, then find out the mapped address,
373 * and make sure that the mapped RTCP port is adjacent with the RTP.
374 */
375 if (pjsua_has_stun()) {
376 status=pj_stun_get_mapped_addr(&pjsua.cp.factory, 2, sock,
377 &pjsua.config.stun_srv1,
378 pjsua.config.stun_port1,
379 &pjsua.config.stun_srv2,
380 pjsua.config.stun_port2,
381 mapped_addr);
382 if (status != PJ_SUCCESS) {
383 pjsua_perror(THIS_FILE, "STUN resolve error", status);
384 goto on_error;
385 }
386
387 if (pj_ntohs(mapped_addr[1].sin_port) ==
388 pj_ntohs(mapped_addr[0].sin_port)+1)
389 {
390 /* Success! */
391 break;
392 }
393
394 pj_sock_close(sock[0]);
395 sock[0] = PJ_INVALID_SOCKET;
396
397 pj_sock_close(sock[1]);
398 sock[1] = PJ_INVALID_SOCKET;
399
400 } else {
401 const pj_str_t *hostname;
402 pj_sockaddr_in addr;
403
404 /* Get local IP address. */
405 hostname = pj_gethostname();
406
407 pj_memset( &addr, 0, sizeof(addr));
408 addr.sin_family = PJ_AF_INET;
409 status = pj_sockaddr_in_set_str_addr( &addr, hostname);
410 if (status != PJ_SUCCESS) {
411 pjsua_perror(THIS_FILE, "Unresolvable local hostname",
412 status);
413 goto on_error;
414 }
415
416 for (i=0; i<2; ++i)
417 pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
418
419 mapped_addr[0].sin_port=pj_htons((pj_uint16_t)rtp_port);
420 mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(rtp_port+1));
421 break;
422 }
423 }
424
425 if (sock[0] == PJ_INVALID_SOCKET) {
426 PJ_LOG(1,(THIS_FILE,
427 "Unable to find appropriate RTP/RTCP ports combination"));
428 goto on_error;
429 }
430
431
432 skinfo->rtp_sock = sock[0];
433 pj_memcpy(&skinfo->rtp_addr_name,
434 &mapped_addr[0], sizeof(pj_sockaddr_in));
435
436 skinfo->rtcp_sock = sock[1];
437 pj_memcpy(&skinfo->rtcp_addr_name,
438 &mapped_addr[1], sizeof(pj_sockaddr_in));
439
440 PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d",
441 pj_inet_ntoa(skinfo->rtp_addr_name.sin_addr),
442 pj_ntohs(skinfo->rtp_addr_name.sin_port)));
443 PJ_LOG(4,(THIS_FILE, "RTCP socket reachable at %s:%d",
444 pj_inet_ntoa(skinfo->rtcp_addr_name.sin_addr),
445 pj_ntohs(skinfo->rtcp_addr_name.sin_port)));
446
447 rtp_port += 2;
448 return PJ_SUCCESS;
449
450on_error:
451 for (i=0; i<2; ++i) {
452 if (sock[i] != PJ_INVALID_SOCKET)
453 pj_sock_close(sock[i]);
454 }
455 return status;
456}
457
458
459
460/**
461 * Create pjsua application.
462 * This initializes pjlib/pjlib-util, and creates memory pool factory to
463 * be used by application.
464 */
465PJ_DEF(pj_status_t) pjsua_create(void)
466{
467 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000468
469 /* Init PJLIB: */
470
471 status = pj_init();
472 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000473 pjsua_perror(THIS_FILE, "pj_init() error", status);
Benny Prijono268ca612006-02-07 12:34:11 +0000474 return status;
475 }
476
Benny Prijonofccab712006-02-22 22:23:22 +0000477 /* Init PJLIB-UTIL: */
478
479 status = pjlib_util_init();
480 if (status != PJ_SUCCESS) {
481 pjsua_perror(THIS_FILE, "pjlib_util_init() error", status);
482 return status;
483 }
484
Benny Prijono268ca612006-02-07 12:34:11 +0000485 /* Init memory pool: */
486
487 /* Init caching pool. */
488 pj_caching_pool_init(&pjsua.cp, &pj_pool_factory_default_policy, 0);
489
490 /* Create memory pool for application. */
491 pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL);
492
Benny Prijonodc39fe82006-05-26 12:17:46 +0000493 /* Must create endpoint to initialize SIP parser. */
494 /* Create global endpoint: */
Benny Prijono268ca612006-02-07 12:34:11 +0000495
Benny Prijonodc39fe82006-05-26 12:17:46 +0000496 status = pjsip_endpt_create(&pjsua.cp.factory,
497 pj_gethostname()->ptr,
498 &pjsua.endpt);
Benny Prijonoccf95622006-02-07 18:48:01 +0000499 if (status != PJ_SUCCESS) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000500 pjsua_perror(THIS_FILE, "Unable to create SIP endpoint", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000501 return status;
502 }
503
Benny Prijonodc39fe82006-05-26 12:17:46 +0000504 /* Must create media endpoint too */
Benny Prijono275fd682006-03-22 11:59:11 +0000505 status = pjmedia_endpt_create(&pjsua.cp.factory,
506 pjsip_endpt_get_ioqueue(pjsua.endpt), 0,
507 &pjsua.med_endpt);
Benny Prijono95196582006-02-09 00:13:40 +0000508 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000509 pjsua_perror(THIS_FILE,
510 "Media stack initialization has returned error",
511 status);
Benny Prijono95196582006-02-09 00:13:40 +0000512 return status;
513 }
514
Benny Prijonodc39fe82006-05-26 12:17:46 +0000515
Benny Prijonoccf95622006-02-07 18:48:01 +0000516 return PJ_SUCCESS;
517}
518
519
520
521/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000522 * Init media.
Benny Prijonoccf95622006-02-07 18:48:01 +0000523 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000524static pj_status_t init_media(void)
Benny Prijonoccf95622006-02-07 18:48:01 +0000525{
Benny Prijonob8c25182006-04-29 08:31:09 +0000526 int i;
Benny Prijono8e3344c2006-03-08 12:37:22 +0000527 unsigned options;
Benny Prijonob8c25182006-04-29 08:31:09 +0000528 unsigned clock_rate;
529 unsigned samples_per_frame;
530 pj_str_t codec_id;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000531 pj_status_t status;
532
Benny Prijonob8c25182006-04-29 08:31:09 +0000533 /* Register all codecs */
Benny Prijono4381efe2006-03-16 14:24:26 +0000534#if PJMEDIA_HAS_SPEEX_CODEC
Benny Prijonob8c25182006-04-29 08:31:09 +0000535 /* Register speex. */
536 status = pjmedia_codec_speex_init(pjsua.med_endpt,
537 PJMEDIA_SPEEX_NO_UWB,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000538 pjsua.config.quality,
539 pjsua.config.complexity );
Benny Prijonob8c25182006-04-29 08:31:09 +0000540 if (status != PJ_SUCCESS) {
541 pjsua_perror(THIS_FILE, "Error initializing Speex codec",
542 status);
543 return status;
544 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000545
Benny Prijonob8c25182006-04-29 08:31:09 +0000546 /* Set "speex/16000/1" to have highest priority */
547 codec_id = pj_str("speex/16000/1");
548 pjmedia_codec_mgr_set_codec_priority(
549 pjmedia_endpt_get_codec_mgr(pjsua.med_endpt),
550 &codec_id,
551 PJMEDIA_CODEC_PRIO_HIGHEST);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000552
Benny Prijono4381efe2006-03-16 14:24:26 +0000553#endif /* PJMEDIA_HAS_SPEEX_CODEC */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000554
Benny Prijono4381efe2006-03-16 14:24:26 +0000555#if PJMEDIA_HAS_GSM_CODEC
Benny Prijonob8c25182006-04-29 08:31:09 +0000556 /* Register GSM */
557 status = pjmedia_codec_gsm_init(pjsua.med_endpt);
558 if (status != PJ_SUCCESS) {
559 pjsua_perror(THIS_FILE, "Error initializing GSM codec",
560 status);
561 return status;
562 }
Benny Prijono4381efe2006-03-16 14:24:26 +0000563#endif /* PJMEDIA_HAS_GSM_CODEC */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000564
Benny Prijono4381efe2006-03-16 14:24:26 +0000565#if PJMEDIA_HAS_G711_CODEC
Benny Prijonob8c25182006-04-29 08:31:09 +0000566 /* Register PCMA and PCMU */
567 status = pjmedia_codec_g711_init(pjsua.med_endpt);
568 if (status != PJ_SUCCESS) {
569 pjsua_perror(THIS_FILE, "Error initializing G711 codec",
570 status);
571 return status;
572 }
Benny Prijono4381efe2006-03-16 14:24:26 +0000573#endif /* PJMEDIA_HAS_G711_CODEC */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000574
Benny Prijono15953012006-04-27 22:37:08 +0000575#if PJMEDIA_HAS_L16_CODEC
Benny Prijonob8c25182006-04-29 08:31:09 +0000576 /* Register L16 family codecs, but disable all */
577 status = pjmedia_codec_l16_init(pjsua.med_endpt, 0);
578 if (status != PJ_SUCCESS) {
579 pjsua_perror(THIS_FILE, "Error initializing L16 codecs",
580 status);
581 return status;
582 }
Benny Prijono15953012006-04-27 22:37:08 +0000583
Benny Prijonob8c25182006-04-29 08:31:09 +0000584 /* Disable ALL L16 codecs */
585 codec_id = pj_str("L16");
586 pjmedia_codec_mgr_set_codec_priority(
587 pjmedia_endpt_get_codec_mgr(pjsua.med_endpt),
588 &codec_id,
589 PJMEDIA_CODEC_PRIO_DISABLED);
Benny Prijono15953012006-04-27 22:37:08 +0000590
Benny Prijono15953012006-04-27 22:37:08 +0000591#endif /* PJMEDIA_HAS_L16_CODEC */
592
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000593
Benny Prijono1d8d6082006-04-29 12:38:25 +0000594 /* Enable those codecs that user put with "--add-codec", and move
595 * the priority to top
Benny Prijonob8c25182006-04-29 08:31:09 +0000596 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000597 for (i=0; i<(int)pjsua.config.codec_cnt; ++i) {
Benny Prijonob8c25182006-04-29 08:31:09 +0000598 pjmedia_codec_mgr_set_codec_priority(
599 pjmedia_endpt_get_codec_mgr(pjsua.med_endpt),
Benny Prijonodc39fe82006-05-26 12:17:46 +0000600 &pjsua.config.codec_arg[i],
Benny Prijonoda1c3e12006-05-14 10:55:14 +0000601 PJMEDIA_CODEC_PRIO_HIGHEST);
Benny Prijonob8c25182006-04-29 08:31:09 +0000602 }
603
604
Benny Prijono8e3344c2006-03-08 12:37:22 +0000605 /* Init options for conference bridge. */
606 options = 0;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000607
608 /* Calculate maximum number of ports, if it's not specified */
609 if (pjsua.config.conf_ports == 0) {
610 pjsua.config.conf_ports = 3 * pjsua.config.max_calls;
611 }
Benny Prijono8e3344c2006-03-08 12:37:22 +0000612
Benny Prijonof8baa872006-02-27 23:54:23 +0000613 /* Init conference bridge. */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000614 clock_rate = pjsua.config.clock_rate ? pjsua.config.clock_rate : 16000;
Benny Prijono65382db2006-05-14 18:50:09 +0000615 samples_per_frame = clock_rate * 10 / 1000;
Benny Prijonof8baa872006-02-27 23:54:23 +0000616 status = pjmedia_conf_create(pjsua.pool,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000617 pjsua.config.conf_ports,
Benny Prijonob8c25182006-04-29 08:31:09 +0000618 clock_rate,
Benny Prijonod0659a32006-03-16 19:03:07 +0000619 1, /* mono */
Benny Prijonob8c25182006-04-29 08:31:09 +0000620 samples_per_frame,
621 16,
Benny Prijono8e3344c2006-03-08 12:37:22 +0000622 options,
Benny Prijono08e0d062006-03-04 14:52:44 +0000623 &pjsua.mconf);
Benny Prijonof8baa872006-02-27 23:54:23 +0000624 if (status != PJ_SUCCESS) {
Benny Prijonof8baa872006-02-27 23:54:23 +0000625 pjsua_perror(THIS_FILE,
626 "Media stack initialization has returned error",
627 status);
628 return status;
629 }
630
Benny Prijono39879152006-02-23 02:09:10 +0000631 /* Create WAV file player if required: */
632
Benny Prijonodc39fe82006-05-26 12:17:46 +0000633 if (pjsua.config.wav_file.slen) {
Benny Prijono39879152006-02-23 02:09:10 +0000634 pj_str_t port_name;
635
636 /* Create the file player port. */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000637 status = pjmedia_wav_player_port_create( pjsua.pool,
638 pjsua.config.wav_file.ptr,
Benny Prijono15953012006-04-27 22:37:08 +0000639 0, 0, -1, NULL,
Benny Prijonode380582006-03-15 19:32:41 +0000640 &pjsua.file_port);
Benny Prijono39879152006-02-23 02:09:10 +0000641 if (status != PJ_SUCCESS) {
642 pjsua_perror(THIS_FILE,
643 "Error playing media file",
644 status);
645 return status;
646 }
647
648 /* Add port to conference bridge: */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000649 port_name = pjsua.config.wav_file;
Benny Prijonode380582006-03-15 19:32:41 +0000650 status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool,
651 pjsua.file_port,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000652 &port_name,
Benny Prijono39879152006-02-23 02:09:10 +0000653 &pjsua.wav_slot);
654 if (status != PJ_SUCCESS) {
655 pjsua_perror(THIS_FILE,
656 "Unable to add file player to conference bridge",
657 status);
658 return status;
659 }
660 }
661
662
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000663 return PJ_SUCCESS;
664}
665
666
667/*
Benny Prijonodc39fe82006-05-26 12:17:46 +0000668 * Copy configuration.
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000669 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000670static void copy_config(pj_pool_t *pool, pjsua_config *dst,
671 const pjsua_config *src)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000672{
Benny Prijonodc39fe82006-05-26 12:17:46 +0000673 unsigned i;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000674
Benny Prijonodc39fe82006-05-26 12:17:46 +0000675 /* Plain memcpy */
676 pj_memcpy(dst, src, sizeof(pjsua_config));
677
678 /* Duplicate strings */
679 pj_strdup_with_null(pool, &dst->sip_host, &src->sip_host);
680 pj_strdup_with_null(pool, &dst->stun_srv1, &src->stun_srv1);
681 pj_strdup_with_null(pool, &dst->stun_srv2, &src->stun_srv2);
682 pj_strdup_with_null(pool, &dst->wav_file, &src->wav_file);
683
684 for (i=0; i<src->codec_cnt; ++i) {
685 pj_strdup_with_null(pool, &dst->codec_arg[i], &src->codec_arg[i]);
686 }
687
688 pj_strdup_with_null(pool, &dst->outbound_proxy, &src->outbound_proxy);
689 pj_strdup_with_null(pool, &dst->uri_to_call, &src->uri_to_call);
690
691 for (i=0; i<src->acc_cnt; ++i) {
692 pjsua_acc_config *dst_acc = &dst->acc_config[i];
693 const pjsua_acc_config *src_acc = &src->acc_config[i];
694 unsigned j;
695
696 pj_strdup_with_null(pool, &dst_acc->id, &src_acc->id);
697 pj_strdup_with_null(pool, &dst_acc->reg_uri, &src_acc->reg_uri);
698 pj_strdup_with_null(pool, &dst_acc->contact, &src_acc->contact);
699 pj_strdup_with_null(pool, &dst_acc->proxy, &src_acc->proxy);
700
701 for (j=0; j<src_acc->cred_count; ++j) {
702 pj_strdup_with_null(pool, &dst_acc->cred_info[j].realm,
703 &src_acc->cred_info[j].realm);
704 pj_strdup_with_null(pool, &dst_acc->cred_info[j].scheme,
705 &src_acc->cred_info[j].scheme);
706 pj_strdup_with_null(pool, &dst_acc->cred_info[j].username,
707 &src_acc->cred_info[j].username);
708 pj_strdup_with_null(pool, &dst_acc->cred_info[j].data,
709 &src_acc->cred_info[j].data);
710 }
711 }
712
713 pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename);
714
715 for (i=0; i<src->buddy_cnt; ++i) {
716 pj_strdup_with_null(pool, &dst->buddy_uri[i], &src->buddy_uri[i]);
717 }
718}
719
720
721/*
722 * Initialize pjsua application.
723 * This will initialize all libraries, create endpoint instance, and register
724 * pjsip modules.
725 */
726PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg,
727 const pjsua_callback *cb)
728{
729 char errmsg[80];
730 unsigned i;
731 pj_status_t status;
732
733
734 /* Init accounts: */
735 for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
736 pjsua.acc[i].index = i;
737 pjsua.acc[i].online_status = PJ_TRUE;
738 pj_list_init(&pjsua.acc[i].route_set);
739 pj_list_init(&pjsua.acc[i].pres_srv_list);
740 }
741
742 /* Init call array: */
743 for (i=0; i<PJ_ARRAY_SIZE(pjsua.calls); ++i) {
744 pjsua.calls[i].index = i;
745 pjsua.calls[i].refresh_tm._timer_id = -1;
746 pjsua.calls[i].hangup_tm._timer_id = -1;
747 pjsua.calls[i].conf_slot = 0;
748 }
749
750 /* Copy configuration */
751 copy_config(pjsua.pool, &pjsua.config, cfg);
752
753 /* Copy callback */
754 pj_memcpy(&pjsua.cb, cb, sizeof(pjsua_callback));
755
756 /* Test configuration */
757 if (pjsua_test_config(&pjsua.config, errmsg, sizeof(errmsg))) {
758 PJ_LOG(1,(THIS_FILE, "Error in configuration: %s", errmsg));
759 return -1;
760 }
761
762
763 /* Init PJLIB logging: */
764
765 pj_log_set_level(pjsua.config.log_level);
766 pj_log_set_decor(pjsua.config.log_decor);
767
768
769 /* Create SIP UDP socket */
770 if (pjsua.config.udp_port) {
771
772 status = create_sip_udp_sock( pjsua.config.udp_port,
773 &pjsua.sip_sock,
774 &pjsua.sip_sock_name);
775 if (status != PJ_SUCCESS)
776 return status;
777
778 pj_strdup2_with_null(pjsua.pool, &pjsua.config.sip_host,
779 pj_inet_ntoa(pjsua.sip_sock_name.sin_addr));
780 pjsua.config.sip_port = pj_ntohs(pjsua.sip_sock_name.sin_port);
781
782 } else {
783
784 /* Check that SIP host and port is configured */
785 if (cfg->sip_host.slen == 0 || cfg->sip_port == 0) {
786 PJ_LOG(1,(THIS_FILE,
787 "Error: sip_host and sip_port must be specified"));
788 return PJ_EINVAL;
789 }
790
791 pjsua.sip_sock = PJ_INVALID_SOCKET;
792 }
793
794
795 /* Init media endpoint */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000796 status = init_media();
797 if (status != PJ_SUCCESS)
798 return status;
799
Benny Prijonodc39fe82006-05-26 12:17:46 +0000800
801 /* Init RTP sockets, only when UDP transport is enabled */
802 for (i=0; pjsua.config.start_rtp_port && i<pjsua.config.max_calls; ++i) {
803 status = create_rtp_rtcp_sock(&pjsua.calls[i].skinfo);
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000804 if (status != PJ_SUCCESS) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000805 unsigned j;
806 for (j=0; j<i; ++j) {
807 pjmedia_transport_udp_close(pjsua.calls[j].med_tp);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000808 }
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000809 return status;
810 }
Benny Prijonodc39fe82006-05-26 12:17:46 +0000811 status = pjmedia_transport_udp_attach(pjsua.med_endpt, NULL,
812 &pjsua.calls[i].skinfo,
813 &pjsua.calls[i].med_tp);
Benny Prijono268ca612006-02-07 12:34:11 +0000814 }
815
Benny Prijonodc39fe82006-05-26 12:17:46 +0000816 /* Init PJSIP : */
817
818 /* Initialize transaction layer: */
819
820 status = pjsip_tsx_layer_init_module(pjsua.endpt);
821 if (status != PJ_SUCCESS) {
822 pjsua_perror(THIS_FILE, "Transaction layer initialization error",
823 status);
824 goto on_error;
825 }
826
827 /* Initialize UA layer module: */
828
829 status = pjsip_ua_init_module( pjsua.endpt, NULL );
830 if (status != PJ_SUCCESS) {
831 pjsua_perror(THIS_FILE, "UA layer initialization error", status);
832 goto on_error;
833 }
834
835 /* Initialize and register pjsua's application module: */
Benny Prijono268ca612006-02-07 12:34:11 +0000836
Benny Prijonoccf95622006-02-07 18:48:01 +0000837 {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000838 pjsip_module my_mod =
839 {
840 NULL, NULL, /* prev, next. */
841 { "mod-pjsua", 9 }, /* Name. */
842 -1, /* Id */
843 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
844 NULL, /* load() */
845 NULL, /* start() */
846 NULL, /* stop() */
847 NULL, /* unload() */
848 &mod_pjsua_on_rx_request, /* on_rx_request() */
849 &mod_pjsua_on_rx_response, /* on_rx_response() */
850 NULL, /* on_tx_request. */
851 NULL, /* on_tx_response() */
852 NULL, /* on_tsx_state() */
853 };
854
855 pjsua.mod = my_mod;
856
857 status = pjsip_endpt_register_module(pjsua.endpt, &pjsua.mod);
858 if (status != PJ_SUCCESS) {
859 pjsua_perror(THIS_FILE, "Unable to register pjsua module",
860 status);
861 goto on_error;
862 }
863 }
864
865 /* Initialize invite session module: */
866
867 status = pjsua_call_init();
868 if (status != PJ_SUCCESS) {
869 pjsua_perror(THIS_FILE, "Invite usage initialization error",
870 status);
871 goto on_error;
872 }
873
874 /* Init core SIMPLE module : */
875
876 pjsip_evsub_init_module(pjsua.endpt);
877
878 /* Init presence module: */
879
880 pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance());
881
882 /* Init xfer/REFER module */
883
884 pjsip_xfer_init_module( pjsua.endpt );
885
886 /* Init pjsua presence handler: */
887
888 pjsua_pres_init();
889
890 /* Init out-of-dialog MESSAGE request handler. */
891
892 pjsua_im_init();
893
894
895 /* Done. */
896 return PJ_SUCCESS;
897
898on_error:
899 pj_caching_pool_destroy(&pjsua.cp);
900 return status;
901}
902
903
904/*
905 * Find account for incoming request.
906 */
907int pjsua_find_account_for_incoming(pjsip_rx_data *rdata)
908{
909 pjsip_uri *uri;
910 pjsip_sip_uri *sip_uri;
911 unsigned acc_index;
912
913 uri = rdata->msg_info.to->uri;
914
915 /* Just return last account if To URI is not SIP: */
916 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
917 !PJSIP_URI_SCHEME_IS_SIPS(uri))
918 {
919 return pjsua.config.acc_cnt;
920 }
921
922
923 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
924
925 /* Find account which has matching username and domain. */
926 for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) {
927
928 pjsua_acc *acc = &pjsua.acc[acc_index];
929
930 if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
931 pj_stricmp(&acc->host_part, &sip_uri->host)==0)
932 {
933 /* Match ! */
934 return acc_index;
935 }
936 }
937
938 /* No matching, try match domain part only. */
939 for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) {
940
941 pjsua_acc *acc = &pjsua.acc[acc_index];
942
943 if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) {
944 /* Match ! */
945 return acc_index;
946 }
947 }
948
949 /* Still no match, just return last account */
950 return pjsua.config.acc_cnt;
951}
952
953
954/*
955 * Find account for outgoing request.
956 */
957int pjsua_find_account_for_outgoing(const pj_str_t *url)
958{
959 PJ_UNUSED_ARG(url);
960
961 /* Just use account #0 */
962 return 0;
963}
964
965
966
967/*
968 * Start pjsua stack.
969 * This will start the registration process, if registration is configured.
970 */
971PJ_DEF(pj_status_t) pjsua_start(void)
972{
973 int i; /* Must be signed */
974 pj_status_t status = PJ_SUCCESS;
975
976
977 /* Add UDP transport: */
978 if (pjsua.sip_sock > 0) {
979
Benny Prijonoccf95622006-02-07 18:48:01 +0000980 /* Init the published name for the transport.
981 * Depending whether STUN is used, this may be the STUN mapped
982 * address, or socket's bound address.
983 */
984 pjsip_host_port addr_name;
985
Benny Prijonodc39fe82006-05-26 12:17:46 +0000986 addr_name.host = pjsua.config.sip_host;
987 addr_name.port = pjsua.config.sip_port;
Benny Prijonoccf95622006-02-07 18:48:01 +0000988
989 /* Create UDP transport from previously created UDP socket: */
990
991 status = pjsip_udp_transport_attach( pjsua.endpt, pjsua.sip_sock,
992 &addr_name, 1,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000993 NULL);
Benny Prijonoccf95622006-02-07 18:48:01 +0000994 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000995 pjsua_perror(THIS_FILE, "Unable to start UDP transport",
996 status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000997 return status;
998 }
Benny Prijono268ca612006-02-07 12:34:11 +0000999 }
1000
Benny Prijonodc39fe82006-05-26 12:17:46 +00001001 /* The last account is default account to be used when nothing match
1002 * any configured accounts.
1003 */
1004 {
1005 char buf[80];
1006 pj_str_t tmp;
1007 pjsua_acc_config *acc_cfg =
1008 &pjsua.config.acc_config[pjsua.config.acc_cnt];
Benny Prijonoccf95622006-02-07 18:48:01 +00001009
Benny Prijonodc39fe82006-05-26 12:17:46 +00001010 tmp.ptr = buf;
1011 tmp.slen = pj_ansi_sprintf(tmp.ptr, "<sip:%s:%d>",
1012 pjsua.config.sip_host.ptr,
1013 pjsua.config.sip_port);
1014
1015 pj_strdup_with_null( pjsua.pool, &acc_cfg->id, &tmp);
1016 acc_cfg->contact = acc_cfg->id;
1017 }
1018
1019
1020 /* Initialize accounts: */
1021 for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
1022
1023 pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[i];
1024 pjsua_acc *acc = &pjsua.acc[i];
Benny Prijonoccf95622006-02-07 18:48:01 +00001025 pjsip_uri *uri;
1026 pjsip_sip_uri *sip_uri;
Benny Prijonoccf95622006-02-07 18:48:01 +00001027
Benny Prijonoccf95622006-02-07 18:48:01 +00001028 /* Need to parse local_uri to get the elements: */
1029
Benny Prijonodc39fe82006-05-26 12:17:46 +00001030 uri = pjsip_parse_uri(pjsua.pool, acc_cfg->id.ptr,
1031 acc_cfg->id.slen, 0);
Benny Prijonoccf95622006-02-07 18:48:01 +00001032 if (uri == NULL) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001033 pjsua_perror(THIS_FILE, "Invalid local URI",
1034 PJSIP_EINVALIDURI);
Benny Prijonoccf95622006-02-07 18:48:01 +00001035 return PJSIP_EINVALIDURI;
1036 }
1037
Benny Prijonoccf95622006-02-07 18:48:01 +00001038 /* Local URI MUST be a SIP or SIPS: */
1039
Benny Prijonoa91a0032006-02-26 21:23:45 +00001040 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
1041 !PJSIP_URI_SCHEME_IS_SIPS(uri))
1042 {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001043 pjsua_perror(THIS_FILE, "Invalid local URI",
1044 PJSIP_EINVALIDSCHEME);
Benny Prijonoccf95622006-02-07 18:48:01 +00001045 return PJSIP_EINVALIDSCHEME;
1046 }
1047
1048
1049 /* Get the SIP URI object: */
1050
1051 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
1052
Benny Prijonodc39fe82006-05-26 12:17:46 +00001053 acc->user_part = sip_uri->user;
1054 acc->host_part = sip_uri->host;
Benny Prijonoccf95622006-02-07 18:48:01 +00001055
Benny Prijonodc39fe82006-05-26 12:17:46 +00001056 /* Build Contact header */
1057
1058 if (acc_cfg->contact.slen == 0) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001059 char contact[128];
Benny Prijonodc39fe82006-05-26 12:17:46 +00001060 const char *addr;
1061 int port;
Benny Prijonoa91a0032006-02-26 21:23:45 +00001062 int len;
Benny Prijonoccf95622006-02-07 18:48:01 +00001063
Benny Prijonodc39fe82006-05-26 12:17:46 +00001064 addr = pjsua.config.sip_host.ptr;
1065 port = pjsua.config.sip_port;
1066
Benny Prijonoa91a0032006-02-26 21:23:45 +00001067 /* The local Contact is the username@ip-addr, where
1068 * - username is taken from the local URI,
1069 * - ip-addr in UDP transport's address name (which may have been
1070 * resolved from STUN.
1071 */
1072
1073 /* Build temporary contact string. */
Benny Prijonoccf95622006-02-07 18:48:01 +00001074
Benny Prijonoa91a0032006-02-26 21:23:45 +00001075 if (sip_uri->user.slen) {
Benny Prijonoccf95622006-02-07 18:48:01 +00001076
Benny Prijonoa91a0032006-02-26 21:23:45 +00001077 /* With the user part. */
Benny Prijonoed811d72006-03-10 12:57:12 +00001078 len = pj_ansi_snprintf(contact, sizeof(contact),
Benny Prijonodc39fe82006-05-26 12:17:46 +00001079 "<sip:%.*s@%s:%d>",
Benny Prijonoa91a0032006-02-26 21:23:45 +00001080 (int)sip_uri->user.slen,
1081 sip_uri->user.ptr,
Benny Prijonodc39fe82006-05-26 12:17:46 +00001082 addr, port);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001083 } else {
1084
1085 /* Without user part */
1086
Benny Prijonoed811d72006-03-10 12:57:12 +00001087 len = pj_ansi_snprintf(contact, sizeof(contact),
Benny Prijonodc39fe82006-05-26 12:17:46 +00001088 "<sip:%s:%d>",
1089 addr, port);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001090 }
1091
1092 if (len < 1 || len >= sizeof(contact)) {
1093 pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG);
1094 return PJSIP_EURITOOLONG;
1095 }
1096
1097 /* Duplicate Contact uri. */
1098
Benny Prijonodc39fe82006-05-26 12:17:46 +00001099 pj_strdup2(pjsua.pool, &acc_cfg->contact, contact);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001100
Benny Prijonoccf95622006-02-07 18:48:01 +00001101 }
Benny Prijonodc39fe82006-05-26 12:17:46 +00001102
1103
1104 /* Build route-set for this account */
1105 if (pjsua.config.outbound_proxy.slen) {
1106 pj_str_t hname = { "Route", 5};
1107 pjsip_route_hdr *r;
1108 pj_str_t tmp;
1109
1110 pj_strdup_with_null(pjsua.pool, &tmp, &pjsua.config.outbound_proxy);
1111 r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
1112 pj_list_push_back(&acc->route_set, r);
1113 }
1114
1115 if (acc_cfg->proxy.slen) {
1116 pj_str_t hname = { "Route", 5};
1117 pjsip_route_hdr *r;
1118 pj_str_t tmp;
1119
1120 pj_strdup_with_null(pjsua.pool, &tmp, &acc_cfg->proxy);
1121 r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
1122 pj_list_push_back(&acc->route_set, r);
1123 }
Benny Prijonoccf95622006-02-07 18:48:01 +00001124 }
1125
Benny Prijonoccf95622006-02-07 18:48:01 +00001126
Benny Prijonoccf95622006-02-07 18:48:01 +00001127
1128
Benny Prijono268ca612006-02-07 12:34:11 +00001129 /* Create worker thread(s), if required: */
1130
Benny Prijonodc39fe82006-05-26 12:17:46 +00001131 for (i=0; i<(int)pjsua.config.thread_cnt; ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001132 status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_poll,
Benny Prijono268ca612006-02-07 12:34:11 +00001133 NULL, 0, 0, &pjsua.threads[i]);
1134 if (status != PJ_SUCCESS) {
1135 pjsua.quit_flag = 1;
1136 for (--i; i>=0; --i) {
1137 pj_thread_join(pjsua.threads[i]);
1138 pj_thread_destroy(pjsua.threads[i]);
1139 }
Benny Prijono268ca612006-02-07 12:34:11 +00001140 return status;
1141 }
1142 }
1143
Benny Prijonoccf95622006-02-07 18:48:01 +00001144 /* Start registration: */
1145
1146 /* Create client registration session: */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001147 for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001148 status = pjsua_regc_init(i);
1149 if (status != PJ_SUCCESS)
1150 return status;
Benny Prijonoccf95622006-02-07 18:48:01 +00001151
Benny Prijonoa91a0032006-02-26 21:23:45 +00001152 /* Perform registration, if required. */
1153 if (pjsua.acc[i].regc) {
1154 pjsua_regc_update(i, 1);
1155 }
1156 }
Benny Prijonoccf95622006-02-07 18:48:01 +00001157
Benny Prijonoa91a0032006-02-26 21:23:45 +00001158
Benny Prijonodc39fe82006-05-26 12:17:46 +00001159 /* Init buddies */
1160 for (i=0; i<(int)pjsua.config.buddy_cnt; ++i) {
1161 pjsua.buddies[i].uri = pjsua.config.buddy_uri[i];
1162 }
1163 pjsua.buddy_cnt = pjsua.config.buddy_cnt;
1164
Benny Prijonoa91a0032006-02-26 21:23:45 +00001165 /* Find account for outgoing preence subscription */
1166 for (i=0; i<pjsua.buddy_cnt; ++i) {
1167 pjsua.buddies[i].acc_index =
1168 pjsua_find_account_for_outgoing(&pjsua.buddies[i].uri);
Benny Prijonoccf95622006-02-07 18:48:01 +00001169 }
1170
1171
Benny Prijono834aee32006-02-19 01:38:06 +00001172 PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION));
Benny Prijono268ca612006-02-07 12:34:11 +00001173 return PJ_SUCCESS;
1174}
1175
1176
Benny Prijono834aee32006-02-19 01:38:06 +00001177/* Sleep with polling */
1178static void busy_sleep(unsigned msec)
1179{
1180 pj_time_val timeout, now;
1181
1182 pj_gettimeofday(&timeout);
1183 timeout.msec += msec;
1184 pj_time_val_normalize(&timeout);
1185
1186 do {
1187 pjsua_poll(NULL);
1188 pj_gettimeofday(&now);
1189 } while (PJ_TIME_VAL_LT(now, timeout));
1190}
1191
Benny Prijono268ca612006-02-07 12:34:11 +00001192/*
1193 * Destroy pjsua.
1194 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001195PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +00001196{
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001197 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +00001198
1199 /* Signal threads to quit: */
Benny Prijono268ca612006-02-07 12:34:11 +00001200 pjsua.quit_flag = 1;
1201
Benny Prijonoa91a0032006-02-26 21:23:45 +00001202 /* Terminate all calls. */
Benny Prijono1a174142006-03-01 20:46:13 +00001203 pjsua_call_hangup_all();
Benny Prijonoa91a0032006-02-26 21:23:45 +00001204
1205 /* Terminate all presence subscriptions. */
1206 pjsua_pres_shutdown();
1207
1208 /* Unregister, if required: */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001209 for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001210 if (pjsua.acc[i].regc) {
1211 pjsua_regc_update(i, 0);
1212 }
1213 }
1214
Benny Prijono268ca612006-02-07 12:34:11 +00001215 /* Wait worker threads to quit: */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001216 for (i=0; i<(int)pjsua.config.thread_cnt; ++i) {
Benny Prijono268ca612006-02-07 12:34:11 +00001217
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001218 if (pjsua.threads[i]) {
1219 pj_thread_join(pjsua.threads[i]);
1220 pj_thread_destroy(pjsua.threads[i]);
1221 pjsua.threads[i] = NULL;
1222 }
Benny Prijono268ca612006-02-07 12:34:11 +00001223 }
1224
Benny Prijono834aee32006-02-19 01:38:06 +00001225
Benny Prijono834aee32006-02-19 01:38:06 +00001226 /* Wait for some time to allow unregistration to complete: */
1227 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1228 busy_sleep(1000);
1229
Benny Prijono26ff9062006-02-21 23:47:00 +00001230 /* Destroy conference bridge. */
1231 if (pjsua.mconf)
1232 pjmedia_conf_destroy(pjsua.mconf);
1233
Benny Prijonode380582006-03-15 19:32:41 +00001234 /* Destroy file port */
Benny Prijono4381efe2006-03-16 14:24:26 +00001235 if (pjsua.file_port)
1236 pjmedia_port_destroy(pjsua.file_port);
Benny Prijonode380582006-03-15 19:32:41 +00001237
Benny Prijonode380582006-03-15 19:32:41 +00001238
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001239 /* Shutdown all codecs: */
Benny Prijonob8c25182006-04-29 08:31:09 +00001240#if PJMEDIA_HAS_SPEEX_CODEC
1241 pjmedia_codec_speex_deinit();
1242#endif /* PJMEDIA_HAS_SPEEX_CODEC */
1243
1244#if PJMEDIA_HAS_GSM_CODEC
1245 pjmedia_codec_gsm_deinit();
1246#endif /* PJMEDIA_HAS_GSM_CODEC */
1247
1248#if PJMEDIA_HAS_G711_CODEC
1249 pjmedia_codec_g711_deinit();
1250#endif /* PJMEDIA_HAS_G711_CODEC */
1251
1252#if PJMEDIA_HAS_L16_CODEC
1253 pjmedia_codec_l16_deinit();
1254#endif /* PJMEDIA_HAS_L16_CODEC */
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001255
Benny Prijonob04c9e02006-05-17 17:17:39 +00001256
1257 /* Close transports */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001258 for (i=0; pjsua.config.start_rtp_port && i<(int)pjsua.config.max_calls; ++i) {
Benny Prijonob04c9e02006-05-17 17:17:39 +00001259 pjmedia_transport_udp_close(pjsua.calls[i].med_tp);
1260 }
1261
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001262 /* Destroy media endpoint. */
1263
1264 pjmedia_endpt_destroy(pjsua.med_endpt);
1265
Benny Prijono268ca612006-02-07 12:34:11 +00001266 /* Destroy endpoint. */
Benny Prijono84126ab2006-02-09 09:30:09 +00001267
Benny Prijono268ca612006-02-07 12:34:11 +00001268 pjsip_endpt_destroy(pjsua.endpt);
1269 pjsua.endpt = NULL;
1270
1271 /* Destroy caching pool. */
Benny Prijono84126ab2006-02-09 09:30:09 +00001272
Benny Prijono268ca612006-02-07 12:34:11 +00001273 pj_caching_pool_destroy(&pjsua.cp);
1274
1275
1276 /* Done. */
1277
1278 return PJ_SUCCESS;
1279}
1280