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