blob: 3f63ce8ff9498e69988257b492fb42e1568182b7 [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;
Benny Prijonob9b32ab2006-06-01 12:28:44 +000054 cfg->media_has_ioqueue = 1;
55 cfg->media_thread_cnt = 1;
Benny Prijonodc39fe82006-05-26 12:17:46 +000056 cfg->udp_port = 5060;
57 cfg->start_rtp_port = 4000;
58 cfg->max_calls = 4;
59 cfg->conf_ports = 0;
Benny Prijono268ca612006-02-07 12:34:11 +000060
Benny Prijonofa137ca2006-03-20 17:42:37 +000061#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
62 pjsua.clock_rate = 44100;
Benny Prijonofa137ca2006-03-20 17:42:37 +000063#endif
Benny Prijonodc39fe82006-05-26 12:17:46 +000064
65 cfg->complexity = 10;
66 cfg->quality = 10;
67
68 cfg->auto_answer = 100;
69 cfg->uas_duration = 3600;
70
71 /* Default logging settings: */
72 cfg->log_level = 5;
73 cfg->app_log_level = 4;
74 cfg->log_decor = PJ_LOG_HAS_SENDER | PJ_LOG_HAS_TIME |
75 PJ_LOG_HAS_MICRO_SEC | PJ_LOG_HAS_NEWLINE;
76
77
78 /* Also init logging settings in pjsua.config, because log
79 * may be written before pjsua_init() is called.
80 */
81 pjsua.config.log_level = 5;
82 pjsua.config.app_log_level = 4;
Benny Prijono1c2bf462006-03-05 11:54:02 +000083
Benny Prijono08e0d062006-03-04 14:52:44 +000084
Benny Prijonoa91a0032006-02-26 21:23:45 +000085 /* Init accounts: */
Benny Prijonoa91a0032006-02-26 21:23:45 +000086 for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
Benny Prijonodc39fe82006-05-26 12:17:46 +000087 cfg->acc_config[i].reg_timeout = 55;
Benny Prijonoa91a0032006-02-26 21:23:45 +000088 }
Benny Prijono9fc735d2006-05-28 14:58:12 +000089
Benny Prijono268ca612006-02-07 12:34:11 +000090}
91
92
Benny Prijonodc39fe82006-05-26 12:17:46 +000093#define strncpy_with_null(dst,src,len) \
94do { \
95 strncpy(dst, src, len); \
96 dst[len-1] = '\0'; \
97} while (0)
98
99
100
101PJ_DEF(pj_status_t) pjsua_test_config( const pjsua_config *cfg,
102 char *errmsg,
103 int len)
104{
105 unsigned i;
106
107 /* If UDP port is zero, then sip_host and sip_port must be specified */
108 if (cfg->udp_port == 0) {
109 if (cfg->sip_host.slen==0 || cfg->sip_port==0) {
110 strncpy_with_null(errmsg,
111 "sip_host and sip_port must be specified",
112 len);
113 return -1;
114 }
115 }
116
117 if (cfg->max_calls < 1) {
118 strncpy_with_null(errmsg,
119 "max_calls needs to be at least 1",
120 len);
121 return -1;
122 }
123
124 /* STUN */
125 if (cfg->stun_srv1.slen || cfg->stun_port1 || cfg->stun_port2 ||
126 cfg->stun_srv2.slen)
127 {
128 if (cfg->stun_port1 == 0) {
129 strncpy_with_null(errmsg, "stun_port1 required", len);
130 return -1;
131 }
132 if (cfg->stun_srv1.slen == 0) {
133 strncpy_with_null(errmsg, "stun_srv1 required", len);
134 return -1;
135 }
136 if (cfg->stun_port2 == 0) {
137 strncpy_with_null(errmsg, "stun_port2 required", len);
138 return -1;
139 }
140 if (cfg->stun_srv2.slen == 0) {
141 strncpy_with_null(errmsg, "stun_srv2 required", len);
142 return -1;
143 }
144 }
145
146 /* Verify accounts */
147 for (i=0; i<cfg->acc_cnt; ++i) {
148 const pjsua_acc_config *acc_cfg = &cfg->acc_config[i];
149 unsigned j;
150
151 if (acc_cfg->id.slen == 0) {
152 strncpy_with_null(errmsg, "missing account ID", len);
153 return -1;
154 }
155
156 if (acc_cfg->id.slen == 0) {
157 strncpy_with_null(errmsg, "missing registrar URI", len);
158 return -1;
159 }
160
161 if (acc_cfg->reg_timeout == 0) {
162 strncpy_with_null(errmsg, "missing registration timeout", len);
163 return -1;
164 }
165
166
167 for (j=0; j<acc_cfg->cred_count; ++j) {
168
169 if (acc_cfg->cred_info[j].scheme.slen == 0) {
170 strncpy_with_null(errmsg, "missing auth scheme in account",
171 len);
172 return -1;
173 }
174
175 if (acc_cfg->cred_info[j].realm.slen == 0) {
176 strncpy_with_null(errmsg, "missing realm in account", len);
177 return -1;
178 }
179
180 if (acc_cfg->cred_info[j].username.slen == 0) {
181 strncpy_with_null(errmsg, "missing username in account", len);
182 return -1;
183 }
184
185 }
186 }
187
188 return PJ_SUCCESS;
189}
190
Benny Prijono268ca612006-02-07 12:34:11 +0000191
192/*
193 * Handler for receiving incoming requests.
194 *
195 * This handler serves multiple purposes:
196 * - it receives requests outside dialogs.
197 * - it receives requests inside dialogs, when the requests are
198 * unhandled by other dialog usages. Example of these
199 * requests are: MESSAGE.
200 */
201static pj_bool_t mod_pjsua_on_rx_request(pjsip_rx_data *rdata)
202{
Benny Prijono38998232006-02-08 22:44:25 +0000203
Benny Prijono84126ab2006-02-09 09:30:09 +0000204 if (rdata->msg_info.msg->line.req.method.id == PJSIP_INVITE_METHOD) {
Benny Prijono38998232006-02-08 22:44:25 +0000205
Benny Prijonoa91a0032006-02-26 21:23:45 +0000206 return pjsua_call_on_incoming(rdata);
Benny Prijono38998232006-02-08 22:44:25 +0000207 }
208
Benny Prijono268ca612006-02-07 12:34:11 +0000209 return PJ_FALSE;
210}
211
212
213/*
214 * Handler for receiving incoming responses.
215 *
216 * This handler serves multiple purposes:
217 * - it receives strayed responses (i.e. outside any dialog and
218 * outside any transactions).
219 * - it receives responses coming to a transaction, when pjsua
220 * module is set as transaction user for the transaction.
221 * - it receives responses inside a dialog, when these responses
222 * are unhandled by other dialog usages.
223 */
224static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata)
225{
226 PJ_UNUSED_ARG(rdata);
Benny Prijono268ca612006-02-07 12:34:11 +0000227 return PJ_FALSE;
228}
229
230
Benny Prijono834aee32006-02-19 01:38:06 +0000231static int PJ_THREAD_FUNC pjsua_poll(void *arg)
Benny Prijono268ca612006-02-07 12:34:11 +0000232{
Benny Prijonof8baa872006-02-27 23:54:23 +0000233 pj_status_t last_err = 0;
234
Benny Prijono268ca612006-02-07 12:34:11 +0000235 PJ_UNUSED_ARG(arg);
236
Benny Prijono834aee32006-02-19 01:38:06 +0000237 do {
Benny Prijono268ca612006-02-07 12:34:11 +0000238 pj_time_val timeout = { 0, 10 };
Benny Prijonof8baa872006-02-27 23:54:23 +0000239 pj_status_t status;
240
241 status = pjsip_endpt_handle_events (pjsua.endpt, &timeout);
Benny Prijono080a2c42006-03-30 20:55:20 +0000242 if (status != PJ_SUCCESS && status != last_err) {
Benny Prijonof8baa872006-02-27 23:54:23 +0000243 last_err = status;
244 pjsua_perror(THIS_FILE, "handle_events() returned error", status);
245 }
Benny Prijono834aee32006-02-19 01:38:06 +0000246 } while (!pjsua.quit_flag);
Benny Prijono268ca612006-02-07 12:34:11 +0000247
248 return 0;
249}
250
Benny Prijonob9b32ab2006-06-01 12:28:44 +0000251/**
252 * Poll pjsua.
253 */
254PJ_DECL(int) pjsua_handle_events(unsigned msec_timeout)
255{
256 unsigned count = 0;
257 pj_time_val tv;
258 pj_status_t status;
259
260 tv.sec = 0;
261 tv.msec = msec_timeout;
262 pj_time_val_normalize(&tv);
263
264 status = pjsip_endpt_handle_events2(pjsua.endpt, &tv, &count);
265 if (status != PJ_SUCCESS)
266 return -status;
267
268 return count;
269}
Benny Prijonodc39fe82006-05-26 12:17:46 +0000270
271
272#define pjsua_has_stun() (pjsua.config.stun_port1 && \
273 pjsua.config.stun_port2)
274
275
Benny Prijono268ca612006-02-07 12:34:11 +0000276/*
Benny Prijonodc39fe82006-05-26 12:17:46 +0000277 * Create and initialize SIP socket (and possibly resolve public
278 * address via STUN, depending on config).
Benny Prijono268ca612006-02-07 12:34:11 +0000279 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000280static pj_status_t create_sip_udp_sock(int port,
281 pj_sock_t *p_sock,
282 pj_sockaddr_in *p_pub_addr)
Benny Prijono268ca612006-02-07 12:34:11 +0000283{
Benny Prijonodc39fe82006-05-26 12:17:46 +0000284 pj_sock_t sock;
Benny Prijono268ca612006-02-07 12:34:11 +0000285 pj_status_t status;
286
Benny Prijonodc39fe82006-05-26 12:17:46 +0000287 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
288 if (status != PJ_SUCCESS) {
289 pjsua_perror(THIS_FILE, "socket() error", status);
290 return status;
291 }
Benny Prijono268ca612006-02-07 12:34:11 +0000292
Benny Prijonodc39fe82006-05-26 12:17:46 +0000293 status = pj_sock_bind_in(sock, 0, (pj_uint16_t)port);
294 if (status != PJ_SUCCESS) {
295 pjsua_perror(THIS_FILE, "bind() error", status);
296 pj_sock_close(sock);
297 return status;
298 }
Benny Prijono268ca612006-02-07 12:34:11 +0000299
Benny Prijonodc39fe82006-05-26 12:17:46 +0000300 if (pjsua_has_stun()) {
301 status = pj_stun_get_mapped_addr(&pjsua.cp.factory, 1, &sock,
302 &pjsua.config.stun_srv1,
303 pjsua.config.stun_port1,
304 &pjsua.config.stun_srv2,
305 pjsua.config.stun_port2,
306 p_pub_addr);
307 if (status != PJ_SUCCESS) {
308 pjsua_perror(THIS_FILE, "STUN resolve error", status);
309 pj_sock_close(sock);
310 return status;
311 }
312
313 } else {
314
315 const pj_str_t *hostname = pj_gethostname();
316 struct pj_hostent he;
317
318 status = pj_gethostbyname(hostname, &he);
319 if (status != PJ_SUCCESS) {
320 pjsua_perror(THIS_FILE, "Unable to resolve local host", status);
321 pj_sock_close(sock);
322 return status;
323 }
324
325 pj_memset(p_pub_addr, 0, sizeof(pj_sockaddr_in));
326 p_pub_addr->sin_family = PJ_AF_INET;
327 p_pub_addr->sin_port = pj_htons((pj_uint16_t)port);
328 p_pub_addr->sin_addr = *(pj_in_addr*)he.h_addr;
329 }
330
331 *p_sock = sock;
332 return PJ_SUCCESS;
333}
334
335
336/*
337 * Create RTP and RTCP socket pair, and possibly resolve their public
338 * address via STUN.
339 */
340static pj_status_t create_rtp_rtcp_sock(pjmedia_sock_info *skinfo)
341{
342 enum {
343 RTP_RETRY = 100
344 };
345 int i;
346 static pj_uint16_t rtp_port;
347 pj_sockaddr_in mapped_addr[2];
348 pj_status_t status = PJ_SUCCESS;
349 pj_sock_t sock[2];
350
351 if (rtp_port == 0)
352 rtp_port = (pj_uint16_t)pjsua.config.start_rtp_port;
353
354 for (i=0; i<2; ++i)
355 sock[i] = PJ_INVALID_SOCKET;
356
357
358 /* Loop retry to bind RTP and RTCP sockets. */
359 for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) {
360
361 /* Create and bind RTP socket. */
362 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[0]);
363 if (status != PJ_SUCCESS) {
364 pjsua_perror(THIS_FILE, "socket() error", status);
365 return status;
366 }
367
368 status = pj_sock_bind_in(sock[0], 0, rtp_port);
369 if (status != PJ_SUCCESS) {
370 pj_sock_close(sock[0]);
371 sock[0] = PJ_INVALID_SOCKET;
372 continue;
373 }
374
375 /* Create and bind RTCP socket. */
376 status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock[1]);
377 if (status != PJ_SUCCESS) {
378 pjsua_perror(THIS_FILE, "socket() error", status);
379 pj_sock_close(sock[0]);
380 return status;
381 }
382
383 status = pj_sock_bind_in(sock[1], 0, (pj_uint16_t)(rtp_port+1));
384 if (status != PJ_SUCCESS) {
385 pj_sock_close(sock[0]);
386 sock[0] = PJ_INVALID_SOCKET;
387
388 pj_sock_close(sock[1]);
389 sock[1] = PJ_INVALID_SOCKET;
390 continue;
391 }
392
393 /*
394 * If we're configured to use STUN, then find out the mapped address,
395 * and make sure that the mapped RTCP port is adjacent with the RTP.
396 */
397 if (pjsua_has_stun()) {
398 status=pj_stun_get_mapped_addr(&pjsua.cp.factory, 2, sock,
399 &pjsua.config.stun_srv1,
400 pjsua.config.stun_port1,
401 &pjsua.config.stun_srv2,
402 pjsua.config.stun_port2,
403 mapped_addr);
404 if (status != PJ_SUCCESS) {
405 pjsua_perror(THIS_FILE, "STUN resolve error", status);
406 goto on_error;
407 }
408
409 if (pj_ntohs(mapped_addr[1].sin_port) ==
410 pj_ntohs(mapped_addr[0].sin_port)+1)
411 {
412 /* Success! */
413 break;
414 }
415
416 pj_sock_close(sock[0]);
417 sock[0] = PJ_INVALID_SOCKET;
418
419 pj_sock_close(sock[1]);
420 sock[1] = PJ_INVALID_SOCKET;
421
422 } else {
423 const pj_str_t *hostname;
424 pj_sockaddr_in addr;
425
426 /* Get local IP address. */
427 hostname = pj_gethostname();
428
429 pj_memset( &addr, 0, sizeof(addr));
430 addr.sin_family = PJ_AF_INET;
431 status = pj_sockaddr_in_set_str_addr( &addr, hostname);
432 if (status != PJ_SUCCESS) {
433 pjsua_perror(THIS_FILE, "Unresolvable local hostname",
434 status);
435 goto on_error;
436 }
437
438 for (i=0; i<2; ++i)
439 pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
440
441 mapped_addr[0].sin_port=pj_htons((pj_uint16_t)rtp_port);
442 mapped_addr[1].sin_port=pj_htons((pj_uint16_t)(rtp_port+1));
443 break;
444 }
445 }
446
447 if (sock[0] == PJ_INVALID_SOCKET) {
448 PJ_LOG(1,(THIS_FILE,
449 "Unable to find appropriate RTP/RTCP ports combination"));
450 goto on_error;
451 }
452
453
454 skinfo->rtp_sock = sock[0];
455 pj_memcpy(&skinfo->rtp_addr_name,
456 &mapped_addr[0], sizeof(pj_sockaddr_in));
457
458 skinfo->rtcp_sock = sock[1];
459 pj_memcpy(&skinfo->rtcp_addr_name,
460 &mapped_addr[1], sizeof(pj_sockaddr_in));
461
462 PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d",
463 pj_inet_ntoa(skinfo->rtp_addr_name.sin_addr),
464 pj_ntohs(skinfo->rtp_addr_name.sin_port)));
465 PJ_LOG(4,(THIS_FILE, "RTCP socket reachable at %s:%d",
466 pj_inet_ntoa(skinfo->rtcp_addr_name.sin_addr),
467 pj_ntohs(skinfo->rtcp_addr_name.sin_port)));
468
469 rtp_port += 2;
470 return PJ_SUCCESS;
471
472on_error:
473 for (i=0; i<2; ++i) {
474 if (sock[i] != PJ_INVALID_SOCKET)
475 pj_sock_close(sock[i]);
476 }
477 return status;
478}
479
480
481
482/**
483 * Create pjsua application.
484 * This initializes pjlib/pjlib-util, and creates memory pool factory to
485 * be used by application.
486 */
487PJ_DEF(pj_status_t) pjsua_create(void)
488{
489 pj_status_t status;
Benny Prijono268ca612006-02-07 12:34:11 +0000490
491 /* Init PJLIB: */
492
493 status = pj_init();
494 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000495 pjsua_perror(THIS_FILE, "pj_init() error", status);
Benny Prijono268ca612006-02-07 12:34:11 +0000496 return status;
497 }
498
Benny Prijonofccab712006-02-22 22:23:22 +0000499 /* Init PJLIB-UTIL: */
500
501 status = pjlib_util_init();
502 if (status != PJ_SUCCESS) {
503 pjsua_perror(THIS_FILE, "pjlib_util_init() error", status);
504 return status;
505 }
506
Benny Prijono268ca612006-02-07 12:34:11 +0000507 /* Init memory pool: */
508
509 /* Init caching pool. */
510 pj_caching_pool_init(&pjsua.cp, &pj_pool_factory_default_policy, 0);
511
512 /* Create memory pool for application. */
513 pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL);
514
Benny Prijonodc39fe82006-05-26 12:17:46 +0000515 /* Must create endpoint to initialize SIP parser. */
516 /* Create global endpoint: */
Benny Prijono268ca612006-02-07 12:34:11 +0000517
Benny Prijonodc39fe82006-05-26 12:17:46 +0000518 status = pjsip_endpt_create(&pjsua.cp.factory,
519 pj_gethostname()->ptr,
520 &pjsua.endpt);
Benny Prijonoccf95622006-02-07 18:48:01 +0000521 if (status != PJ_SUCCESS) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000522 pjsua_perror(THIS_FILE, "Unable to create SIP endpoint", status);
Benny Prijonoccf95622006-02-07 18:48:01 +0000523 return status;
524 }
525
Benny Prijonodc39fe82006-05-26 12:17:46 +0000526 /* Must create media endpoint too */
Benny Prijono275fd682006-03-22 11:59:11 +0000527 status = pjmedia_endpt_create(&pjsua.cp.factory,
Benny Prijonob9b32ab2006-06-01 12:28:44 +0000528 pjsua.config.media_has_ioqueue? NULL :
529 pjsip_endpt_get_ioqueue(pjsua.endpt),
530 pjsua.config.media_thread_cnt,
Benny Prijono275fd682006-03-22 11:59:11 +0000531 &pjsua.med_endpt);
Benny Prijono95196582006-02-09 00:13:40 +0000532 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +0000533 pjsua_perror(THIS_FILE,
534 "Media stack initialization has returned error",
535 status);
Benny Prijono95196582006-02-09 00:13:40 +0000536 return status;
537 }
538
Benny Prijonodc39fe82006-05-26 12:17:46 +0000539
Benny Prijonoccf95622006-02-07 18:48:01 +0000540 return PJ_SUCCESS;
541}
542
543
544
545/*
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000546 * Init media.
Benny Prijonoccf95622006-02-07 18:48:01 +0000547 */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000548static pj_status_t init_media(void)
Benny Prijonoccf95622006-02-07 18:48:01 +0000549{
Benny Prijonob8c25182006-04-29 08:31:09 +0000550 int i;
Benny Prijono8e3344c2006-03-08 12:37:22 +0000551 unsigned options;
Benny Prijonob8c25182006-04-29 08:31:09 +0000552 pj_str_t codec_id;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000553 pj_status_t status;
554
Benny Prijonob8c25182006-04-29 08:31:09 +0000555 /* Register all codecs */
Benny Prijono4381efe2006-03-16 14:24:26 +0000556#if PJMEDIA_HAS_SPEEX_CODEC
Benny Prijonob8c25182006-04-29 08:31:09 +0000557 /* Register speex. */
558 status = pjmedia_codec_speex_init(pjsua.med_endpt,
559 PJMEDIA_SPEEX_NO_UWB,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000560 pjsua.config.quality,
561 pjsua.config.complexity );
Benny Prijonob8c25182006-04-29 08:31:09 +0000562 if (status != PJ_SUCCESS) {
563 pjsua_perror(THIS_FILE, "Error initializing Speex codec",
564 status);
565 return status;
566 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000567
Benny Prijonob8c25182006-04-29 08:31:09 +0000568 /* Set "speex/16000/1" to have highest priority */
569 codec_id = pj_str("speex/16000/1");
570 pjmedia_codec_mgr_set_codec_priority(
571 pjmedia_endpt_get_codec_mgr(pjsua.med_endpt),
572 &codec_id,
573 PJMEDIA_CODEC_PRIO_HIGHEST);
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000574
Benny Prijono4381efe2006-03-16 14:24:26 +0000575#endif /* PJMEDIA_HAS_SPEEX_CODEC */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000576
Benny Prijono4381efe2006-03-16 14:24:26 +0000577#if PJMEDIA_HAS_GSM_CODEC
Benny Prijonob8c25182006-04-29 08:31:09 +0000578 /* Register GSM */
579 status = pjmedia_codec_gsm_init(pjsua.med_endpt);
580 if (status != PJ_SUCCESS) {
581 pjsua_perror(THIS_FILE, "Error initializing GSM codec",
582 status);
583 return status;
584 }
Benny Prijono4381efe2006-03-16 14:24:26 +0000585#endif /* PJMEDIA_HAS_GSM_CODEC */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000586
Benny Prijono4381efe2006-03-16 14:24:26 +0000587#if PJMEDIA_HAS_G711_CODEC
Benny Prijonob8c25182006-04-29 08:31:09 +0000588 /* Register PCMA and PCMU */
589 status = pjmedia_codec_g711_init(pjsua.med_endpt);
590 if (status != PJ_SUCCESS) {
591 pjsua_perror(THIS_FILE, "Error initializing G711 codec",
592 status);
593 return status;
594 }
Benny Prijono4381efe2006-03-16 14:24:26 +0000595#endif /* PJMEDIA_HAS_G711_CODEC */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000596
Benny Prijono15953012006-04-27 22:37:08 +0000597#if PJMEDIA_HAS_L16_CODEC
Benny Prijonob8c25182006-04-29 08:31:09 +0000598 /* Register L16 family codecs, but disable all */
599 status = pjmedia_codec_l16_init(pjsua.med_endpt, 0);
600 if (status != PJ_SUCCESS) {
601 pjsua_perror(THIS_FILE, "Error initializing L16 codecs",
602 status);
603 return status;
604 }
Benny Prijono15953012006-04-27 22:37:08 +0000605
Benny Prijonob8c25182006-04-29 08:31:09 +0000606 /* Disable ALL L16 codecs */
607 codec_id = pj_str("L16");
608 pjmedia_codec_mgr_set_codec_priority(
609 pjmedia_endpt_get_codec_mgr(pjsua.med_endpt),
610 &codec_id,
611 PJMEDIA_CODEC_PRIO_DISABLED);
Benny Prijono15953012006-04-27 22:37:08 +0000612
Benny Prijono15953012006-04-27 22:37:08 +0000613#endif /* PJMEDIA_HAS_L16_CODEC */
614
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000615
Benny Prijono1d8d6082006-04-29 12:38:25 +0000616 /* Enable those codecs that user put with "--add-codec", and move
617 * the priority to top
Benny Prijonob8c25182006-04-29 08:31:09 +0000618 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000619 for (i=0; i<(int)pjsua.config.codec_cnt; ++i) {
Benny Prijonob8c25182006-04-29 08:31:09 +0000620 pjmedia_codec_mgr_set_codec_priority(
621 pjmedia_endpt_get_codec_mgr(pjsua.med_endpt),
Benny Prijonodc39fe82006-05-26 12:17:46 +0000622 &pjsua.config.codec_arg[i],
Benny Prijonoda1c3e12006-05-14 10:55:14 +0000623 PJMEDIA_CODEC_PRIO_HIGHEST);
Benny Prijonob8c25182006-04-29 08:31:09 +0000624 }
625
626
Benny Prijono8e3344c2006-03-08 12:37:22 +0000627 /* Init options for conference bridge. */
Benny Prijono9fc735d2006-05-28 14:58:12 +0000628 options = PJMEDIA_CONF_NO_DEVICE;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000629
630 /* Calculate maximum number of ports, if it's not specified */
631 if (pjsua.config.conf_ports == 0) {
632 pjsua.config.conf_ports = 3 * pjsua.config.max_calls;
633 }
Benny Prijono8e3344c2006-03-08 12:37:22 +0000634
Benny Prijonof8baa872006-02-27 23:54:23 +0000635 /* Init conference bridge. */
Benny Prijono9fc735d2006-05-28 14:58:12 +0000636 pjsua.clock_rate = pjsua.config.clock_rate ? pjsua.config.clock_rate : 16000;
637 pjsua.samples_per_frame = pjsua.clock_rate * 10 / 1000;
Benny Prijonof8baa872006-02-27 23:54:23 +0000638 status = pjmedia_conf_create(pjsua.pool,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000639 pjsua.config.conf_ports,
Benny Prijono9fc735d2006-05-28 14:58:12 +0000640 pjsua.clock_rate,
Benny Prijonod0659a32006-03-16 19:03:07 +0000641 1, /* mono */
Benny Prijono9fc735d2006-05-28 14:58:12 +0000642 pjsua.samples_per_frame,
Benny Prijonob8c25182006-04-29 08:31:09 +0000643 16,
Benny Prijono8e3344c2006-03-08 12:37:22 +0000644 options,
Benny Prijono08e0d062006-03-04 14:52:44 +0000645 &pjsua.mconf);
Benny Prijonof8baa872006-02-27 23:54:23 +0000646 if (status != PJ_SUCCESS) {
Benny Prijonof8baa872006-02-27 23:54:23 +0000647 pjsua_perror(THIS_FILE,
648 "Media stack initialization has returned error",
649 status);
650 return status;
651 }
652
Benny Prijono9fc735d2006-05-28 14:58:12 +0000653 if (pjsua.config.null_audio == PJ_FALSE) {
654 pjmedia_port *conf_port;
Benny Prijono39879152006-02-23 02:09:10 +0000655
Benny Prijono9fc735d2006-05-28 14:58:12 +0000656 /* Create sound device port */
657 status = pjmedia_snd_port_create(pjsua.pool,
658 pjsua.config.snd_capture_id,
659 pjsua.config.snd_player_id,
660 pjsua.clock_rate, 1 /* mono */,
661 pjsua.samples_per_frame, 16,
662 0, &pjsua.snd_port);
Benny Prijono39879152006-02-23 02:09:10 +0000663 if (status != PJ_SUCCESS) {
Benny Prijono9fc735d2006-05-28 14:58:12 +0000664 pjsua_perror(THIS_FILE, "Unable to create sound device", status);
Benny Prijono39879152006-02-23 02:09:10 +0000665 return status;
666 }
667
Benny Prijono9fc735d2006-05-28 14:58:12 +0000668 /* Get the port interface of the conference bridge */
669 conf_port = pjmedia_conf_get_master_port(pjsua.mconf);
670
671 /* Connect conference port interface to sound port */
672 pjmedia_snd_port_connect( pjsua.snd_port, conf_port);
673
674 } else {
675 pjmedia_port *null_port, *conf_port;
676
677 /* Create NULL port */
678 status = pjmedia_null_port_create(pjsua.pool, pjsua.clock_rate,
679 1, pjsua.samples_per_frame, 16,
680 &null_port);
Benny Prijono39879152006-02-23 02:09:10 +0000681 if (status != PJ_SUCCESS) {
Benny Prijono9fc735d2006-05-28 14:58:12 +0000682 pjsua_perror(THIS_FILE, "Unable to create NULL port", status);
683 return status;
684 }
685
686 /* Get the port interface of the conference bridge */
687 conf_port = pjmedia_conf_get_master_port(pjsua.mconf);
688
689 /* Create master port to control conference bridge's clock */
690 status = pjmedia_master_port_create(pjsua.pool, null_port, conf_port,
691 0, &pjsua.master_port);
692 if (status != PJ_SUCCESS) {
693 pjsua_perror(THIS_FILE, "Unable to create master port", status);
694 return status;
695 }
696 }
697
698 /* Create WAV file player if required: */
699
700 if (pjsua.config.wav_file.slen) {
701
702 status = pjsua_player_create(&pjsua.config.wav_file, NULL);
703 if (status != PJ_SUCCESS) {
704 pjsua_perror(THIS_FILE, "Unable to create file player",
Benny Prijono39879152006-02-23 02:09:10 +0000705 status);
706 return status;
707 }
708 }
709
710
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000711 return PJ_SUCCESS;
712}
713
714
715/*
Benny Prijono9fc735d2006-05-28 14:58:12 +0000716 * Copy account configuration.
717 */
718static void copy_acc_config(pj_pool_t *pool,
719 pjsua_acc_config *dst_acc,
720 const pjsua_acc_config *src_acc)
721{
722 unsigned j;
723
724 pj_strdup_with_null(pool, &dst_acc->id, &src_acc->id);
725 pj_strdup_with_null(pool, &dst_acc->reg_uri, &src_acc->reg_uri);
726 pj_strdup_with_null(pool, &dst_acc->contact, &src_acc->contact);
727 pj_strdup_with_null(pool, &dst_acc->proxy, &src_acc->proxy);
728
729 for (j=0; j<src_acc->cred_count; ++j) {
730 pj_strdup_with_null(pool, &dst_acc->cred_info[j].realm,
731 &src_acc->cred_info[j].realm);
732 pj_strdup_with_null(pool, &dst_acc->cred_info[j].scheme,
733 &src_acc->cred_info[j].scheme);
734 pj_strdup_with_null(pool, &dst_acc->cred_info[j].username,
735 &src_acc->cred_info[j].username);
736 pj_strdup_with_null(pool, &dst_acc->cred_info[j].data,
737 &src_acc->cred_info[j].data);
738 }
739}
740
741
742/*
Benny Prijonodc39fe82006-05-26 12:17:46 +0000743 * Copy configuration.
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000744 */
Benny Prijonodc39fe82006-05-26 12:17:46 +0000745static void copy_config(pj_pool_t *pool, pjsua_config *dst,
746 const pjsua_config *src)
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000747{
Benny Prijonodc39fe82006-05-26 12:17:46 +0000748 unsigned i;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000749
Benny Prijonodc39fe82006-05-26 12:17:46 +0000750 /* Plain memcpy */
751 pj_memcpy(dst, src, sizeof(pjsua_config));
752
753 /* Duplicate strings */
754 pj_strdup_with_null(pool, &dst->sip_host, &src->sip_host);
755 pj_strdup_with_null(pool, &dst->stun_srv1, &src->stun_srv1);
756 pj_strdup_with_null(pool, &dst->stun_srv2, &src->stun_srv2);
757 pj_strdup_with_null(pool, &dst->wav_file, &src->wav_file);
758
759 for (i=0; i<src->codec_cnt; ++i) {
760 pj_strdup_with_null(pool, &dst->codec_arg[i], &src->codec_arg[i]);
761 }
762
763 pj_strdup_with_null(pool, &dst->outbound_proxy, &src->outbound_proxy);
764 pj_strdup_with_null(pool, &dst->uri_to_call, &src->uri_to_call);
765
766 for (i=0; i<src->acc_cnt; ++i) {
767 pjsua_acc_config *dst_acc = &dst->acc_config[i];
768 const pjsua_acc_config *src_acc = &src->acc_config[i];
Benny Prijono9fc735d2006-05-28 14:58:12 +0000769 copy_acc_config(pool, dst_acc, src_acc);
Benny Prijonodc39fe82006-05-26 12:17:46 +0000770 }
771
772 pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename);
773
774 for (i=0; i<src->buddy_cnt; ++i) {
775 pj_strdup_with_null(pool, &dst->buddy_uri[i], &src->buddy_uri[i]);
776 }
777}
778
779
Benny Prijonob9b32ab2006-06-01 12:28:44 +0000780/*****************************************************************************
781 * Console application custom logging:
782 */
783
784
785static void log_writer(int level, const char *buffer, int len)
786{
787 /* Write to both stdout and file. */
788
789 if (level <= (int)pjsua.config.app_log_level)
790 pj_log_write(level, buffer, len);
791
792 if (pjsua.log_file) {
793 fwrite(buffer, len, 1, pjsua.log_file);
794 fflush(pjsua.log_file);
795 }
796}
797
798
799static pj_status_t logging_init()
800{
801 /* Redirect log function to ours */
802
803 pj_log_set_log_func( &log_writer );
804
805 /* If output log file is desired, create the file: */
806
807 if (pjsua.config.log_filename.slen) {
808 pjsua.log_file = fopen(pjsua.config.log_filename.ptr, "wt");
809 if (pjsua.log_file == NULL) {
810 PJ_LOG(1,(THIS_FILE, "Unable to open log file %s",
811 pjsua.config.log_filename.ptr));
812 return -1;
813 }
814 }
815
816 return PJ_SUCCESS;
817}
818
819
820static void logging_shutdown(void)
821{
822 /* Close logging file, if any: */
823
824 if (pjsua.log_file) {
825 fclose(pjsua.log_file);
826 pjsua.log_file = NULL;
827 }
828}
829
830
Benny Prijonodc39fe82006-05-26 12:17:46 +0000831/*
832 * Initialize pjsua application.
833 * This will initialize all libraries, create endpoint instance, and register
834 * pjsip modules.
835 */
836PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg,
837 const pjsua_callback *cb)
838{
839 char errmsg[80];
840 unsigned i;
841 pj_status_t status;
842
843
844 /* Init accounts: */
845 for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) {
846 pjsua.acc[i].index = i;
847 pjsua.acc[i].online_status = PJ_TRUE;
848 pj_list_init(&pjsua.acc[i].route_set);
849 pj_list_init(&pjsua.acc[i].pres_srv_list);
850 }
851
852 /* Init call array: */
853 for (i=0; i<PJ_ARRAY_SIZE(pjsua.calls); ++i) {
854 pjsua.calls[i].index = i;
855 pjsua.calls[i].refresh_tm._timer_id = -1;
856 pjsua.calls[i].hangup_tm._timer_id = -1;
857 pjsua.calls[i].conf_slot = 0;
858 }
859
Benny Prijono9fc735d2006-05-28 14:58:12 +0000860 /* Init buddies array */
861 for (i=0; i<PJ_ARRAY_SIZE(pjsua.buddies); ++i) {
862 pjsua.buddies[i].index = i;
863 }
864
Benny Prijonodc39fe82006-05-26 12:17:46 +0000865 /* Copy configuration */
866 copy_config(pjsua.pool, &pjsua.config, cfg);
867
868 /* Copy callback */
869 pj_memcpy(&pjsua.cb, cb, sizeof(pjsua_callback));
870
871 /* Test configuration */
872 if (pjsua_test_config(&pjsua.config, errmsg, sizeof(errmsg))) {
873 PJ_LOG(1,(THIS_FILE, "Error in configuration: %s", errmsg));
Benny Prijono9fc735d2006-05-28 14:58:12 +0000874 status = -1;
875 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000876 }
877
878
879 /* Init PJLIB logging: */
880
881 pj_log_set_level(pjsua.config.log_level);
882 pj_log_set_decor(pjsua.config.log_decor);
883
Benny Prijonob9b32ab2006-06-01 12:28:44 +0000884 status = logging_init();
885 if (status != PJ_SUCCESS)
886 goto on_error;
887
Benny Prijonodc39fe82006-05-26 12:17:46 +0000888
889 /* Create SIP UDP socket */
890 if (pjsua.config.udp_port) {
891
892 status = create_sip_udp_sock( pjsua.config.udp_port,
893 &pjsua.sip_sock,
894 &pjsua.sip_sock_name);
895 if (status != PJ_SUCCESS)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000896 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000897
898 pj_strdup2_with_null(pjsua.pool, &pjsua.config.sip_host,
899 pj_inet_ntoa(pjsua.sip_sock_name.sin_addr));
900 pjsua.config.sip_port = pj_ntohs(pjsua.sip_sock_name.sin_port);
901
902 } else {
903
904 /* Check that SIP host and port is configured */
905 if (cfg->sip_host.slen == 0 || cfg->sip_port == 0) {
906 PJ_LOG(1,(THIS_FILE,
907 "Error: sip_host and sip_port must be specified"));
Benny Prijono9fc735d2006-05-28 14:58:12 +0000908 status = PJ_EINVAL;
909 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +0000910 }
911
912 pjsua.sip_sock = PJ_INVALID_SOCKET;
913 }
914
915
916 /* Init media endpoint */
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000917 status = init_media();
918 if (status != PJ_SUCCESS)
Benny Prijono9fc735d2006-05-28 14:58:12 +0000919 goto on_error;
Benny Prijonoeb30bf52006-03-04 20:43:52 +0000920
Benny Prijonodc39fe82006-05-26 12:17:46 +0000921
922 /* Init RTP sockets, only when UDP transport is enabled */
923 for (i=0; pjsua.config.start_rtp_port && i<pjsua.config.max_calls; ++i) {
924 status = create_rtp_rtcp_sock(&pjsua.calls[i].skinfo);
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000925 if (status != PJ_SUCCESS) {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000926 unsigned j;
927 for (j=0; j<i; ++j) {
Benny Prijono9fc735d2006-05-28 14:58:12 +0000928 if (pjsua.calls[i].med_tp)
929 pjsua.calls[i].med_tp->op->destroy(pjsua.calls[i].med_tp);
Benny Prijonoa91a0032006-02-26 21:23:45 +0000930 }
Benny Prijono9fc735d2006-05-28 14:58:12 +0000931 goto on_error;
Benny Prijonof04ffdd2006-02-21 00:11:18 +0000932 }
Benny Prijonodc39fe82006-05-26 12:17:46 +0000933 status = pjmedia_transport_udp_attach(pjsua.med_endpt, NULL,
Benny Prijonob9b32ab2006-06-01 12:28:44 +0000934 &pjsua.calls[i].skinfo, 0,
Benny Prijonodc39fe82006-05-26 12:17:46 +0000935 &pjsua.calls[i].med_tp);
Benny Prijono268ca612006-02-07 12:34:11 +0000936 }
937
Benny Prijonodc39fe82006-05-26 12:17:46 +0000938 /* Init PJSIP : */
939
940 /* Initialize transaction layer: */
941
942 status = pjsip_tsx_layer_init_module(pjsua.endpt);
943 if (status != PJ_SUCCESS) {
944 pjsua_perror(THIS_FILE, "Transaction layer initialization error",
945 status);
946 goto on_error;
947 }
948
949 /* Initialize UA layer module: */
950
951 status = pjsip_ua_init_module( pjsua.endpt, NULL );
952 if (status != PJ_SUCCESS) {
953 pjsua_perror(THIS_FILE, "UA layer initialization error", status);
954 goto on_error;
955 }
956
957 /* Initialize and register pjsua's application module: */
Benny Prijono268ca612006-02-07 12:34:11 +0000958
Benny Prijonoccf95622006-02-07 18:48:01 +0000959 {
Benny Prijonodc39fe82006-05-26 12:17:46 +0000960 pjsip_module my_mod =
961 {
962 NULL, NULL, /* prev, next. */
963 { "mod-pjsua", 9 }, /* Name. */
964 -1, /* Id */
965 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
966 NULL, /* load() */
967 NULL, /* start() */
968 NULL, /* stop() */
969 NULL, /* unload() */
970 &mod_pjsua_on_rx_request, /* on_rx_request() */
971 &mod_pjsua_on_rx_response, /* on_rx_response() */
972 NULL, /* on_tx_request. */
973 NULL, /* on_tx_response() */
974 NULL, /* on_tsx_state() */
975 };
976
977 pjsua.mod = my_mod;
978
979 status = pjsip_endpt_register_module(pjsua.endpt, &pjsua.mod);
980 if (status != PJ_SUCCESS) {
981 pjsua_perror(THIS_FILE, "Unable to register pjsua module",
982 status);
983 goto on_error;
984 }
985 }
986
987 /* Initialize invite session module: */
988
989 status = pjsua_call_init();
990 if (status != PJ_SUCCESS) {
991 pjsua_perror(THIS_FILE, "Invite usage initialization error",
992 status);
993 goto on_error;
994 }
995
996 /* Init core SIMPLE module : */
997
998 pjsip_evsub_init_module(pjsua.endpt);
999
1000 /* Init presence module: */
1001
1002 pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance());
1003
1004 /* Init xfer/REFER module */
1005
1006 pjsip_xfer_init_module( pjsua.endpt );
1007
1008 /* Init pjsua presence handler: */
1009
1010 pjsua_pres_init();
1011
1012 /* Init out-of-dialog MESSAGE request handler. */
1013
1014 pjsua_im_init();
1015
1016
1017 /* Done. */
1018 return PJ_SUCCESS;
1019
1020on_error:
Benny Prijono9fc735d2006-05-28 14:58:12 +00001021 pjsua_destroy();
Benny Prijonodc39fe82006-05-26 12:17:46 +00001022 return status;
1023}
1024
1025
1026/*
1027 * Find account for incoming request.
1028 */
1029int pjsua_find_account_for_incoming(pjsip_rx_data *rdata)
1030{
1031 pjsip_uri *uri;
1032 pjsip_sip_uri *sip_uri;
1033 unsigned acc_index;
1034
1035 uri = rdata->msg_info.to->uri;
1036
1037 /* Just return last account if To URI is not SIP: */
1038 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
1039 !PJSIP_URI_SCHEME_IS_SIPS(uri))
1040 {
1041 return pjsua.config.acc_cnt;
1042 }
1043
1044
1045 sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);
1046
1047 /* Find account which has matching username and domain. */
1048 for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) {
1049
1050 pjsua_acc *acc = &pjsua.acc[acc_index];
1051
1052 if (pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
1053 pj_stricmp(&acc->host_part, &sip_uri->host)==0)
1054 {
1055 /* Match ! */
1056 return acc_index;
1057 }
1058 }
1059
1060 /* No matching, try match domain part only. */
1061 for (acc_index=0; acc_index < pjsua.config.acc_cnt; ++acc_index) {
1062
1063 pjsua_acc *acc = &pjsua.acc[acc_index];
1064
1065 if (pj_stricmp(&acc->host_part, &sip_uri->host)==0) {
1066 /* Match ! */
1067 return acc_index;
1068 }
1069 }
1070
1071 /* Still no match, just return last account */
1072 return pjsua.config.acc_cnt;
1073}
1074
1075
1076/*
1077 * Find account for outgoing request.
1078 */
1079int pjsua_find_account_for_outgoing(const pj_str_t *url)
1080{
1081 PJ_UNUSED_ARG(url);
1082
1083 /* Just use account #0 */
1084 return 0;
1085}
1086
1087
Benny Prijono9fc735d2006-05-28 14:58:12 +00001088/*
1089 * Init account
1090 */
1091static pj_status_t init_acc(unsigned acc_index)
1092{
1093 pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index];
1094 pjsua_acc *acc = &pjsua.acc[acc_index];
1095 pjsip_uri *uri;
1096 pjsip_sip_uri *sip_uri;
1097
1098 /* Need to parse local_uri to get the elements: */
1099
1100 uri = pjsip_parse_uri(pjsua.pool, acc_cfg->id.ptr,
1101 acc_cfg->id.slen, 0);
1102 if (uri == NULL) {
1103 pjsua_perror(THIS_FILE, "Invalid local URI",
1104 PJSIP_EINVALIDURI);
1105 return PJSIP_EINVALIDURI;
1106 }
1107
1108 /* Local URI MUST be a SIP or SIPS: */
1109
1110 if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
1111 !PJSIP_URI_SCHEME_IS_SIPS(uri))
1112 {
1113 pjsua_perror(THIS_FILE, "Invalid local URI",
1114 PJSIP_EINVALIDSCHEME);
1115 return PJSIP_EINVALIDSCHEME;
1116 }
1117
1118
1119 /* Get the SIP URI object: */
1120
1121 sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri);
1122
1123 acc->user_part = sip_uri->user;
1124 acc->host_part = sip_uri->host;
1125
1126 /* Build Contact header */
1127
1128 if (acc_cfg->contact.slen == 0) {
1129 char contact[128];
1130 const char *addr;
1131 int port;
1132 int len;
1133
1134 addr = pjsua.config.sip_host.ptr;
1135 port = pjsua.config.sip_port;
1136
1137 /* The local Contact is the username@ip-addr, where
1138 * - username is taken from the local URI,
1139 * - ip-addr in UDP transport's address name (which may have been
1140 * resolved from STUN.
1141 */
1142
1143 /* Build temporary contact string. */
1144
1145 if (sip_uri->user.slen) {
1146
1147 /* With the user part. */
1148 len = pj_ansi_snprintf(contact, sizeof(contact),
1149 "<sip:%.*s@%s:%d>",
1150 (int)sip_uri->user.slen,
1151 sip_uri->user.ptr,
1152 addr, port);
1153 } else {
1154
1155 /* Without user part */
1156
1157 len = pj_ansi_snprintf(contact, sizeof(contact),
1158 "<sip:%s:%d>",
1159 addr, port);
1160 }
1161
1162 if (len < 1 || len >= sizeof(contact)) {
1163 pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG);
1164 return PJSIP_EURITOOLONG;
1165 }
1166
1167 /* Duplicate Contact uri. */
1168
1169 pj_strdup2(pjsua.pool, &acc_cfg->contact, contact);
1170
1171 }
1172
1173
1174 /* Build route-set for this account */
1175 if (pjsua.config.outbound_proxy.slen) {
1176 pj_str_t hname = { "Route", 5};
1177 pjsip_route_hdr *r;
1178 pj_str_t tmp;
1179
1180 pj_strdup_with_null(pjsua.pool, &tmp, &pjsua.config.outbound_proxy);
1181 r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
1182 pj_list_push_back(&acc->route_set, r);
1183 }
1184
1185 if (acc_cfg->proxy.slen) {
1186 pj_str_t hname = { "Route", 5};
1187 pjsip_route_hdr *r;
1188 pj_str_t tmp;
1189
1190 pj_strdup_with_null(pjsua.pool, &tmp, &acc_cfg->proxy);
1191 r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL);
1192 pj_list_push_back(&acc->route_set, r);
1193 }
1194
1195 return PJ_SUCCESS;
1196}
1197
1198/*
1199 * Add a new account.
1200 */
1201PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
1202 int *acc_index)
1203{
1204 pj_status_t status;
1205
1206 PJ_ASSERT_RETURN(pjsua.config.acc_cnt<PJ_ARRAY_SIZE(pjsua.config.acc_config),
1207 PJ_ETOOMANY);
1208
1209 copy_acc_config(pjsua.pool, &pjsua.config.acc_config[pjsua.config.acc_cnt], cfg);
1210
1211 status = init_acc(pjsua.config.acc_cnt);
1212 if (status != PJ_SUCCESS) {
1213 pjsua_perror(THIS_FILE, "Error adding account", status);
1214 return status;
1215 }
1216
1217 if (acc_index)
1218 *acc_index = pjsua.config.acc_cnt;
1219
1220 pjsua.config.acc_cnt++;
1221
1222 return PJ_SUCCESS;
1223}
1224
1225
Benny Prijonodc39fe82006-05-26 12:17:46 +00001226
1227/*
1228 * Start pjsua stack.
1229 * This will start the registration process, if registration is configured.
1230 */
1231PJ_DEF(pj_status_t) pjsua_start(void)
1232{
1233 int i; /* Must be signed */
Benny Prijono9fc735d2006-05-28 14:58:12 +00001234 unsigned count;
Benny Prijonodc39fe82006-05-26 12:17:46 +00001235 pj_status_t status = PJ_SUCCESS;
1236
1237
1238 /* Add UDP transport: */
1239 if (pjsua.sip_sock > 0) {
1240
Benny Prijonoccf95622006-02-07 18:48:01 +00001241 /* Init the published name for the transport.
1242 * Depending whether STUN is used, this may be the STUN mapped
1243 * address, or socket's bound address.
1244 */
1245 pjsip_host_port addr_name;
1246
Benny Prijonodc39fe82006-05-26 12:17:46 +00001247 addr_name.host = pjsua.config.sip_host;
1248 addr_name.port = pjsua.config.sip_port;
Benny Prijonoccf95622006-02-07 18:48:01 +00001249
1250 /* Create UDP transport from previously created UDP socket: */
1251
1252 status = pjsip_udp_transport_attach( pjsua.endpt, pjsua.sip_sock,
1253 &addr_name, 1,
Benny Prijonodc39fe82006-05-26 12:17:46 +00001254 NULL);
Benny Prijonoccf95622006-02-07 18:48:01 +00001255 if (status != PJ_SUCCESS) {
Benny Prijonobcaed6c2006-02-19 15:37:19 +00001256 pjsua_perror(THIS_FILE, "Unable to start UDP transport",
1257 status);
Benny Prijono9fc735d2006-05-28 14:58:12 +00001258 goto on_error;
Benny Prijonoccf95622006-02-07 18:48:01 +00001259 }
Benny Prijono268ca612006-02-07 12:34:11 +00001260 }
1261
Benny Prijono9fc735d2006-05-28 14:58:12 +00001262 /* Initialize all unused accounts with default id and contact.
Benny Prijonodc39fe82006-05-26 12:17:46 +00001263 */
1264 {
1265 char buf[80];
Benny Prijono9fc735d2006-05-28 14:58:12 +00001266 pj_str_t tmp, id;
Benny Prijonoccf95622006-02-07 18:48:01 +00001267
Benny Prijonodc39fe82006-05-26 12:17:46 +00001268 tmp.ptr = buf;
1269 tmp.slen = pj_ansi_sprintf(tmp.ptr, "<sip:%s:%d>",
1270 pjsua.config.sip_host.ptr,
1271 pjsua.config.sip_port);
Benny Prijono9fc735d2006-05-28 14:58:12 +00001272 pj_strdup_with_null( pjsua.pool, &id, &tmp);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001273
Benny Prijono9fc735d2006-05-28 14:58:12 +00001274 for (i=pjsua.config.acc_cnt; i<PJ_ARRAY_SIZE(pjsua.config.acc_config);
1275 ++i)
1276 {
1277 pjsua_acc_config *acc_cfg =
1278 &pjsua.config.acc_config[pjsua.config.acc_cnt];
1279
1280 acc_cfg->id = id;
1281 acc_cfg->contact = id;
1282 }
Benny Prijonodc39fe82006-05-26 12:17:46 +00001283 }
1284
1285
1286 /* Initialize accounts: */
1287 for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
Benny Prijono9fc735d2006-05-28 14:58:12 +00001288 status = init_acc(i);
1289 if (status != PJ_SUCCESS) {
1290 pjsua_perror(THIS_FILE, "Error initializing account", status);
1291 goto on_error;
Benny Prijonodc39fe82006-05-26 12:17:46 +00001292 }
Benny Prijonoccf95622006-02-07 18:48:01 +00001293 }
1294
Benny Prijonoccf95622006-02-07 18:48:01 +00001295
Benny Prijonoccf95622006-02-07 18:48:01 +00001296
Benny Prijono268ca612006-02-07 12:34:11 +00001297 /* Create worker thread(s), if required: */
1298
Benny Prijonodc39fe82006-05-26 12:17:46 +00001299 for (i=0; i<(int)pjsua.config.thread_cnt; ++i) {
Benny Prijono834aee32006-02-19 01:38:06 +00001300 status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_poll,
Benny Prijono268ca612006-02-07 12:34:11 +00001301 NULL, 0, 0, &pjsua.threads[i]);
1302 if (status != PJ_SUCCESS) {
1303 pjsua.quit_flag = 1;
1304 for (--i; i>=0; --i) {
1305 pj_thread_join(pjsua.threads[i]);
1306 pj_thread_destroy(pjsua.threads[i]);
1307 }
Benny Prijono9fc735d2006-05-28 14:58:12 +00001308 goto on_error;
Benny Prijono268ca612006-02-07 12:34:11 +00001309 }
1310 }
1311
Benny Prijonoccf95622006-02-07 18:48:01 +00001312 /* Start registration: */
1313
1314 /* Create client registration session: */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001315 for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
Benny Prijonoa91a0032006-02-26 21:23:45 +00001316 status = pjsua_regc_init(i);
1317 if (status != PJ_SUCCESS)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001318 goto on_error;
Benny Prijonoccf95622006-02-07 18:48:01 +00001319
Benny Prijonoa91a0032006-02-26 21:23:45 +00001320 /* Perform registration, if required. */
1321 if (pjsua.acc[i].regc) {
Benny Prijono9fc735d2006-05-28 14:58:12 +00001322 pjsua_acc_set_registration(i, PJ_TRUE);
Benny Prijonoa91a0032006-02-26 21:23:45 +00001323 }
1324 }
Benny Prijonoccf95622006-02-07 18:48:01 +00001325
Benny Prijonoa91a0032006-02-26 21:23:45 +00001326
Benny Prijono9fc735d2006-05-28 14:58:12 +00001327 /* Re-init buddies */
1328 count = pjsua.config.buddy_cnt;
1329 pjsua.config.buddy_cnt = 0;
1330 for (i=0; i<(int)count; ++i) {
1331 pj_str_t uri = pjsua.config.buddy_uri[i];
1332 pjsua_buddy_add(&uri, NULL);
Benny Prijonodc39fe82006-05-26 12:17:46 +00001333 }
Benny Prijonodc39fe82006-05-26 12:17:46 +00001334
Benny Prijonoccf95622006-02-07 18:48:01 +00001335
1336
Benny Prijono834aee32006-02-19 01:38:06 +00001337 PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION));
Benny Prijono268ca612006-02-07 12:34:11 +00001338 return PJ_SUCCESS;
Benny Prijono9fc735d2006-05-28 14:58:12 +00001339
1340on_error:
1341 pjsua_destroy();
1342 return status;
Benny Prijono268ca612006-02-07 12:34:11 +00001343}
1344
1345
Benny Prijono834aee32006-02-19 01:38:06 +00001346/* Sleep with polling */
1347static void busy_sleep(unsigned msec)
1348{
1349 pj_time_val timeout, now;
1350
1351 pj_gettimeofday(&timeout);
1352 timeout.msec += msec;
1353 pj_time_val_normalize(&timeout);
1354
1355 do {
1356 pjsua_poll(NULL);
1357 pj_gettimeofday(&now);
1358 } while (PJ_TIME_VAL_LT(now, timeout));
1359}
1360
Benny Prijono9fc735d2006-05-28 14:58:12 +00001361/**
1362 * Get maxinum number of conference ports.
1363 */
1364PJ_DEF(unsigned) pjsua_conf_max_ports(void)
1365{
1366 return pjsua.config.conf_ports;
1367}
1368
1369
1370/**
1371 * Enum all conference ports.
1372 */
1373PJ_DEF(pj_status_t) pjsua_conf_enum_ports( unsigned *count,
1374 pjmedia_conf_port_info info[])
1375{
1376 return pjmedia_conf_get_ports_info(pjsua.mconf, count, info);
1377}
1378
1379
1380
1381
1382/**
1383 * Connect conference port.
1384 */
1385PJ_DEF(pj_status_t) pjsua_conf_connect( unsigned src_port,
1386 unsigned dst_port)
1387{
1388 return pjmedia_conf_connect_port(pjsua.mconf, src_port, dst_port, 0);
1389}
1390
1391
1392/**
1393 * Connect conference port connection.
1394 */
1395PJ_DEF(pj_status_t) pjsua_conf_disconnect( unsigned src_port,
1396 unsigned dst_port)
1397{
1398 return pjmedia_conf_disconnect_port(pjsua.mconf, src_port, dst_port);
1399}
1400
1401
1402
1403/**
1404 * Create a file player.
1405 */
1406PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename,
1407 pjsua_player_id *id)
1408{
1409 unsigned slot;
1410 char path[128];
1411 pjmedia_port *port;
1412 pj_status_t status;
1413
1414 if (pjsua.player_cnt >= PJ_ARRAY_SIZE(pjsua.player))
1415 return PJ_ETOOMANY;
1416
1417 pj_memcpy(path, filename->ptr, filename->slen);
1418 path[filename->slen] = '\0';
1419 status = pjmedia_wav_player_port_create(pjsua.pool, path,
1420 pjsua.samples_per_frame *
1421 1000 / pjsua.clock_rate,
1422 0, 0, NULL,
1423 &port);
1424 if (status != PJ_SUCCESS)
1425 return status;
1426
1427 status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool,
1428 port, filename, &slot);
1429 if (status != PJ_SUCCESS) {
1430 pjmedia_port_destroy(port);
1431 return status;
1432 }
1433
1434 pjsua.player[pjsua.player_cnt].port = port;
1435 pjsua.player[pjsua.player_cnt].slot = slot;
1436
1437 if (*id)
1438 *id = pjsua.player_cnt;
1439
1440 ++pjsua.player_cnt;
1441
1442 return PJ_SUCCESS;
1443}
1444
1445
1446/**
1447 * Get conference port associated with player.
1448 */
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001449PJ_DEF(int) pjsua_player_get_conf_port(pjsua_player_id id)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001450{
1451 PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);
1452 return pjsua.player[id].slot;
1453}
1454
1455
1456/**
1457 * Re-wind playback.
1458 */
1459PJ_DEF(pj_status_t) pjsua_player_set_pos(pjsua_player_id id,
1460 pj_uint32_t samples)
1461{
1462 PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);
1463 PJ_ASSERT_RETURN(pjsua.player[id].port != NULL, PJ_EINVALIDOP);
1464
1465 return pjmedia_wav_player_port_set_pos(pjsua.player[id].port, samples);
1466}
1467
1468
1469/**
1470 * Get conference port associated with player.
1471 */
1472PJ_DEF(pj_status_t) pjsua_player_destroy(pjsua_player_id id)
1473{
1474 PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL);
1475
1476 if (pjsua.player[id].port) {
1477 pjmedia_port_destroy(pjsua.player[id].port);
1478 pjsua.player[id].port = NULL;
1479 pjsua.player[id].slot = 0xFFFF;
1480 pjsua.player_cnt--;
1481 }
1482
1483 return PJ_SUCCESS;
1484}
1485
1486
1487/**
1488 * Create a file recorder.
1489 */
1490PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename,
1491 pjsua_recorder_id *id)
1492{
1493 unsigned slot;
1494 char path[128];
1495 pjmedia_port *port;
1496 pj_status_t status;
1497
1498 if (pjsua.recorder_cnt >= PJ_ARRAY_SIZE(pjsua.recorder))
1499 return PJ_ETOOMANY;
1500
1501 pj_memcpy(path, filename->ptr, filename->slen);
1502 path[filename->slen] = '\0';
1503 status = pjmedia_wav_writer_port_create(pjsua.pool, path,
1504 pjsua.clock_rate, 1,
1505 pjsua.samples_per_frame,
1506 16, 0, 0, NULL,
1507 &port);
1508 if (status != PJ_SUCCESS)
1509 return status;
1510
1511 status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool,
1512 port, filename, &slot);
1513 if (status != PJ_SUCCESS) {
1514 pjmedia_port_destroy(port);
1515 return status;
1516 }
1517
1518 pjsua.recorder[pjsua.recorder_cnt].port = port;
1519 pjsua.recorder[pjsua.recorder_cnt].slot = slot;
1520
1521 if (*id)
1522 *id = pjsua.recorder_cnt;
1523
1524 ++pjsua.recorder_cnt;
1525
1526 return PJ_SUCCESS;
1527}
1528
1529
1530/**
1531 * Get conference port associated with recorder.
1532 */
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001533PJ_DEF(int) pjsua_recorder_get_conf_port(pjsua_recorder_id id)
Benny Prijono9fc735d2006-05-28 14:58:12 +00001534{
1535 PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.recorder), PJ_EINVAL);
1536 return pjsua.recorder[id].slot;
1537}
1538
1539
1540/**
1541 * Destroy recorder (will complete recording).
1542 */
1543PJ_DEF(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id)
1544{
1545 PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.recorder), PJ_EINVAL);
1546
1547 if (pjsua.recorder[id].port) {
1548 pjmedia_port_destroy(pjsua.recorder[id].port);
1549 pjsua.recorder[id].port = NULL;
1550 pjsua.recorder[id].slot = 0xFFFF;
1551 pjsua.recorder_cnt--;
1552 }
1553
1554 return PJ_SUCCESS;
1555}
1556
1557/**
1558 * Enum sound devices.
1559 */
1560PJ_DEF(pj_status_t) pjsua_enum_snd_devices( unsigned *count,
1561 pjmedia_snd_dev_info info[])
1562{
1563 int i, dev_count;
1564
1565 dev_count = pjmedia_snd_get_dev_count();
1566 if (dev_count > (int)*count)
1567 dev_count = *count;
1568
1569 for (i=0; i<dev_count; ++i) {
1570 const pjmedia_snd_dev_info *dev_info;
1571 dev_info = pjmedia_snd_get_dev_info(i);
1572 pj_memcpy(&info[i], dev_info, sizeof(pjmedia_snd_dev_info));
1573 }
1574
1575 *count = dev_count;
1576 return PJ_SUCCESS;
1577}
1578
1579
1580/**
1581 * Select or change sound device.
1582 */
1583PJ_DEF(pj_status_t) pjsua_set_snd_dev( int snd_capture_id,
1584 int snd_player_id)
1585{
1586 pjsua.config.snd_capture_id = snd_capture_id;
1587 pjsua.config.snd_player_id = snd_player_id;
1588 return PJ_SUCCESS;
1589}
1590
1591
Benny Prijono268ca612006-02-07 12:34:11 +00001592/*
1593 * Destroy pjsua.
1594 */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001595PJ_DEF(pj_status_t) pjsua_destroy(void)
Benny Prijono268ca612006-02-07 12:34:11 +00001596{
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001597 int i; /* Must be signed */
Benny Prijono268ca612006-02-07 12:34:11 +00001598
1599 /* Signal threads to quit: */
Benny Prijono268ca612006-02-07 12:34:11 +00001600 pjsua.quit_flag = 1;
1601
1602 /* Wait worker threads to quit: */
Benny Prijonodc39fe82006-05-26 12:17:46 +00001603 for (i=0; i<(int)pjsua.config.thread_cnt; ++i) {
Benny Prijono268ca612006-02-07 12:34:11 +00001604
Benny Prijonoe4f2abb2006-02-10 14:04:05 +00001605 if (pjsua.threads[i]) {
1606 pj_thread_join(pjsua.threads[i]);
1607 pj_thread_destroy(pjsua.threads[i]);
1608 pjsua.threads[i] = NULL;
1609 }
Benny Prijono268ca612006-02-07 12:34:11 +00001610 }
1611
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001612
Benny Prijono9fc735d2006-05-28 14:58:12 +00001613 if (pjsua.endpt) {
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001614 /* Terminate all calls. */
1615 pjsua_call_hangup_all();
1616
1617 /* Terminate all presence subscriptions. */
1618 pjsua_pres_shutdown();
1619
1620 /* Unregister, if required: */
1621 for (i=0; i<(int)pjsua.config.acc_cnt; ++i) {
1622 if (pjsua.acc[i].regc) {
1623 pjsua_acc_set_registration(i, PJ_FALSE);
1624 }
1625 }
1626
1627 /* Wait for some time to allow unregistration to complete: */
Benny Prijono9fc735d2006-05-28 14:58:12 +00001628 PJ_LOG(4,(THIS_FILE, "Shutting down..."));
1629 busy_sleep(1000);
1630 }
Benny Prijono834aee32006-02-19 01:38:06 +00001631
Benny Prijono9fc735d2006-05-28 14:58:12 +00001632 /* If we have master port, destroying master port will recursively
1633 * destroy conference bridge, otherwise must destroy it manually.
1634 */
1635 if (pjsua.master_port) {
1636 pjmedia_master_port_destroy(pjsua.master_port);
1637 pjsua.master_port = NULL;
1638 } else {
1639 if (pjsua.snd_port) {
1640 pjmedia_snd_port_destroy(pjsua.snd_port);
1641 pjsua.snd_port = NULL;
1642 }
1643 if (pjsua.mconf) {
1644 pjmedia_conf_destroy(pjsua.mconf);
1645 pjsua.mconf = NULL;
1646 }
1647 }
Benny Prijono26ff9062006-02-21 23:47:00 +00001648
Benny Prijono9fc735d2006-05-28 14:58:12 +00001649 /* Destroy file players */
1650 for (i=0; i<PJ_ARRAY_SIZE(pjsua.player); ++i) {
1651 if (pjsua.player[i].port) {
1652 pjmedia_port_destroy(pjsua.player[i].port);
1653 pjsua.player[i].port = NULL;
1654 }
1655 }
Benny Prijonode380582006-03-15 19:32:41 +00001656
Benny Prijonode380582006-03-15 19:32:41 +00001657
Benny Prijono9fc735d2006-05-28 14:58:12 +00001658 /* Destroy file recorders */
1659 for (i=0; i<PJ_ARRAY_SIZE(pjsua.recorder); ++i) {
1660 if (pjsua.recorder[i].port) {
1661 pjmedia_port_destroy(pjsua.recorder[i].port);
1662 pjsua.recorder[i].port = NULL;
1663 }
1664 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001665
Benny Prijonob04c9e02006-05-17 17:17:39 +00001666
1667 /* Close transports */
Benny Prijono9fc735d2006-05-28 14:58:12 +00001668 for (i=0; i<(int)pjsua.config.max_calls; ++i) {
1669 if (pjsua.calls[i].med_tp) {
1670 (*pjsua.calls[i].med_tp->op->destroy)(pjsua.calls[i].med_tp);
1671 pjsua.calls[i].med_tp = NULL;
1672 }
Benny Prijonob04c9e02006-05-17 17:17:39 +00001673 }
1674
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001675 /* Destroy media endpoint. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00001676 if (pjsua.med_endpt) {
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001677
Benny Prijono9fc735d2006-05-28 14:58:12 +00001678 /* Shutdown all codecs: */
1679# if PJMEDIA_HAS_SPEEX_CODEC
1680 pjmedia_codec_speex_deinit();
1681# endif /* PJMEDIA_HAS_SPEEX_CODEC */
1682
1683# if PJMEDIA_HAS_GSM_CODEC
1684 pjmedia_codec_gsm_deinit();
1685# endif /* PJMEDIA_HAS_GSM_CODEC */
1686
1687# if PJMEDIA_HAS_G711_CODEC
1688 pjmedia_codec_g711_deinit();
1689# endif /* PJMEDIA_HAS_G711_CODEC */
1690
1691# if PJMEDIA_HAS_L16_CODEC
1692 pjmedia_codec_l16_deinit();
1693# endif /* PJMEDIA_HAS_L16_CODEC */
1694
1695
1696 pjmedia_endpt_destroy(pjsua.med_endpt);
1697 pjsua.med_endpt = NULL;
1698 }
Benny Prijonoeb30bf52006-03-04 20:43:52 +00001699
Benny Prijono268ca612006-02-07 12:34:11 +00001700 /* Destroy endpoint. */
Benny Prijono9fc735d2006-05-28 14:58:12 +00001701 if (pjsua.endpt) {
1702 pjsip_endpt_destroy(pjsua.endpt);
1703 pjsua.endpt = NULL;
1704 }
Benny Prijono268ca612006-02-07 12:34:11 +00001705
1706 /* Destroy caching pool. */
1707 pj_caching_pool_destroy(&pjsua.cp);
1708
1709
Benny Prijonob9b32ab2006-06-01 12:28:44 +00001710 PJ_LOG(4,(THIS_FILE, "PJSUA destroyed..."));
1711
1712 /* End logging */
1713 logging_shutdown();
1714
Benny Prijono268ca612006-02-07 12:34:11 +00001715 /* Done. */
1716
1717 return PJ_SUCCESS;
1718}
1719
Benny Prijono9fc735d2006-05-28 14:58:12 +00001720
1721/**
1722 * Get SIP endpoint instance.
1723 * Only valid after pjsua_init().
1724 */
1725PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void)
1726{
1727 return pjsua.endpt;
1728}
1729
1730/**
1731 * Get media endpoint instance.
1732 * Only valid after pjsua_init().
1733 */
1734PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void)
1735{
1736 return pjsua.med_endpt;
1737}
1738