blob: c5cad7eb8cc086d7ec94672eceff6088787571b7 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id: audiodev.c 4435 2013-03-11 06:32:58Z nanang $ */
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002/*
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 <pjmedia-audiodev/audiodev_imp.h>
21#include <pj/assert.h>
22#include <pj/errno.h>
23#include <pj/log.h>
24#include <pj/pool.h>
25#include <pj/string.h>
26
27#define THIS_FILE "audiodev.c"
28
29#define DEFINE_CAP(name, info) {name, info}
30
31/* Capability names */
32static struct cap_info
33{
34 const char *name;
35 const char *info;
36} cap_infos[] =
37{
38 DEFINE_CAP("ext-fmt", "Extended/non-PCM format"),
39 DEFINE_CAP("latency-in", "Input latency/buffer size setting"),
40 DEFINE_CAP("latency-out", "Output latency/buffer size setting"),
41 DEFINE_CAP("vol-in", "Input volume setting"),
42 DEFINE_CAP("vol-out", "Output volume setting"),
43 DEFINE_CAP("meter-in", "Input meter"),
44 DEFINE_CAP("meter-out", "Output meter"),
45 DEFINE_CAP("route-in", "Input routing"),
46 DEFINE_CAP("route-out", "Output routing"),
47 DEFINE_CAP("aec", "Accoustic echo cancellation"),
48 DEFINE_CAP("aec-tail", "Tail length setting for AEC"),
49 DEFINE_CAP("vad", "Voice activity detection"),
50 DEFINE_CAP("cng", "Comfort noise generation"),
51 DEFINE_CAP("plg", "Packet loss concealment")
52};
53
54
55/*
56 * The device index seen by application and driver is different.
57 *
58 * At application level, device index is index to global list of device.
59 * At driver level, device index is index to device list on that particular
60 * factory only.
61 */
62#define MAKE_DEV_ID(f_id, index) (((f_id & 0xFFFF) << 16) | (index & 0xFFFF))
63#define GET_INDEX(dev_id) ((dev_id) & 0xFFFF)
64#define GET_FID(dev_id) ((dev_id) >> 16)
65#define DEFAULT_DEV_ID 0
66
67
68/* extern functions to create factories */
69#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
70pjmedia_aud_dev_factory* pjmedia_pa_factory(pj_pool_factory *pf);
71#endif
72
73#if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO
74pjmedia_aud_dev_factory* pjmedia_coreaudio_factory(pj_pool_factory *pf);
75#endif
76
77#if PJMEDIA_AUDIO_DEV_HAS_ALSA
78pjmedia_aud_dev_factory* pjmedia_alsa_factory(pj_pool_factory *pf);
79#endif
80
81#if PJMEDIA_AUDIO_DEV_HAS_OPENSL
82pjmedia_aud_dev_factory* pjmedia_opensl_factory(pj_pool_factory *pf);
83#endif
84
85#if PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI
86pjmedia_aud_dev_factory* pjmedia_android_factory(pj_pool_factory *pf);
87#endif
88
89#if PJMEDIA_AUDIO_DEV_HAS_BB10
90pjmedia_aud_dev_factory* pjmedia_bb10_factory(pj_pool_factory *pf);
91#endif
92
93#if PJMEDIA_AUDIO_DEV_HAS_WMME
94pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf);
95#endif
96
97#if PJMEDIA_AUDIO_DEV_HAS_BDIMAD
98pjmedia_aud_dev_factory* pjmedia_bdimad_factory(pj_pool_factory *pf);
99#endif
100
101#if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
102pjmedia_aud_dev_factory* pjmedia_symb_vas_factory(pj_pool_factory *pf);
103#endif
104
105#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
106pjmedia_aud_dev_factory* pjmedia_aps_factory(pj_pool_factory *pf);
107#endif
108
109#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
110pjmedia_aud_dev_factory* pjmedia_symb_mda_factory(pj_pool_factory *pf);
111#endif
112
113#if PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO
114pjmedia_aud_dev_factory* pjmedia_null_audio_factory(pj_pool_factory *pf);
115#endif
116
117#define MAX_DRIVERS 16
118#define MAX_DEVS 64
119
120
121/* driver structure */
122struct driver
123{
124 /* Creation function */
125 pjmedia_aud_dev_factory_create_func_ptr create;
126 /* Factory instance */
127 pjmedia_aud_dev_factory *f;
128 char name[32]; /* Driver name */
129 unsigned dev_cnt; /* Number of devices */
130 unsigned start_idx; /* Start index in global list */
131 int rec_dev_idx;/* Default capture device. */
132 int play_dev_idx;/* Default playback device */
133 int dev_idx; /* Default device. */
134};
135
136/* The audio subsystem */
137static struct aud_subsys
138{
139 unsigned init_count; /* How many times init() is called */
140 pj_pool_factory *pf; /* The pool factory. */
141
142 unsigned drv_cnt; /* Number of drivers. */
143 struct driver drv[MAX_DRIVERS]; /* Array of drivers. */
144
145 unsigned dev_cnt; /* Total number of devices. */
146 pj_uint32_t dev_list[MAX_DEVS];/* Array of device IDs. */
147
148} aud_subsys;
149
150/* API: get capability name/info */
151PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
152 const char **p_desc)
153{
154 const char *desc;
155 unsigned i;
156
157 if (p_desc==NULL) p_desc = &desc;
158
159 for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
160 if ((1 << i)==cap)
161 break;
162 }
163
164 if (i==PJ_ARRAY_SIZE(cap_infos)) {
165 *p_desc = "??";
166 return "??";
167 }
168
169 *p_desc = cap_infos[i].info;
170 return cap_infos[i].name;
171}
172
173static pj_status_t get_cap_pointer(const pjmedia_aud_param *param,
174 pjmedia_aud_dev_cap cap,
175 void **ptr,
176 unsigned *size)
177{
178#define FIELD_INFO(name) *ptr = (void*)&param->name; \
179 *size = sizeof(param->name)
180
181 switch (cap) {
182 case PJMEDIA_AUD_DEV_CAP_EXT_FORMAT:
183 FIELD_INFO(ext_fmt);
184 break;
185 case PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY:
186 FIELD_INFO(input_latency_ms);
187 break;
188 case PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY:
189 FIELD_INFO(output_latency_ms);
190 break;
191 case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
192 FIELD_INFO(input_vol);
193 break;
194 case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
195 FIELD_INFO(output_vol);
196 break;
197 case PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE:
198 FIELD_INFO(input_route);
199 break;
200 case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
201 FIELD_INFO(output_route);
202 break;
203 case PJMEDIA_AUD_DEV_CAP_EC:
204 FIELD_INFO(ec_enabled);
205 break;
206 case PJMEDIA_AUD_DEV_CAP_EC_TAIL:
207 FIELD_INFO(ec_tail_ms);
208 break;
209 /* vad is no longer in "fmt" in 2.0.
210 case PJMEDIA_AUD_DEV_CAP_VAD:
211 FIELD_INFO(ext_fmt.vad);
212 break;
213 */
214 case PJMEDIA_AUD_DEV_CAP_CNG:
215 FIELD_INFO(cng_enabled);
216 break;
217 case PJMEDIA_AUD_DEV_CAP_PLC:
218 FIELD_INFO(plc_enabled);
219 break;
220 default:
221 return PJMEDIA_EAUD_INVCAP;
222 }
223
224#undef FIELD_INFO
225
226 return PJ_SUCCESS;
227}
228
229/* API: set cap value to param */
230PJ_DEF(pj_status_t) pjmedia_aud_param_set_cap( pjmedia_aud_param *param,
231 pjmedia_aud_dev_cap cap,
232 const void *pval)
233{
234 void *cap_ptr;
235 unsigned cap_size;
236 pj_status_t status;
237
238 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
239 if (status != PJ_SUCCESS)
240 return status;
241
242 pj_memcpy(cap_ptr, pval, cap_size);
243 param->flags |= cap;
244
245 return PJ_SUCCESS;
246}
247
248/* API: get cap value from param */
249PJ_DEF(pj_status_t) pjmedia_aud_param_get_cap( const pjmedia_aud_param *param,
250 pjmedia_aud_dev_cap cap,
251 void *pval)
252{
253 void *cap_ptr;
254 unsigned cap_size;
255 pj_status_t status;
256
257 status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
258 if (status != PJ_SUCCESS)
259 return status;
260
261 if ((param->flags & cap) == 0) {
262 pj_bzero(cap_ptr, cap_size);
263 return PJMEDIA_EAUD_INVCAP;
264 }
265
266 pj_memcpy(pval, cap_ptr, cap_size);
267 return PJ_SUCCESS;
268}
269
270/* Internal: init driver */
271static pj_status_t init_driver(unsigned drv_idx, pj_bool_t refresh)
272{
273 struct driver *drv = &aud_subsys.drv[drv_idx];
274 pjmedia_aud_dev_factory *f;
275 unsigned i, dev_cnt;
276 pj_status_t status;
277
278 if (!refresh) {
279 /* Create the factory */
280 f = (*drv->create)(aud_subsys.pf);
281 if (!f)
282 return PJ_EUNKNOWN;
283
284 /* Call factory->init() */
285 status = f->op->init(f);
286 if (status != PJ_SUCCESS) {
287 f->op->destroy(f);
288 return status;
289 }
290 } else {
291 f = drv->f;
292 }
293
294 /* Get number of devices */
295 dev_cnt = f->op->get_dev_count(f);
296 if (dev_cnt + aud_subsys.dev_cnt > MAX_DEVS) {
297 PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
298 " there are too many devices",
299 aud_subsys.dev_cnt + dev_cnt - MAX_DEVS));
300 dev_cnt = MAX_DEVS - aud_subsys.dev_cnt;
301 }
302
303 /* enabling this will cause pjsua-lib initialization to fail when there
304 * is no sound device installed in the system, even when pjsua has been
305 * run with --null-audio
306 *
307 if (dev_cnt == 0) {
308 f->op->destroy(f);
309 return PJMEDIA_EAUD_NODEV;
310 }
311 */
312
313 /* Fill in default devices */
314 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
315 for (i=0; i<dev_cnt; ++i) {
316 pjmedia_aud_dev_info info;
317
318 status = f->op->get_dev_info(f, i, &info);
319 if (status != PJ_SUCCESS) {
320 f->op->destroy(f);
321 return status;
322 }
323
324 if (drv->name[0]=='\0') {
325 /* Set driver name */
326 pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
327 drv->name[sizeof(drv->name)-1] = '\0';
328 }
329
330 if (drv->play_dev_idx < 0 && info.output_count) {
331 /* Set default playback device */
332 drv->play_dev_idx = i;
333 }
334 if (drv->rec_dev_idx < 0 && info.input_count) {
335 /* Set default capture device */
336 drv->rec_dev_idx = i;
337 }
338 if (drv->dev_idx < 0 && info.input_count &&
339 info.output_count)
340 {
341 /* Set default capture and playback device */
342 drv->dev_idx = i;
343 }
344
345 if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0 &&
346 drv->dev_idx >= 0)
347 {
348 /* Done. */
349 break;
350 }
351 }
352
353 /* Register the factory */
354 drv->f = f;
355 drv->f->sys.drv_idx = drv_idx;
356 drv->start_idx = aud_subsys.dev_cnt;
357 drv->dev_cnt = dev_cnt;
358
359 /* Register devices to global list */
360 for (i=0; i<dev_cnt; ++i) {
361 aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
362 }
363
364 return PJ_SUCCESS;
365}
366
367/* Internal: deinit driver */
368static void deinit_driver(unsigned drv_idx)
369{
370 struct driver *drv = &aud_subsys.drv[drv_idx];
371
372 if (drv->f) {
373 drv->f->op->destroy(drv->f);
374 drv->f = NULL;
375 }
376
377 drv->dev_cnt = 0;
378 drv->play_dev_idx = drv->rec_dev_idx = drv->dev_idx = -1;
379}
380
381/* API: Initialize the audio subsystem. */
382PJ_DEF(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf)
383{
384 unsigned i;
385 pj_status_t status;
386
387 /* Allow init() to be called multiple times as long as there is matching
388 * number of shutdown().
389 */
390 if (aud_subsys.init_count++ != 0) {
391 return PJ_SUCCESS;
392 }
393
394 /* Register error subsystem */
395 status = pj_register_strerror(PJMEDIA_AUDIODEV_ERRNO_START,
396 PJ_ERRNO_SPACE_SIZE,
397 &pjmedia_audiodev_strerror);
398 pj_assert(status == PJ_SUCCESS);
399
400 /* Init */
401 aud_subsys.pf = pf;
402 aud_subsys.drv_cnt = 0;
403 aud_subsys.dev_cnt = 0;
404
405 /* Register creation functions */
406#if PJMEDIA_AUDIO_DEV_HAS_OPENSL
407 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_opensl_factory;
408#endif
409#if PJMEDIA_AUDIO_DEV_HAS_ANDROID_JNI
410 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_android_factory;
411#endif
412#if PJMEDIA_AUDIO_DEV_HAS_BB10
413 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_bb10_factory;
414#endif
415#if PJMEDIA_AUDIO_DEV_HAS_ALSA
416 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_alsa_factory;
417#endif
418#if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO
419 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_coreaudio_factory;
420#endif
421#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
422 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_pa_factory;
423#endif
424#if PJMEDIA_AUDIO_DEV_HAS_WMME
425 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory;
426#endif
427#if PJMEDIA_AUDIO_DEV_HAS_BDIMAD
428 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_bdimad_factory;
429#endif
430#if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
431 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_vas_factory;
432#endif
433#if PJMEDIA_AUDIO_DEV_HAS_SYMB_APS
434 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_aps_factory;
435#endif
436#if PJMEDIA_AUDIO_DEV_HAS_SYMB_MDA
437 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_mda_factory;
438#endif
439#if PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO
440 aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_null_audio_factory;
441#endif
442
443 /* Initialize each factory and build the device ID list */
444 for (i=0; i<aud_subsys.drv_cnt; ++i) {
445 status = init_driver(i, PJ_FALSE);
446 if (status != PJ_SUCCESS) {
447 deinit_driver(i);
448 continue;
449 }
450 }
451
452 return aud_subsys.dev_cnt ? PJ_SUCCESS : status;
453}
454
455/* API: register an audio device factory to the audio subsystem. */
456PJ_DEF(pj_status_t)
457pjmedia_aud_register_factory(pjmedia_aud_dev_factory_create_func_ptr adf)
458{
459 pj_status_t status;
460
461 if (aud_subsys.init_count == 0)
462 return PJMEDIA_EAUD_INIT;
463
464 aud_subsys.drv[aud_subsys.drv_cnt].create = adf;
465 status = init_driver(aud_subsys.drv_cnt, PJ_FALSE);
466 if (status == PJ_SUCCESS) {
467 aud_subsys.drv_cnt++;
468 } else {
469 deinit_driver(aud_subsys.drv_cnt);
470 }
471
472 return status;
473}
474
475/* API: unregister an audio device factory from the audio subsystem. */
476PJ_DEF(pj_status_t)
477pjmedia_aud_unregister_factory(pjmedia_aud_dev_factory_create_func_ptr adf)
478{
479 unsigned i, j;
480
481 if (aud_subsys.init_count == 0)
482 return PJMEDIA_EAUD_INIT;
483
484 for (i=0; i<aud_subsys.drv_cnt; ++i) {
485 struct driver *drv = &aud_subsys.drv[i];
486
487 if (drv->create == adf) {
488 for (j = drv->start_idx; j < drv->start_idx + drv->dev_cnt; j++)
489 {
490 aud_subsys.dev_list[j] = (pj_uint32_t)PJMEDIA_AUD_INVALID_DEV;
491 }
492
493 deinit_driver(i);
494 pj_bzero(drv, sizeof(*drv));
495 return PJ_SUCCESS;
496 }
497 }
498
499 return PJMEDIA_EAUD_ERR;
500}
501
502/* API: get the pool factory registered to the audio subsystem. */
503PJ_DEF(pj_pool_factory*) pjmedia_aud_subsys_get_pool_factory(void)
504{
505 return aud_subsys.pf;
506}
507
508/* API: Shutdown the audio subsystem. */
509PJ_DEF(pj_status_t) pjmedia_aud_subsys_shutdown(void)
510{
511 unsigned i;
512
513 /* Allow shutdown() to be called multiple times as long as there is matching
514 * number of init().
515 */
516 if (aud_subsys.init_count == 0) {
517 return PJ_SUCCESS;
518 }
519 --aud_subsys.init_count;
520
521 if (aud_subsys.init_count == 0) {
522 for (i=0; i<aud_subsys.drv_cnt; ++i) {
523 deinit_driver(i);
524 }
525
526 aud_subsys.pf = NULL;
527 }
528 return PJ_SUCCESS;
529}
530
531/* API: Refresh the list of sound devices installed in the system. */
532PJ_DEF(pj_status_t) pjmedia_aud_dev_refresh(void)
533{
534 unsigned i;
535
536 aud_subsys.dev_cnt = 0;
537 for (i=0; i<aud_subsys.drv_cnt; ++i) {
538 struct driver *drv = &aud_subsys.drv[i];
539
540 if (drv->f && drv->f->op->refresh) {
541 pj_status_t status = drv->f->op->refresh(drv->f);
542 if (status != PJ_SUCCESS) {
543 PJ_PERROR(4, (THIS_FILE, status, "Unable to refresh device "
544 "list for %s", drv->name));
545 }
546 }
547 init_driver(i, PJ_TRUE);
548 }
549 return PJ_SUCCESS;
550}
551
552/* API: Get the number of sound devices installed in the system. */
553PJ_DEF(unsigned) pjmedia_aud_dev_count(void)
554{
555 return aud_subsys.dev_cnt;
556}
557
558/* Internal: convert local index to global device index */
559static pj_status_t make_global_index(unsigned drv_idx,
560 pjmedia_aud_dev_index *id)
561{
562 if (*id < 0) {
563 return PJ_SUCCESS;
564 }
565
566 /* Check that factory still exists */
567 PJ_ASSERT_RETURN(aud_subsys.drv[drv_idx].f, PJ_EBUG);
568
569 /* Check that device index is valid */
570 PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[drv_idx].dev_cnt,
571 PJ_EBUG);
572
573 *id += aud_subsys.drv[drv_idx].start_idx;
574 return PJ_SUCCESS;
575}
576
577/* Internal: lookup device id */
578static pj_status_t lookup_dev(pjmedia_aud_dev_index id,
579 pjmedia_aud_dev_factory **p_f,
580 unsigned *p_local_index)
581{
582 int f_id, index;
583
584 if (id < 0) {
585 unsigned i;
586
587 if (id == PJMEDIA_AUD_INVALID_DEV)
588 return PJMEDIA_EAUD_INVDEV;
589
590 for (i=0; i<aud_subsys.drv_cnt; ++i) {
591 struct driver *drv = &aud_subsys.drv[i];
592 if (drv->dev_idx >= 0) {
593 id = drv->dev_idx;
594 make_global_index(i, &id);
595 break;
596 } else if (id==PJMEDIA_AUD_DEFAULT_CAPTURE_DEV &&
597 drv->rec_dev_idx >= 0)
598 {
599 id = drv->rec_dev_idx;
600 make_global_index(i, &id);
601 break;
602 } else if (id==PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV &&
603 drv->play_dev_idx >= 0)
604 {
605 id = drv->play_dev_idx;
606 make_global_index(i, &id);
607 break;
608 }
609 }
610
611 if (id < 0) {
612 return PJMEDIA_EAUD_NODEFDEV;
613 }
614 }
615
616 f_id = GET_FID(aud_subsys.dev_list[id]);
617 index = GET_INDEX(aud_subsys.dev_list[id]);
618
619 if (f_id < 0 || f_id >= (int)aud_subsys.drv_cnt)
620 return PJMEDIA_EAUD_INVDEV;
621
622 if (index < 0 || index >= (int)aud_subsys.drv[f_id].dev_cnt)
623 return PJMEDIA_EAUD_INVDEV;
624
625 *p_f = aud_subsys.drv[f_id].f;
626 *p_local_index = (unsigned)index;
627
628 return PJ_SUCCESS;
629
630}
631
632/* API: Get device information. */
633PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
634 pjmedia_aud_dev_info *info)
635{
636 pjmedia_aud_dev_factory *f;
637 unsigned index;
638 pj_status_t status;
639
640 PJ_ASSERT_RETURN(info && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
641 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
642
643 status = lookup_dev(id, &f, &index);
644 if (status != PJ_SUCCESS)
645 return status;
646
647 return f->op->get_dev_info(f, index, info);
648}
649
650/* API: find device */
651PJ_DEF(pj_status_t) pjmedia_aud_dev_lookup( const char *drv_name,
652 const char *dev_name,
653 pjmedia_aud_dev_index *id)
654{
655 pjmedia_aud_dev_factory *f = NULL;
656 unsigned drv_idx, dev_idx;
657
658 PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
659 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
660
661 for (drv_idx=0; drv_idx<aud_subsys.drv_cnt; ++drv_idx) {
662 if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[drv_idx].name)) {
663 f = aud_subsys.drv[drv_idx].f;
664 break;
665 }
666 }
667
668 if (!f)
669 return PJ_ENOTFOUND;
670
671 for (dev_idx=0; dev_idx<aud_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
672 pjmedia_aud_dev_info info;
673 pj_status_t status;
674
675 status = f->op->get_dev_info(f, dev_idx, &info);
676 if (status != PJ_SUCCESS)
677 return status;
678
679 if (!pj_ansi_stricmp(dev_name, info.name))
680 break;
681 }
682
683 if (dev_idx==aud_subsys.drv[drv_idx].dev_cnt)
684 return PJ_ENOTFOUND;
685
686 *id = dev_idx;
687 make_global_index(drv_idx, id);
688
689 return PJ_SUCCESS;
690}
691
692/* API: Initialize the audio device parameters with default values for the
693 * specified device.
694 */
695PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
696 pjmedia_aud_param *param)
697{
698 pjmedia_aud_dev_factory *f;
699 unsigned index;
700 pj_status_t status;
701
702 PJ_ASSERT_RETURN(param && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
703 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
704
705 status = lookup_dev(id, &f, &index);
706 if (status != PJ_SUCCESS)
707 return status;
708
709 status = f->op->default_param(f, index, param);
710 if (status != PJ_SUCCESS)
711 return status;
712
713 /* Normalize device IDs */
714 make_global_index(f->sys.drv_idx, &param->rec_id);
715 make_global_index(f->sys.drv_idx, &param->play_id);
716
717 return PJ_SUCCESS;
718}
719
720/* API: Open audio stream object using the specified parameters. */
721PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *prm,
722 pjmedia_aud_rec_cb rec_cb,
723 pjmedia_aud_play_cb play_cb,
724 void *user_data,
725 pjmedia_aud_stream **p_aud_strm)
726{
727 pjmedia_aud_dev_factory *rec_f=NULL, *play_f=NULL, *f=NULL;
728 pjmedia_aud_param param;
729 pj_status_t status;
730
731 PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL);
732 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
733 PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE ||
734 prm->dir==PJMEDIA_DIR_PLAYBACK ||
735 prm->dir==PJMEDIA_DIR_CAPTURE_PLAYBACK,
736 PJ_EINVAL);
737
738 /* Must make copy of param because we're changing device ID */
739 pj_memcpy(&param, prm, sizeof(param));
740
741 /* Normalize rec_id */
742 if (param.dir & PJMEDIA_DIR_CAPTURE) {
743 unsigned index;
744
745 if (param.rec_id < 0)
746 param.rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
747
748 status = lookup_dev(param.rec_id, &rec_f, &index);
749 if (status != PJ_SUCCESS)
750 return status;
751
752 param.rec_id = index;
753 f = rec_f;
754 }
755
756 /* Normalize play_id */
757 if (param.dir & PJMEDIA_DIR_PLAYBACK) {
758 unsigned index;
759
760 if (param.play_id < 0)
761 param.play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
762
763 status = lookup_dev(param.play_id, &play_f, &index);
764 if (status != PJ_SUCCESS)
765 return status;
766
767 param.play_id = index;
768 f = play_f;
769 }
770
771 PJ_ASSERT_RETURN(f != NULL, PJ_EBUG);
772
773 /* For now, rec_id and play_id must belong to the same factory */
774 PJ_ASSERT_RETURN((param.dir != PJMEDIA_DIR_CAPTURE_PLAYBACK) ||
775 (rec_f == play_f),
776 PJMEDIA_EAUD_INVDEV);
777
778 /* Create the stream */
779 status = f->op->create_stream(f, &param, rec_cb, play_cb,
780 user_data, p_aud_strm);
781 if (status != PJ_SUCCESS)
782 return status;
783
784 /* Assign factory id to the stream */
785 (*p_aud_strm)->sys.drv_idx = f->sys.drv_idx;
786 return PJ_SUCCESS;
787}
788
789/* API: Get the running parameters for the specified audio stream. */
790PJ_DEF(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
791 pjmedia_aud_param *param)
792{
793 pj_status_t status;
794
795 PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
796 PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
797
798 status = strm->op->get_param(strm, param);
799 if (status != PJ_SUCCESS)
800 return status;
801
802 /* Normalize device id's */
803 make_global_index(strm->sys.drv_idx, &param->rec_id);
804 make_global_index(strm->sys.drv_idx, &param->play_id);
805
806 return PJ_SUCCESS;
807}
808
809/* API: Get the value of a specific capability of the audio stream. */
810PJ_DEF(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm,
811 pjmedia_aud_dev_cap cap,
812 void *value)
813{
814 return strm->op->get_cap(strm, cap, value);
815}
816
817/* API: Set the value of a specific capability of the audio stream. */
818PJ_DEF(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm,
819 pjmedia_aud_dev_cap cap,
820 const void *value)
821{
822 return strm->op->set_cap(strm, cap, value);
823}
824
825/* API: Start the stream. */
826PJ_DEF(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm)
827{
828 return strm->op->start(strm);
829}
830
831/* API: Stop the stream. */
832PJ_DEF(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm)
833{
834 return strm->op->stop(strm);
835}
836
837/* API: Destroy the stream. */
838PJ_DEF(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm)
839{
840 return strm->op->destroy(strm);
841}
842
843