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(¶m);
991 param.ec_options = pjsua_var.media_cfg.ec_options;
992
993 /* Create parameter based on peer info */
994 status = create_aud_param(¶m.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(¶m);
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, ¶m);
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, ¶m->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, ¶m);
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(¶m);
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(¶m);
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(¶m);
2188 param.ec_options = pjsua_var.media_cfg.ec_options;
2189 status = create_aud_param(¶m.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(¶m);
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