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 
21 #include "pjsua_app_common.h"
22 
23 #define THIS_FILE	"pjsua_app_common.c"
24 
25 #if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
26 #   define SOME_BUF_SIZE	(1024 * 10)
27 #else
28 #   define SOME_BUF_SIZE	(1024 * 3)
29 #endif
30 
31 #ifdef USE_GUI
32 void displayWindow(pjsua_vid_win_id wid);
33 #endif
34 
35 static char some_buf[SOME_BUF_SIZE];
36 
37 /** Variable definition **/
38 int		    stdout_refresh = -1;
39 pj_bool_t	    stdout_refresh_quit = PJ_FALSE;
40 pjsua_call_id	    current_call = PJSUA_INVALID_ID;
41 pjsua_app_config    app_config;
42 pjsua_call_setting  call_opt;
43 pjsua_msg_data	    msg_data;
44 
my_atoi(const char * cs)45 int my_atoi(const char *cs)
46 {
47     pj_str_t s;
48     pj_cstr(&s, cs);
49     return my_atoi2(&s);
50 }
51 
my_atoi2(const pj_str_t * str)52 int my_atoi2(const pj_str_t *str)
53 {
54     const char *cs = str->ptr;
55     pj_str_t s = *str;
56 
57     if (cs[0] == '-') {
58 	s.ptr++; s.slen--;
59 	return 0 - (int)pj_strtoul(&s);
60     } else if (cs[0] == '+') {
61 	s.ptr++; s.slen--;
62 	return (int)pj_strtoul(&s);
63     } else {
64 	return (int)pj_strtoul(&s);
65     }
66 }
67 
68 /*
69  * Find next call when current call is disconnected or when user
70  * press ']'
71  */
find_next_call(void)72 pj_bool_t find_next_call(void)
73 {
74     int i, max;
75 
76     max = pjsua_call_get_max_count();
77     for (i=current_call+1; i<max; ++i) {
78 	if (pjsua_call_is_active(i)) {
79 	    current_call = i;
80 	    return PJ_TRUE;
81 	}
82     }
83 
84     for (i=0; i<current_call; ++i) {
85 	if (pjsua_call_is_active(i)) {
86 	    current_call = i;
87 	    return PJ_TRUE;
88 	}
89     }
90 
91     current_call = PJSUA_INVALID_ID;
92     return PJ_FALSE;
93 }
94 
find_prev_call(void)95 pj_bool_t find_prev_call(void)
96 {
97     int i, max;
98 
99     max = pjsua_call_get_max_count();
100     for (i=current_call-1; i>=0; --i) {
101 	if (pjsua_call_is_active(i)) {
102 	    current_call = i;
103 	    return PJ_TRUE;
104 	}
105     }
106 
107     for (i=max-1; i>current_call; --i) {
108 	if (pjsua_call_is_active(i)) {
109 	    current_call = i;
110 	    return PJ_TRUE;
111 	}
112     }
113 
114     current_call = PJSUA_INVALID_ID;
115     return PJ_FALSE;
116 }
117 
118 /*
119  * Send arbitrary request to remote host
120  */
send_request(char * cstr_method,const pj_str_t * dst_uri)121 void send_request(char *cstr_method, const pj_str_t *dst_uri)
122 {
123     pj_str_t str_method;
124     pjsip_method method;
125     pjsip_tx_data *tdata;
126     pjsip_endpoint *endpt;
127     pj_status_t status;
128 
129     endpt = pjsua_get_pjsip_endpt();
130 
131     str_method = pj_str(cstr_method);
132     pjsip_method_init_np(&method, &str_method);
133 
134     status = pjsua_acc_create_request(current_acc, &method, dst_uri, &tdata);
135 
136     status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL);
137     if (status != PJ_SUCCESS) {
138 	pjsua_perror(THIS_FILE, "Unable to send request", status);
139 	return;
140     }
141 }
142 
143 /*
144  * Print log of call states. Since call states may be too long for logger,
145  * printing it is a bit tricky, it should be printed part by part as long
146  * as the logger can accept.
147  */
log_call_dump(int call_id)148 void log_call_dump(int call_id)
149 {
150     unsigned call_dump_len;
151     unsigned part_len;
152     unsigned part_idx;
153     unsigned log_decor;
154 
155     pjsua_call_dump(call_id, PJ_TRUE, some_buf, sizeof(some_buf), "  ");
156     call_dump_len = (unsigned)strlen(some_buf);
157 
158     log_decor = pj_log_get_decor();
159     pj_log_set_decor(log_decor & ~(PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR));
160     PJ_LOG(3,(THIS_FILE, "\n"));
161     pj_log_set_decor(0);
162 
163     part_idx = 0;
164     part_len = PJ_LOG_MAX_SIZE-80;
165     while (part_idx < call_dump_len) {
166 	char p_orig, *p;
167 
168 	p = &some_buf[part_idx];
169 	if (part_idx + part_len > call_dump_len)
170 	    part_len = call_dump_len - part_idx;
171 	p_orig = p[part_len];
172 	p[part_len] = '\0';
173 	PJ_LOG(3,(THIS_FILE, "%s", p));
174 	p[part_len] = p_orig;
175 	part_idx += part_len;
176     }
177     pj_log_set_decor(log_decor);
178 }
179 
180 #ifdef PJSUA_HAS_VIDEO
app_config_init_video(pjsua_acc_config * acc_cfg)181 void app_config_init_video(pjsua_acc_config *acc_cfg)
182 {
183     acc_cfg->vid_in_auto_show = app_config.vid.in_auto_show;
184     acc_cfg->vid_out_auto_transmit = app_config.vid.out_auto_transmit;
185     /* Note that normally GUI application will prefer a borderless
186      * window.
187      */
188     acc_cfg->vid_wnd_flags = PJMEDIA_VID_DEV_WND_BORDER |
189                              PJMEDIA_VID_DEV_WND_RESIZABLE;
190     acc_cfg->vid_cap_dev = app_config.vid.vcapture_dev;
191     acc_cfg->vid_rend_dev = app_config.vid.vrender_dev;
192 
193     if (app_config.avi_auto_play &&
194 	app_config.avi_def_idx != PJSUA_INVALID_ID &&
195 	app_config.avi[app_config.avi_def_idx].dev_id != PJMEDIA_VID_INVALID_DEV)
196     {
197 	acc_cfg->vid_cap_dev = app_config.avi[app_config.avi_def_idx].dev_id;
198     }
199 }
200 #else
app_config_init_video(pjsua_acc_config * acc_cfg)201 void app_config_init_video(pjsua_acc_config *acc_cfg)
202 {
203     PJ_UNUSED_ARG(acc_cfg);
204 }
205 #endif
206 
207 #ifdef HAVE_MULTIPART_TEST
208   /*
209    * Enable multipart in msg_data and add a dummy body into the
210    * multipart bodies.
211    */
add_multipart(pjsua_msg_data * msg_data)212   void add_multipart(pjsua_msg_data *msg_data)
213   {
214       static pjsip_multipart_part *alt_part;
215 
216       if (!alt_part) {
217 	  pj_str_t type, subtype, content;
218 
219 	  alt_part = pjsip_multipart_create_part(app_config.pool);
220 
221 	  type = pj_str("text");
222 	  subtype = pj_str("plain");
223 	  content = pj_str("Sample text body of a multipart bodies");
224 	  alt_part->body = pjsip_msg_body_create(app_config.pool, &type,
225 						 &subtype, &content);
226       }
227 
228       msg_data->multipart_ctype.type = pj_str("multipart");
229       msg_data->multipart_ctype.subtype = pj_str("mixed");
230       pj_list_push_back(&msg_data->multipart_parts, alt_part);
231   }
232 #endif
233 
234 /* arrange windows. arg:
235  *   -1:    arrange all windows
236  *   != -1: arrange only this window id
237  */
arrange_window(pjsua_vid_win_id wid)238 void arrange_window(pjsua_vid_win_id wid)
239 {
240 #if PJSUA_HAS_VIDEO
241     pjmedia_coord pos;
242     int i, last;
243 
244     pos.x = 0;
245     pos.y = 10;
246     last = (wid == PJSUA_INVALID_ID) ? PJSUA_MAX_VID_WINS : wid;
247 
248     for (i=0; i<last; ++i) {
249 	pjsua_vid_win_info wi;
250 	pj_status_t status;
251 
252 	status = pjsua_vid_win_get_info(i, &wi);
253 	if (status != PJ_SUCCESS)
254 	    continue;
255 
256 	if (wid == PJSUA_INVALID_ID)
257 	    pjsua_vid_win_set_pos(i, &pos);
258 
259 	if (wi.show)
260 	    pos.y += wi.size.h;
261     }
262 
263     if (wid != PJSUA_INVALID_ID)
264 	pjsua_vid_win_set_pos(wid, &pos);
265 
266 #ifdef USE_GUI
267     displayWindow(wid);
268 #endif
269 
270 #else
271     PJ_UNUSED_ARG(wid);
272 #endif
273 }
274 
275 
276 #if PJSUA_HAS_VIDEO
vid_print_dev(int id,const pjmedia_vid_dev_info * vdi,const char * title)277 void vid_print_dev(int id, const pjmedia_vid_dev_info *vdi, const char *title)
278 {
279     char capnames[120];
280     char formats[200];
281     const char *dirname;
282     unsigned i;
283     int st_len;
284 
285     if (vdi->dir == PJMEDIA_DIR_CAPTURE_RENDER) {
286 	dirname = "capture, render";
287     } else if (vdi->dir == PJMEDIA_DIR_CAPTURE) {
288 	dirname = "capture";
289     } else {
290 	dirname = "render";
291     }
292 
293 
294     capnames[0] = '\0';
295     st_len = 0;
296     for (i=0; i<sizeof(int)*8 && (1 << i) < PJMEDIA_VID_DEV_CAP_MAX; ++i) {
297 	if (vdi->caps & (1 << i)) {
298 	    const char *capname = pjmedia_vid_dev_cap_name(1 << i, NULL);
299 	    if (capname) {
300 		int tmp_len = (int)strlen(capname);
301 		if ((int)sizeof(capnames) - st_len <= tmp_len)
302 		    break;
303 
304 		st_len += (tmp_len + 2);
305 		if (*capnames)
306 		    strcat(capnames, ", ");
307 		strcat(capnames, capname);
308 	    }
309 	}
310     }
311 
312     formats[0] = '\0';
313     st_len = 0;
314     for (i=0; i<vdi->fmt_cnt; ++i) {
315 	const pjmedia_video_format_info *vfi =
316 		pjmedia_get_video_format_info(NULL, vdi->fmt[i].id);
317 	if (vfi) {
318 	    int tmp_len = (int)strlen(vfi->name);
319 	    if ((int)sizeof(formats) - st_len <= tmp_len) {
320 		st_len = -1;
321 		break;
322 	    }
323 
324 	    st_len += (tmp_len + 2);
325 	    if (*formats)
326 		strcat(formats, ", ");
327 	    strcat(formats, vfi->name);
328 	}
329     }
330 
331     PJ_LOG(3,(THIS_FILE, "%3d %s [%s][%s] %s", id, vdi->name, vdi->driver,
332 	      dirname, title));
333     PJ_LOG(3,(THIS_FILE, "    Supported capabilities: %s", capnames));
334     PJ_LOG(3,(THIS_FILE, "    Supported formats: %s%s", formats,
335 			      (st_len<0? " ..." : "")));
336 }
337 
vid_list_devs(void)338 void vid_list_devs(void)
339 {
340     unsigned i, count;
341     pjmedia_vid_dev_info vdi;
342     pj_status_t status;
343 
344     PJ_LOG(3,(THIS_FILE, "Video device list:"));
345     count = pjsua_vid_dev_count();
346     if (count == 0) {
347 	PJ_LOG(3,(THIS_FILE, " - no device detected -"));
348 	return;
349     } else {
350 	PJ_LOG(3,(THIS_FILE, "%d device(s) detected:", count));
351     }
352 
353     status = pjsua_vid_dev_get_info(PJMEDIA_VID_DEFAULT_RENDER_DEV, &vdi);
354     if (status == PJ_SUCCESS)
355 	vid_print_dev(PJMEDIA_VID_DEFAULT_RENDER_DEV, &vdi,
356 	              "(default renderer device)");
357 
358     status = pjsua_vid_dev_get_info(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &vdi);
359     if (status == PJ_SUCCESS)
360 	vid_print_dev(PJMEDIA_VID_DEFAULT_CAPTURE_DEV, &vdi,
361 	              "(default capture device)");
362 
363     for (i=0; i<count; ++i) {
364 	status = pjsua_vid_dev_get_info(i, &vdi);
365 	if (status == PJ_SUCCESS)
366 	    vid_print_dev(i, &vdi, "");
367     }
368 }
369 
app_config_show_video(int acc_id,const pjsua_acc_config * acc_cfg)370 void app_config_show_video(int acc_id, const pjsua_acc_config *acc_cfg)
371 {
372     PJ_LOG(3,(THIS_FILE,
373 	      "Account %d:\n"
374 	      "  RX auto show:     %d\n"
375 	      "  TX auto transmit: %d\n"
376 	      "  Capture dev:      %d\n"
377 	      "  Render dev:       %d",
378 	      acc_id,
379 	      acc_cfg->vid_in_auto_show,
380 	      acc_cfg->vid_out_auto_transmit,
381 	      acc_cfg->vid_cap_dev,
382 	      acc_cfg->vid_rend_dev));
383 }
384 
385 
386 #endif
387