BLOG | DOCUMENTATION | TRAC

Home --> Documentations --> PJSIP Reference

PJSUA

This is the reference implementation for PJSIP and PJMEDIA. PJSUA is a console based application, designed to be simple enough to be readble, but powerful enough to demonstrate all features available in PJSIP and PJMEDIA.

This file is pjsip-apps/src/pjsua/pjsua_app.c

Screenshot on WinXP:

pjsua on WinXP
1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  */
20 #include "pjsua_app.h"
21 
22 #define THIS_FILE "pjsua_app.c"
23 
24 //#define STEREO_DEMO
25 //#define TRANSPORT_ADAPTER_SAMPLE
26 //#define HAVE_MULTIPART_TEST
27 
28 /* Ringtones US UK */
29 #define RINGBACK_FREQ1 440 /* 400 */
30 #define RINGBACK_FREQ2 480 /* 450 */
31 #define RINGBACK_ON 2000 /* 400 */
32 #define RINGBACK_OFF 4000 /* 200 */
33 #define RINGBACK_CNT 1 /* 2 */
34 #define RINGBACK_INTERVAL 4000 /* 2000 */
35 
36 #define RING_FREQ1 800
37 #define RING_FREQ2 640
38 #define RING_ON 200
39 #define RING_OFF 100
40 #define RING_CNT 3
41 #define RING_INTERVAL 3000
42 
43 #define current_acc pjsua_acc_get_default()
44 
45 #ifdef STEREO_DEMO
46 static void stereo_demo();
47 #endif
48 
49 #ifdef USE_GUI
50 pj_bool_t showNotification(pjsua_call_id call_id);
51 #endif
52 
53 static void ringback_start(pjsua_call_id call_id);
54 static void ring_start(pjsua_call_id call_id);
55 static void ring_stop(pjsua_call_id call_id);
56 static pj_status_t app_init(void);
57 static pj_status_t app_destroy(void);
58 
59 static pjsua_app_cfg_t app_cfg;
60 pj_str_t uri_arg;
61 pj_bool_t app_running = PJ_FALSE;
62 
63 /*****************************************************************************
64  * Configuration manipulation
65  */
66 
67 /*****************************************************************************
68  * Callback
69  */
70 static void ringback_start(pjsua_call_id call_id)
71 {
72  if (app_config.no_tones)
73  return;
74 
75  if (app_config.call_data[call_id].ringback_on)
76  return;
77 
78  app_config.call_data[call_id].ringback_on = PJ_TRUE;
79 
80  if (++app_config.ringback_cnt==1 &&
81  app_config.ringback_slot!=PJSUA_INVALID_ID)
82  {
83  pjsua_conf_connect(app_config.ringback_slot, 0);
84  }
85 }
86 
87 static void ring_stop(pjsua_call_id call_id)
88 {
89  if (app_config.no_tones)
90  return;
91 
92  if (app_config.call_data[call_id].ringback_on) {
93  app_config.call_data[call_id].ringback_on = PJ_FALSE;
94 
95  pj_assert(app_config.ringback_cnt>0);
96  if (--app_config.ringback_cnt == 0 &&
97  app_config.ringback_slot!=PJSUA_INVALID_ID)
98  {
99  pjsua_conf_disconnect(app_config.ringback_slot, 0);
100  pjmedia_tonegen_rewind(app_config.ringback_port);
101  }
102  }
103 
104  if (app_config.call_data[call_id].ring_on) {
105  app_config.call_data[call_id].ring_on = PJ_FALSE;
106 
107  pj_assert(app_config.ring_cnt>0);
108  if (--app_config.ring_cnt == 0 &&
109  app_config.ring_slot!=PJSUA_INVALID_ID)
110  {
111  pjsua_conf_disconnect(app_config.ring_slot, 0);
112  pjmedia_tonegen_rewind(app_config.ring_port);
113  }
114  }
115 }
116 
117 static void ring_start(pjsua_call_id call_id)
118 {
119  if (app_config.no_tones)
120  return;
121 
122  if (app_config.call_data[call_id].ring_on)
123  return;
124 
125  app_config.call_data[call_id].ring_on = PJ_TRUE;
126 
127  if (++app_config.ring_cnt==1 &&
128  app_config.ring_slot!=PJSUA_INVALID_ID)
129  {
130  pjsua_conf_connect(app_config.ring_slot, 0);
131  }
132 }
133 
134 /* Callback from timer when the maximum call duration has been
135  * exceeded.
136  */
137 static void call_timeout_callback(pj_timer_heap_t *timer_heap,
138  struct pj_timer_entry *entry)
139 {
140  pjsua_call_id call_id = entry->id;
141  pjsua_msg_data msg_data_;
143  pj_str_t hname = pj_str("Warning");
144  pj_str_t hvalue = pj_str("399 pjsua \"Call duration exceeded\"");
145 
146  PJ_UNUSED_ARG(timer_heap);
147 
148  if (call_id == PJSUA_INVALID_ID) {
149  PJ_LOG(1,(THIS_FILE, "Invalid call ID in timer callback"));
150  return;
151  }
152 
153  /* Add warning header */
154  pjsua_msg_data_init(&msg_data_);
155  pjsip_generic_string_hdr_init2(&warn, &hname, &hvalue);
156  pj_list_push_back(&msg_data_.hdr_list, &warn);
157 
158  /* Call duration has been exceeded; disconnect the call */
159  PJ_LOG(3,(THIS_FILE, "Duration (%d seconds) has been exceeded "
160  "for call %d, disconnecting the call",
161  app_config.duration, call_id));
162  entry->id = PJSUA_INVALID_ID;
163  pjsua_call_hangup(call_id, 200, NULL, &msg_data_);
164 }
165 
166 /*
167  * Handler when invite state has changed.
168  */
169 static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
170 {
171  pjsua_call_info call_info;
172 
173  PJ_UNUSED_ARG(e);
174 
175  pjsua_call_get_info(call_id, &call_info);
176 
177  if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) {
178 
179  /* Stop all ringback for this call */
180  ring_stop(call_id);
181 
182  /* Cancel duration timer, if any */
183  if (app_config.call_data[call_id].timer.id != PJSUA_INVALID_ID) {
184  app_call_data *cd = &app_config.call_data[call_id];
186 
187  cd->timer.id = PJSUA_INVALID_ID;
188  pjsip_endpt_cancel_timer(endpt, &cd->timer);
189  }
190 
191  /* Rewind play file when hangup automatically,
192  * since file is not looped
193  */
194  if (app_config.auto_play_hangup)
195  pjsua_player_set_pos(app_config.wav_id, 0);
196 
197 
198  PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%.*s)]",
199  call_id,
200  call_info.last_status,
201  (int)call_info.last_status_text.slen,
202  call_info.last_status_text.ptr));
203 
204  if (call_id == current_call) {
205  find_next_call();
206  }
207 
208  /* Dump media state upon disconnected */
209  if (1) {
210  PJ_LOG(5,(THIS_FILE,
211  "Call %d disconnected, dumping media stats..",
212  call_id));
213  log_call_dump(call_id);
214  }
215 
216  } else {
217 
218  if (app_config.duration != PJSUA_APP_NO_LIMIT_DURATION &&
219  call_info.state == PJSIP_INV_STATE_CONFIRMED)
220  {
221  /* Schedule timer to hangup call after the specified duration */
222  app_call_data *cd = &app_config.call_data[call_id];
224  pj_time_val delay;
225 
226  cd->timer.id = call_id;
227  delay.sec = app_config.duration;
228  delay.msec = 0;
229  pjsip_endpt_schedule_timer(endpt, &cd->timer, &delay);
230  }
231 
232  if (call_info.state == PJSIP_INV_STATE_EARLY) {
233  int code;
234  pj_str_t reason;
235  pjsip_msg *msg;
236 
237  /* This can only occur because of TX or RX message */
239 
240  if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
241  msg = e->body.tsx_state.src.rdata->msg_info.msg;
242  } else {
243  msg = e->body.tsx_state.src.tdata->msg;
244  }
245 
246  code = msg->line.status.code;
247  reason = msg->line.status.reason;
248 
249  /* Start ringback for 180 for UAC unless there's SDP in 180 */
250  if (call_info.role==PJSIP_ROLE_UAC && code==180 &&
251  msg->body == NULL &&
253  {
254  ringback_start(call_id);
255  }
256 
257  PJ_LOG(3,(THIS_FILE, "Call %d state changed to %.*s (%d %.*s)",
258  call_id, (int)call_info.state_text.slen,
259  call_info.state_text.ptr, code,
260  (int)reason.slen, reason.ptr));
261  } else {
262  PJ_LOG(3,(THIS_FILE, "Call %d state changed to %.*s",
263  call_id,
264  (int)call_info.state_text.slen,
265  call_info.state_text.ptr));
266  }
267 
268  if (current_call==PJSUA_INVALID_ID)
269  current_call = call_id;
270 
271  }
272 }
273 
277 static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
278  pjsip_rx_data *rdata)
279 {
280  pjsua_call_info call_info;
281 
282  PJ_UNUSED_ARG(acc_id);
283  PJ_UNUSED_ARG(rdata);
284 
285  pjsua_call_get_info(call_id, &call_info);
286 
287  if (current_call==PJSUA_INVALID_ID)
288  current_call = call_id;
289 
290 #ifdef USE_GUI
291  if (!showNotification(call_id))
292  return;
293 #endif
294 
295  /* Start ringback */
296  if (call_info.rem_aud_cnt)
297  ring_start(call_id);
298 
299  if (app_config.auto_answer > 0) {
300  pjsua_call_setting opt;
301 
303  opt.aud_cnt = app_config.aud_cnt;
304  opt.vid_cnt = app_config.vid.vid_cnt;
305 
306  pjsua_call_answer2(call_id, &opt, app_config.auto_answer, NULL,
307  NULL);
308  }
309 
310  if (app_config.auto_answer < 200) {
311  char notif_st[80] = {0};
312 
313 #if PJSUA_HAS_VIDEO
314  if (call_info.rem_offerer && call_info.rem_vid_cnt) {
315  snprintf(notif_st, sizeof(notif_st),
316  "To %s the video, type \"vid %s\" first, "
317  "before answering the call!\n",
318  (app_config.vid.vid_cnt? "reject":"accept"),
319  (app_config.vid.vid_cnt? "disable":"enable"));
320  }
321 #endif
322 
323  PJ_LOG(3,(THIS_FILE,
324  "Incoming call for account %d!\n"
325  "Media count: %d audio & %d video\n"
326  "%s"
327  "From: %.*s\n"
328  "To: %.*s\n"
329  "Press %s to answer or %s to reject call",
330  acc_id,
331  call_info.rem_aud_cnt,
332  call_info.rem_vid_cnt,
333  notif_st,
334  (int)call_info.remote_info.slen,
335  call_info.remote_info.ptr,
336  (int)call_info.local_info.slen,
337  call_info.local_info.ptr,
338  (app_config.use_cli?"ca a":"a"),
339  (app_config.use_cli?"g":"h")));
340  }
341 }
342 
343 /* General processing for media state. "mi" is the media index */
344 static void on_call_generic_media_state(pjsua_call_info *ci, unsigned mi,
345  pj_bool_t *has_error)
346 {
347  const char *status_name[] = {
348  "None",
349  "Active",
350  "Local hold",
351  "Remote hold",
352  "Error"
353  };
354 
355  PJ_UNUSED_ARG(has_error);
356 
357  pj_assert(ci->media[mi].status <= PJ_ARRAY_SIZE(status_name));
359 
360  PJ_LOG(4,(THIS_FILE, "Call %d media %d [type=%s], status is %s",
361  ci->id, mi, pjmedia_type_name(ci->media[mi].type),
362  status_name[ci->media[mi].status]));
363 }
364 
365 /* Process audio media state. "mi" is the media index. */
366 static void on_call_audio_state(pjsua_call_info *ci, unsigned mi,
367  pj_bool_t *has_error)
368 {
369  PJ_UNUSED_ARG(has_error);
370 
371  /* Stop ringback */
372  ring_stop(ci->id);
373 
374  /* Connect ports appropriately when media status is ACTIVE or REMOTE HOLD,
375  * otherwise we should NOT connect the ports.
376  */
377  if (ci->media[mi].status == PJSUA_CALL_MEDIA_ACTIVE ||
379  {
380  pj_bool_t connect_sound = PJ_TRUE;
381  pj_bool_t disconnect_mic = PJ_FALSE;
382  pjsua_conf_port_id call_conf_slot;
383 
384  call_conf_slot = ci->media[mi].stream.aud.conf_slot;
385 
386  /* Loopback sound, if desired */
387  if (app_config.auto_loop) {
388  pjsua_conf_connect(call_conf_slot, call_conf_slot);
389  connect_sound = PJ_FALSE;
390  }
391 
392  /* Automatically record conversation, if desired */
393  if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID) {
394  pjsua_conf_connect(call_conf_slot, app_config.rec_port);
395  }
396 
397  /* Stream a file, if desired */
398  if ((app_config.auto_play || app_config.auto_play_hangup) &&
399  app_config.wav_port != PJSUA_INVALID_ID)
400  {
401  pjsua_conf_connect(app_config.wav_port, call_conf_slot);
402  connect_sound = PJ_FALSE;
403  }
404 
405  /* Stream AVI, if desired */
406  if (app_config.avi_auto_play &&
407  app_config.avi_def_idx != PJSUA_INVALID_ID &&
408  app_config.avi[app_config.avi_def_idx].slot != PJSUA_INVALID_ID)
409  {
410  pjsua_conf_connect(app_config.avi[app_config.avi_def_idx].slot,
411  call_conf_slot);
412  disconnect_mic = PJ_TRUE;
413  }
414 
415  /* Put call in conference with other calls, if desired */
416  if (app_config.auto_conf) {
417  pjsua_call_id call_ids[PJSUA_MAX_CALLS];
418  unsigned call_cnt=PJ_ARRAY_SIZE(call_ids);
419  unsigned i;
420 
421  /* Get all calls, and establish media connection between
422  * this call and other calls.
423  */
424  pjsua_enum_calls(call_ids, &call_cnt);
425 
426  for (i=0; i<call_cnt; ++i) {
427  if (call_ids[i] == ci->id)
428  continue;
429 
430  if (!pjsua_call_has_media(call_ids[i]))
431  continue;
432 
433  pjsua_conf_connect(call_conf_slot,
434  pjsua_call_get_conf_port(call_ids[i]));
436  call_conf_slot);
437 
438  /* Automatically record conversation, if desired */
439  if (app_config.auto_rec && app_config.rec_port !=
440  PJSUA_INVALID_ID)
441  {
443  app_config.rec_port);
444  }
445 
446  }
447 
448  /* Also connect call to local sound device */
449  connect_sound = PJ_TRUE;
450  }
451 
452  /* Otherwise connect to sound device */
453  if (connect_sound) {
454  pjsua_conf_connect(call_conf_slot, 0);
455  if (!disconnect_mic)
456  pjsua_conf_connect(0, call_conf_slot);
457 
458  /* Automatically record conversation, if desired */
459  if (app_config.auto_rec && app_config.rec_port != PJSUA_INVALID_ID)
460  {
461  pjsua_conf_connect(call_conf_slot, app_config.rec_port);
462  pjsua_conf_connect(0, app_config.rec_port);
463  }
464  }
465  }
466 }
467 
468 /* Process video media state. "mi" is the media index. */
469 static void on_call_video_state(pjsua_call_info *ci, unsigned mi,
470  pj_bool_t *has_error)
471 {
473  return;
474 
475  arrange_window(ci->media[mi].stream.vid.win_in);
476 
477  PJ_UNUSED_ARG(has_error);
478 }
479 
480 /*
481  * Callback on media state changed event.
482  * The action may connect the call to sound device, to file, or
483  * to loop the call.
484  */
485 static void on_call_media_state(pjsua_call_id call_id)
486 {
487  pjsua_call_info call_info;
488  unsigned mi;
489  pj_bool_t has_error = PJ_FALSE;
490 
491  pjsua_call_get_info(call_id, &call_info);
492 
493  for (mi=0; mi<call_info.media_cnt; ++mi) {
494  on_call_generic_media_state(&call_info, mi, &has_error);
495 
496  switch (call_info.media[mi].type) {
497  case PJMEDIA_TYPE_AUDIO:
498  on_call_audio_state(&call_info, mi, &has_error);
499  break;
500  case PJMEDIA_TYPE_VIDEO:
501  on_call_video_state(&call_info, mi, &has_error);
502  break;
503  default:
504  /* Make gcc happy about enum not handled by switch/case */
505  break;
506  }
507  }
508 
509  if (has_error) {
510  pj_str_t reason = pj_str("Media failed");
511  pjsua_call_hangup(call_id, 500, &reason, NULL);
512  }
513 
514 #if PJSUA_HAS_VIDEO
515  /* Check if remote has just tried to enable video */
516  if (call_info.rem_offerer && call_info.rem_vid_cnt)
517  {
518  int vid_idx;
519 
520  /* Check if there is active video */
521  vid_idx = pjsua_call_get_vid_stream_idx(call_id);
522  if (vid_idx == -1 || call_info.media[vid_idx].dir == PJMEDIA_DIR_NONE) {
523  PJ_LOG(3,(THIS_FILE,
524  "Just rejected incoming video offer on call %d, "
525  "use \"vid call enable %d\" or \"vid call add\" to "
526  "enable video!", call_id, vid_idx));
527  }
528  }
529 #endif
530 }
531 
532 /*
533  * DTMF callback.
534  */
535 /*
536 static void call_on_dtmf_callback(pjsua_call_id call_id, int dtmf)
537 {
538  PJ_LOG(3,(THIS_FILE, "Incoming DTMF on call %d: %c", call_id, dtmf));
539 }
540 */
541 
542 static void call_on_dtmf_callback2(pjsua_call_id call_id,
543  const pjsua_dtmf_info *info)
544 {
545  char duration[16];
546  char method[16];
547 
548  duration[0] = '\0';
549 
550  switch (info->method) {
552  pj_ansi_snprintf(method, sizeof(method), "RFC2833");
553  break;
555  pj_ansi_snprintf(method, sizeof(method), "SIP INFO");
556  pj_ansi_snprintf(duration, sizeof(duration), ":duration(%d)",
557  info->duration);
558  break;
559  };
560  PJ_LOG(3,(THIS_FILE, "Incoming DTMF on call %d: %c%s, using %s method",
561  call_id, info->digit, duration, method));
562 }
563 
564 /*
565  * Redirection handler.
566  */
567 static pjsip_redirect_op call_on_redirected(pjsua_call_id call_id,
568  const pjsip_uri *target,
569  const pjsip_event *e)
570 {
571  PJ_UNUSED_ARG(e);
572 
573  if (app_config.redir_op == PJSIP_REDIRECT_PENDING) {
574  char uristr[PJSIP_MAX_URL_SIZE];
575  int len;
576 
577  len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, target, uristr,
578  sizeof(uristr));
579  if (len < 1) {
580  pj_ansi_strcpy(uristr, "--URI too long--");
581  }
582 
583  PJ_LOG(3,(THIS_FILE, "Call %d is being redirected to %.*s. "
584  "Press 'Ra' to accept+replace To header, 'RA' to accept, "
585  "'Rr' to reject, or 'Rd' to disconnect.",
586  call_id, len, uristr));
587  }
588 
589  return app_config.redir_op;
590 }
591 
592 /*
593  * Handler registration status has changed.
594  */
595 static void on_reg_state(pjsua_acc_id acc_id)
596 {
597  PJ_UNUSED_ARG(acc_id);
598 
599  // Log already written.
600 }
601 
602 /*
603  * Handler for incoming presence subscription request
604  */
605 static void on_incoming_subscribe(pjsua_acc_id acc_id,
606  pjsua_srv_pres *srv_pres,
607  pjsua_buddy_id buddy_id,
608  const pj_str_t *from,
609  pjsip_rx_data *rdata,
610  pjsip_status_code *code,
611  pj_str_t *reason,
612  pjsua_msg_data *msg_data_)
613 {
614  /* Just accept the request (the default behavior) */
615  PJ_UNUSED_ARG(acc_id);
616  PJ_UNUSED_ARG(srv_pres);
617  PJ_UNUSED_ARG(buddy_id);
618  PJ_UNUSED_ARG(from);
619  PJ_UNUSED_ARG(rdata);
620  PJ_UNUSED_ARG(code);
621  PJ_UNUSED_ARG(reason);
622  PJ_UNUSED_ARG(msg_data_);
623 }
624 
625 
626 /*
627  * Handler on buddy state changed.
628  */
629 static void on_buddy_state(pjsua_buddy_id buddy_id)
630 {
632  pjsua_buddy_get_info(buddy_id, &info);
633 
634  PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s, subscription state is %s "
635  "(last termination reason code=%d %.*s)",
636  (int)info.uri.slen,
637  info.uri.ptr,
638  (int)info.status_text.slen,
639  info.status_text.ptr,
640  info.sub_state_name,
641  info.sub_term_code,
642  (int)info.sub_term_reason.slen,
643  info.sub_term_reason.ptr));
644 }
645 
646 
647 /*
648  * Subscription state has changed.
649  */
650 static void on_buddy_evsub_state(pjsua_buddy_id buddy_id,
651  pjsip_evsub *sub,
652  pjsip_event *event)
653 {
654  char event_info[80];
655 
656  PJ_UNUSED_ARG(sub);
657 
658  event_info[0] = '\0';
659 
660  if (event->type == PJSIP_EVENT_TSX_STATE &&
661  event->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
662  {
663  pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
664  snprintf(event_info, sizeof(event_info),
665  " (RX %s)",
666  pjsip_rx_data_get_info(rdata));
667  }
668 
669  PJ_LOG(4,(THIS_FILE,
670  "Buddy %d: subscription state: %s (event: %s%s)",
671  buddy_id, pjsip_evsub_get_state_name(sub),
672  pjsip_event_str(event->type),
673  event_info));
674 
675 }
676 
677 
681 static void on_pager(pjsua_call_id call_id, const pj_str_t *from,
682  const pj_str_t *to, const pj_str_t *contact,
683  const pj_str_t *mime_type, const pj_str_t *text)
684 {
685  /* Note: call index may be -1 */
686  PJ_UNUSED_ARG(call_id);
687  PJ_UNUSED_ARG(to);
688  PJ_UNUSED_ARG(contact);
689  PJ_UNUSED_ARG(mime_type);
690 
691  PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s (%.*s)",
692  (int)from->slen, from->ptr,
693  (int)text->slen, text->ptr,
694  (int)mime_type->slen, mime_type->ptr));
695 }
696 
697 
701 static void on_typing(pjsua_call_id call_id, const pj_str_t *from,
702  const pj_str_t *to, const pj_str_t *contact,
703  pj_bool_t is_typing)
704 {
705  PJ_UNUSED_ARG(call_id);
706  PJ_UNUSED_ARG(to);
707  PJ_UNUSED_ARG(contact);
708 
709  PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s",
710  (int)from->slen, from->ptr,
711  (is_typing?"is typing..":"has stopped typing")));
712 }
713 
714 
718 static void on_call_transfer_status(pjsua_call_id call_id,
719  int status_code,
720  const pj_str_t *status_text,
721  pj_bool_t final,
722  pj_bool_t *p_cont)
723 {
724  PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s",
725  call_id, status_code,
726  (int)status_text->slen, status_text->ptr,
727  (final ? "[final]" : "")));
728 
729  if (status_code/100 == 2) {
730  PJ_LOG(3,(THIS_FILE,
731  "Call %d: call transferred successfully, disconnecting call",
732  call_id));
733  pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL);
734  *p_cont = PJ_FALSE;
735  }
736 }
737 
738 
739 /*
740  * Notification that call is being replaced.
741  */
742 static void on_call_replaced(pjsua_call_id old_call_id,
743  pjsua_call_id new_call_id)
744 {
745  pjsua_call_info old_ci, new_ci;
746 
747  pjsua_call_get_info(old_call_id, &old_ci);
748  pjsua_call_get_info(new_call_id, &new_ci);
749 
750  PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by "
751  "call %d with %.*s",
752  old_call_id,
753  (int)old_ci.remote_info.slen, old_ci.remote_info.ptr,
754  new_call_id,
755  (int)new_ci.remote_info.slen, new_ci.remote_info.ptr));
756 }
757 
758 
759 /*
760  * NAT type detection callback.
761  */
762 static void on_nat_detect(const pj_stun_nat_detect_result *res)
763 {
764  if (res->status != PJ_SUCCESS) {
765  pjsua_perror(THIS_FILE, "NAT detection failed", res->status);
766  } else {
767  PJ_LOG(3, (THIS_FILE, "NAT detected as %s", res->nat_type_name));
768  }
769 }
770 
771 
772 /*
773  * MWI indication
774  */
775 static void on_mwi_info(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info)
776 {
777  pj_str_t body;
778 
779  PJ_LOG(3,(THIS_FILE, "Received MWI for acc %d:", acc_id));
780 
781  if (mwi_info->rdata->msg_info.ctype) {
782  const pjsip_ctype_hdr *ctype = mwi_info->rdata->msg_info.ctype;
783 
784  PJ_LOG(3,(THIS_FILE, " Content-Type: %.*s/%.*s",
785  (int)ctype->media.type.slen,
786  ctype->media.type.ptr,
787  (int)ctype->media.subtype.slen,
788  ctype->media.subtype.ptr));
789  }
790 
791  if (!mwi_info->rdata->msg_info.msg->body) {
792  PJ_LOG(3,(THIS_FILE, " no message body"));
793  return;
794  }
795 
796  body.ptr = (char *)mwi_info->rdata->msg_info.msg->body->data;
797  body.slen = mwi_info->rdata->msg_info.msg->body->len;
798 
799  PJ_LOG(3,(THIS_FILE, " Body:\n%.*s", (int)body.slen, body.ptr));
800 }
801 
802 
803 /*
804  * Transport status notification
805  */
806 static void on_transport_state(pjsip_transport *tp,
807  pjsip_transport_state state,
809 {
810  char host_port[128];
811 
813  tp->remote_name.port, host_port, sizeof(host_port), 1);
814  switch (state) {
816  {
817  PJ_LOG(3,(THIS_FILE, "SIP %s transport is connected to %s",
818  tp->type_name, host_port));
819  }
820  break;
821 
823  {
824  char buf[100];
825 
826  snprintf(buf, sizeof(buf), "SIP %s transport is disconnected "
827  "from %s", tp->type_name, host_port);
828  pjsua_perror(THIS_FILE, buf, info->status);
829  }
830  break;
831 
832  default:
833  break;
834  }
835 
836 #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
837 
838  if (!pj_ansi_stricmp(tp->type_name, "tls") && info->ext_info &&
839  (state == PJSIP_TP_STATE_CONNECTED ||
840  ((pjsip_tls_state_info*)info->ext_info)->
841  ssl_sock_info->verify_status != PJ_SUCCESS))
842  {
843  pjsip_tls_state_info *tls_info = (pjsip_tls_state_info*)info->ext_info;
844  pj_ssl_sock_info *ssl_sock_info = tls_info->ssl_sock_info;
845  char buf[2048];
846  const char *verif_msgs[32];
847  unsigned verif_msg_cnt;
848 
849  /* Dump server TLS cipher */
850  PJ_LOG(4,(THIS_FILE, "TLS cipher used: 0x%06X/%s",
851  ssl_sock_info->cipher,
852  pj_ssl_cipher_name(ssl_sock_info->cipher) ));
853 
854  /* Dump server TLS certificate */
855  pj_ssl_cert_info_dump(ssl_sock_info->remote_cert_info, " ",
856  buf, sizeof(buf));
857  PJ_LOG(4,(THIS_FILE, "TLS cert info of %s:\n%s", host_port, buf));
858 
859  /* Dump server TLS certificate verification result */
860  verif_msg_cnt = PJ_ARRAY_SIZE(verif_msgs);
861  pj_ssl_cert_get_verify_status_strings(ssl_sock_info->verify_status,
862  verif_msgs, &verif_msg_cnt);
863  PJ_LOG(3,(THIS_FILE, "TLS cert verification result of %s : %s",
864  host_port,
865  (verif_msg_cnt == 1? verif_msgs[0]:"")));
866  if (verif_msg_cnt > 1) {
867  unsigned i;
868  for (i = 0; i < verif_msg_cnt; ++i)
869  PJ_LOG(3,(THIS_FILE, "- %s", verif_msgs[i]));
870  }
871 
872  if (ssl_sock_info->verify_status &&
873  !app_config.udp_cfg.tls_setting.verify_server)
874  {
875  PJ_LOG(3,(THIS_FILE, "PJSUA is configured to ignore TLS cert "
876  "verification errors"));
877  }
878  }
879 
880 #endif
881 
882 }
883 
884 /*
885  * Notification on ICE error.
886  */
887 static void on_ice_transport_error(int index, pj_ice_strans_op op,
888  pj_status_t status, void *param)
889 {
890  PJ_UNUSED_ARG(op);
891  PJ_UNUSED_ARG(param);
892  PJ_PERROR(1,(THIS_FILE, status,
893  "ICE keep alive failure for transport %d", index));
894 }
895 
896 /*
897  * Notification on sound device operation.
898  */
899 static pj_status_t on_snd_dev_operation(int operation)
900 {
901  int cap_dev, play_dev;
902 
903  pjsua_get_snd_dev(&cap_dev, &play_dev);
904  PJ_LOG(3,(THIS_FILE, "Turning sound device %d %d %s", cap_dev, play_dev,
905  (operation? "ON":"OFF")));
906  return PJ_SUCCESS;
907 }
908 
909 static char *get_media_dir(pjmedia_dir dir) {
910  switch (dir) {
912  return "TX";
914  return "RX";
916  return "TX+RX";
917  default:
918  return "unknown dir";
919  }
920 }
921 
922 /* Callback on media events */
923 static void on_call_media_event(pjsua_call_id call_id,
924  unsigned med_idx,
925  pjmedia_event *event)
926 {
927  char event_name[5];
928 
929  PJ_LOG(5,(THIS_FILE, "Event %s",
930  pjmedia_fourcc_name(event->type, event_name)));
931 
932  if (event->type == PJMEDIA_EVENT_MEDIA_TP_ERR) {
934 
935  err_data = &event->data.med_tp_err;
936  PJ_PERROR(3, (THIS_FILE, err_data->status,
937  "Media transport error event (%s %s %s)",
938  (err_data->type==PJMEDIA_TYPE_AUDIO)?"Audio":"Video",
939  (err_data->is_rtp)?"RTP":"RTCP",
940  get_media_dir(err_data->dir)));
941  }
942 #if PJSUA_HAS_VIDEO
943  else if (event->type == PJMEDIA_EVENT_FMT_CHANGED) {
944  /* Adjust renderer window size to original video size */
945  pjsua_call_info ci;
946 
947  pjsua_call_get_info(call_id, &ci);
948 
949  if ((ci.media[med_idx].type == PJMEDIA_TYPE_VIDEO) &&
950  (ci.media[med_idx].dir & PJMEDIA_DIR_DECODING))
951  {
952  pjsua_vid_win_id wid;
953  pjmedia_rect_size size;
954  pjsua_vid_win_info win_info;
955 
956  wid = ci.media[med_idx].stream.vid.win_in;
957  pjsua_vid_win_get_info(wid, &win_info);
958 
959  size = event->data.fmt_changed.new_fmt.det.vid.size;
960  if (size.w != win_info.size.w || size.h != win_info.size.h) {
961  pjsua_vid_win_set_size(wid, &size);
962 
963  /* Re-arrange video windows */
964  arrange_window(PJSUA_INVALID_ID);
965  }
966  }
967  }
968 #else
969  PJ_UNUSED_ARG(call_id);
970  PJ_UNUSED_ARG(med_idx);
971 #endif
972 }
973 
974 #ifdef TRANSPORT_ADAPTER_SAMPLE
975 /*
976  * This callback is called when media transport needs to be created.
977  */
978 static pjmedia_transport* on_create_media_transport(pjsua_call_id call_id,
979  unsigned media_idx,
980  pjmedia_transport *base_tp,
981  unsigned flags)
982 {
983  pjmedia_transport *adapter;
984  pj_status_t status;
985 
986  /* Create the adapter */
988  NULL, base_tp,
990  &adapter);
991  if (status != PJ_SUCCESS) {
992  PJ_PERROR(1,(THIS_FILE, status, "Error creating adapter"));
993  return NULL;
994  }
995 
996  PJ_LOG(3,(THIS_FILE, "Media transport is created for call %d media %d",
997  call_id, media_idx));
998 
999  return adapter;
1000 }
1001 #endif
1002 
1003 /* Playfile done notification, set timer to hangup calls */
1004 void on_playfile_done(pjmedia_port *port, void *usr_data)
1005 {
1006  pj_time_val delay;
1007 
1008  PJ_UNUSED_ARG(port);
1009  PJ_UNUSED_ARG(usr_data);
1010 
1011  /* Just rewind WAV when it is played outside of call */
1012  if (pjsua_call_get_count() == 0) {
1013  pjsua_player_set_pos(app_config.wav_id, 0);
1014  }
1015 
1016  /* Timer is already active */
1017  if (app_config.auto_hangup_timer.id == 1)
1018  return;
1019 
1020  app_config.auto_hangup_timer.id = 1;
1021  delay.sec = 0;
1022  delay.msec = 200; /* Give 200 ms before hangup */
1024  &app_config.auto_hangup_timer,
1025  &delay);
1026 }
1027 
1028 /* IP change progress callback. */
1029 void on_ip_change_progress(pjsua_ip_change_op op,
1030  pj_status_t status,
1032 {
1033  char info_str[128];
1034  pjsua_acc_info acc_info;
1035  pjsua_transport_info tp_info;
1036 
1037  if (status == PJ_SUCCESS) {
1038  switch (op) {
1040  pjsua_transport_get_info(info->lis_restart.transport_id, &tp_info);
1041  pj_ansi_snprintf(info_str, sizeof(info_str),
1042  "restart transport %.*s",
1043  (int)tp_info.info.slen, tp_info.info.ptr);
1044  break;
1046  pjsua_acc_get_info(info->acc_shutdown_tp.acc_id, &acc_info);
1047 
1048  pj_ansi_snprintf(info_str, sizeof(info_str),
1049  "transport shutdown for account %.*s",
1050  (int)acc_info.acc_uri.slen,
1051  acc_info.acc_uri.ptr);
1052  break;
1054  pjsua_acc_get_info(info->acc_shutdown_tp.acc_id, &acc_info);
1055  if (info->acc_update_contact.code) {
1056  pj_ansi_snprintf(info_str, sizeof(info_str),
1057  "update contact for account %.*s, code[%d]",
1058  (int)acc_info.acc_uri.slen,
1059  acc_info.acc_uri.ptr,
1060  info->acc_update_contact.code);
1061  } else {
1062  pj_ansi_snprintf(info_str, sizeof(info_str),
1063  "update contact for account %.*s",
1064  (int)acc_info.acc_uri.slen,
1065  acc_info.acc_uri.ptr);
1066  }
1067  break;
1069  pjsua_acc_get_info(info->acc_shutdown_tp.acc_id, &acc_info);
1070  pj_ansi_snprintf(info_str, sizeof(info_str),
1071  "hangup call for account %.*s, call_id[%d]",
1072  (int)acc_info.acc_uri.slen, acc_info.acc_uri.ptr,
1073  info->acc_hangup_calls.call_id);
1074  break;
1076  pjsua_acc_get_info(info->acc_shutdown_tp.acc_id, &acc_info);
1077  pj_ansi_snprintf(info_str, sizeof(info_str),
1078  "reinvite call for account %.*s, call_id[%d]",
1079  (int)acc_info.acc_uri.slen, acc_info.acc_uri.ptr,
1080  info->acc_reinvite_calls.call_id);
1081  break;
1083  pj_ansi_snprintf(info_str, sizeof(info_str),
1084  "done");
1085  default:
1086  break;
1087  }
1088  PJ_LOG(3,(THIS_FILE, "IP change progress report : %s", info_str));
1089 
1090  } else {
1091  PJ_PERROR(3,(THIS_FILE, status, "IP change progress fail"));
1092  }
1093 }
1094 
1095 /* Auto hangup timer callback */
1096 static void hangup_timeout_callback(pj_timer_heap_t *timer_heap,
1097  struct pj_timer_entry *entry)
1098 {
1099  PJ_UNUSED_ARG(timer_heap);
1100  PJ_UNUSED_ARG(entry);
1101 
1102  app_config.auto_hangup_timer.id = 0;
1104 }
1105 
1106 /*
1107  * A simple registrar, invoked by default_mod_on_rx_request()
1108  */
1109 static void simple_registrar(pjsip_rx_data *rdata)
1110 {
1111  pjsip_tx_data *tdata;
1112  const pjsip_expires_hdr *exp;
1113  const pjsip_hdr *h;
1114  unsigned cnt = 0;
1116  pj_status_t status;
1117 
1119  rdata, 200, NULL, &tdata);
1120  if (status != PJ_SUCCESS)
1121  return;
1122 
1124  PJSIP_H_EXPIRES, NULL);
1125 
1126  h = rdata->msg_info.msg->hdr.next;
1127  while (h != &rdata->msg_info.msg->hdr) {
1128  if (h->type == PJSIP_H_CONTACT) {
1129  const pjsip_contact_hdr *c = (const pjsip_contact_hdr*)h;
1130  unsigned e = c->expires;
1131 
1132  if (e != PJSIP_EXPIRES_NOT_SPECIFIED) {
1133  if (exp)
1134  e = exp->ivalue;
1135  else
1136  e = 3600;
1137  }
1138 
1139  if (e > 0) {
1141  tdata->pool, h);
1142  nc->expires = e;
1143  pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)nc);
1144  ++cnt;
1145  }
1146  }
1147  h = h->next;
1148  }
1149 
1150  srv = pjsip_generic_string_hdr_create(tdata->pool, NULL, NULL);
1151  srv->name = pj_str("Server");
1152  srv->hvalue = pj_str("pjsua simple registrar");
1153  pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)srv);
1154 
1156  rdata, tdata, NULL, NULL);
1157 }
1158 
1159 /*****************************************************************************
1160  * A simple module to handle otherwise unhandled request. We will register
1161  * this with the lowest priority.
1162  */
1163 
1164 /* Notification on incoming request */
1165 static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata)
1166 {
1167  pjsip_tx_data *tdata;
1168  pjsip_status_code status_code;
1169  pj_status_t status;
1170 
1171  /* Don't respond to ACK! */
1172  if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
1173  &pjsip_ack_method) == 0)
1174  return PJ_TRUE;
1175 
1176  /* Simple registrar */
1177  if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
1178  &pjsip_register_method) == 0)
1179  {
1180  simple_registrar(rdata);
1181  return PJ_TRUE;
1182  }
1183 
1184  /* Create basic response. */
1185  if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
1186  &pjsip_notify_method) == 0)
1187  {
1188  /* Unsolicited NOTIFY's, send with Bad Request */
1189  status_code = PJSIP_SC_BAD_REQUEST;
1190  } else {
1191  /* Probably unknown method */
1192  status_code = PJSIP_SC_METHOD_NOT_ALLOWED;
1193  }
1195  rdata, status_code,
1196  NULL, &tdata);
1197  if (status != PJ_SUCCESS) {
1198  pjsua_perror(THIS_FILE, "Unable to create response", status);
1199  return PJ_TRUE;
1200  }
1201 
1202  /* Add Allow if we're responding with 405 */
1203  if (status_code == PJSIP_SC_METHOD_NOT_ALLOWED) {
1204  const pjsip_hdr *cap_hdr;
1206  PJSIP_H_ALLOW, NULL);
1207  if (cap_hdr) {
1209  tdata->pool, cap_hdr));
1210  }
1211  }
1212 
1213  /* Add User-Agent header */
1214  {
1215  pj_str_t user_agent;
1216  char tmp[80];
1217  const pj_str_t USER_AGENT = { "User-Agent", 10};
1218  pjsip_hdr *h;
1219 
1220  pj_ansi_snprintf(tmp, sizeof(tmp), "PJSUA v%s/%s",
1221  pj_get_version(), PJ_OS_NAME);
1222  pj_strdup2_with_null(tdata->pool, &user_agent, tmp);
1223 
1225  &USER_AGENT,
1226  &user_agent);
1227  pjsip_msg_add_hdr(tdata->msg, h);
1228  }
1229 
1231  NULL, NULL);
1232 
1233  return PJ_TRUE;
1234 }
1235 
1236 /* The module instance. */
1237 static pjsip_module mod_default_handler =
1238 {
1239  NULL, NULL, /* prev, next. */
1240  { "mod-default-handler", 19 }, /* Name. */
1241  -1, /* Id */
1242  PJSIP_MOD_PRIORITY_APPLICATION+99, /* Priority */
1243  NULL, /* load() */
1244  NULL, /* start() */
1245  NULL, /* stop() */
1246  NULL, /* unload() */
1247  &default_mod_on_rx_request, /* on_rx_request() */
1248  NULL, /* on_rx_response() */
1249  NULL, /* on_tx_request. */
1250  NULL, /* on_tx_response() */
1251  NULL, /* on_tsx_state() */
1252 
1253 };
1254 
1257 /* Called on CLI (re)started, e.g: initial start, after iOS bg */
1258 void cli_on_started(pj_status_t status)
1259 {
1260  /* Notify app */
1261  if (app_cfg.on_started) {
1262  if (status == PJ_SUCCESS) {
1263  char info[128];
1264  cli_get_info(info, sizeof(info));
1265  if (app_cfg.on_started) {
1266  (*app_cfg.on_started)(status, info);
1267  }
1268  } else {
1269  if (app_cfg.on_started) {
1270  (*app_cfg.on_started)(status, NULL);
1271  }
1272  }
1273  }
1274 }
1275 
1276 /* Called on CLI quit */
1277 void cli_on_stopped(pj_bool_t restart, int argc, char* argv[])
1278 {
1279  /* Notify app */
1280  if (app_cfg.on_stopped)
1281  (*app_cfg.on_stopped)(restart, argc, argv);
1282 }
1283 
1284 
1285 /* Called on pjsua legacy quit */
1286 void legacy_on_stopped(pj_bool_t restart)
1287 {
1288  /* Notify app */
1289  if (app_cfg.on_stopped)
1290  (*app_cfg.on_stopped)(restart, 1, NULL);
1291 }
1292 
1293 /*****************************************************************************
1294  * Public API
1295  */
1296 
1297 int stdout_refresh_proc(void *arg)
1298 {
1299  extern char *stdout_refresh_text;
1300 
1301  PJ_UNUSED_ARG(arg);
1302 
1303  /* Set thread to lowest priority so that it doesn't clobber
1304  * stdout output
1305  */
1308 
1309  while (!stdout_refresh_quit) {
1310  pj_thread_sleep(stdout_refresh * 1000);
1311  puts(stdout_refresh_text);
1312  fflush(stdout);
1313  }
1314 
1315  return 0;
1316 }
1317 
1318 static pj_status_t app_init(void)
1319 {
1320  pjsua_transport_id transport_id = -1;
1321  pjsua_transport_config tcp_cfg;
1322  unsigned i;
1323  pj_pool_t *tmp_pool;
1324  pj_status_t status;
1325 
1327  status = pjsua_create();
1328  if (status != PJ_SUCCESS)
1329  return status;
1330 
1331  /* Create pool for application */
1332  app_config.pool = pjsua_pool_create("pjsua-app", 1000, 1000);
1333  tmp_pool = pjsua_pool_create("tmp-pjsua", 1000, 1000);;
1334 
1335  /* Init CLI & its FE settings */
1336  if (!app_running) {
1337  pj_cli_cfg_default(&app_config.cli_cfg.cfg);
1338  pj_cli_telnet_cfg_default(&app_config.cli_cfg.telnet_cfg);
1339  pj_cli_console_cfg_default(&app_config.cli_cfg.console_cfg);
1340  app_config.cli_cfg.telnet_cfg.on_started = cli_on_started;
1341  }
1342 
1344  status = load_config(app_cfg.argc, app_cfg.argv, &uri_arg);
1345  if (status != PJ_SUCCESS) {
1346  pj_pool_release(tmp_pool);
1347  return status;
1348  }
1349 
1350  /* Initialize application callbacks */
1351  app_config.cfg.cb.on_call_state = &on_call_state;
1352  app_config.cfg.cb.on_call_media_state = &on_call_media_state;
1353  app_config.cfg.cb.on_incoming_call = &on_incoming_call;
1354  app_config.cfg.cb.on_dtmf_digit2 = &call_on_dtmf_callback2;
1355  app_config.cfg.cb.on_call_redirected = &call_on_redirected;
1356  app_config.cfg.cb.on_reg_state = &on_reg_state;
1357  app_config.cfg.cb.on_incoming_subscribe = &on_incoming_subscribe;
1358  app_config.cfg.cb.on_buddy_state = &on_buddy_state;
1359  app_config.cfg.cb.on_buddy_evsub_state = &on_buddy_evsub_state;
1360  app_config.cfg.cb.on_pager = &on_pager;
1361  app_config.cfg.cb.on_typing = &on_typing;
1362  app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
1363  app_config.cfg.cb.on_call_replaced = &on_call_replaced;
1364  app_config.cfg.cb.on_nat_detect = &on_nat_detect;
1365  app_config.cfg.cb.on_mwi_info = &on_mwi_info;
1366  app_config.cfg.cb.on_transport_state = &on_transport_state;
1367  app_config.cfg.cb.on_ice_transport_error = &on_ice_transport_error;
1368  app_config.cfg.cb.on_snd_dev_operation = &on_snd_dev_operation;
1369  app_config.cfg.cb.on_call_media_event = &on_call_media_event;
1370  app_config.cfg.cb.on_ip_change_progress = &on_ip_change_progress;
1371 #ifdef TRANSPORT_ADAPTER_SAMPLE
1372  app_config.cfg.cb.on_create_media_transport = &on_create_media_transport;
1373 #endif
1374 
1375  /* Set sound device latency */
1376  if (app_config.capture_lat > 0)
1377  app_config.media_cfg.snd_rec_latency = app_config.capture_lat;
1378  if (app_config.playback_lat)
1379  app_config.media_cfg.snd_play_latency = app_config.playback_lat;
1380 
1381  if (app_cfg.on_config_init)
1382  (*app_cfg.on_config_init)(&app_config);
1383 
1384  /* Initialize pjsua */
1385  status = pjsua_init(&app_config.cfg, &app_config.log_cfg,
1386  &app_config.media_cfg);
1387  if (status != PJ_SUCCESS) {
1388  pj_pool_release(tmp_pool);
1389  return status;
1390  }
1391 
1392  /* Initialize our module to handle otherwise unhandled request */
1394  &mod_default_handler);
1395  if (status != PJ_SUCCESS)
1396  return status;
1397 
1398 #ifdef STEREO_DEMO
1399  stereo_demo();
1400 #endif
1401 
1402  /* Initialize calls data */
1403  for (i=0; i<PJ_ARRAY_SIZE(app_config.call_data); ++i) {
1404  app_config.call_data[i].timer.id = PJSUA_INVALID_ID;
1405  app_config.call_data[i].timer.cb = &call_timeout_callback;
1406  }
1407 
1408  /* Optionally registers WAV file */
1409  for (i=0; i<app_config.wav_count; ++i) {
1410  pjsua_player_id wav_id;
1411  unsigned play_options = 0;
1412 
1413  if (app_config.auto_play_hangup)
1414  play_options |= PJMEDIA_FILE_NO_LOOP;
1415 
1416  status = pjsua_player_create(&app_config.wav_files[i], play_options,
1417  &wav_id);
1418  if (status != PJ_SUCCESS)
1419  goto on_error;
1420 
1421  if (app_config.wav_id == PJSUA_INVALID_ID) {
1422  app_config.wav_id = wav_id;
1423  app_config.wav_port = pjsua_player_get_conf_port(app_config.wav_id);
1424  if (app_config.auto_play_hangup) {
1425  pjmedia_port *port;
1426 
1427  pjsua_player_get_port(app_config.wav_id, &port);
1428  status = pjmedia_wav_player_set_eof_cb2(port, NULL,
1429  &on_playfile_done);
1430  if (status != PJ_SUCCESS)
1431  goto on_error;
1432 
1433  pj_timer_entry_init(&app_config.auto_hangup_timer, 0, NULL,
1434  &hangup_timeout_callback);
1435  }
1436  }
1437  }
1438 
1439  /* Optionally registers tone players */
1440  for (i=0; i<app_config.tone_count; ++i) {
1441  pjmedia_port *tport;
1442  char name[80];
1443  pj_str_t label;
1444  pj_status_t status2;
1445 
1446  pj_ansi_snprintf(name, sizeof(name), "tone-%d,%d",
1447  app_config.tones[i].freq1,
1448  app_config.tones[i].freq2);
1449  label = pj_str(name);
1450  status2 = pjmedia_tonegen_create2(app_config.pool, &label,
1451  8000, 1, 160, 16,
1452  PJMEDIA_TONEGEN_LOOP, &tport);
1453  if (status2 != PJ_SUCCESS) {
1454  pjsua_perror(THIS_FILE, "Unable to create tone generator", status);
1455  goto on_error;
1456  }
1457 
1458  status2 = pjsua_conf_add_port(app_config.pool, tport,
1459  &app_config.tone_slots[i]);
1460  pj_assert(status2 == PJ_SUCCESS);
1461 
1462  status2 = pjmedia_tonegen_play(tport, 1, &app_config.tones[i], 0);
1463  pj_assert(status2 == PJ_SUCCESS);
1464  }
1465 
1466  /* Optionally create recorder file, if any. */
1467  if (app_config.rec_file.slen) {
1468  status = pjsua_recorder_create(&app_config.rec_file, 0, NULL, 0, 0,
1469  &app_config.rec_id);
1470  if (status != PJ_SUCCESS)
1471  goto on_error;
1472 
1473  app_config.rec_port = pjsua_recorder_get_conf_port(app_config.rec_id);
1474  }
1475 
1476  pj_memcpy(&tcp_cfg, &app_config.udp_cfg, sizeof(tcp_cfg));
1477 
1478  /* Create ringback tones */
1479  if (app_config.no_tones == PJ_FALSE) {
1480  unsigned samples_per_frame;
1481  pjmedia_tone_desc tone[RING_CNT+RINGBACK_CNT];
1482  pj_str_t name;
1483 
1484  samples_per_frame = app_config.media_cfg.audio_frame_ptime *
1485  app_config.media_cfg.clock_rate *
1486  app_config.media_cfg.channel_count / 1000;
1487 
1488  /* Ringback tone (call is ringing) */
1489  name = pj_str("ringback");
1490  status = pjmedia_tonegen_create2(app_config.pool, &name,
1491  app_config.media_cfg.clock_rate,
1492  app_config.media_cfg.channel_count,
1493  samples_per_frame,
1494  16, PJMEDIA_TONEGEN_LOOP,
1495  &app_config.ringback_port);
1496  if (status != PJ_SUCCESS)
1497  goto on_error;
1498 
1499  pj_bzero(&tone, sizeof(tone));
1500  for (i=0; i<RINGBACK_CNT; ++i) {
1501  tone[i].freq1 = RINGBACK_FREQ1;
1502  tone[i].freq2 = RINGBACK_FREQ2;
1503  tone[i].on_msec = RINGBACK_ON;
1504  tone[i].off_msec = RINGBACK_OFF;
1505  }
1506  tone[RINGBACK_CNT-1].off_msec = RINGBACK_INTERVAL;
1507 
1508  pjmedia_tonegen_play(app_config.ringback_port, RINGBACK_CNT, tone,
1510 
1511 
1512  status = pjsua_conf_add_port(app_config.pool, app_config.ringback_port,
1513  &app_config.ringback_slot);
1514  if (status != PJ_SUCCESS)
1515  goto on_error;
1516 
1517  /* Ring (to alert incoming call) */
1518  name = pj_str("ring");
1519  status = pjmedia_tonegen_create2(app_config.pool, &name,
1520  app_config.media_cfg.clock_rate,
1521  app_config.media_cfg.channel_count,
1522  samples_per_frame,
1523  16, PJMEDIA_TONEGEN_LOOP,
1524  &app_config.ring_port);
1525  if (status != PJ_SUCCESS)
1526  goto on_error;
1527 
1528  for (i=0; i<RING_CNT; ++i) {
1529  tone[i].freq1 = RING_FREQ1;
1530  tone[i].freq2 = RING_FREQ2;
1531  tone[i].on_msec = RING_ON;
1532  tone[i].off_msec = RING_OFF;
1533  }
1534  tone[RING_CNT-1].off_msec = RING_INTERVAL;
1535 
1536  pjmedia_tonegen_play(app_config.ring_port, RING_CNT,
1537  tone, PJMEDIA_TONEGEN_LOOP);
1538 
1539  status = pjsua_conf_add_port(app_config.pool, app_config.ring_port,
1540  &app_config.ring_slot);
1541  if (status != PJ_SUCCESS)
1542  goto on_error;
1543 
1544  }
1545 
1546  /* Create AVI player virtual devices */
1547  if (app_config.avi_cnt) {
1548 #if PJMEDIA_HAS_VIDEO && PJMEDIA_VIDEO_DEV_HAS_AVI
1549  pjmedia_vid_dev_factory *avi_factory;
1550 
1552  app_config.avi_cnt,
1553  &avi_factory);
1554  if (status != PJ_SUCCESS) {
1555  PJ_PERROR(1,(THIS_FILE, status, "Error creating AVI factory"));
1556  goto on_error;
1557  }
1558 
1559  for (i=0; i<app_config.avi_cnt; ++i) {
1560  pjmedia_avi_dev_param avdp;
1561  pjmedia_vid_dev_index avid;
1562  unsigned strm_idx, strm_cnt;
1563 
1564  app_config.avi[i].dev_id = PJMEDIA_VID_INVALID_DEV;
1565  app_config.avi[i].slot = PJSUA_INVALID_ID;
1566 
1568  avdp.path = app_config.avi[i].path;
1569 
1570  status = pjmedia_avi_dev_alloc(avi_factory, &avdp, &avid);
1571  if (status != PJ_SUCCESS) {
1572  PJ_PERROR(1,(THIS_FILE, status,
1573  "Error creating AVI player for %.*s",
1574  (int)avdp.path.slen, avdp.path.ptr));
1575  goto on_error;
1576  }
1577 
1578  PJ_LOG(4,(THIS_FILE, "AVI player %.*s created, dev_id=%d",
1579  (int)avdp.title.slen, avdp.title.ptr, avid));
1580 
1581  app_config.avi[i].dev_id = avid;
1582  if (app_config.avi_def_idx == PJSUA_INVALID_ID)
1583  app_config.avi_def_idx = i;
1584 
1586  for (strm_idx=0; strm_idx<strm_cnt; ++strm_idx) {
1587  pjmedia_port *aud;
1588  pjmedia_format *fmt;
1589  pjsua_conf_port_id slot;
1590  char fmt_name[5];
1591 
1593  strm_idx);
1594  fmt = &aud->info.fmt;
1595 
1596  pjmedia_fourcc_name(fmt->id, fmt_name);
1597 
1598  if (fmt->id == PJMEDIA_FORMAT_PCM) {
1599  status = pjsua_conf_add_port(app_config.pool, aud,
1600  &slot);
1601  if (status == PJ_SUCCESS) {
1602  PJ_LOG(4,(THIS_FILE,
1603  "AVI %.*s: audio added to slot %d",
1604  (int)avdp.title.slen, avdp.title.ptr,
1605  slot));
1606  app_config.avi[i].slot = slot;
1607  }
1608  } else {
1609  PJ_LOG(4,(THIS_FILE,
1610  "AVI %.*s: audio ignored, format=%s",
1611  (int)avdp.title.slen, avdp.title.ptr,
1612  fmt_name));
1613  }
1614  }
1615  }
1616 #else
1617  PJ_LOG(2,(THIS_FILE,
1618  "Warning: --play-avi is ignored because AVI is disabled"));
1619 #endif /* PJMEDIA_VIDEO_DEV_HAS_AVI */
1620  }
1621 
1622  /* Add UDP transport unless it's disabled. */
1623  if (!app_config.no_udp) {
1624  pjsua_acc_id aid;
1626 
1627  status = pjsua_transport_create(type,
1628  &app_config.udp_cfg,
1629  &transport_id);
1630  if (status != PJ_SUCCESS)
1631  goto on_error;
1632 
1633  /* Add local account */
1634  pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
1635 
1636  /* Adjust local account config based on pjsua app config */
1637  {
1638  pjsua_acc_config acc_cfg;
1639  pjsua_acc_get_config(aid, tmp_pool, &acc_cfg);
1640 
1641  app_config_init_video(&acc_cfg);
1642  acc_cfg.rtp_cfg = app_config.rtp_cfg;
1643  pjsua_acc_modify(aid, &acc_cfg);
1644  }
1645 
1646  //pjsua_acc_set_transport(aid, transport_id);
1647  pjsua_acc_set_online_status(current_acc, PJ_TRUE);
1648 
1649  if (app_config.udp_cfg.port == 0) {
1651  pj_sockaddr_in *a;
1652 
1653  pjsua_transport_get_info(transport_id, &ti);
1654  a = (pj_sockaddr_in*)&ti.local_addr;
1655 
1656  tcp_cfg.port = pj_ntohs(a->sin_port);
1657  }
1658  }
1659 
1660  /* Add UDP IPv6 transport unless it's disabled. */
1661  if (!app_config.no_udp && app_config.ipv6) {
1662  pjsua_acc_id aid;
1664  pjsua_transport_config udp_cfg;
1665 
1666  udp_cfg = app_config.udp_cfg;
1667  if (udp_cfg.port == 0)
1668  udp_cfg.port = 5060;
1669  else
1670  udp_cfg.port += 10;
1671  status = pjsua_transport_create(type,
1672  &udp_cfg,
1673  &transport_id);
1674  if (status != PJ_SUCCESS)
1675  goto on_error;
1676 
1677  /* Add local account */
1678  pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
1679 
1680  /* Adjust local account config based on pjsua app config */
1681  {
1682  pjsua_acc_config acc_cfg;
1683  pjsua_acc_get_config(aid, tmp_pool, &acc_cfg);
1684 
1685  app_config_init_video(&acc_cfg);
1686  acc_cfg.rtp_cfg = app_config.rtp_cfg;
1688  pjsua_acc_modify(aid, &acc_cfg);
1689  }
1690 
1691  //pjsua_acc_set_transport(aid, transport_id);
1692  pjsua_acc_set_online_status(current_acc, PJ_TRUE);
1693 
1694  if (app_config.udp_cfg.port == 0) {
1696 
1697  pjsua_transport_get_info(transport_id, &ti);
1698  tcp_cfg.port = pj_sockaddr_get_port(&ti.local_addr);
1699  }
1700  }
1701 
1702  /* Add TCP transport unless it's disabled */
1703  if (!app_config.no_tcp) {
1704  pjsua_acc_id aid;
1705 
1707  &tcp_cfg,
1708  &transport_id);
1709  if (status != PJ_SUCCESS)
1710  goto on_error;
1711 
1712  /* Add local account */
1713  pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
1714 
1715  /* Adjust local account config based on pjsua app config */
1716  {
1717  pjsua_acc_config acc_cfg;
1718  pjsua_acc_get_config(aid, tmp_pool, &acc_cfg);
1719 
1720  app_config_init_video(&acc_cfg);
1721  acc_cfg.rtp_cfg = app_config.rtp_cfg;
1722  pjsua_acc_modify(aid, &acc_cfg);
1723  }
1724 
1725  pjsua_acc_set_online_status(current_acc, PJ_TRUE);
1726 
1727  }
1728 
1729  /* Add TCP IPv6 transport unless it's disabled. */
1730  if (!app_config.no_tcp && app_config.ipv6) {
1731  pjsua_acc_id aid;
1733 
1734  tcp_cfg.port += 10;
1735 
1736  status = pjsua_transport_create(type,
1737  &tcp_cfg,
1738  &transport_id);
1739  if (status != PJ_SUCCESS)
1740  goto on_error;
1741 
1742  /* Add local account */
1743  pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
1744 
1745  /* Adjust local account config based on pjsua app config */
1746  {
1747  pjsua_acc_config acc_cfg;
1748  pjsua_acc_get_config(aid, tmp_pool, &acc_cfg);
1749 
1750  app_config_init_video(&acc_cfg);
1751  acc_cfg.rtp_cfg = app_config.rtp_cfg;
1753  pjsua_acc_modify(aid, &acc_cfg);
1754  }
1755 
1756  //pjsua_acc_set_transport(aid, transport_id);
1757  pjsua_acc_set_online_status(current_acc, PJ_TRUE);
1758  }
1759 
1760 
1761 #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1762  /* Add TLS transport when application wants one */
1763  if (app_config.use_tls) {
1764 
1765  pjsua_acc_id acc_id;
1766 
1767  /* Copy the QoS settings */
1768  tcp_cfg.tls_setting.qos_type = tcp_cfg.qos_type;
1769  pj_memcpy(&tcp_cfg.tls_setting.qos_params, &tcp_cfg.qos_params,
1770  sizeof(tcp_cfg.qos_params));
1771 
1772  /* Set TLS port as TCP port+1 */
1773  tcp_cfg.port++;
1775  &tcp_cfg,
1776  &transport_id);
1777  tcp_cfg.port--;
1778  if (status != PJ_SUCCESS)
1779  goto on_error;
1780 
1781  /* Add local account */
1782  pjsua_acc_add_local(transport_id, PJ_FALSE, &acc_id);
1783 
1784  /* Adjust local account config based on pjsua app config */
1785  {
1786  pjsua_acc_config acc_cfg;
1787  pjsua_acc_get_config(acc_id, tmp_pool, &acc_cfg);
1788 
1789  app_config_init_video(&acc_cfg);
1790  acc_cfg.rtp_cfg = app_config.rtp_cfg;
1791  pjsua_acc_modify(acc_id, &acc_cfg);
1792  }
1793 
1795  }
1796 
1797  /* Add TLS IPv6 transport unless it's disabled. */
1798  if (app_config.use_tls && app_config.ipv6) {
1799  pjsua_acc_id aid;
1801 
1802  tcp_cfg.port += 10;
1803 
1804  status = pjsua_transport_create(type,
1805  &tcp_cfg,
1806  &transport_id);
1807  if (status != PJ_SUCCESS)
1808  goto on_error;
1809 
1810  /* Add local account */
1811  pjsua_acc_add_local(transport_id, PJ_TRUE, &aid);
1812 
1813  /* Adjust local account config based on pjsua app config */
1814  {
1815  pjsua_acc_config acc_cfg;
1816  pjsua_acc_get_config(aid, tmp_pool, &acc_cfg);
1817 
1818  app_config_init_video(&acc_cfg);
1819  acc_cfg.rtp_cfg = app_config.rtp_cfg;
1821  pjsua_acc_modify(aid, &acc_cfg);
1822  }
1823 
1824  //pjsua_acc_set_transport(aid, transport_id);
1825  pjsua_acc_set_online_status(current_acc, PJ_TRUE);
1826  }
1827 
1828 #endif
1829 
1830  if (transport_id == -1) {
1831  PJ_LOG(1,(THIS_FILE, "Error: no transport is configured"));
1832  status = -1;
1833  goto on_error;
1834  }
1835 
1836 
1837  /* Add accounts */
1838  for (i=0; i<app_config.acc_cnt; ++i) {
1839  app_config.acc_cfg[i].rtp_cfg = app_config.rtp_cfg;
1840  app_config.acc_cfg[i].reg_retry_interval = 300;
1841  app_config.acc_cfg[i].reg_first_retry_interval = 60;
1842 
1843  app_config_init_video(&app_config.acc_cfg[i]);
1844 
1845  status = pjsua_acc_add(&app_config.acc_cfg[i], PJ_TRUE, NULL);
1846  if (status != PJ_SUCCESS)
1847  goto on_error;
1848  pjsua_acc_set_online_status(current_acc, PJ_TRUE);
1849  }
1850 
1851  /* Add buddies */
1852  for (i=0; i<app_config.buddy_cnt; ++i) {
1853  status = pjsua_buddy_add(&app_config.buddy_cfg[i], NULL);
1854  if (status != PJ_SUCCESS) {
1855  PJ_PERROR(1,(THIS_FILE, status, "Error adding buddy"));
1856  goto on_error;
1857  }
1858  }
1859 
1860  /* Optionally disable some codec */
1861  for (i=0; i<app_config.codec_dis_cnt; ++i) {
1862  pjsua_codec_set_priority(&app_config.codec_dis[i],
1864 #if PJSUA_HAS_VIDEO
1865  pjsua_vid_codec_set_priority(&app_config.codec_dis[i],
1867 #endif
1868  }
1869 
1870  /* Optionally set codec orders */
1871  for (i=0; i<app_config.codec_cnt; ++i) {
1872  pjsua_codec_set_priority(&app_config.codec_arg[i],
1874 #if PJSUA_HAS_VIDEO
1875  pjsua_vid_codec_set_priority(&app_config.codec_arg[i],
1877 #endif
1878  }
1879 
1880  /* Use null sound device? */
1881 #ifndef STEREO_DEMO
1882  if (app_config.null_audio) {
1883  status = pjsua_set_null_snd_dev();
1884  if (status != PJ_SUCCESS)
1885  goto on_error;
1886  }
1887 #endif
1888 
1889  if (app_config.capture_dev != PJSUA_INVALID_ID ||
1890  app_config.playback_dev != PJSUA_INVALID_ID)
1891  {
1892  status = pjsua_set_snd_dev(app_config.capture_dev,
1893  app_config.playback_dev);
1894  if (status != PJ_SUCCESS)
1895  goto on_error;
1896  }
1897 
1898  /* Init call setting */
1899  pjsua_call_setting_default(&call_opt);
1900  call_opt.aud_cnt = app_config.aud_cnt;
1901  call_opt.vid_cnt = app_config.vid.vid_cnt;
1902 
1903 #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
1904  /* Wipe out TLS key settings in transport configs */
1905  pjsip_tls_setting_wipe_keys(&app_config.udp_cfg.tls_setting);
1906 #endif
1907 
1908  pj_pool_release(tmp_pool);
1909  return PJ_SUCCESS;
1910 
1911 on_error:
1912  pj_pool_release(tmp_pool);
1913  app_destroy();
1914  return status;
1915 }
1916 
1917 pj_status_t pjsua_app_init(const pjsua_app_cfg_t *cfg)
1918 {
1919  pj_status_t status;
1920  pj_memcpy(&app_cfg, cfg, sizeof(app_cfg));
1921 
1922  status = app_init();
1923  if (status != PJ_SUCCESS)
1924  return status;
1925 
1926  /* Init CLI if configured */
1927  if (app_config.use_cli) {
1928  status = cli_init();
1929  }
1930  return status;
1931 }
1932 
1933 pj_status_t pjsua_app_run(pj_bool_t wait_telnet_cli)
1934 {
1935  pj_thread_t *stdout_refresh_thread = NULL;
1936  pj_status_t status;
1937 
1938  /* Start console refresh thread */
1939  if (stdout_refresh > 0) {
1940  pj_thread_create(app_config.pool, "stdout", &stdout_refresh_proc,
1941  NULL, 0, 0, &stdout_refresh_thread);
1942  }
1943 
1944  status = pjsua_start();
1945  if (status != PJ_SUCCESS)
1946  goto on_return;
1947 
1948  if (app_config.use_cli && (app_config.cli_cfg.cli_fe & CLI_FE_TELNET)) {
1949  char info[128];
1950  cli_get_info(info, sizeof(info));
1951  if (app_cfg.on_started) {
1952  (*app_cfg.on_started)(status, info);
1953  }
1954  } else {
1955  if (app_cfg.on_started) {
1956  (*app_cfg.on_started)(status, "Ready");
1957  }
1958  }
1959 
1960  /* If user specifies URI to call, then call the URI */
1961  if (uri_arg.slen) {
1962  pjsua_call_setting_default(&call_opt);
1963  call_opt.aud_cnt = app_config.aud_cnt;
1964  call_opt.vid_cnt = app_config.vid.vid_cnt;
1965 
1966  pjsua_call_make_call(current_acc, &uri_arg, &call_opt, NULL,
1967  NULL, NULL);
1968  }
1969 
1970  app_running = PJ_TRUE;
1971 
1972  if (app_config.use_cli)
1973  cli_main(wait_telnet_cli);
1974  else
1975  legacy_main();
1976 
1977  status = PJ_SUCCESS;
1978 
1979 on_return:
1980  if (stdout_refresh_thread) {
1981  stdout_refresh_quit = PJ_TRUE;
1982  pj_thread_join(stdout_refresh_thread);
1983  pj_thread_destroy(stdout_refresh_thread);
1984  stdout_refresh_quit = PJ_FALSE;
1985  }
1986  return status;
1987 }
1988 
1989 static pj_status_t app_destroy(void)
1990 {
1991  pj_status_t status = PJ_SUCCESS;
1992  unsigned i;
1993  pj_bool_t use_cli = PJ_FALSE;
1994  int cli_fe = 0;
1995  pj_uint16_t cli_telnet_port = 0;
1996 
1997 #ifdef STEREO_DEMO
1998  if (app_config.snd) {
1999  pjmedia_snd_port_destroy(app_config.snd);
2000  app_config.snd = NULL;
2001  }
2002  if (app_config.sc_ch1) {
2003  pjsua_conf_remove_port(app_config.sc_ch1_slot);
2004  app_config.sc_ch1_slot = PJSUA_INVALID_ID;
2005  pjmedia_port_destroy(app_config.sc_ch1);
2006  app_config.sc_ch1 = NULL;
2007  }
2008  if (app_config.sc) {
2009  pjmedia_port_destroy(app_config.sc);
2010  app_config.sc = NULL;
2011  }
2012 #endif
2013 
2014  /* Close avi devs and ports */
2015  for (i=0; i<app_config.avi_cnt; ++i) {
2016  if (app_config.avi[i].slot != PJSUA_INVALID_ID)
2017  pjsua_conf_remove_port(app_config.avi[i].slot);
2018 #if PJMEDIA_HAS_VIDEO && PJMEDIA_VIDEO_DEV_HAS_AVI
2019  if (app_config.avi[i].dev_id != PJMEDIA_VID_INVALID_DEV)
2020  pjmedia_avi_dev_free(app_config.avi[i].dev_id);
2021 #endif
2022  }
2023 
2024  /* Close ringback port */
2025  if (app_config.ringback_port &&
2026  app_config.ringback_slot != PJSUA_INVALID_ID)
2027  {
2028  pjsua_conf_remove_port(app_config.ringback_slot);
2029  app_config.ringback_slot = PJSUA_INVALID_ID;
2030  pjmedia_port_destroy(app_config.ringback_port);
2031  app_config.ringback_port = NULL;
2032  }
2033 
2034  /* Close ring port */
2035  if (app_config.ring_port && app_config.ring_slot != PJSUA_INVALID_ID) {
2036  pjsua_conf_remove_port(app_config.ring_slot);
2037  app_config.ring_slot = PJSUA_INVALID_ID;
2038  pjmedia_port_destroy(app_config.ring_port);
2039  app_config.ring_port = NULL;
2040  }
2041 
2042  /* Close tone generators */
2043  for (i=0; i<app_config.tone_count; ++i) {
2044  pjsua_conf_remove_port(app_config.tone_slots[i]);
2045  }
2046 
2047  pj_pool_safe_release(&app_config.pool);
2048 
2049  status = pjsua_destroy();
2050 
2051  if (app_config.use_cli) {
2052  use_cli = app_config.use_cli;
2053  cli_fe = app_config.cli_cfg.cli_fe;
2054  cli_telnet_port = app_config.cli_cfg.telnet_cfg.port;
2055  }
2056 
2057 #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
2058  /* Wipe out TLS key settings in transport configs */
2059  pjsip_tls_setting_wipe_keys(&app_config.udp_cfg.tls_setting);
2060 #endif
2061 
2062  /* Reset config */
2063  pj_bzero(&app_config, sizeof(app_config));
2064 
2065  if (use_cli) {
2066  app_config.use_cli = use_cli;
2067  app_config.cli_cfg.cli_fe = cli_fe;
2068  app_config.cli_cfg.telnet_cfg.port = cli_telnet_port;
2069  }
2070 
2071  return status;
2072 }
2073 
2074 pj_status_t pjsua_app_destroy(void)
2075 {
2076  pj_status_t status;
2077 
2078  status = app_destroy();
2079 
2080  if (app_config.use_cli) {
2081  cli_destroy();
2082  }
2083 
2084  return status;
2085 }
2086 
2089 #ifdef STEREO_DEMO
2090 /*
2091  * In this stereo demo, we open the sound device in stereo mode and
2092  * arrange the attachment to the PJSUA-LIB conference bridge as such
2093  * so that channel0/left channel of the sound device corresponds to
2094  * slot 0 in the bridge, and channel1/right channel of the sound
2095  * device corresponds to slot 1 in the bridge. Then user can independently
2096  * feed different media to/from the speakers/microphones channels, by
2097  * connecting them to slot 0 or 1 respectively.
2098  *
2099  * Here's how the connection looks like:
2100  *
2101  +-----------+ stereo +-----------------+ 2x mono +-----------+
2102  | AUDIO DEV |<------>| SPLITCOMB left|<------->|#0 BRIDGE |
2103  +-----------+ | right|<------->|#1 |
2104  +-----------------+ +-----------+
2105  */
2106 static void stereo_demo()
2107 {
2108  pjmedia_port *conf;
2109  pj_status_t status;
2110 
2111  /* Disable existing sound device */
2112  conf = pjsua_set_no_snd_dev();
2113 
2114  /* Create stereo-mono splitter/combiner */
2115  status = pjmedia_splitcomb_create(app_config.pool,
2116  PJMEDIA_PIA_SRATE(&conf->info) /* clock rate */,
2117  2 /* stereo */,
2118  2 * PJMEDIA_PIA_SPF(&conf->info),
2119  PJMEDIA_PIA_BITS(&conf->info),
2120  0 /* options */,
2121  &app_config.sc);
2122  pj_assert(status == PJ_SUCCESS);
2123 
2124  /* Connect channel0 (left channel?) to conference port slot0 */
2125  status = pjmedia_splitcomb_set_channel(app_config.sc, 0 /* ch0 */,
2126  0 /*options*/,
2127  conf);
2128  pj_assert(status == PJ_SUCCESS);
2129 
2130  /* Create reverse channel for channel1 (right channel?)... */
2131  status = pjmedia_splitcomb_create_rev_channel(app_config.pool,
2132  app_config.sc,
2133  1 /* ch1 */,
2134  0 /* options */,
2135  &app_config.sc_ch1);
2136  pj_assert(status == PJ_SUCCESS);
2137 
2138  /* .. and register it to conference bridge (it would be slot1
2139  * if there's no other devices connected to the bridge)
2140  */
2141  status = pjsua_conf_add_port(app_config.pool, app_config.sc_ch1,
2142  &app_config.sc_ch1_slot);
2143  pj_assert(status == PJ_SUCCESS);
2144 
2145  /* Create sound device */
2146  status = pjmedia_snd_port_create(app_config.pool, -1, -1,
2147  PJMEDIA_PIA_SRATE(&conf->info),
2148  2 /* stereo */,
2149  2 * PJMEDIA_PIA_SPF(&conf->info),
2150  PJMEDIA_PIA_BITS(&conf->info),
2151  0, &app_config.snd);
2152 
2153  pj_assert(status == PJ_SUCCESS);
2154 
2155 
2156  /* Connect the splitter to the sound device */
2157  status = pjmedia_snd_port_connect(app_config.snd, app_config.sc);
2158  pj_assert(status == PJ_SUCCESS);
2159 }
2160 #endif
pj_status_t pjsua_acc_add(const pjsua_acc_config *acc_cfg, pj_bool_t is_default, pjsua_acc_id *p_acc_id)
typedefPJ_BEGIN_DECL struct pjsip_evsub pjsip_evsub
Definition: evsub.h:51
PJMEDIA_DIR_ENCODING
Definition: sip_transport.h:806
void pjsip_tls_setting_wipe_keys(pjsip_tls_setting *opt)
unsigned PJMEDIA_PIA_SPF(const pjmedia_port_info *pia)
pj_status_t pjsua_enum_calls(pjsua_call_id ids[], unsigned *count)
pjsip_msg * msg
Definition: sip_transport.h:365
@ PJSIP_TRANSPORT_TLS
Definition: sip_types.h:74
pj_status_t pjsua_call_get_info(pjsua_call_id call_id, pjsua_call_info *info)
PJMEDIA_TYPE_VIDEO
pj_status_t pjsua_conf_add_port(pj_pool_t *pool, pjmedia_port *port, pjsua_conf_port_id *p_id)
struct pjsip_event::@9::@11 tsx_state
int pjsua_call_get_vid_stream_idx(pjsua_call_id call_id)
pjsua_call_media_info media[PJMEDIA_MAX_SDP_MEDIA]
Definition: pjsua-lib/pjsua.h:4805
Definition: pjsua-lib/pjsua.h:2773
unsigned pjsua_call_get_count(void)
int pj_bool_t
pjmedia_format fmt
pj_uint32_t expires
Definition: sip_msg.h:1370
pj_status_t pjsua_set_null_snd_dev(void)
pjsua_call_media_status media_status
Definition: pjsua-lib/pjsua.h:4789
@ PJSUA_DTMF_METHOD_RFC2833
Definition: pjsua-lib/pjsua.h:813
pj_status_t pjsua_init(const pjsua_config *ua_cfg, const pjsua_logging_config *log_cfg, const pjsua_media_config *media_cfg)
int pjsua_conf_port_id
Definition: pjsua-lib/pjsua.h:278
unsigned len
Definition: sip_msg.h:645
const char * pjsip_evsub_get_state_name(pjsip_evsub *sub)
pj_str_t name
Definition: sip_msg.h:998
pjmedia_avi_streams * avi_streams
PJMEDIA_DIR_DECODING
int pjsua_player_id
Definition: pjsua-lib/pjsua.h:272
pj_status_t pj_thread_create(pj_pool_t *pool, const char *thread_name, pj_thread_proc *proc, void *arg, pj_size_t stack_size, unsigned flags, pj_thread_t **thread)
@ PJSUA_CALL_MEDIA_ACTIVE
Definition: pjsua-lib/pjsua.h:4638
pj_qos_params qos_params
Definition: pjsua-lib/pjsua.h:2839
@ PJSIP_EVENT_RX_MSG
Definition: sip_event.h:55
pj_str_t pj_str(char *str)
@ PJSUA_IP_CHANGE_OP_ACC_SHUTDOWN_TP
Definition: pjsua-lib/pjsua.h:736
@ PJSIP_INV_STATE_DISCONNECTED
Definition: sip_inv.h:96
int pjsua_buddy_id
Definition: pjsua-lib/pjsua.h:269
pj_status_t pjsua_player_get_port(pjsua_player_id id, pjmedia_port **p_port)
int pjsua_transport_id
Definition: pjsua-lib/pjsua.h:2764
@ PJSIP_URI_IN_FROMTO_HDR
Definition: sip_uri.h:156
PJMEDIA_TONEGEN_LOOP
const char * pj_ssl_cipher_name(pj_ssl_cipher cipher)
pj_bool_t rem_offerer
Definition: pjsua-lib/pjsua.h:4825
pjsip_transport_type_e
Definition: sip_types.h:62
struct pj_thread_t pj_thread_t
Definition: pjsua-lib/pjsua.h:3414
unsigned media_cnt
Definition: pjsua-lib/pjsua.h:4802
void pjsua_call_setting_default(pjsua_call_setting *opt)
pj_status_t pjmedia_avi_dev_create_factory(pj_pool_factory *pf, unsigned max_dev, pjmedia_vid_dev_factory **p_ret)
pj_status_t pjsua_codec_set_priority(const pj_str_t *codec_id, pj_uint8_t priority)
pj_str_t last_status_text
Definition: pjsua-lib/pjsua.h:4779
pj_status_t pjsua_vid_win_set_size(pjsua_vid_win_id wid, const pjmedia_rect_size *size)
pj_status_t pjsua_buddy_add(const pjsua_buddy_config *buddy_cfg, pjsua_buddy_id *p_buddy_id)
pj_status_t pjsua_player_set_pos(pjsua_player_id id, pj_uint32_t samples)
Definition: pjsua-lib/pjsua.h:2876
void pj_bzero(void *dst, pj_size_t size)
pj_status_t pjmedia_splitcomb_set_channel(pjmedia_port *splitcomb, unsigned ch_num, unsigned options, pjmedia_port *port)
pj_status_t pj_thread_join(pj_thread_t *thread)
PJMEDIA_EVENT_FMT_CHANGED
Definition: pjsua-lib/pjsua.h:4740
pj_status_t pjsua_acc_get_config(pjsua_acc_id acc_id, pj_pool_t *pool, pjsua_acc_config *acc_cfg)
unsigned rem_vid_cnt
Definition: pjsua-lib/pjsua.h:4831
pjmedia_event_type type
pjsip_redirect_op
Definition: sip_util.h:80
pjsua_ipv6_use ipv6_media_use
Definition: pjsua-lib/pjsua.h:3856
pj_uint32_t ivalue
Definition: sip_msg.h:1073
pj_status_t pj_thread_sleep(unsigned msec)
void pjsip_msg_add_hdr(pjsip_msg *msg, pjsip_hdr *hdr)
Definition: sip_msg.h:906
unsigned rem_aud_cnt
Definition: pjsua-lib/pjsua.h:4828
pj_pool_factory * pjsua_get_pool_factory(void)
@ PJSIP_TP_STATE_CONNECTED
Definition: sip_transport.h:1443
unsigned aud_cnt
Definition: pjsua-lib/pjsua.h:879
int pjsua_acc_id
Definition: pjsua-lib/pjsua.h:266
void pj_cli_telnet_cfg_default(pj_cli_telnet_cfg *param)
Definition: sip_transport.h:522
@ PJSIP_TRANSPORT_TCP6
Definition: sip_types.h:98
pj_status_t pjmedia_tonegen_rewind(pjmedia_port *tonegen)
@ PJSUA_MED_TP_CLOSE_MEMBER
Definition: pjsua-lib/pjsua.h:635
pj_status_t pjsua_conf_remove_port(pjsua_conf_port_id port_id)
pjmedia_rect_size size
Definition: pjsua-lib/pjsua.h:7972
unsigned vid_cnt
Definition: pjsua-lib/pjsua.h:887
pjmedia_port_info info
#define PJ_PERROR(level, arg)
pj_uint32_t id
pj_status_t pjsua_transport_get_info(pjsua_transport_id id, pjsua_transport_info *info)
const pjsip_method pjsip_register_method
Definition: sip_msg.h:101
Definition: pjsua-lib/pjsua.h:2159
pj_str_t * pj_strdup2_with_null(pj_pool_t *pool, pj_str_t *dst, const char *src)
void pj_pool_release(pj_pool_t *pool)
pjsip_tls_setting tls_setting
Definition: pjsua-lib/pjsua.h:2821
pj_status_t pj_thread_set_prio(pj_thread_t *thread, int prio)
PJ_BEGIN_DECL pj_status_t pjmedia_splitcomb_create(pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_splitcomb)
unsigned PJMEDIA_PIA_SRATE(const pjmedia_port_info *pia)
@ PJSIP_REDIRECT_PENDING
Definition: sip_util.h:105
@ PJSIP_EVENT_TSX_STATE
Definition: sip_event.h:61
@ PJSUA_IPV6_ENABLED
Definition: pjsua-lib/pjsua.h:3387
@ PJSIP_MOD_PRIORITY_APPLICATION
Definition: sip_module.h:211
pj_status_t pjmedia_snd_port_destroy(pjmedia_snd_port *snd_port)
pj_status_t pj_ssl_cert_get_verify_status_strings(pj_uint32_t verify_status, const char *error_strings[], unsigned *count)
pj_status_t pjsua_acc_add_local(pjsua_transport_id tid, pj_bool_t is_default, pjsua_acc_id *p_acc_id)
@ PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS
Definition: pjsua-lib/pjsua.h:751
pjsip_event_id_e type
Definition: sip_event.h:88
pj_ice_strans_op
int pj_status_t
pjsua_call_media_status status
Definition: pjsua-lib/pjsua.h:4695
Definition: sip_msg.h:995
void * data
Definition: sip_msg.h:635
pj_uint16_t sin_port
int code
Definition: sip_msg.h:392
pjsip_method method
Definition: sip_msg.h:382
#define PJSIP_EXPIRES_NOT_SPECIFIED
Definition: sip_msg.h:1354
void pjsua_msg_data_init(pjsua_msg_data *msg_data)
PJMEDIA_FILE_NO_LOOP
pjmedia_type type
Definition: pjsua-lib/pjsua.h:4689
#define pj_assert(expr)
struct pjsua_call_media_info::@36::@37 aud
pj_status_t pjsua_call_answer2(pjsua_call_id call_id, const pjsua_call_setting *opt, unsigned code, const pj_str_t *reason, const pjsua_msg_data *msg_data)
pjsua_conf_port_id pjsua_call_get_conf_port(pjsua_call_id call_id)
Definition: pjsua-lib/pjsua.h:855
unsigned pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams)
pj_status_t pj_thread_destroy(pj_thread_t *thread)
PJ_BEGIN_DECL pj_status_t pjmedia_tp_adapter_create(pjmedia_endpt *endpt, const char *name, pjmedia_transport *base_tp, pj_bool_t del_base, pjmedia_transport **p_tp)
#define PJ_UNUSED_ARG(arg)
pj_str_t acc_uri
Definition: pjsua-lib/pjsua.h:4176
pj_str_t local_info
Definition: pjsua-lib/pjsua.h:4752
pj_status_t pjsua_buddy_get_info(pjsua_buddy_id buddy_id, pjsua_buddy_info *info)
Definition: pjsua_internal.h:214
void pj_cli_console_cfg_default(pj_cli_console_cfg *param)
pj_str_t subtype
Definition: sip_msg.h:519
pjmedia_port * pjsua_set_no_snd_dev(void)
pjmedia_endpt * pjsua_get_pjmedia_endpt(void)
Definition: pjsua-lib/pjsua.h:484
pj_str_t remote_info
Definition: pjsua-lib/pjsua.h:4758
PJ_SUCCESS
Definition: sip_msg.h:324
@ PJSUA_CALL_MEDIA_NONE
Definition: pjsua-lib/pjsua.h:4633
pjsua_ip_change_op
Definition: pjsua-lib/pjsua.h:722
const char * pj_get_version(void)
pjmedia_dir dir
Definition: pjsua-lib/pjsua.h:4692
pjsip_role_e role
Definition: pjsua-lib/pjsua.h:4746
const pjsip_method pjsip_notify_method
Definition: evsub.h:208
char * ptr
pj_status_t pjmedia_splitcomb_create_rev_channel(pj_pool_t *pool, pjmedia_port *splitcomb, unsigned ch_num, unsigned options, pjmedia_port **p_chport)
struct pjsua_call_media_info::@36::@38 vid
pjsip_status_code
Definition: sip_msg.h:403
pj_status_t pjsua_call_hangup(pjsua_call_id call_id, unsigned code, const pj_str_t *reason, const pjsua_msg_data *msg_data)
@ PJSUA_CALL_MEDIA_ERROR
Definition: pjsua-lib/pjsua.h:4653
@ PJSUA_IP_CHANGE_OP_COMPLETED
Definition: pjsua-lib/pjsua.h:756
int id
pjsip_msg * msg
Definition: sip_transport.h:556
pjsua_call_id id
Definition: pjsua-lib/pjsua.h:4743
@ PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT
Definition: pjsua-lib/pjsua.h:741
pj_status_t pjsua_vid_codec_set_priority(const pj_str_t *codec_id, pj_uint8_t priority)
pj_qos_type qos_type
Definition: pjsua-lib/pjsua.h:2830
@ PJSIP_TRANSPORT_TCP
Definition: sip_types.h:71
PJMEDIA_CODEC_PRIO_NORMAL
pj_pool_t * pool
Definition: sip_transport.h:528
Definition: pjsua-lib/pjsua.h:5932
Definition: sip_module.h:54
pj_status_t pjsip_endpt_schedule_timer(pjsip_endpoint *endpt, pj_timer_entry *entry, const pj_time_val *delay)
pj_status_t pjmedia_tonegen_play(pjmedia_port *tonegen, unsigned count, const pjmedia_tone_desc tones[], unsigned options)
void pjsua_perror(const char *sender, const char *title, pj_status_t status)
PJMEDIA_FORMAT_PCM
pj_status_t pjsua_conf_disconnect(pjsua_conf_port_id source, pjsua_conf_port_id sink)
const pjsip_hdr * pjsip_endpt_get_capability(pjsip_endpoint *endpt, int htype, const pj_str_t *hname)
void * pj_memcpy(void *dst, const void *src, pj_size_t size)
pjsip_hdr hdr_list
Definition: pjsua-lib/pjsua.h:2175
pj_status_t pjsua_create(void)
pj_ssl_sock_info * ssl_sock_info
Definition: sip_transport_tls.h:359
pjsip_host_port remote_name
Definition: sip_transport.h:829
int pjsip_uri_print(pjsip_uri_context_e context, const void *uri, char *buf, pj_size_t size)
Definition: sip_uri.h:299
pjsip_endpoint * pjsua_get_pjsip_endpt(void)
pj_thread_t * pj_thread_this(void)
pj_str_t hvalue
Definition: sip_msg.h:1000
#define PJ_LOG(level, arg)
Definition: pjsua-lib/pjsua.h:764
pjsip_ctype_hdr * ctype
Definition: sip_transport.h:397
char * pj_addr_str_print(const pj_str_t *host_str, int port, char *buf, int size, unsigned flag)
pj_status_t pjsua_transport_create(pjsip_transport_type_e type, const pjsua_transport_config *cfg, pjsua_transport_id *p_id)
pj_int32_t pjmedia_vid_dev_index
@ PJSUA_CALL_MEDIA_REMOTE_HOLD
Definition: pjsua-lib/pjsua.h:4648
pj_status_t pjmedia_snd_port_connect(pjmedia_snd_port *snd_port, pjmedia_port *port)
pj_status_t pjmedia_wav_player_set_eof_cb2(pjmedia_port *port, void *user_data, void(*cb)(pjmedia_port *port, void *usr_data))
PJ_TRUE
unsigned char pj_uint8_t
union pjsip_event::@9 body
PJMEDIA_DIR_NONE
struct pjsip_request_line req
Definition: sip_msg.h:794
@ PJSUA_DTMF_METHOD_SIP_INFO
Definition: pjsua-lib/pjsua.h:824
PJMEDIA_CODEC_PRIO_DISABLED
struct pjsip_endpoint pjsip_endpoint
Definition: sip_types.h:112
Definition: sip_msg.h:782
pjmedia_avi_stream * pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams, unsigned idx)
void pj_pool_safe_release(pj_pool_t **ppool)
@ PJSIP_TRANSPORT_UDP6
Definition: sip_types.h:95
const char * pjmedia_type_name(pjmedia_type t)
void pjmedia_avi_dev_param_default(pjmedia_avi_dev_param *p)
pjsua_conf_port_id pjsua_recorder_get_conf_port(pjsua_recorder_id id)
pj_status_t pjsua_call_make_call(pjsua_acc_id acc_id, const pj_str_t *dst_uri, const pjsua_call_setting *opt, void *user_data, const pjsua_msg_data *msg_data, pjsua_call_id *p_call_id)
@ PJSUA_IP_CHANGE_OP_ACC_HANGUP_CALLS
Definition: pjsua-lib/pjsua.h:746
pj_status_t pjsua_acc_modify(pjsua_acc_id acc_id, const pjsua_acc_config *acc_cfg)
Definition: sip_transport.h:295
pj_sockaddr local_addr
Definition: pjsua-lib/pjsua.h:2911
void pjsip_endpt_cancel_timer(pjsip_endpoint *endpt, pj_timer_entry *entry)
@ PJSIP_INV_STATE_EARLY
Definition: sip_inv.h:93
Definition: sip_transport_tls.h:354
pj_str_t reason
Definition: sip_msg.h:393
pj_status_t pjmedia_tonegen_create2(pj_pool_t *pool, const pj_str_t *name, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_port **p_port)
pj_str_t type
Definition: sip_msg.h:518
void pj_cli_cfg_default(pj_cli_cfg *param)
int pjsua_call_id
Definition: pjsua-lib/pjsua.h:263
unsigned port
Definition: pjsua-lib/pjsua.h:2781
int port
Definition: sip_types.h:227
int pjsip_method_cmp(const pjsip_method *m1, const pjsip_method *m2)
pjsua_conf_port_id pjsua_player_get_conf_port(pjsua_player_id id)
pjsip_hdr hdr
Definition: sip_msg.h:801
Definition: sip_msg.h:1068
char * type_name
Definition: sip_transport.h:822
pj_str_t info
pj_qos_params qos_params
Definition: sip_transport_tls.h:312
pj_status_t pjsip_endpt_send_response2(pjsip_endpoint *endpt, pjsip_rx_data *rdata, pjsip_tx_data *tdata, void *token, pjsip_send_callback cb)
Definition: pjsua-lib/pjsua.h:4161
#define PJ_ARRAY_SIZE(a)
pj_status_t pjsip_endpt_create_response(pjsip_endpoint *endpt, const pjsip_rx_data *rdata, int st_code, const pj_str_t *st_text, pjsip_tx_data **p_tdata)
PJMEDIA_EVENT_MEDIA_TP_ERR
unsigned PJMEDIA_PIA_BITS(const pjmedia_port_info *pia)
const char * pjmedia_fourcc_name(pj_uint32_t sig, char buf[])
pj_status_t pjsua_acc_set_online_status(pjsua_acc_id acc_id, pj_bool_t is_online)
pj_str_t state_text
Definition: pjsua-lib/pjsua.h:4773
pjsip_media_type media
Definition: sip_msg.h:1411
int pj_thread_get_prio_min(pj_thread_t *thread)
pj_timer_entry * pj_timer_entry_init(pj_timer_entry *entry, int id, void *user_data, pj_timer_heap_callback *cb)
@ PJSIP_INV_STATE_CONFIRMED
Definition: sip_inv.h:95
void * pjsip_msg_find_hdr(const pjsip_msg *msg, pjsip_hdr_e type, const void *start)
pj_status_t pjmedia_avi_dev_free(pjmedia_vid_dev_index id)
pj_status_t pjmedia_snd_port_create(pj_pool_t *pool, int rec_id, int play_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, unsigned options, pjmedia_snd_port **p_port)
#define PJSIP_MAX_URL_SIZE
Definition: sip_config.h:344
#define PJSUA_MAX_CALLS
Definition: pjsua-lib/pjsua.h:4608
pj_qos_type qos_type
Definition: sip_transport_tls.h:303
pjsua_transport_config rtp_cfg
Definition: pjsua-lib/pjsua.h:3844
void pjsua_call_hangup_all(void)
pjsip_msg_body * body
Definition: sip_msg.h:806
union pjsua_call_media_info::@36 stream
PJ_FALSE
@ PJSIP_TRANSPORT_UDP
Definition: sip_types.h:68
pjsip_rx_data * rdata
Definition: pjsua-lib/pjsua.h:488
pjmedia_dir
Definition: pjsua-lib/pjsua.h:832
void pj_list_push_back(pj_list_type *list, pj_list_type *node)
union pjsip_msg::@17 line
@ PJSIP_ROLE_UAC
Definition: sip_types.h:191
pjsip_status_code last_status
Definition: pjsua-lib/pjsua.h:4776
int pjsua_vid_win_id
Definition: pjsua-lib/pjsua.h:4621
Definition: sip_msg.h:1408
pj_uint32_t flags
pj_status_t pjsua_destroy(void)
unsigned short pj_uint16_t
pj_status_t pjsua_acc_get_info(pjsua_acc_id acc_id, pjsua_acc_info *info)
void * pjsip_hdr_clone(pj_pool_t *pool, const void *hdr)
pj_ssize_t slen
const pjsip_method pjsip_ack_method
Definition: sip_msg.h:95
pj_status_t pjmedia_avi_dev_alloc(pjmedia_vid_dev_factory *f, pjmedia_avi_dev_param *param, pjmedia_vid_dev_index *p_id)
Definition: sip_transport.h:1466
struct pjsip_rx_data::@24 msg_info
pj_status_t pjsua_recorder_create(const pj_str_t *filename, unsigned enc_type, void *enc_param, pj_ssize_t max_size, unsigned options, pjsua_recorder_id *p_id)
pj_status_t pjsua_set_snd_dev(int capture_dev, int playback_dev)
pj_status_t pjsip_endpt_register_module(pjsip_endpoint *endpt, pjsip_module *module)
pjsip_generic_string_hdr * pjsip_generic_string_hdr_create(pj_pool_t *pool, const pj_str_t *hname, const pj_str_t *hvalue)
Definition: sip_msg.h:1362
@ PJSIP_TP_STATE_DISCONNECTED
Definition: sip_transport.h:1446
pj_status_t pjsua_get_snd_dev(int *capture_dev, int *playback_dev)
pj_uint16_t pj_sockaddr_get_port(const pj_sockaddr_t *addr)
@ PJSIP_TRANSPORT_TLS6
Definition: sip_types.h:101
const char * pjsip_event_str(pjsip_event_id_e e)
pj_str_t host
Definition: sip_types.h:226
pj_status_t pjsua_conf_connect(pjsua_conf_port_id source, pjsua_conf_port_id sink)
PJMEDIA_TYPE_AUDIO
char * pjsip_rx_data_get_info(pjsip_rx_data *rdata)
pj_status_t pjmedia_port_destroy(pjmedia_port *port)
PJMEDIA_VID_INVALID_DEV
Definition: sip_uri.h:222
pj_status_t pjsua_player_create(const pj_str_t *filename, unsigned options, pjsua_player_id *p_id)
pjsip_inv_state state
Definition: pjsua-lib/pjsua.h:4770
pjsip_transport_state
Definition: sip_transport.h:1441
struct pj_timer_heap_t pj_timer_heap_t
pj_str_t info
Definition: pjsua-lib/pjsua.h:2896
pj_uint16_t pj_ntohs(pj_uint16_t netshort)
pj_status_t pjsua_vid_win_get_info(pjsua_vid_win_id wid, pjsua_vid_win_info *wi)
Definition: sip_event.h:81
pj_status_t pjsua_start(void)
pjsip_hdr_e type
Definition: sip_msg.h:326
Definition: pjsua-lib/pjsua.h:7934
@ PJSUA_IP_CHANGE_OP_RESTART_LIS
Definition: pjsua-lib/pjsua.h:731
pj_bool_t pjsua_call_has_media(pjsua_call_id call_id)
pj_pool_t * pjsua_pool_create(const char *name, pj_size_t init_size, pj_size_t increment)
pj_ssize_t pj_ssl_cert_info_dump(const pj_ssl_cert_info *ci, const char *indent, char *buf, pj_size_t buf_size)
struct pjsip_status_line status
Definition: sip_msg.h:797
void pjsip_generic_string_hdr_init2(pjsip_generic_string_hdr *h, pj_str_t *hname, pj_str_t *hvalue)

 


PJSIP Open Source, high performance, small footprint, and very very portable SIP stack
Copyright (C) 2006-2008 Teluu Inc.