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-lib/pjsua.h>
21 #include <pjsua-lib/pjsua_internal.h>
22 
23 #if defined(PJSUA_MEDIA_HAS_PJMEDIA) && PJSUA_MEDIA_HAS_PJMEDIA != 0
24 
25 #define THIS_FILE		"pjsua_aud.c"
26 
27 /*****************************************************************************
28  *
29  * Prototypes
30  */
31 /* Open sound dev */
32 static pj_status_t open_snd_dev(pjmedia_snd_port_param *param);
33 /* Close existing sound device */
34 static void close_snd_dev(void);
35 /* Create audio device param */
36 static pj_status_t create_aud_param(pjmedia_aud_param *param,
37 				    pjmedia_aud_dev_index capture_dev,
38 				    pjmedia_aud_dev_index playback_dev,
39 				    unsigned clock_rate,
40 				    unsigned channel_count,
41 				    unsigned samples_per_frame,
42 				    unsigned bits_per_sample);
43 
44 /*****************************************************************************
45  *
46  * Call API that are closely tied to PJMEDIA
47  */
48 /*
49  * Check if call has an active media session.
50  */
pjsua_call_has_media(pjsua_call_id call_id)51 PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id)
52 {
53     pjsua_call *call = &pjsua_var.calls[call_id];
54     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
55 		     PJ_EINVAL);
56     return call->audio_idx >= 0 && call->media[call->audio_idx].strm.a.stream;
57 }
58 
59 
60 /*
61  * Get the conference port identification associated with the call.
62  */
pjsua_call_get_conf_port(pjsua_call_id call_id)63 PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id)
64 {
65     pjsua_call *call;
66     pjsua_conf_port_id port_id = PJSUA_INVALID_ID;
67 
68     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
69 		     PJ_EINVAL);
70 
71     /* Use PJSUA_LOCK() instead of acquire_call():
72      *  https://trac.pjsip.org/repos/ticket/1371
73      */
74     PJSUA_LOCK();
75 
76     if (!pjsua_call_is_active(call_id))
77 	goto on_return;
78 
79     call = &pjsua_var.calls[call_id];
80     if (call->audio_idx >= 0)
81 	port_id = call->media[call->audio_idx].strm.a.conf_slot;
82 
83 on_return:
84     PJSUA_UNLOCK();
85 
86     return port_id;
87 }
88 
89 
90 /*
91  * Get media stream info for the specified media index.
92  */
pjsua_call_get_stream_info(pjsua_call_id call_id,unsigned med_idx,pjsua_stream_info * psi)93 PJ_DEF(pj_status_t) pjsua_call_get_stream_info( pjsua_call_id call_id,
94                                                 unsigned med_idx,
95                                                 pjsua_stream_info *psi)
96 {
97     pjsua_call *call;
98     pjsua_call_media *call_med;
99     pj_status_t status;
100 
101     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
102 		     PJ_EINVAL);
103     PJ_ASSERT_RETURN(psi, PJ_EINVAL);
104 
105     PJSUA_LOCK();
106 
107     call = &pjsua_var.calls[call_id];
108 
109     if (med_idx >= call->med_cnt) {
110 	PJSUA_UNLOCK();
111 	return PJ_EINVAL;
112     }
113 
114     call_med = &call->media[med_idx];
115     psi->type = call_med->type;
116     switch (call_med->type) {
117     case PJMEDIA_TYPE_AUDIO:
118 	status = pjmedia_stream_get_info(call_med->strm.a.stream,
119 					 &psi->info.aud);
120 	break;
121 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
122     case PJMEDIA_TYPE_VIDEO:
123 	status = pjmedia_vid_stream_get_info(call_med->strm.v.stream,
124 					     &psi->info.vid);
125 	break;
126 #endif
127     default:
128 	status = PJMEDIA_EINVALIMEDIATYPE;
129 	break;
130     }
131 
132     PJSUA_UNLOCK();
133     return status;
134 }
135 
136 
137 /*
138  *  Get media stream statistic for the specified media index.
139  */
pjsua_call_get_stream_stat(pjsua_call_id call_id,unsigned med_idx,pjsua_stream_stat * stat)140 PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id,
141                                                 unsigned med_idx,
142                                                 pjsua_stream_stat *stat)
143 {
144     pjsua_call *call;
145     pjsua_call_media *call_med;
146     pj_status_t status;
147 
148     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
149 		     PJ_EINVAL);
150     PJ_ASSERT_RETURN(stat, PJ_EINVAL);
151 
152     PJSUA_LOCK();
153 
154     call = &pjsua_var.calls[call_id];
155 
156     if (med_idx >= call->med_cnt) {
157 	PJSUA_UNLOCK();
158 	return PJ_EINVAL;
159     }
160 
161     call_med = &call->media[med_idx];
162     switch (call_med->type) {
163     case PJMEDIA_TYPE_AUDIO:
164 	status = pjmedia_stream_get_stat(call_med->strm.a.stream,
165 					 &stat->rtcp);
166 	if (status == PJ_SUCCESS)
167 	    status = pjmedia_stream_get_stat_jbuf(call_med->strm.a.stream,
168 						  &stat->jbuf);
169 	break;
170 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
171     case PJMEDIA_TYPE_VIDEO:
172 	status = pjmedia_vid_stream_get_stat(call_med->strm.v.stream,
173 					     &stat->rtcp);
174 	if (status == PJ_SUCCESS)
175 	    status = pjmedia_vid_stream_get_stat_jbuf(call_med->strm.v.stream,
176 						  &stat->jbuf);
177 	break;
178 #endif
179     default:
180 	status = PJMEDIA_EINVALIMEDIATYPE;
181 	break;
182     }
183 
184     PJSUA_UNLOCK();
185     return status;
186 }
187 
188 /*
189  * Send DTMF digits to remote using RFC 2833 payload formats.
190  */
pjsua_call_dial_dtmf(pjsua_call_id call_id,const pj_str_t * digits)191 PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,
192 					  const pj_str_t *digits)
193 {
194     pjsua_call *call;
195     pjsip_dialog *dlg = NULL;
196     pj_status_t status;
197 
198     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
199 		     PJ_EINVAL);
200 
201     PJ_LOG(4,(THIS_FILE, "Call %d dialing DTMF %.*s",
202     			 call_id, (int)digits->slen, digits->ptr));
203     pj_log_push_indent();
204 
205     status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg);
206     if (status != PJ_SUCCESS)
207 	goto on_return;
208 
209     if (!pjsua_call_has_media(call_id)) {
210 	PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
211 	status = PJ_EINVALIDOP;
212 	goto on_return;
213     }
214 
215     status = pjmedia_stream_dial_dtmf(
216 		call->media[call->audio_idx].strm.a.stream, digits);
217 
218 on_return:
219     if (dlg) pjsip_dlg_dec_lock(dlg);
220     pj_log_pop_indent();
221     return status;
222 }
223 
224 
225 /*****************************************************************************
226  *
227  * Audio media with PJMEDIA backend
228  */
229 
230 /* Init pjmedia audio subsystem */
pjsua_aud_subsys_init()231 pj_status_t pjsua_aud_subsys_init()
232 {
233     pj_str_t codec_id = {NULL, 0};
234     unsigned opt;
235     pjmedia_audio_codec_config codec_cfg;
236     pj_status_t status;
237 
238     /* To suppress warning about unused var when all codecs are disabled */
239     PJ_UNUSED_ARG(codec_id);
240 
241     /*
242      * Register all codecs
243      */
244     pjmedia_audio_codec_config_default(&codec_cfg);
245     codec_cfg.speex.quality = pjsua_var.media_cfg.quality;
246     codec_cfg.speex.complexity = -1;
247     codec_cfg.ilbc.mode = pjsua_var.media_cfg.ilbc_mode;
248 
249 #if PJMEDIA_HAS_PASSTHROUGH_CODECS
250     /* Register passthrough codecs */
251     {
252 	unsigned aud_idx;
253 	unsigned ext_fmt_cnt = 0;
254 	pjmedia_format ext_fmts[32];
255 
256 	/* List extended formats supported by audio devices */
257 	for (aud_idx = 0; aud_idx < pjmedia_aud_dev_count(); ++aud_idx) {
258 	    pjmedia_aud_dev_info aud_info;
259 	    unsigned i;
260 
261 	    status = pjmedia_aud_dev_get_info(aud_idx, &aud_info);
262 	    if (status != PJ_SUCCESS) {
263 		pjsua_perror(THIS_FILE, "Error querying audio device info",
264 			     status);
265 		goto on_error;
266 	    }
267 
268 	    /* Collect extended formats supported by this audio device */
269 	    for (i = 0; i < aud_info.ext_fmt_cnt; ++i) {
270 		unsigned j;
271 		pj_bool_t is_listed = PJ_FALSE;
272 
273 		/* See if this extended format is already in the list */
274 		for (j = 0; j < ext_fmt_cnt && !is_listed; ++j) {
275 		    if (ext_fmts[j].id == aud_info.ext_fmt[i].id &&
276 			ext_fmts[j].det.aud.avg_bps ==
277 			aud_info.ext_fmt[i].det.aud.avg_bps)
278 		    {
279 			is_listed = PJ_TRUE;
280 		    }
281 		}
282 
283 		/* Put this format into the list, if it is not in the list */
284 		if (!is_listed)
285 		    ext_fmts[ext_fmt_cnt++] = aud_info.ext_fmt[i];
286 
287 		pj_assert(ext_fmt_cnt <= PJ_ARRAY_SIZE(ext_fmts));
288 	    }
289 	}
290 
291 	/* Init the passthrough codec with supported formats only */
292 	codec_cfg.passthrough.setting.fmt_cnt = ext_fmt_cnt;
293 	codec_cfg.passthrough.setting.fmts = ext_fmts;
294 	codec_cfg.passthrough.setting.ilbc_mode =
295             pjsua_var.media_cfg.ilbc_mode;
296     }
297 #endif /* PJMEDIA_HAS_PASSTHROUGH_CODECS */
298 
299     /* Register all codecs */
300     status = pjmedia_codec_register_audio_codecs(pjsua_var.med_endpt,
301                                                  &codec_cfg);
302     if (status != PJ_SUCCESS) {
303 	pjsua_perror(THIS_FILE, "Error registering codecs", status);
304 	goto on_error;
305     }
306 
307     /* Set speex/16000 to higher priority*/
308     codec_id = pj_str("speex/16000");
309     pjmedia_codec_mgr_set_codec_priority(
310 	pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt),
311 	&codec_id, PJMEDIA_CODEC_PRIO_NORMAL+2);
312 
313     /* Set speex/8000 to next higher priority*/
314     codec_id = pj_str("speex/8000");
315     pjmedia_codec_mgr_set_codec_priority(
316 	pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt),
317 	&codec_id, PJMEDIA_CODEC_PRIO_NORMAL+1);
318 
319     /* Disable ALL L16 codecs */
320     codec_id = pj_str("L16");
321     pjmedia_codec_mgr_set_codec_priority(
322 	pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt),
323 	&codec_id, PJMEDIA_CODEC_PRIO_DISABLED);
324 
325 
326     /* Save additional conference bridge parameters for future
327      * reference.
328      */
329     pjsua_var.mconf_cfg.channel_count = pjsua_var.media_cfg.channel_count;
330     pjsua_var.mconf_cfg.bits_per_sample = 16;
331     pjsua_var.mconf_cfg.samples_per_frame = pjsua_var.media_cfg.clock_rate *
332 					    pjsua_var.mconf_cfg.channel_count *
333 					    pjsua_var.media_cfg.audio_frame_ptime /
334 					    1000;
335 
336     /* Init options for conference bridge. */
337     opt = PJMEDIA_CONF_NO_DEVICE;
338     if (pjsua_var.media_cfg.quality >= 3 &&
339 	pjsua_var.media_cfg.quality <= 4)
340     {
341 	opt |= PJMEDIA_CONF_SMALL_FILTER;
342     }
343     else if (pjsua_var.media_cfg.quality < 3) {
344 	opt |= PJMEDIA_CONF_USE_LINEAR;
345     }
346 
347     /* Init conference bridge. */
348     status = pjmedia_conf_create(pjsua_var.pool,
349 				 pjsua_var.media_cfg.max_media_ports,
350 				 pjsua_var.media_cfg.clock_rate,
351 				 pjsua_var.mconf_cfg.channel_count,
352 				 pjsua_var.mconf_cfg.samples_per_frame,
353 				 pjsua_var.mconf_cfg.bits_per_sample,
354 				 opt, &pjsua_var.mconf);
355     if (status != PJ_SUCCESS) {
356 	pjsua_perror(THIS_FILE, "Error creating conference bridge",
357 		     status);
358 	goto on_error;
359     }
360 
361     /* Are we using the audio switchboard (a.k.a APS-Direct)? */
362     pjsua_var.is_mswitch = pjmedia_conf_get_master_port(pjsua_var.mconf)
363 			    ->info.signature == PJMEDIA_CONF_SWITCH_SIGNATURE;
364 
365     /* Create null port just in case user wants to use null sound. */
366     status = pjmedia_null_port_create(pjsua_var.pool,
367 				      pjsua_var.media_cfg.clock_rate,
368 				      pjsua_var.mconf_cfg.channel_count,
369 				      pjsua_var.mconf_cfg.samples_per_frame,
370 				      pjsua_var.mconf_cfg.bits_per_sample,
371 				      &pjsua_var.null_port);
372     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
373 
374     return status;
375 
376 on_error:
377     return status;
378 }
379 
380 /* Check if sound device is idle. */
pjsua_check_snd_dev_idle()381 void pjsua_check_snd_dev_idle()
382 {
383     unsigned call_cnt;
384 
385     /* Check if the sound device auto-close feature is disabled. */
386     if (pjsua_var.media_cfg.snd_auto_close_time < 0)
387 	return;
388 
389     /* Check if the sound device is currently closed. */
390     if (!pjsua_var.snd_is_on)
391 	return;
392 
393     /* Get the call count, we shouldn't close the sound device when there is
394      * any calls active.
395      */
396     call_cnt = pjsua_call_get_count();
397 
398     /* When this function is called from pjsua_media_channel_deinit() upon
399      * disconnecting call, actually the call count hasn't been updated/
400      * decreased. So we put additional check here, if there is only one
401      * call and it's in DISCONNECTED state, there is actually no active
402      * call.
403      */
404     if (call_cnt == 1) {
405 	pjsua_call_id call_id;
406 	pj_status_t status;
407 
408 	status = pjsua_enum_calls(&call_id, &call_cnt);
409 	if (status == PJ_SUCCESS && call_cnt > 0 &&
410 	    !pjsua_call_is_active(call_id))
411 	{
412 	    call_cnt = 0;
413 	}
414     }
415 
416     /* Activate sound device auto-close timer if sound device is idle.
417      * It is idle when there is no port connection in the bridge and
418      * there is no active call.
419      */
420     if (pjsua_var.snd_idle_timer.id == PJ_FALSE &&
421 	call_cnt == 0 &&
422 	pjmedia_conf_get_connect_count(pjsua_var.mconf) == 0)
423     {
424 	pj_time_val delay;
425 
426 	delay.msec = 0;
427 	delay.sec = pjsua_var.media_cfg.snd_auto_close_time;
428 
429 	pjsua_var.snd_idle_timer.id = PJ_TRUE;
430 	pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.snd_idle_timer,
431 				   &delay);
432     }
433 }
434 
435 /* Timer callback to close sound device */
close_snd_timer_cb(pj_timer_heap_t * th,pj_timer_entry * entry)436 static void close_snd_timer_cb( pj_timer_heap_t *th,
437 				pj_timer_entry *entry)
438 {
439     PJ_UNUSED_ARG(th);
440 
441     PJSUA_LOCK();
442     if (entry->id) {
443 	PJ_LOG(4,(THIS_FILE,"Closing sound device after idle for %d second(s)",
444 		  pjsua_var.media_cfg.snd_auto_close_time));
445 
446 	entry->id = PJ_FALSE;
447 
448 	close_snd_dev();
449     }
450     PJSUA_UNLOCK();
451 }
452 
pjsua_aud_subsys_start(void)453 pj_status_t pjsua_aud_subsys_start(void)
454 {
455     pj_status_t status = PJ_SUCCESS;
456 
457     pj_timer_entry_init(&pjsua_var.snd_idle_timer, PJ_FALSE, NULL,
458 			&close_snd_timer_cb);
459 
460     pjsua_check_snd_dev_idle();
461     return status;
462 }
463 
pjsua_aud_subsys_destroy()464 pj_status_t pjsua_aud_subsys_destroy()
465 {
466     unsigned i;
467 
468     close_snd_dev();
469 
470     if (pjsua_var.mconf) {
471 	pjmedia_conf_destroy(pjsua_var.mconf);
472 	pjsua_var.mconf = NULL;
473     }
474 
475     if (pjsua_var.null_port) {
476 	pjmedia_port_destroy(pjsua_var.null_port);
477 	pjsua_var.null_port = NULL;
478     }
479 
480     /* Destroy file players */
481     for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.player); ++i) {
482 	if (pjsua_var.player[i].port) {
483 	    pjmedia_port_destroy(pjsua_var.player[i].port);
484 	    pjsua_var.player[i].port = NULL;
485 	}
486     }
487 
488     /* Destroy file recorders */
489     for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.recorder); ++i) {
490 	if (pjsua_var.recorder[i].port) {
491 	    pjmedia_port_destroy(pjsua_var.recorder[i].port);
492 	    pjsua_var.recorder[i].port = NULL;
493 	}
494     }
495 
496     return PJ_SUCCESS;
497 }
498 
pjsua_aud_stop_stream(pjsua_call_media * call_med)499 void pjsua_aud_stop_stream(pjsua_call_media *call_med)
500 {
501     pjmedia_stream *strm = call_med->strm.a.stream;
502     pjmedia_rtcp_stat stat;
503 
504     if (strm) {
505 	/* Unsubscribe from stream events */
506 	pjmedia_event_unsubscribe(NULL, &call_media_on_event, call_med, strm);
507 
508 	pjmedia_stream_send_rtcp_bye(strm);
509 
510 	if (call_med->strm.a.conf_slot != PJSUA_INVALID_ID) {
511 	    if (pjsua_var.mconf) {
512 		pjsua_conf_remove_port(call_med->strm.a.conf_slot);
513 	    }
514 	    call_med->strm.a.conf_slot = PJSUA_INVALID_ID;
515 	}
516 
517 	/* Don't check for direction and transmitted packets count as we
518 	 * assume that RTP timestamp remains increasing when outgoing
519 	 * direction is disabled/paused.
520 	 */
521 	//if ((call_med->dir & PJMEDIA_DIR_ENCODING) &&
522 	//    (pjmedia_stream_get_stat(strm, &stat) == PJ_SUCCESS) &&
523 	//    stat.tx.pkt)
524 	if (pjmedia_stream_get_stat(strm, &stat) == PJ_SUCCESS)
525 	{
526 	    /* Save RTP timestamp & sequence, so when media session is
527 	     * restarted, those values will be restored as the initial
528 	     * RTP timestamp & sequence of the new media session. So in
529 	     * the same call session, RTP timestamp and sequence are
530 	     * guaranteed to be contigue.
531 	     */
532 	    call_med->rtp_tx_seq_ts_set = 1 | (1 << 1);
533 	    call_med->rtp_tx_seq = stat.rtp_tx_last_seq;
534 	    call_med->rtp_tx_ts = stat.rtp_tx_last_ts;
535 	}
536 
537 	if (!call_med->call->hanging_up &&
538 	    pjsua_var.ua_cfg.cb.on_stream_destroyed)
539 	{
540 	    pjsua_var.ua_cfg.cb.on_stream_destroyed(call_med->call->index,
541 	                                            strm, call_med->idx);
542 	}
543 
544 	if (call_med->strm.a.media_port) {
545 	    if (call_med->strm.a.destroy_port)
546 		pjmedia_port_destroy(call_med->strm.a.media_port);
547 	    call_med->strm.a.media_port = NULL;
548 	}
549 
550 	pjmedia_stream_destroy(strm);
551 	call_med->strm.a.stream = NULL;
552     }
553 
554     pjsua_check_snd_dev_idle();
555 }
556 
557 /*
558  * DTMF callback from the stream.
559  */
dtmf_callback(pjmedia_stream * strm,void * user_data,int digit)560 static void dtmf_callback(pjmedia_stream *strm, void *user_data,
561 			  int digit)
562 {
563     pjsua_call_id call_id;
564 
565     PJ_UNUSED_ARG(strm);
566 
567     call_id = (pjsua_call_id)(pj_ssize_t)user_data;
568     if (pjsua_var.calls[call_id].hanging_up)
569     	return;
570 
571     pj_log_push_indent();
572 
573     if (pjsua_var.ua_cfg.cb.on_dtmf_digit2) {
574 	pjsua_dtmf_info info;
575 
576 	info.method = PJSUA_DTMF_METHOD_RFC2833;
577 	info.digit = digit;
578         info.duration = PJSUA_UNKNOWN_DTMF_DURATION;
579 	(*pjsua_var.ua_cfg.cb.on_dtmf_digit2)(call_id, &info);
580     } else if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
581 	/* For discussions about call mutex protection related to this
582 	 * callback, please see ticket #460:
583 	 *	http://trac.pjsip.org/repos/ticket/460#comment:4
584 	 */
585 	(*pjsua_var.ua_cfg.cb.on_dtmf_digit)(call_id, digit);
586     }
587 
588     pj_log_pop_indent();
589 }
590 
591 /*
592  * DTMF callback from the stream.
593  */
dtmf_event_callback(pjmedia_stream * strm,void * user_data,const pjmedia_stream_dtmf_event * event)594 static void dtmf_event_callback(pjmedia_stream *strm, void *user_data,
595                                 const pjmedia_stream_dtmf_event *event)
596 {
597     pjsua_call_id call_id;
598     pjsua_dtmf_event evt;
599 
600     PJ_UNUSED_ARG(strm);
601 
602     call_id = (pjsua_call_id)(pj_ssize_t)user_data;
603     if (pjsua_var.calls[call_id].hanging_up)
604     	return;
605 
606     pj_log_push_indent();
607 
608     if (pjsua_var.ua_cfg.cb.on_dtmf_event) {
609         evt.method = PJSUA_DTMF_METHOD_RFC2833;
610         evt.timestamp = event->timestamp;
611         evt.digit = event->digit;
612         evt.duration = event->duration;
613         evt.flags = event->flags;
614         (*pjsua_var.ua_cfg.cb.on_dtmf_event)(call_id, &evt);
615     }
616 
617     pj_log_pop_indent();
618 }
619 
620 /* Internal function: update audio channel after SDP negotiation.
621  * Warning: do not use temporary/flip-flop pool, e.g: inv->pool_prov,
622  *          for creating stream, etc, as after SDP negotiation and when
623  *	    the SDP media is not changed, the stream should remain running
624  *          while the temporary/flip-flop pool may be released.
625  */
pjsua_aud_channel_update(pjsua_call_media * call_med,pj_pool_t * tmp_pool,pjmedia_stream_info * si,const pjmedia_sdp_session * local_sdp,const pjmedia_sdp_session * remote_sdp)626 pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med,
627                                      pj_pool_t *tmp_pool,
628                                      pjmedia_stream_info *si,
629 				     const pjmedia_sdp_session *local_sdp,
630 				     const pjmedia_sdp_session *remote_sdp)
631 {
632     pjsua_call *call = call_med->call;
633     unsigned strm_idx = call_med->idx;
634     pj_status_t status = PJ_SUCCESS;
635 
636     PJ_UNUSED_ARG(tmp_pool);
637     PJ_UNUSED_ARG(local_sdp);
638     PJ_UNUSED_ARG(remote_sdp);
639 
640     PJ_LOG(4,(THIS_FILE,"Audio channel update.."));
641     pj_log_push_indent();
642 
643     si->rtcp_sdes_bye_disabled = pjsua_var.media_cfg.no_rtcp_sdes_bye;
644 
645     /* Check if no media is active */
646     if (local_sdp->media[strm_idx]->desc.port != 0) {
647 
648 	/* Optionally, application may modify other stream settings here
649 	 * (such as jitter buffer parameters, codec ptime, etc.)
650 	 */
651 	si->jb_init = pjsua_var.media_cfg.jb_init;
652 	si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
653 	si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
654 	si->jb_max = pjsua_var.media_cfg.jb_max;
655         si->jb_discard_algo = pjsua_var.media_cfg.jb_discard_algo;
656 
657 	/* Set SSRC and CNAME */
658 	si->ssrc = call_med->ssrc;
659 	si->cname = call->cname;
660 
661 	/* Set RTP timestamp & sequence, normally these value are intialized
662 	 * automatically when stream session created, but for some cases (e.g:
663 	 * call reinvite, call update) timestamp and sequence need to be kept
664 	 * contigue.
665 	 */
666 	si->rtp_ts = call_med->rtp_tx_ts;
667 	si->rtp_seq = call_med->rtp_tx_seq;
668 	si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set;
669 
670 #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
671 	/* Enable/disable stream keep-alive and NAT hole punch. */
672 	si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
673 
674         si->ka_cfg = pjsua_var.acc[call->acc_id].cfg.stream_ka_cfg;
675 #endif
676 
677         if (!call->hanging_up && pjsua_var.ua_cfg.cb.on_stream_precreate) {
678             pjsua_on_stream_precreate_param prm;
679             prm.stream_idx = strm_idx;
680             prm.stream_info.type = PJMEDIA_TYPE_AUDIO;
681             prm.stream_info.info.aud = *si;
682             (*pjsua_var.ua_cfg.cb.on_stream_precreate)(call->index, &prm);
683 
684             /* Copy back only the fields which are allowed to be changed. */
685             si->jb_init = prm.stream_info.info.aud.jb_init;
686             si->jb_min_pre = prm.stream_info.info.aud.jb_min_pre;
687             si->jb_max_pre = prm.stream_info.info.aud.jb_max_pre;
688             si->jb_max = prm.stream_info.info.aud.jb_max;
689             si->jb_discard_algo = prm.stream_info.info.aud.jb_discard_algo;
690 #if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0)
691             si->use_ka = prm.stream_info.info.aud.use_ka;
692 #endif
693             si->rtcp_sdes_bye_disabled = prm.stream_info.info.aud.rtcp_sdes_bye_disabled;
694         }
695 
696 	/* Create session based on session info. */
697 	status = pjmedia_stream_create(pjsua_var.med_endpt, NULL, si,
698 				       call_med->tp, NULL,
699 				       &call_med->strm.a.stream);
700 	if (status != PJ_SUCCESS) {
701 	    goto on_return;
702 	}
703 
704 	/* Start stream */
705 	status = pjmedia_stream_start(call_med->strm.a.stream);
706 	if (status != PJ_SUCCESS) {
707 	    goto on_return;
708 	}
709 
710         if (call_med->prev_state == PJSUA_CALL_MEDIA_NONE)
711             pjmedia_stream_send_rtcp_sdes(call_med->strm.a.stream);
712 
713 	/* If DTMF callback is installed by application, install our
714 	 * callback to the session.
715 	 */
716         if (!call->hanging_up && pjsua_var.ua_cfg.cb.on_dtmf_event) {
717             pjmedia_stream_set_dtmf_event_callback(call_med->strm.a.stream,
718                                               &dtmf_event_callback,
719                                               (void*)(pj_ssize_t)(call->index));
720         } else if (!call->hanging_up &&
721         	   (pjsua_var.ua_cfg.cb.on_dtmf_digit ||
722 	            pjsua_var.ua_cfg.cb.on_dtmf_digit2))
723 	{
724 	    pjmedia_stream_set_dtmf_callback(call_med->strm.a.stream,
725 					     &dtmf_callback,
726 					     (void*)(pj_ssize_t)(call->index));
727 	}
728 
729 	/* Get the port interface of the first stream in the session.
730 	 * We need the port interface to add to the conference bridge.
731 	 */
732 	pjmedia_stream_get_port(call_med->strm.a.stream,
733 				&call_med->strm.a.media_port);
734 
735 	/* Notify application about stream creation.
736 	 * Note: application may modify media_port to point to different
737 	 * media port
738 	 */
739 	if (!call->hanging_up && pjsua_var.ua_cfg.cb.on_stream_created2) {
740 	    pjsua_on_stream_created_param prm;
741 
742 	    prm.stream = call_med->strm.a.stream;
743 	    prm.stream_idx = strm_idx;
744 	    prm.destroy_port = PJ_FALSE;
745 	    prm.port = call_med->strm.a.media_port;
746 	    (*pjsua_var.ua_cfg.cb.on_stream_created2)(call->index, &prm);
747 
748 	    call_med->strm.a.destroy_port = prm.destroy_port;
749 	    call_med->strm.a.media_port = prm.port;
750 
751 	} else if (!call->hanging_up && pjsua_var.ua_cfg.cb.on_stream_created)
752 	{
753 	    (*pjsua_var.ua_cfg.cb.on_stream_created)(call->index,
754 						  call_med->strm.a.stream,
755 						  strm_idx,
756 						  &call_med->strm.a.media_port);
757 	}
758 
759 	/*
760 	 * Add the call to conference bridge.
761 	 */
762 	{
763 	    char tmp[PJSIP_MAX_URL_SIZE];
764 	    pj_str_t port_name;
765 
766 	    port_name.ptr = tmp;
767 	    port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
768 					     call->inv->dlg->remote.info->uri,
769 					     tmp, sizeof(tmp));
770 	    if (port_name.slen < 1) {
771 		port_name = pj_str("call");
772 	    }
773 	    status = pjmedia_conf_add_port(pjsua_var.mconf,
774 					   call->inv->pool,
775 					   call_med->strm.a.media_port,
776 					   &port_name,
777 					   (unsigned*)
778 					   &call_med->strm.a.conf_slot);
779 	    if (status != PJ_SUCCESS) {
780 		goto on_return;
781 	    }
782 	}
783 
784 	/* Subscribe to stream events */
785 	pjmedia_event_subscribe(NULL, &call_media_on_event, call_med,
786 				call_med->strm.a.stream);
787     }
788 
789 on_return:
790     pj_log_pop_indent();
791     return status;
792 }
793 
pjsua_snd_dev_param_default(pjsua_snd_dev_param * prm)794 PJ_DEF(void) pjsua_snd_dev_param_default(pjsua_snd_dev_param *prm)
795 {
796     pj_bzero(prm, sizeof(*prm));
797     prm->capture_dev = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
798     prm->playback_dev = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
799 }
800 
pjsua_conf_connect_param_default(pjsua_conf_connect_param * prm)801 PJ_DEF(void) pjsua_conf_connect_param_default(pjsua_conf_connect_param *prm)
802 {
803     pj_bzero(prm, sizeof(*prm));
804     prm->level = 1.0;
805 }
806 
807 /*
808  * Get maxinum number of conference ports.
809  */
pjsua_conf_get_max_ports(void)810 PJ_DEF(unsigned) pjsua_conf_get_max_ports(void)
811 {
812     return pjsua_var.media_cfg.max_media_ports;
813 }
814 
815 
816 /*
817  * Get current number of active ports in the bridge.
818  */
pjsua_conf_get_active_ports(void)819 PJ_DEF(unsigned) pjsua_conf_get_active_ports(void)
820 {
821     unsigned ports[PJSUA_MAX_CONF_PORTS];
822     unsigned count = PJ_ARRAY_SIZE(ports);
823     pj_status_t status;
824 
825     status = pjmedia_conf_enum_ports(pjsua_var.mconf, ports, &count);
826     if (status != PJ_SUCCESS)
827 	count = 0;
828 
829     return count;
830 }
831 
832 
833 /*
834  * Enumerate all conference ports.
835  */
pjsua_enum_conf_ports(pjsua_conf_port_id id[],unsigned * count)836 PJ_DEF(pj_status_t) pjsua_enum_conf_ports(pjsua_conf_port_id id[],
837 					  unsigned *count)
838 {
839     return pjmedia_conf_enum_ports(pjsua_var.mconf, (unsigned*)id, count);
840 }
841 
842 
843 /*
844  * Get information about the specified conference port
845  */
pjsua_conf_get_port_info(pjsua_conf_port_id id,pjsua_conf_port_info * info)846 PJ_DEF(pj_status_t) pjsua_conf_get_port_info( pjsua_conf_port_id id,
847 					      pjsua_conf_port_info *info)
848 {
849     pjmedia_conf_port_info cinfo;
850     unsigned i;
851     pj_status_t status;
852 
853     status = pjmedia_conf_get_port_info( pjsua_var.mconf, id, &cinfo);
854     if (status != PJ_SUCCESS)
855 	return status;
856 
857     pj_bzero(info, sizeof(*info));
858     info->slot_id = id;
859     info->name = cinfo.name;
860     pjmedia_format_copy(&info->format, &cinfo.format);
861     info->clock_rate = cinfo.clock_rate;
862     info->channel_count = cinfo.channel_count;
863     info->samples_per_frame = cinfo.samples_per_frame;
864     info->bits_per_sample = cinfo.bits_per_sample;
865     info->tx_level_adj = ((float)cinfo.tx_adj_level) / 128 + 1;
866     info->rx_level_adj = ((float)cinfo.rx_adj_level) / 128 + 1;
867 
868     /* Build array of listeners */
869     info->listener_cnt = cinfo.listener_cnt;
870     for (i=0; i<cinfo.listener_cnt; ++i) {
871 	info->listeners[i] = cinfo.listener_slots[i];
872     }
873 
874     return PJ_SUCCESS;
875 }
876 
877 
878 /*
879  * Add arbitrary media port to PJSUA's conference bridge.
880  */
pjsua_conf_add_port(pj_pool_t * pool,pjmedia_port * port,pjsua_conf_port_id * p_id)881 PJ_DEF(pj_status_t) pjsua_conf_add_port( pj_pool_t *pool,
882 					 pjmedia_port *port,
883 					 pjsua_conf_port_id *p_id)
884 {
885     pj_status_t status;
886 
887     status = pjmedia_conf_add_port(pjsua_var.mconf, pool,
888 				   port, NULL, (unsigned*)p_id);
889     if (status != PJ_SUCCESS) {
890 	if (p_id)
891 	    *p_id = PJSUA_INVALID_ID;
892     }
893 
894     return status;
895 }
896 
897 
898 /*
899  * Remove arbitrary slot from the conference bridge.
900  */
pjsua_conf_remove_port(pjsua_conf_port_id id)901 PJ_DEF(pj_status_t) pjsua_conf_remove_port(pjsua_conf_port_id id)
902 {
903     pj_status_t status;
904 
905     status = pjmedia_conf_remove_port(pjsua_var.mconf, (unsigned)id);
906     pjsua_check_snd_dev_idle();
907 
908     return status;
909 }
910 
911 
912 /*
913  * Establish unidirectional media flow from souce to sink.
914  */
pjsua_conf_connect(pjsua_conf_port_id source,pjsua_conf_port_id sink)915 PJ_DEF(pj_status_t) pjsua_conf_connect( pjsua_conf_port_id source,
916 					pjsua_conf_port_id sink)
917 {
918     pjsua_conf_connect_param prm;
919 
920     pjsua_conf_connect_param_default(&prm);
921     return pjsua_conf_connect2(source, sink, &prm);
922 }
923 
924 /*
925  * Establish unidirectional media flow from souce to sink, with signal
926  * level adjustment.
927  */
pjsua_conf_connect2(pjsua_conf_port_id source,pjsua_conf_port_id sink,const pjsua_conf_connect_param * prm)928 PJ_DEF(pj_status_t) pjsua_conf_connect2( pjsua_conf_port_id source,
929 					 pjsua_conf_port_id sink,
930 					 const pjsua_conf_connect_param *prm)
931 {
932     pj_status_t status = PJ_SUCCESS;
933 
934     PJ_LOG(4,(THIS_FILE, "%s connect: %d --> %d",
935 	      (pjsua_var.is_mswitch ? "Switch" : "Conf"),
936 	      source, sink));
937     pj_log_push_indent();
938 
939     PJSUA_LOCK();
940 
941     /* If sound device idle timer is active, cancel it first. */
942     if (pjsua_var.snd_idle_timer.id) {
943 	pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.snd_idle_timer);
944 	pjsua_var.snd_idle_timer.id = PJ_FALSE;
945     }
946 
947 
948     /* For audio switchboard (i.e. APS-Direct):
949      * Check if sound device need to be reopened, i.e: its attributes
950      * (format, clock rate, channel count) must match to peer's.
951      * Note that sound device can be reopened only if it doesn't have
952      * any connection.
953      */
954     if (pjsua_var.is_mswitch) {
955 	pjmedia_conf_port_info port0_info;
956 	pjmedia_conf_port_info peer_info;
957 	unsigned peer_id;
958 	pj_bool_t need_reopen = PJ_FALSE;
959 
960 	peer_id = (source!=0)? source : sink;
961 	status = pjmedia_conf_get_port_info(pjsua_var.mconf, peer_id,
962 					    &peer_info);
963 	pj_assert(status == PJ_SUCCESS);
964 
965 	status = pjmedia_conf_get_port_info(pjsua_var.mconf, 0, &port0_info);
966 	pj_assert(status == PJ_SUCCESS);
967 
968 	/* Check if sound device is instantiated. */
969 	need_reopen = (pjsua_var.snd_port==NULL && pjsua_var.null_snd==NULL &&
970 		      !pjsua_var.no_snd);
971 
972 	/* Check if sound device need to reopen because it needs to modify
973 	 * settings to match its peer. Sound device must be idle in this case
974 	 * though.
975 	 */
976 	if (!need_reopen &&
977 	    port0_info.listener_cnt==0 && port0_info.transmitter_cnt==0)
978 	{
979 	    need_reopen = (peer_info.format.id != port0_info.format.id ||
980 			   peer_info.format.det.aud.avg_bps !=
981 				   port0_info.format.det.aud.avg_bps ||
982 			   peer_info.clock_rate != port0_info.clock_rate ||
983 			   peer_info.channel_count!=port0_info.channel_count);
984 	}
985 
986 	if (need_reopen) {
987 	    if (pjsua_var.cap_dev != PJSUA_SND_NULL_DEV) {
988 		pjmedia_snd_port_param param;
989 
990 		pjmedia_snd_port_param_default(&param);
991 		param.ec_options = pjsua_var.media_cfg.ec_options;
992 
993 		/* Create parameter based on peer info */
994 		status = create_aud_param(&param.base, pjsua_var.cap_dev,
995 					  pjsua_var.play_dev,
996 					  peer_info.clock_rate,
997 					  peer_info.channel_count,
998 					  peer_info.samples_per_frame,
999 					  peer_info.bits_per_sample);
1000 		if (status != PJ_SUCCESS) {
1001 		    pjsua_perror(THIS_FILE, "Error opening sound device",
1002 				 status);
1003 		    goto on_return;
1004 		}
1005 
1006 		/* And peer format */
1007 		if (peer_info.format.id != PJMEDIA_FORMAT_PCM) {
1008 		    param.base.flags |= PJMEDIA_AUD_DEV_CAP_EXT_FORMAT;
1009 		    param.base.ext_fmt = peer_info.format;
1010 		}
1011 
1012 		param.options = 0;
1013 		status = open_snd_dev(&param);
1014 		if (status != PJ_SUCCESS) {
1015 		    pjsua_perror(THIS_FILE, "Error opening sound device",
1016 				 status);
1017 		    goto on_return;
1018 		}
1019 	    } else {
1020 		/* Null-audio */
1021 		status = pjsua_set_snd_dev(pjsua_var.cap_dev,
1022 					   pjsua_var.play_dev);
1023 		if (status != PJ_SUCCESS) {
1024 		    pjsua_perror(THIS_FILE, "Error opening sound device",
1025 				 status);
1026 		    goto on_return;
1027 		}
1028 	    }
1029 	} else if (pjsua_var.no_snd) {
1030 	    if (!pjsua_var.snd_is_on) {
1031 		pjsua_var.snd_is_on = PJ_TRUE;
1032 	    	/* Notify app */
1033 	    	if (pjsua_var.ua_cfg.cb.on_snd_dev_operation) {
1034 	    	    (*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(1);
1035 	    	}
1036 	    }
1037 	}
1038 
1039     } else {
1040 	/* The bridge version */
1041 
1042 	/* Create sound port if none is instantiated */
1043 	if (pjsua_var.snd_port==NULL && pjsua_var.null_snd==NULL &&
1044 	    !pjsua_var.no_snd)
1045 	{
1046 	    status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
1047 	    if (status != PJ_SUCCESS) {
1048 		pjsua_perror(THIS_FILE, "Error opening sound device", status);
1049 		goto on_return;
1050 	    }
1051 	} else if (pjsua_var.no_snd && !pjsua_var.snd_is_on) {
1052 	    pjsua_var.snd_is_on = PJ_TRUE;
1053 	    /* Notify app */
1054 	    if (pjsua_var.ua_cfg.cb.on_snd_dev_operation) {
1055 		(*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(1);
1056 	    }
1057 	}
1058     }
1059 
1060 on_return:
1061     PJSUA_UNLOCK();
1062 
1063     if (status == PJ_SUCCESS) {
1064     	pjsua_conf_connect_param cc_param;
1065 
1066     	if (!prm)
1067     	    pjsua_conf_connect_param_default(&cc_param);
1068     	else
1069     	    pj_memcpy(&cc_param, prm, sizeof(cc_param));
1070 	status = pjmedia_conf_connect_port(pjsua_var.mconf, source, sink,
1071 					   (int)((cc_param.level-1) * 128));
1072     }
1073 
1074     pj_log_pop_indent();
1075     return status;
1076 }
1077 
1078 
1079 /*
1080  * Disconnect media flow from the source to destination port.
1081  */
pjsua_conf_disconnect(pjsua_conf_port_id source,pjsua_conf_port_id sink)1082 PJ_DEF(pj_status_t) pjsua_conf_disconnect( pjsua_conf_port_id source,
1083 					   pjsua_conf_port_id sink)
1084 {
1085     pj_status_t status;
1086 
1087     PJ_LOG(4,(THIS_FILE, "%s disconnect: %d -x- %d",
1088 	      (pjsua_var.is_mswitch ? "Switch" : "Conf"),
1089 	      source, sink));
1090     pj_log_push_indent();
1091 
1092     status = pjmedia_conf_disconnect_port(pjsua_var.mconf, source, sink);
1093     pjsua_check_snd_dev_idle();
1094 
1095     pj_log_pop_indent();
1096     return status;
1097 }
1098 
1099 
1100 /*
1101  * Adjust the signal level to be transmitted from the bridge to the
1102  * specified port by making it louder or quieter.
1103  */
pjsua_conf_adjust_tx_level(pjsua_conf_port_id slot,float level)1104 PJ_DEF(pj_status_t) pjsua_conf_adjust_tx_level(pjsua_conf_port_id slot,
1105 					       float level)
1106 {
1107     return pjmedia_conf_adjust_tx_level(pjsua_var.mconf, slot,
1108 					(int)((level-1) * 128));
1109 }
1110 
1111 /*
1112  * Adjust the signal level to be received from the specified port (to
1113  * the bridge) by making it louder or quieter.
1114  */
pjsua_conf_adjust_rx_level(pjsua_conf_port_id slot,float level)1115 PJ_DEF(pj_status_t) pjsua_conf_adjust_rx_level(pjsua_conf_port_id slot,
1116 					       float level)
1117 {
1118     return pjmedia_conf_adjust_rx_level(pjsua_var.mconf, slot,
1119 					(int)((level-1) * 128));
1120 }
1121 
1122 
1123 /*
1124  * Get last signal level transmitted to or received from the specified port.
1125  */
pjsua_conf_get_signal_level(pjsua_conf_port_id slot,unsigned * tx_level,unsigned * rx_level)1126 PJ_DEF(pj_status_t) pjsua_conf_get_signal_level(pjsua_conf_port_id slot,
1127 						unsigned *tx_level,
1128 						unsigned *rx_level)
1129 {
1130     return pjmedia_conf_get_signal_level(pjsua_var.mconf, slot,
1131 					 tx_level, rx_level);
1132 }
1133 
1134 /*****************************************************************************
1135  * File player.
1136  */
1137 
get_basename(const char * path,unsigned len)1138 static char* get_basename(const char *path, unsigned len)
1139 {
1140     char *p = ((char*)path) + len;
1141 
1142     if (len==0)
1143 	return p;
1144 
1145     for (--p; p!=path && *p!='/' && *p!='\\'; ) --p;
1146 
1147     return (p==path) ? p : p+1;
1148 }
1149 
1150 
1151 /*
1152  * Create a file player, and automatically connect this player to
1153  * the conference bridge.
1154  */
pjsua_player_create(const pj_str_t * filename,unsigned options,pjsua_player_id * p_id)1155 PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename,
1156 					 unsigned options,
1157 					 pjsua_player_id *p_id)
1158 {
1159     unsigned slot, file_id;
1160     char path[PJ_MAXPATH];
1161     pj_pool_t *pool = NULL;
1162     pjmedia_port *port;
1163     pj_status_t status = PJ_SUCCESS;
1164 
1165     if (pjsua_var.player_cnt >= PJ_ARRAY_SIZE(pjsua_var.player))
1166 	return PJ_ETOOMANY;
1167 
1168     PJ_LOG(4,(THIS_FILE, "Creating file player: %.*s..",
1169 	      (int)filename->slen, filename->ptr));
1170     pj_log_push_indent();
1171 
1172     PJSUA_LOCK();
1173 
1174     for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.player); ++file_id) {
1175 	if (pjsua_var.player[file_id].port == NULL)
1176 	    break;
1177     }
1178 
1179     if (file_id == PJ_ARRAY_SIZE(pjsua_var.player)) {
1180 	/* This is unexpected */
1181 	pj_assert(0);
1182 	status = PJ_EBUG;
1183 	goto on_error;
1184     }
1185 
1186     pj_memcpy(path, filename->ptr, filename->slen);
1187     path[filename->slen] = '\0';
1188 
1189     pool = pjsua_pool_create(get_basename(path, (unsigned)filename->slen), 1000,
1190 			     1000);
1191     if (!pool) {
1192 	status = PJ_ENOMEM;
1193 	goto on_error;
1194     }
1195 
1196     status = pjmedia_wav_player_port_create(
1197 				    pool, path,
1198 				    pjsua_var.mconf_cfg.samples_per_frame *
1199 				    1000 / pjsua_var.media_cfg.channel_count /
1200 				    pjsua_var.media_cfg.clock_rate,
1201 				    options, 0, &port);
1202     if (status != PJ_SUCCESS) {
1203 	pjsua_perror(THIS_FILE, "Unable to open file for playback", status);
1204 	goto on_error;
1205     }
1206 
1207     status = pjmedia_conf_add_port(pjsua_var.mconf, pool,
1208 				   port, filename, &slot);
1209     if (status != PJ_SUCCESS) {
1210 	pjmedia_port_destroy(port);
1211 	pjsua_perror(THIS_FILE, "Unable to add file to conference bridge",
1212 		     status);
1213 	goto on_error;
1214     }
1215 
1216     pjsua_var.player[file_id].type = 0;
1217     pjsua_var.player[file_id].pool = pool;
1218     pjsua_var.player[file_id].port = port;
1219     pjsua_var.player[file_id].slot = slot;
1220 
1221     if (p_id) *p_id = file_id;
1222 
1223     ++pjsua_var.player_cnt;
1224 
1225     PJSUA_UNLOCK();
1226 
1227     PJ_LOG(4,(THIS_FILE, "Player created, id=%d, slot=%d", file_id, slot));
1228 
1229     pj_log_pop_indent();
1230     return PJ_SUCCESS;
1231 
1232 on_error:
1233     PJSUA_UNLOCK();
1234     if (pool) pj_pool_release(pool);
1235     pj_log_pop_indent();
1236     return status;
1237 }
1238 
1239 
1240 /*
1241  * Create a file playlist media port, and automatically add the port
1242  * to the conference bridge.
1243  */
pjsua_playlist_create(const pj_str_t file_names[],unsigned file_count,const pj_str_t * label,unsigned options,pjsua_player_id * p_id)1244 PJ_DEF(pj_status_t) pjsua_playlist_create( const pj_str_t file_names[],
1245 					   unsigned file_count,
1246 					   const pj_str_t *label,
1247 					   unsigned options,
1248 					   pjsua_player_id *p_id)
1249 {
1250     unsigned slot, file_id, ptime;
1251     pj_pool_t *pool = NULL;
1252     pjmedia_port *port;
1253     pj_status_t status = PJ_SUCCESS;
1254 
1255     if (pjsua_var.player_cnt >= PJ_ARRAY_SIZE(pjsua_var.player))
1256 	return PJ_ETOOMANY;
1257 
1258     PJ_LOG(4,(THIS_FILE, "Creating playlist with %d file(s)..", file_count));
1259     pj_log_push_indent();
1260 
1261     PJSUA_LOCK();
1262 
1263     for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.player); ++file_id) {
1264 	if (pjsua_var.player[file_id].port == NULL)
1265 	    break;
1266     }
1267 
1268     if (file_id == PJ_ARRAY_SIZE(pjsua_var.player)) {
1269 	/* This is unexpected */
1270 	pj_assert(0);
1271 	status = PJ_EBUG;
1272 	goto on_error;
1273     }
1274 
1275 
1276     ptime = pjsua_var.mconf_cfg.samples_per_frame * 1000 /
1277 	    pjsua_var.media_cfg.clock_rate;
1278 
1279     pool = pjsua_pool_create("playlist", 1000, 1000);
1280     if (!pool) {
1281 	status = PJ_ENOMEM;
1282 	goto on_error;
1283     }
1284 
1285     status = pjmedia_wav_playlist_create(pool, label,
1286 					 file_names, file_count,
1287 					 ptime, options, 0, &port);
1288     if (status != PJ_SUCCESS) {
1289 	pjsua_perror(THIS_FILE, "Unable to create playlist", status);
1290 	goto on_error;
1291     }
1292 
1293     status = pjmedia_conf_add_port(pjsua_var.mconf, pool,
1294 				   port, &port->info.name, &slot);
1295     if (status != PJ_SUCCESS) {
1296 	pjmedia_port_destroy(port);
1297 	pjsua_perror(THIS_FILE, "Unable to add port", status);
1298 	goto on_error;
1299     }
1300 
1301     pjsua_var.player[file_id].type = 1;
1302     pjsua_var.player[file_id].pool = pool;
1303     pjsua_var.player[file_id].port = port;
1304     pjsua_var.player[file_id].slot = slot;
1305 
1306     if (p_id) *p_id = file_id;
1307 
1308     ++pjsua_var.player_cnt;
1309 
1310     PJSUA_UNLOCK();
1311 
1312     PJ_LOG(4,(THIS_FILE, "Playlist created, id=%d, slot=%d", file_id, slot));
1313 
1314     pj_log_pop_indent();
1315 
1316     return PJ_SUCCESS;
1317 
1318 on_error:
1319     PJSUA_UNLOCK();
1320     if (pool) pj_pool_release(pool);
1321     pj_log_pop_indent();
1322 
1323     return status;
1324 }
1325 
1326 
1327 /*
1328  * Get conference port ID associated with player.
1329  */
pjsua_player_get_conf_port(pjsua_player_id id)1330 PJ_DEF(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id)
1331 {
1332     PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player),PJ_EINVAL);
1333     PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL);
1334 
1335     return pjsua_var.player[id].slot;
1336 }
1337 
1338 /*
1339  * Get the media port for the player.
1340  */
pjsua_player_get_port(pjsua_player_id id,pjmedia_port ** p_port)1341 PJ_DEF(pj_status_t) pjsua_player_get_port( pjsua_player_id id,
1342 					   pjmedia_port **p_port)
1343 {
1344     PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player),PJ_EINVAL);
1345     PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL);
1346     PJ_ASSERT_RETURN(p_port != NULL, PJ_EINVAL);
1347 
1348     *p_port = pjsua_var.player[id].port;
1349 
1350     return PJ_SUCCESS;
1351 }
1352 
1353 /*
1354  * Get player info.
1355  */
pjsua_player_get_info(pjsua_player_id id,pjmedia_wav_player_info * info)1356 PJ_DEF(pj_status_t) pjsua_player_get_info(pjsua_player_id id,
1357                                           pjmedia_wav_player_info *info)
1358 {
1359     PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player),
1360                      -PJ_EINVAL);
1361     PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL);
1362     PJ_ASSERT_RETURN(pjsua_var.player[id].type == 0, PJ_EINVAL);
1363 
1364     return pjmedia_wav_player_get_info(pjsua_var.player[id].port, info);
1365 }
1366 
1367 /*
1368  * Get playback position.
1369  */
pjsua_player_get_pos(pjsua_player_id id)1370 PJ_DEF(pj_ssize_t) pjsua_player_get_pos( pjsua_player_id id )
1371 {
1372     pj_ssize_t pos_bytes;
1373     pjmedia_wav_player_info info;
1374     pj_status_t status;
1375 
1376     PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player),
1377                      -PJ_EINVAL);
1378     PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, -PJ_EINVAL);
1379     PJ_ASSERT_RETURN(pjsua_var.player[id].type == 0, -PJ_EINVAL);
1380 
1381     pos_bytes = pjmedia_wav_player_port_get_pos(pjsua_var.player[id].port);
1382     if (pos_bytes < 0)
1383 	return pos_bytes;
1384 
1385     status = pjmedia_wav_player_get_info(pjsua_var.player[id].port, &info);
1386     if (status != PJ_SUCCESS)
1387 	return -status;
1388 
1389     return pos_bytes / (info.payload_bits_per_sample / 8);
1390 }
1391 
1392 /*
1393  * Set playback position.
1394  */
pjsua_player_set_pos(pjsua_player_id id,pj_uint32_t samples)1395 PJ_DEF(pj_status_t) pjsua_player_set_pos( pjsua_player_id id,
1396 					  pj_uint32_t samples)
1397 {
1398     pjmedia_wav_player_info info;
1399     pj_uint32_t pos_bytes;
1400     pj_status_t status;
1401 
1402     PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player),PJ_EINVAL);
1403     PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL);
1404     PJ_ASSERT_RETURN(pjsua_var.player[id].type == 0, PJ_EINVAL);
1405 
1406     status = pjmedia_wav_player_get_info(pjsua_var.player[id].port, &info);
1407     if (status != PJ_SUCCESS)
1408 	return status;
1409 
1410     pos_bytes = samples * (info.payload_bits_per_sample / 8);
1411     return pjmedia_wav_player_port_set_pos(pjsua_var.player[id].port,
1412                                            pos_bytes);
1413 }
1414 
1415 
1416 /*
1417  * Close the file, remove the player from the bridge, and free
1418  * resources associated with the file player.
1419  */
pjsua_player_destroy(pjsua_player_id id)1420 PJ_DEF(pj_status_t) pjsua_player_destroy(pjsua_player_id id)
1421 {
1422     PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player),PJ_EINVAL);
1423     PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL);
1424 
1425     PJ_LOG(4,(THIS_FILE, "Destroying player %d..", id));
1426     pj_log_push_indent();
1427 
1428     PJSUA_LOCK();
1429 
1430     if (pjsua_var.player[id].port) {
1431 	pjsua_conf_remove_port(pjsua_var.player[id].slot);
1432 	pjmedia_port_destroy(pjsua_var.player[id].port);
1433 	pjsua_var.player[id].port = NULL;
1434 	pjsua_var.player[id].slot = 0xFFFF;
1435 	pj_pool_release(pjsua_var.player[id].pool);
1436 	pjsua_var.player[id].pool = NULL;
1437 	pjsua_var.player_cnt--;
1438     }
1439 
1440     PJSUA_UNLOCK();
1441     pj_log_pop_indent();
1442 
1443     return PJ_SUCCESS;
1444 }
1445 
1446 
1447 /*****************************************************************************
1448  * File recorder.
1449  */
1450 
1451 /*
1452  * Create a file recorder, and automatically connect this recorder to
1453  * the conference bridge.
1454  */
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)1455 PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename,
1456 					   unsigned enc_type,
1457 					   void *enc_param,
1458 					   pj_ssize_t max_size,
1459 					   unsigned options,
1460 					   pjsua_recorder_id *p_id)
1461 {
1462     enum Format
1463     {
1464 	FMT_UNKNOWN,
1465 	FMT_WAV,
1466 	FMT_MP3,
1467     };
1468     unsigned slot, file_id;
1469     char path[PJ_MAXPATH];
1470     pj_str_t ext;
1471     int file_format;
1472     pj_pool_t *pool = NULL;
1473     pjmedia_port *port;
1474     pj_status_t status = PJ_SUCCESS;
1475 
1476     /* Filename must present */
1477     PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL);
1478 
1479     /* Don't support max_size at present */
1480     PJ_ASSERT_RETURN(max_size == 0 || max_size == -1, PJ_EINVAL);
1481 
1482     /* Don't support encoding type at present */
1483     PJ_ASSERT_RETURN(enc_type == 0, PJ_EINVAL);
1484 
1485     PJ_LOG(4,(THIS_FILE, "Creating recorder %.*s..",
1486 	      (int)filename->slen, filename->ptr));
1487     pj_log_push_indent();
1488 
1489     if (pjsua_var.rec_cnt >= PJ_ARRAY_SIZE(pjsua_var.recorder)) {
1490 	pj_log_pop_indent();
1491 	return PJ_ETOOMANY;
1492     }
1493 
1494     /* Determine the file format */
1495     ext.ptr = filename->ptr + filename->slen - 4;
1496     ext.slen = 4;
1497 
1498     if (pj_stricmp2(&ext, ".wav") == 0)
1499 	file_format = FMT_WAV;
1500     else if (pj_stricmp2(&ext, ".mp3") == 0)
1501 	file_format = FMT_MP3;
1502     else {
1503 	PJ_LOG(1,(THIS_FILE, "pjsua_recorder_create() error: unable to "
1504 			     "determine file format for %.*s",
1505 			     (int)filename->slen, filename->ptr));
1506 	pj_log_pop_indent();
1507 	return PJ_ENOTSUP;
1508     }
1509 
1510     PJSUA_LOCK();
1511 
1512     for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.recorder); ++file_id) {
1513 	if (pjsua_var.recorder[file_id].port == NULL)
1514 	    break;
1515     }
1516 
1517     if (file_id == PJ_ARRAY_SIZE(pjsua_var.recorder)) {
1518 	/* This is unexpected */
1519 	pj_assert(0);
1520 	status = PJ_EBUG;
1521 	goto on_return;
1522     }
1523 
1524     pj_memcpy(path, filename->ptr, filename->slen);
1525     path[filename->slen] = '\0';
1526 
1527     pool = pjsua_pool_create(get_basename(path, (unsigned)filename->slen), 1000,
1528 			     1000);
1529     if (!pool) {
1530 	status = PJ_ENOMEM;
1531 	goto on_return;
1532     }
1533 
1534     if (file_format == FMT_WAV) {
1535 	status = pjmedia_wav_writer_port_create(pool, path,
1536 						pjsua_var.media_cfg.clock_rate,
1537 						pjsua_var.mconf_cfg.channel_count,
1538 						pjsua_var.mconf_cfg.samples_per_frame,
1539 						pjsua_var.mconf_cfg.bits_per_sample,
1540 						options, 0, &port);
1541     } else {
1542 	PJ_UNUSED_ARG(enc_param);
1543 	port = NULL;
1544 	status = PJ_ENOTSUP;
1545     }
1546 
1547     if (status != PJ_SUCCESS) {
1548 	pjsua_perror(THIS_FILE, "Unable to open file for recording", status);
1549 	goto on_return;
1550     }
1551 
1552     status = pjmedia_conf_add_port(pjsua_var.mconf, pool,
1553 				   port, filename, &slot);
1554     if (status != PJ_SUCCESS) {
1555 	pjmedia_port_destroy(port);
1556 	goto on_return;
1557     }
1558 
1559     pjsua_var.recorder[file_id].port = port;
1560     pjsua_var.recorder[file_id].slot = slot;
1561     pjsua_var.recorder[file_id].pool = pool;
1562 
1563     if (p_id) *p_id = file_id;
1564 
1565     ++pjsua_var.rec_cnt;
1566 
1567     PJSUA_UNLOCK();
1568 
1569     PJ_LOG(4,(THIS_FILE, "Recorder created, id=%d, slot=%d", file_id, slot));
1570 
1571     pj_log_pop_indent();
1572     return PJ_SUCCESS;
1573 
1574 on_return:
1575     PJSUA_UNLOCK();
1576     if (pool) pj_pool_release(pool);
1577     pj_log_pop_indent();
1578     return status;
1579 }
1580 
1581 
1582 /*
1583  * Get conference port associated with recorder.
1584  */
pjsua_recorder_get_conf_port(pjsua_recorder_id id)1585 PJ_DEF(pjsua_conf_port_id) pjsua_recorder_get_conf_port(pjsua_recorder_id id)
1586 {
1587     PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.recorder),
1588 		     PJ_EINVAL);
1589     PJ_ASSERT_RETURN(pjsua_var.recorder[id].port != NULL, PJ_EINVAL);
1590 
1591     return pjsua_var.recorder[id].slot;
1592 }
1593 
1594 /*
1595  * Get the media port for the recorder.
1596  */
pjsua_recorder_get_port(pjsua_recorder_id id,pjmedia_port ** p_port)1597 PJ_DEF(pj_status_t) pjsua_recorder_get_port( pjsua_recorder_id id,
1598 					     pjmedia_port **p_port)
1599 {
1600     PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.recorder),
1601 		     PJ_EINVAL);
1602     PJ_ASSERT_RETURN(pjsua_var.recorder[id].port != NULL, PJ_EINVAL);
1603     PJ_ASSERT_RETURN(p_port != NULL, PJ_EINVAL);
1604 
1605     *p_port = pjsua_var.recorder[id].port;
1606     return PJ_SUCCESS;
1607 }
1608 
1609 /*
1610  * Destroy recorder (this will complete recording).
1611  */
pjsua_recorder_destroy(pjsua_recorder_id id)1612 PJ_DEF(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id)
1613 {
1614     PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.recorder),
1615 		     PJ_EINVAL);
1616     PJ_ASSERT_RETURN(pjsua_var.recorder[id].port != NULL, PJ_EINVAL);
1617 
1618     PJ_LOG(4,(THIS_FILE, "Destroying recorder %d..", id));
1619     pj_log_push_indent();
1620 
1621     PJSUA_LOCK();
1622 
1623     if (pjsua_var.recorder[id].port) {
1624 	pjsua_conf_remove_port(pjsua_var.recorder[id].slot);
1625 	pjmedia_port_destroy(pjsua_var.recorder[id].port);
1626 	pjsua_var.recorder[id].port = NULL;
1627 	pjsua_var.recorder[id].slot = 0xFFFF;
1628 	pj_pool_release(pjsua_var.recorder[id].pool);
1629 	pjsua_var.recorder[id].pool = NULL;
1630 	pjsua_var.rec_cnt--;
1631     }
1632 
1633     PJSUA_UNLOCK();
1634     pj_log_pop_indent();
1635 
1636     return PJ_SUCCESS;
1637 }
1638 
1639 
1640 /*****************************************************************************
1641  * Sound devices.
1642  */
1643 
1644 /*
1645  * Enum sound devices.
1646  */
1647 
pjsua_enum_aud_devs(pjmedia_aud_dev_info info[],unsigned * count)1648 PJ_DEF(pj_status_t) pjsua_enum_aud_devs( pjmedia_aud_dev_info info[],
1649 					 unsigned *count)
1650 {
1651     unsigned i, dev_count;
1652 
1653     dev_count = pjmedia_aud_dev_count();
1654 
1655     if (dev_count > *count) dev_count = *count;
1656 
1657     for (i=0; i<dev_count; ++i) {
1658 	pj_status_t status;
1659 
1660 	status = pjmedia_aud_dev_get_info(i, &info[i]);
1661 	if (status != PJ_SUCCESS)
1662 	    return status;
1663     }
1664 
1665     *count = dev_count;
1666 
1667     return PJ_SUCCESS;
1668 }
1669 
1670 
pjsua_enum_snd_devs(pjmedia_snd_dev_info info[],unsigned * count)1671 PJ_DEF(pj_status_t) pjsua_enum_snd_devs( pjmedia_snd_dev_info info[],
1672 					 unsigned *count)
1673 {
1674     unsigned i, dev_count;
1675 
1676     dev_count = pjmedia_aud_dev_count();
1677 
1678     if (dev_count > *count) dev_count = *count;
1679     pj_bzero(info, dev_count * sizeof(pjmedia_snd_dev_info));
1680 
1681     for (i=0; i<dev_count; ++i) {
1682 	pjmedia_aud_dev_info ai;
1683 	pj_status_t status;
1684 
1685 	status = pjmedia_aud_dev_get_info(i, &ai);
1686 	if (status != PJ_SUCCESS)
1687 	    return status;
1688 
1689 	strncpy(info[i].name, ai.name, sizeof(info[i].name));
1690 	info[i].name[sizeof(info[i].name)-1] = '\0';
1691 	info[i].input_count = ai.input_count;
1692 	info[i].output_count = ai.output_count;
1693 	info[i].default_samples_per_sec = ai.default_samples_per_sec;
1694     }
1695 
1696     *count = dev_count;
1697 
1698     return PJ_SUCCESS;
1699 }
1700 
1701 /* Create audio device parameter to open the device */
create_aud_param(pjmedia_aud_param * param,pjmedia_aud_dev_index capture_dev,pjmedia_aud_dev_index playback_dev,unsigned clock_rate,unsigned channel_count,unsigned samples_per_frame,unsigned bits_per_sample)1702 static pj_status_t create_aud_param(pjmedia_aud_param *param,
1703 				    pjmedia_aud_dev_index capture_dev,
1704 				    pjmedia_aud_dev_index playback_dev,
1705 				    unsigned clock_rate,
1706 				    unsigned channel_count,
1707 				    unsigned samples_per_frame,
1708 				    unsigned bits_per_sample)
1709 {
1710     pj_status_t status;
1711     pj_bool_t speaker_only = (pjsua_var.snd_mode & PJSUA_SND_DEV_SPEAKER_ONLY);
1712 
1713     /* Normalize device ID with new convention about default device ID */
1714     if (playback_dev == PJMEDIA_AUD_DEFAULT_CAPTURE_DEV)
1715 	playback_dev = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
1716 
1717     /* Create default parameters for the device */
1718     status = pjmedia_aud_dev_default_param((speaker_only? playback_dev:
1719     					    capture_dev), param);
1720     if (status != PJ_SUCCESS) {
1721 	pjsua_perror(THIS_FILE, "Error retrieving default audio "
1722 				"device parameters", status);
1723 	return status;
1724     }
1725     param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
1726     param->rec_id = capture_dev;
1727     param->play_id = playback_dev;
1728     param->clock_rate = clock_rate;
1729     param->channel_count = channel_count;
1730     param->samples_per_frame = samples_per_frame;
1731     param->bits_per_sample = bits_per_sample;
1732 
1733     /* Update the setting with user preference */
1734 #define update_param(cap, field)    \
1735 	if (pjsua_var.aud_param.flags & cap) { \
1736 	    param->flags |= cap; \
1737 	    param->field = pjsua_var.aud_param.field; \
1738 	}
1739     update_param( PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING, input_vol);
1740     update_param( PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, output_vol);
1741     update_param( PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE, input_route);
1742     update_param( PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, output_route);
1743 #undef update_param
1744 
1745     /* Latency settings */
1746     param->flags |= (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
1747 		     PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY);
1748     param->input_latency_ms = pjsua_var.media_cfg.snd_rec_latency;
1749     param->output_latency_ms = pjsua_var.media_cfg.snd_play_latency;
1750 
1751     /* EC settings */
1752     if (pjsua_var.media_cfg.ec_tail_len) {
1753 	param->flags |= (PJMEDIA_AUD_DEV_CAP_EC | PJMEDIA_AUD_DEV_CAP_EC_TAIL);
1754 	param->ec_enabled = PJ_TRUE;
1755 	param->ec_tail_ms = pjsua_var.media_cfg.ec_tail_len;
1756     } else {
1757 	param->flags &= ~(PJMEDIA_AUD_DEV_CAP_EC|PJMEDIA_AUD_DEV_CAP_EC_TAIL);
1758     }
1759 
1760     /* VAD settings */
1761     if (pjsua_var.media_cfg.no_vad) {
1762 	param->flags &= ~PJMEDIA_AUD_DEV_CAP_VAD;
1763     } else {
1764 	param->flags |= PJMEDIA_AUD_DEV_CAP_VAD;
1765 	param->vad_enabled = PJ_TRUE;
1766     }
1767 
1768     return PJ_SUCCESS;
1769 }
1770 
1771 /* Internal: the first time the audio device is opened (during app
1772  *   startup), retrieve the audio settings such as volume level
1773  *   so that aud_get_settings() will work.
1774  */
update_initial_aud_param()1775 static pj_status_t update_initial_aud_param()
1776 {
1777     pjmedia_aud_stream *strm;
1778     pjmedia_aud_param param;
1779     pj_status_t status;
1780 
1781     PJ_ASSERT_RETURN(pjsua_var.snd_port != NULL, PJ_EBUG);
1782 
1783     strm = pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port);
1784 
1785     status = pjmedia_aud_stream_get_param(strm, &param);
1786     if (status != PJ_SUCCESS) {
1787 	pjsua_perror(THIS_FILE, "Error audio stream "
1788 				"device parameters", status);
1789 	return status;
1790     }
1791 
1792 #define update_saved_param(cap, field)  \
1793 	if (param.flags & cap) { \
1794 	    pjsua_var.aud_param.flags |= cap; \
1795 	    pjsua_var.aud_param.field = param.field; \
1796 	}
1797 
1798     update_saved_param(PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING, input_vol);
1799     update_saved_param(PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, output_vol);
1800     update_saved_param(PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE, input_route);
1801     update_saved_param(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, output_route);
1802 #undef update_saved_param
1803 
1804     return PJ_SUCCESS;
1805 }
1806 
1807 /* Get format name */
get_fmt_name(pj_uint32_t id)1808 static const char *get_fmt_name(pj_uint32_t id)
1809 {
1810     static char name[8];
1811 
1812     if (id == PJMEDIA_FORMAT_L16)
1813 	return "PCM";
1814     pj_memcpy(name, &id, 4);
1815     name[4] = '\0';
1816     return name;
1817 }
1818 
on_aud_prev_play_frame(void * user_data,pjmedia_frame * frame)1819 static pj_status_t on_aud_prev_play_frame(void *user_data, pjmedia_frame *frame)
1820 {
1821     PJ_UNUSED_ARG(user_data);
1822     (*pjsua_var.media_cfg.on_aud_prev_play_frame)(frame);
1823     return PJ_SUCCESS;
1824 }
1825 
on_aud_prev_rec_frame(void * user_data,pjmedia_frame * frame)1826 static pj_status_t on_aud_prev_rec_frame(void *user_data, pjmedia_frame *frame)
1827 {
1828     PJ_UNUSED_ARG(user_data);
1829     (*pjsua_var.media_cfg.on_aud_prev_rec_frame)(frame);
1830     return PJ_SUCCESS;
1831 }
1832 
1833 /* Open sound device with the setting. */
open_snd_dev(pjmedia_snd_port_param * param)1834 static pj_status_t open_snd_dev(pjmedia_snd_port_param *param)
1835 {
1836     pjmedia_port *conf_port;
1837     pj_status_t status;
1838     pj_bool_t speaker_only = (pjsua_var.snd_mode & PJSUA_SND_DEV_SPEAKER_ONLY);
1839 
1840     PJ_ASSERT_RETURN(param, PJ_EINVAL);
1841 
1842     /* Check if NULL sound device is used */
1843     if (PJSUA_SND_NULL_DEV==param->base.rec_id ||
1844 	PJSUA_SND_NULL_DEV==param->base.play_id)
1845     {
1846 	return pjsua_set_null_snd_dev();
1847     }
1848 
1849     /* Close existing sound port */
1850     close_snd_dev();
1851 
1852     /* Save the device IDs */
1853     pjsua_var.cap_dev = param->base.rec_id;
1854     pjsua_var.play_dev = param->base.play_id;
1855 
1856     /* Notify app */
1857     if (pjsua_var.ua_cfg.cb.on_snd_dev_operation) {
1858 	(*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(1);
1859     }
1860 
1861     /* Create memory pool for sound device. */
1862     pjsua_var.snd_pool = pjsua_pool_create("pjsua_snd", 4000, 4000);
1863     PJ_ASSERT_RETURN(pjsua_var.snd_pool, PJ_ENOMEM);
1864 
1865     /* Setup preview callbacks, if configured */
1866     if (pjsua_var.media_cfg.on_aud_prev_play_frame)
1867 	param->on_play_frame = &on_aud_prev_play_frame;
1868     if (pjsua_var.media_cfg.on_aud_prev_rec_frame)
1869 	param->on_rec_frame = &on_aud_prev_rec_frame;
1870 
1871     PJ_LOG(4,(THIS_FILE, "Opening sound device (%s) %s@%d/%d/%dms",
1872 	      speaker_only?"speaker only":"speaker + mic",
1873 	      get_fmt_name(param->base.ext_fmt.id),
1874 	      param->base.clock_rate, param->base.channel_count,
1875 	      param->base.samples_per_frame / param->base.channel_count *
1876 	      1000 / param->base.clock_rate));
1877     pj_log_push_indent();
1878 
1879     if (speaker_only) {
1880 	pjmedia_snd_port_param cp_param;
1881 	int dev_id = param->base.play_id;
1882 
1883 	/* Normalize dev_id */
1884 	if (dev_id < 0)
1885 	    dev_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
1886 
1887 	pjmedia_snd_port_param_default(&cp_param);
1888 	pj_memcpy(&cp_param.base, &param->base, sizeof(cp_param.base));
1889 	cp_param.base.dir = PJMEDIA_DIR_PLAYBACK;
1890 	cp_param.base.play_id = dev_id;
1891 
1892 	status = pjmedia_snd_port_create2(pjsua_var.snd_pool, &cp_param,
1893 					  &pjsua_var.snd_port);
1894 
1895     } else {
1896 	status = pjmedia_snd_port_create2(pjsua_var.snd_pool,
1897 					  param, &pjsua_var.snd_port);
1898     }
1899 
1900     if (status != PJ_SUCCESS)
1901 	goto on_error;
1902 
1903     /* Get the port0 of the conference bridge. */
1904     conf_port = pjmedia_conf_get_master_port(pjsua_var.mconf);
1905     pj_assert(conf_port != NULL);
1906 
1907     /* For conference bridge, resample if necessary if the bridge's
1908      * clock rate is different than the sound device's clock rate.
1909      */
1910     if (!pjsua_var.is_mswitch &&
1911 	param->base.ext_fmt.id == PJMEDIA_FORMAT_PCM &&
1912 	PJMEDIA_PIA_SRATE(&conf_port->info) != param->base.clock_rate)
1913     {
1914 	pjmedia_port *resample_port;
1915 	unsigned resample_opt = 0;
1916 
1917 	if (pjsua_var.media_cfg.quality >= 3 &&
1918 	    pjsua_var.media_cfg.quality <= 4)
1919 	{
1920 	    resample_opt |= PJMEDIA_RESAMPLE_USE_SMALL_FILTER;
1921 	}
1922 	else if (pjsua_var.media_cfg.quality < 3) {
1923 	    resample_opt |= PJMEDIA_RESAMPLE_USE_LINEAR;
1924 	}
1925 
1926 	status = pjmedia_resample_port_create(pjsua_var.snd_pool,
1927 					      conf_port,
1928 					      param->base.clock_rate,
1929 					      resample_opt,
1930 					      &resample_port);
1931 	if (status != PJ_SUCCESS) {
1932 	    char errmsg[PJ_ERR_MSG_SIZE];
1933 	    pj_strerror(status, errmsg, sizeof(errmsg));
1934 	    PJ_LOG(4, (THIS_FILE,
1935 		       "Error creating resample port: %s",
1936 		       errmsg));
1937 	    close_snd_dev();
1938 	    goto on_error;
1939 	}
1940 
1941 	conf_port = resample_port;
1942     }
1943 
1944     /* Otherwise for audio switchboard, the switch's port0 setting is
1945      * derived from the sound device setting, so update the setting.
1946      */
1947     if (pjsua_var.is_mswitch) {
1948 	if (param->base.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) {
1949 	    conf_port->info.fmt = param->base.ext_fmt;
1950 	} else {
1951 	    unsigned bps, ptime_usec;
1952 	    bps = param->base.clock_rate * param->base.bits_per_sample;
1953 	    ptime_usec = param->base.samples_per_frame /
1954 			 param->base.channel_count * 1000000 /
1955 			 param->base.clock_rate;
1956 	    pjmedia_format_init_audio(&conf_port->info.fmt,
1957 				      PJMEDIA_FORMAT_PCM,
1958 				      param->base.clock_rate,
1959 				      param->base.channel_count,
1960 				      param->base.bits_per_sample,
1961 				      ptime_usec,
1962 				      bps, bps);
1963 	}
1964     }
1965 
1966 
1967     /* Connect sound port to the bridge */
1968     status = pjmedia_snd_port_connect(pjsua_var.snd_port,
1969 				      conf_port );
1970     if (status != PJ_SUCCESS) {
1971 	pjsua_perror(THIS_FILE, "Unable to connect conference port to "
1972 			        "sound device", status);
1973 	pjmedia_snd_port_destroy(pjsua_var.snd_port);
1974 	pjsua_var.snd_port = NULL;
1975 	goto on_error;
1976     }
1977 
1978     /* Update sound device name. */
1979     if (!speaker_only) {
1980 	pjmedia_aud_dev_info rec_info;
1981 	pjmedia_aud_stream *strm;
1982 	pjmedia_aud_param si;
1983         pj_str_t tmp;
1984 
1985 	strm = pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port);
1986 	status = pjmedia_aud_stream_get_param(strm, &si);
1987 	if (status == PJ_SUCCESS)
1988 	    status = pjmedia_aud_dev_get_info(si.rec_id, &rec_info);
1989 
1990 	if (status==PJ_SUCCESS) {
1991 	    if (param->base.clock_rate != pjsua_var.media_cfg.clock_rate) {
1992 		char tmp_buf[128];
1993 		int tmp_buf_len;
1994 
1995 		tmp_buf_len = pj_ansi_snprintf(tmp_buf, sizeof(tmp_buf),
1996 					       "%s (%dKHz)",
1997 					       rec_info.name,
1998 					       param->base.clock_rate/1000);
1999 		if (tmp_buf_len < 1 || tmp_buf_len >= (int)sizeof(tmp_buf))
2000 		    tmp_buf_len = sizeof(tmp_buf) - 1;
2001 		pj_strset(&tmp, tmp_buf, tmp_buf_len);
2002 		pjmedia_conf_set_port0_name(pjsua_var.mconf, &tmp);
2003 	    } else {
2004 		pjmedia_conf_set_port0_name(pjsua_var.mconf,
2005 					    pj_cstr(&tmp, rec_info.name));
2006 	    }
2007 	}
2008 
2009 	/* Any error is not major, let it through */
2010 	status = PJ_SUCCESS;
2011     }
2012 
2013     /* If this is the first time the audio device is open, retrieve some
2014      * settings from the device (such as volume settings) so that the
2015      * pjsua_snd_get_setting() work.
2016      */
2017     if (pjsua_var.aud_open_cnt == 0) {
2018 	update_initial_aud_param();
2019 	++pjsua_var.aud_open_cnt;
2020     }
2021 
2022     pjsua_var.snd_is_on = PJ_TRUE;
2023 
2024     /* Subscribe to audio device events */
2025     pjmedia_event_subscribe(NULL, &on_media_event, NULL,
2026 		    pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port));
2027 
2028     pj_log_pop_indent();
2029     return PJ_SUCCESS;
2030 
2031 on_error:
2032     pj_log_pop_indent();
2033     return status;
2034 }
2035 
2036 
2037 /* Close existing sound device */
close_snd_dev(void)2038 static void close_snd_dev(void)
2039 {
2040     pj_log_push_indent();
2041 
2042     /* Notify app */
2043     if (pjsua_var.snd_is_on && pjsua_var.ua_cfg.cb.on_snd_dev_operation) {
2044 	(*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(0);
2045     }
2046 
2047     /* Close sound device */
2048     if (pjsua_var.snd_port) {
2049 	pjmedia_aud_dev_info cap_info, play_info;
2050 	pjmedia_aud_stream *strm;
2051 	pjmedia_aud_param param;
2052 
2053 	strm = pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port);
2054 	pjmedia_aud_stream_get_param(strm, &param);
2055 
2056 	if (param.rec_id == PJSUA_SND_NO_DEV ||
2057 	    pjmedia_aud_dev_get_info(param.rec_id, &cap_info) != PJ_SUCCESS)
2058 	{
2059 	    cap_info.name[0] = '\0';
2060 	}
2061 	if (pjmedia_aud_dev_get_info(param.play_id, &play_info) != PJ_SUCCESS)
2062 	    play_info.name[0] = '\0';
2063 
2064 	PJ_LOG(4,(THIS_FILE, "Closing %s sound playback device and "
2065 			     "%s sound capture device",
2066 			     play_info.name, cap_info.name));
2067 
2068 	/* Unsubscribe from audio device events */
2069 	pjmedia_event_unsubscribe(NULL, &on_media_event, NULL, strm);
2070 
2071 	pjmedia_snd_port_disconnect(pjsua_var.snd_port);
2072 	pjmedia_snd_port_destroy(pjsua_var.snd_port);
2073 	pjsua_var.snd_port = NULL;
2074     }
2075 
2076     /* Close null sound device */
2077     if (pjsua_var.null_snd) {
2078 	PJ_LOG(4,(THIS_FILE, "Closing null sound device.."));
2079 	pjmedia_master_port_destroy(pjsua_var.null_snd, PJ_FALSE);
2080 	pjsua_var.null_snd = NULL;
2081     }
2082 
2083     if (pjsua_var.snd_pool)
2084 	pj_pool_release(pjsua_var.snd_pool);
2085 
2086     pjsua_var.snd_pool = NULL;
2087     pjsua_var.snd_is_on = PJ_FALSE;
2088 
2089     pj_log_pop_indent();
2090 }
2091 
2092 
pjsua_set_snd_dev(int capture_dev,int playback_dev)2093 PJ_DEF(pj_status_t) pjsua_set_snd_dev(int capture_dev,
2094 				      int playback_dev)
2095 {
2096     pjsua_snd_dev_param param;
2097 
2098     pjsua_snd_dev_param_default(&param);
2099 
2100     param.capture_dev = capture_dev;
2101     param.playback_dev = playback_dev;
2102     /* Always open the sound device. */
2103     param.mode = 0;
2104 
2105     return pjsua_set_snd_dev2(&param);
2106 }
2107 
2108 /*
2109  * Select or change sound device. Application may call this function at
2110  * any time to replace current sound device.
2111  */
pjsua_set_snd_dev2(pjsua_snd_dev_param * snd_param)2112 PJ_DEF(pj_status_t) pjsua_set_snd_dev2(pjsua_snd_dev_param *snd_param)
2113 {
2114     unsigned alt_cr_cnt = 1;
2115     unsigned alt_cr[] = {0, 44100, 48000, 32000, 16000, 8000};
2116     unsigned i;
2117     pj_status_t status = -1;
2118     unsigned orig_snd_dev_mode = pjsua_var.snd_mode;
2119     pj_bool_t no_change = (pjsua_var.snd_is_on || (!pjsua_var.snd_is_on &&
2120 			   (snd_param->mode &
2121 			    PJSUA_SND_DEV_NO_IMMEDIATE_OPEN)));
2122 
2123     PJ_LOG(4,(THIS_FILE, "Set sound device: capture=%d, playback=%d",
2124 	      snd_param->capture_dev, snd_param->playback_dev));
2125     pj_log_push_indent();
2126 
2127     PJSUA_LOCK();
2128 
2129     if (pjsua_var.cap_dev == snd_param->capture_dev &&
2130 	pjsua_var.play_dev == snd_param->playback_dev &&
2131 	pjsua_var.snd_mode == snd_param->mode &&
2132 	!pjsua_var.no_snd && no_change)
2133     {
2134 	PJ_LOG(4, (THIS_FILE, "No changes in capture and playback devices"));
2135         PJSUA_UNLOCK();
2136         pj_log_pop_indent();
2137 	return PJ_SUCCESS;
2138     }
2139 
2140     /* Null-sound */
2141     if (snd_param->capture_dev == PJSUA_SND_NULL_DEV &&
2142 	snd_param->playback_dev == PJSUA_SND_NULL_DEV)
2143     {
2144 	PJSUA_UNLOCK();
2145 	status = pjsua_set_null_snd_dev();
2146 	pj_log_pop_indent();
2147 	return status;
2148     }
2149 
2150     pjsua_var.snd_mode = snd_param->mode;
2151 
2152     if (!pjsua_var.no_snd && !pjsua_var.snd_is_on &&
2153 	(snd_param->mode & PJSUA_SND_DEV_NO_IMMEDIATE_OPEN))
2154     {
2155 	pjsua_var.cap_dev = snd_param->capture_dev;
2156 	pjsua_var.play_dev = snd_param->playback_dev;
2157 
2158 	PJSUA_UNLOCK();
2159 	pj_log_pop_indent();
2160 	return PJ_SUCCESS;
2161     }
2162 
2163     /* Set default clock rate */
2164     alt_cr[0] = pjsua_var.media_cfg.snd_clock_rate;
2165     if (alt_cr[0] == 0)
2166 	alt_cr[0] = pjsua_var.media_cfg.clock_rate;
2167 
2168     /* Allow retrying of different clock rate if we're using conference
2169      * bridge (meaning audio format is always PCM), otherwise lock on
2170      * to one clock rate.
2171      */
2172     if (pjsua_var.is_mswitch) {
2173 	alt_cr_cnt = 1;
2174     } else {
2175 	alt_cr_cnt = PJ_ARRAY_SIZE(alt_cr);
2176     }
2177 
2178     /* Attempts to open the sound device with different clock rates */
2179     for (i=0; i<alt_cr_cnt; ++i) {
2180 	pjmedia_snd_port_param param;
2181 	unsigned samples_per_frame;
2182 
2183 	/* Create the default audio param */
2184 	samples_per_frame = alt_cr[i] *
2185 			    pjsua_var.media_cfg.audio_frame_ptime *
2186 			    pjsua_var.media_cfg.channel_count / 1000;
2187 	pjmedia_snd_port_param_default(&param);
2188 	param.ec_options = pjsua_var.media_cfg.ec_options;
2189 	status = create_aud_param(&param.base, snd_param->capture_dev,
2190 				  snd_param->playback_dev,
2191 				  alt_cr[i], pjsua_var.media_cfg.channel_count,
2192 				  samples_per_frame, 16);
2193 	if (status != PJ_SUCCESS)
2194 	    goto on_error;
2195 
2196 	/* Open! */
2197 	param.options = 0;
2198 	status = open_snd_dev(&param);
2199 	if (status == PJ_SUCCESS)
2200 	    break;
2201     }
2202 
2203     if (status != PJ_SUCCESS) {
2204 	pjsua_perror(THIS_FILE, "Unable to open sound device", status);
2205 	goto on_error;
2206     }
2207 
2208     pjsua_var.no_snd = PJ_FALSE;
2209     pjsua_var.snd_is_on = PJ_TRUE;
2210 
2211     PJSUA_UNLOCK();
2212     pj_log_pop_indent();
2213     return PJ_SUCCESS;
2214 
2215 on_error:
2216     pjsua_var.snd_mode = orig_snd_dev_mode;
2217     PJSUA_UNLOCK();
2218     pj_log_pop_indent();
2219     return status;
2220 }
2221 
2222 
2223 /*
2224  * Get currently active sound devices. If sound devices has not been created
2225  * (for example when pjsua_start() is not called), it is possible that
2226  * the function returns PJ_SUCCESS with -1 as device IDs.
2227  */
pjsua_get_snd_dev(int * capture_dev,int * playback_dev)2228 PJ_DEF(pj_status_t) pjsua_get_snd_dev(int *capture_dev,
2229 				      int *playback_dev)
2230 {
2231     PJSUA_LOCK();
2232 
2233     if (capture_dev) {
2234 	*capture_dev = pjsua_var.cap_dev;
2235     }
2236     if (playback_dev) {
2237 	*playback_dev = pjsua_var.play_dev;
2238     }
2239 
2240     PJSUA_UNLOCK();
2241     return PJ_SUCCESS;
2242 }
2243 
2244 
2245 /*
2246  * Use null sound device.
2247  */
pjsua_set_null_snd_dev(void)2248 PJ_DEF(pj_status_t) pjsua_set_null_snd_dev(void)
2249 {
2250     pjmedia_port *conf_port;
2251     pj_status_t status;
2252 
2253     PJ_LOG(4,(THIS_FILE, "Setting null sound device.."));
2254     pj_log_push_indent();
2255 
2256     PJSUA_LOCK();
2257 
2258     /* Close existing sound device */
2259     close_snd_dev();
2260 
2261     pjsua_var.cap_dev = PJSUA_SND_NULL_DEV;
2262     pjsua_var.play_dev = PJSUA_SND_NULL_DEV;
2263 
2264     /* Notify app */
2265     if (pjsua_var.ua_cfg.cb.on_snd_dev_operation) {
2266 	(*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(1);
2267     }
2268 
2269     /* Create memory pool for sound device. */
2270     pjsua_var.snd_pool = pjsua_pool_create("pjsua_snd", 4000, 4000);
2271     PJ_ASSERT_RETURN(pjsua_var.snd_pool, PJ_ENOMEM);
2272 
2273     PJ_LOG(4,(THIS_FILE, "Opening null sound device.."));
2274 
2275     /* Get the port0 of the conference bridge. */
2276     conf_port = pjmedia_conf_get_master_port(pjsua_var.mconf);
2277     pj_assert(conf_port != NULL);
2278 
2279     /* Create master port, connecting port0 of the conference bridge to
2280      * a null port.
2281      */
2282     status = pjmedia_master_port_create(pjsua_var.snd_pool, pjsua_var.null_port,
2283 					conf_port, 0, &pjsua_var.null_snd);
2284     if (status != PJ_SUCCESS) {
2285 	pjsua_perror(THIS_FILE, "Unable to create null sound device",
2286 		     status);
2287 	PJSUA_UNLOCK();
2288 	pj_log_pop_indent();
2289 	return status;
2290     }
2291 
2292     /* Start the master port */
2293     status = pjmedia_master_port_start(pjsua_var.null_snd);
2294     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
2295 
2296     pjsua_var.no_snd = PJ_FALSE;
2297     pjsua_var.snd_is_on = PJ_TRUE;
2298 
2299     PJSUA_UNLOCK();
2300     pj_log_pop_indent();
2301     return PJ_SUCCESS;
2302 }
2303 
2304 
2305 
2306 /*
2307  * Use no device!
2308  */
pjsua_set_no_snd_dev(void)2309 PJ_DEF(pjmedia_port*) pjsua_set_no_snd_dev(void)
2310 {
2311     PJSUA_LOCK();
2312 
2313     /* Close existing sound device */
2314     close_snd_dev();
2315     pjsua_var.no_snd = PJ_TRUE;
2316     pjsua_var.cap_dev = PJSUA_SND_NO_DEV;
2317     pjsua_var.play_dev = PJSUA_SND_NO_DEV;
2318 
2319     PJSUA_UNLOCK();
2320 
2321     return pjmedia_conf_get_master_port(pjsua_var.mconf);
2322 }
2323 
2324 
2325 /*
2326  * Configure the AEC settings of the sound port.
2327  */
pjsua_set_ec(unsigned tail_ms,unsigned options)2328 PJ_DEF(pj_status_t) pjsua_set_ec(unsigned tail_ms, unsigned options)
2329 {
2330     pj_status_t status = PJ_SUCCESS;
2331 
2332     PJSUA_LOCK();
2333 
2334     pjsua_var.media_cfg.ec_tail_len = tail_ms;
2335     pjsua_var.media_cfg.ec_options = options;
2336 
2337     if (pjsua_var.snd_port)
2338 	status = pjmedia_snd_port_set_ec(pjsua_var.snd_port, pjsua_var.pool,
2339 					 tail_ms, options);
2340 
2341     PJSUA_UNLOCK();
2342     return status;
2343 }
2344 
2345 
2346 /*
2347  * Get current AEC tail length.
2348  */
pjsua_get_ec_tail(unsigned * p_tail_ms)2349 PJ_DEF(pj_status_t) pjsua_get_ec_tail(unsigned *p_tail_ms)
2350 {
2351     *p_tail_ms = pjsua_var.media_cfg.ec_tail_len;
2352     return PJ_SUCCESS;
2353 }
2354 
2355 
2356 /*
2357  * Get echo canceller statistics.
2358  */
pjsua_get_ec_stat(pjmedia_echo_stat * p_stat)2359 PJ_DEF(pj_status_t) pjsua_get_ec_stat(pjmedia_echo_stat *p_stat)
2360 {
2361     if (pjsua_var.snd_port) {
2362     	return pjmedia_snd_port_get_ec_stat(pjsua_var.snd_port, p_stat);
2363     } else {
2364     	return PJ_ENOTFOUND;
2365     }
2366 }
2367 
2368 
2369 /*
2370  * Check whether the sound device is currently active.
2371  */
pjsua_snd_is_active(void)2372 PJ_DEF(pj_bool_t) pjsua_snd_is_active(void)
2373 {
2374     return pjsua_var.snd_port != NULL;
2375 }
2376 
2377 
2378 /*
2379  * Configure sound device setting to the sound device being used.
2380  */
pjsua_snd_set_setting(pjmedia_aud_dev_cap cap,const void * pval,pj_bool_t keep)2381 PJ_DEF(pj_status_t) pjsua_snd_set_setting( pjmedia_aud_dev_cap cap,
2382 					   const void *pval,
2383 					   pj_bool_t keep)
2384 {
2385     pj_status_t status;
2386 
2387     /* Check if we are allowed to set the cap */
2388     if ((cap & pjsua_var.aud_svmask) == 0) {
2389 	return PJMEDIA_EAUD_INVCAP;
2390     }
2391 
2392     PJSUA_LOCK();
2393 
2394     /* If sound is active, set it immediately */
2395     if (pjsua_snd_is_active()) {
2396 	pjmedia_aud_stream *strm;
2397 
2398 	strm = pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port);
2399 	status = pjmedia_aud_stream_set_cap(strm, cap, pval);
2400     } else {
2401 	status = PJ_SUCCESS;
2402     }
2403 
2404     if (status != PJ_SUCCESS) {
2405 	PJSUA_UNLOCK();
2406 	return status;
2407     }
2408 
2409     /* Save in internal param for later device open */
2410     if (keep) {
2411 	status = pjmedia_aud_param_set_cap(&pjsua_var.aud_param,
2412 					   cap, pval);
2413     }
2414 
2415     PJSUA_UNLOCK();
2416     return status;
2417 }
2418 
2419 /*
2420  * Retrieve a sound device setting.
2421  */
pjsua_snd_get_setting(pjmedia_aud_dev_cap cap,void * pval)2422 PJ_DEF(pj_status_t) pjsua_snd_get_setting( pjmedia_aud_dev_cap cap,
2423 					   void *pval)
2424 {
2425     pj_status_t status;
2426 
2427     PJSUA_LOCK();
2428 
2429     /* If sound device has never been opened before, open it to
2430      * retrieve the initial setting from the device (e.g. audio
2431      * volume)
2432      */
2433     if (pjsua_var.aud_open_cnt==0) {
2434 	PJ_LOG(4,(THIS_FILE, "Opening sound device to get initial settings"));
2435 	pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
2436 	close_snd_dev();
2437     }
2438 
2439     if (pjsua_snd_is_active()) {
2440 	/* Sound is active, retrieve from device directly */
2441 	pjmedia_aud_stream *strm;
2442 
2443 	strm = pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port);
2444 	status = pjmedia_aud_stream_get_cap(strm, cap, pval);
2445     } else {
2446 	/* Otherwise retrieve from internal param */
2447 	status = pjmedia_aud_param_get_cap(&pjsua_var.aud_param,
2448 					   cap, pval);
2449     }
2450 
2451     PJSUA_UNLOCK();
2452     return status;
2453 }
2454 
2455 
2456 /*
2457  * Extra sound device
2458  */
2459 struct pjsua_ext_snd_dev
2460 {
2461     pj_pool_t		*pool;
2462     pjmedia_port	*splitcomb;
2463     pjmedia_port	*rev_port;
2464     pjmedia_snd_port	*snd_port;
2465     pjsua_conf_port_id	 port_id;
2466 };
2467 
2468 
2469 /*
2470  * Create an extra sound device and register it to conference bridge.
2471  */
pjsua_ext_snd_dev_create(pjmedia_snd_port_param * param,pjsua_ext_snd_dev ** p_snd)2472 PJ_DEF(pj_status_t) pjsua_ext_snd_dev_create( pjmedia_snd_port_param *param,
2473 					      pjsua_ext_snd_dev **p_snd)
2474 {
2475     pjsua_ext_snd_dev *snd = NULL;
2476     pj_pool_t *pool;
2477     pj_status_t status;
2478 
2479     PJ_ASSERT_RETURN(param && p_snd, PJ_EINVAL);
2480     PJ_ASSERT_RETURN(param->base.channel_count == 1, PJMEDIA_ENCCHANNEL);
2481 
2482     pool = pjsua_pool_create("extsnd%p", 512, 512);
2483     if (!pool)
2484 	return PJ_ENOMEM;
2485 
2486     snd = PJ_POOL_ZALLOC_T(pool, pjsua_ext_snd_dev);
2487     if (!snd) {
2488 	pj_pool_release(pool);
2489 	return PJ_ENOMEM;
2490     }
2491 
2492     snd->pool = pool;
2493     snd->port_id = PJSUA_INVALID_ID;
2494 
2495     /* Create mono splitter/combiner */
2496     status = pjmedia_splitcomb_create(
2497 				    pool,
2498 				    param->base.clock_rate,
2499 				    param->base.channel_count,
2500 				    param->base.samples_per_frame,
2501 				    param->base.bits_per_sample,
2502 				    0,	/* options */
2503 				    &snd->splitcomb);
2504     if (status != PJ_SUCCESS)
2505 	goto on_return;
2506 
2507     /* Create reverse channel */
2508     status = pjmedia_splitcomb_create_rev_channel(
2509 				    pool,
2510 				    snd->splitcomb,
2511 				    0	/* channel #1 */,
2512 				    0	/* options */,
2513 				    &snd->rev_port);
2514     if (status != PJ_SUCCESS)
2515 	goto on_return;
2516 
2517     /* And register it to conference bridge */
2518     status = pjsua_conf_add_port(pool, snd->rev_port, &snd->port_id);
2519     if (status != PJ_SUCCESS)
2520 	goto on_return;
2521 
2522     /* Create sound device */
2523     status = pjmedia_snd_port_create2(pool, param, &snd->snd_port);
2524     if (status != PJ_SUCCESS)
2525 	goto on_return;
2526 
2527     /* Connect the splitter to the sound device */
2528     status = pjmedia_snd_port_connect(snd->snd_port, snd->splitcomb);
2529     if (status != PJ_SUCCESS)
2530 	goto on_return;
2531 
2532     /* Finally */
2533     *p_snd = snd;
2534     PJ_LOG(4,(THIS_FILE, "Extra sound device created"));
2535 
2536 on_return:
2537     if (status != PJ_SUCCESS) {
2538 	pjsua_perror(THIS_FILE, "Failed creating extra sound device", status);
2539 	pjsua_ext_snd_dev_destroy(snd);
2540     }
2541 
2542     return status;
2543 }
2544 
2545 
2546 /*
2547  * Destroy an extra sound device and unregister it from conference bridge.
2548  */
pjsua_ext_snd_dev_destroy(pjsua_ext_snd_dev * snd)2549 PJ_DEF(pj_status_t) pjsua_ext_snd_dev_destroy(pjsua_ext_snd_dev *snd)
2550 {
2551     PJ_ASSERT_RETURN(snd, PJ_EINVAL);
2552 
2553     /* Unregister from the conference bridge */
2554     if (snd->port_id != PJSUA_INVALID_ID) {
2555 	pjsua_conf_remove_port(snd->port_id);
2556 	snd->port_id = PJSUA_INVALID_ID;
2557     }
2558 
2559     /* Destroy all components */
2560     if (snd->snd_port) {
2561 	pjmedia_snd_port_disconnect(snd->snd_port);
2562 	pjmedia_snd_port_destroy(snd->snd_port);
2563 	snd->snd_port = NULL;
2564     }
2565     if (snd->rev_port) {
2566 	pjmedia_port_destroy(snd->rev_port);
2567 	snd->rev_port = NULL;
2568     }
2569     if (snd->splitcomb) {
2570 	pjmedia_port_destroy(snd->splitcomb);
2571 	snd->splitcomb = NULL;
2572     }
2573 
2574     /* Finally */
2575     pj_pool_safe_release(&snd->pool);
2576 
2577     PJ_LOG(4,(THIS_FILE, "Extra sound device destroyed"));
2578 
2579     return PJ_SUCCESS;
2580 }
2581 
2582 
2583 /*
2584  * Get sound port instance of an extra sound device.
2585  */
pjsua_ext_snd_dev_get_snd_port(pjsua_ext_snd_dev * snd)2586 PJ_DEF(pjmedia_snd_port*) pjsua_ext_snd_dev_get_snd_port(
2587 					    pjsua_ext_snd_dev *snd)
2588 {
2589     PJ_ASSERT_RETURN(snd, NULL);
2590     return snd->snd_port;
2591 }
2592 
2593 /*
2594  * Get conference port ID of an extra sound device.
2595  */
pjsua_ext_snd_dev_get_conf_port(pjsua_ext_snd_dev * snd)2596 PJ_DEF(pjsua_conf_port_id) pjsua_ext_snd_dev_get_conf_port(
2597 					    pjsua_ext_snd_dev *snd)
2598 {
2599     PJ_ASSERT_RETURN(snd, PJSUA_INVALID_ID);
2600     return snd->port_id;
2601 }
2602 
2603 
2604 #endif /* PJSUA_MEDIA_HAS_PJMEDIA */
2605