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 /* The audio subsystem */
69 static pjmedia_aud_subsys aud_subsys;
70 
71 /* API: get the audio subsystem. */
pjmedia_get_aud_subsys(void)72 PJ_DEF(pjmedia_aud_subsys*) pjmedia_get_aud_subsys(void)
73 {
74     return &aud_subsys;
75 }
76 
77 /* API: init driver */
pjmedia_aud_driver_init(unsigned drv_idx,pj_bool_t refresh)78 PJ_DEF(pj_status_t) pjmedia_aud_driver_init(unsigned drv_idx,
79 					    pj_bool_t refresh)
80 {
81     pjmedia_aud_driver *drv = &aud_subsys.drv[drv_idx];
82     pjmedia_aud_dev_factory *f;
83     unsigned i, dev_cnt;
84     pj_status_t status;
85 
86     if (!refresh && drv->create) {
87 	/* Create the factory */
88 	f = (*drv->create)(aud_subsys.pf);
89 	if (!f)
90 	    return PJ_EUNKNOWN;
91 
92 	/* Call factory->init() */
93 	status = f->op->init(f);
94 	if (status != PJ_SUCCESS) {
95 	    f->op->destroy(f);
96 	    return status;
97 	}
98     } else {
99 	f = drv->f;
100     }
101 
102     if (!f)
103 	return PJ_EUNKNOWN;
104 
105     /* Get number of devices */
106     dev_cnt = f->op->get_dev_count(f);
107     if (dev_cnt + aud_subsys.dev_cnt > PJMEDIA_AUD_MAX_DEVS) {
108 	PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
109 			      " there are too many devices",
110 			      aud_subsys.dev_cnt + dev_cnt -
111 			      PJMEDIA_AUD_MAX_DEVS));
112 	dev_cnt = PJMEDIA_AUD_MAX_DEVS - aud_subsys.dev_cnt;
113     }
114 
115     /* enabling this will cause pjsua-lib initialization to fail when there
116      * is no sound device installed in the system, even when pjsua has been
117      * run with --null-audio
118      *
119     if (dev_cnt == 0) {
120 	f->op->destroy(f);
121 	return PJMEDIA_EAUD_NODEV;
122     }
123     */
124 
125     /* Fill in default devices */
126     drv->play_dev_idx = drv->rec_dev_idx =
127 			drv->dev_idx = PJMEDIA_AUD_INVALID_DEV;
128     for (i=0; i<dev_cnt; ++i) {
129 	pjmedia_aud_dev_info info;
130 
131 	status = f->op->get_dev_info(f, i, &info);
132 	if (status != PJ_SUCCESS) {
133 	    f->op->destroy(f);
134 	    return status;
135 	}
136 
137 	if (drv->name[0]=='\0') {
138 	    /* Set driver name */
139 	    pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
140 	    drv->name[sizeof(drv->name)-1] = '\0';
141 	}
142 
143 	if (drv->play_dev_idx < 0 && info.output_count) {
144 	    /* Set default playback device */
145 	    drv->play_dev_idx = i;
146 	}
147 	if (drv->rec_dev_idx < 0 && info.input_count) {
148 	    /* Set default capture device */
149 	    drv->rec_dev_idx = i;
150 	}
151 	if (drv->dev_idx < 0 && info.input_count &&
152 	    info.output_count)
153 	{
154 	    /* Set default capture and playback device */
155 	    drv->dev_idx = i;
156 	}
157 
158 	if (drv->play_dev_idx >= 0 && drv->rec_dev_idx >= 0 &&
159 	    drv->dev_idx >= 0)
160 	{
161 	    /* Done. */
162 	    break;
163 	}
164     }
165 
166     /* Register the factory */
167     drv->f = f;
168     drv->f->sys.drv_idx = drv_idx;
169     drv->start_idx = aud_subsys.dev_cnt;
170     drv->dev_cnt = dev_cnt;
171 
172     /* Register devices to global list */
173     for (i=0; i<dev_cnt; ++i) {
174 	aud_subsys.dev_list[aud_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
175     }
176 
177     return PJ_SUCCESS;
178 }
179 
180 /* API: deinit driver */
pjmedia_aud_driver_deinit(unsigned drv_idx)181 PJ_DEF(void) pjmedia_aud_driver_deinit(unsigned drv_idx)
182 {
183     pjmedia_aud_driver *drv = &aud_subsys.drv[drv_idx];
184 
185     if (drv->f) {
186 	drv->f->op->destroy(drv->f);
187 	drv->f = NULL;
188     }
189 
190     pj_bzero(drv, sizeof(*drv));
191     drv->play_dev_idx = drv->rec_dev_idx =
192 			drv->dev_idx = PJMEDIA_AUD_INVALID_DEV;
193 }
194 
195 /* API: get capability name/info */
pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,const char ** p_desc)196 PJ_DEF(const char*) pjmedia_aud_dev_cap_name(pjmedia_aud_dev_cap cap,
197 					     const char **p_desc)
198 {
199     const char *desc;
200     unsigned i;
201 
202     if (p_desc==NULL) p_desc = &desc;
203 
204     for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
205 	if ((1 << i)==cap)
206 	    break;
207     }
208 
209     if (i==PJ_ARRAY_SIZE(cap_infos)) {
210 	*p_desc = "??";
211 	return "??";
212     }
213 
214     *p_desc = cap_infos[i].info;
215     return cap_infos[i].name;
216 }
217 
get_cap_pointer(const pjmedia_aud_param * param,pjmedia_aud_dev_cap cap,void ** ptr,unsigned * size)218 static pj_status_t get_cap_pointer(const pjmedia_aud_param *param,
219 				   pjmedia_aud_dev_cap cap,
220 				   void **ptr,
221 				   unsigned *size)
222 {
223 #define FIELD_INFO(name)    *ptr = (void*)&param->name; \
224 			    *size = sizeof(param->name)
225 
226     switch (cap) {
227     case PJMEDIA_AUD_DEV_CAP_EXT_FORMAT:
228 	FIELD_INFO(ext_fmt);
229 	break;
230     case PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY:
231 	FIELD_INFO(input_latency_ms);
232 	break;
233     case PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY:
234 	FIELD_INFO(output_latency_ms);
235 	break;
236     case PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING:
237 	FIELD_INFO(input_vol);
238 	break;
239     case PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING:
240 	FIELD_INFO(output_vol);
241 	break;
242     case PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE:
243 	FIELD_INFO(input_route);
244 	break;
245     case PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE:
246 	FIELD_INFO(output_route);
247 	break;
248     case PJMEDIA_AUD_DEV_CAP_EC:
249 	FIELD_INFO(ec_enabled);
250 	break;
251     case PJMEDIA_AUD_DEV_CAP_EC_TAIL:
252 	FIELD_INFO(ec_tail_ms);
253 	break;
254     /* vad is no longer in "fmt" in 2.0.
255     case PJMEDIA_AUD_DEV_CAP_VAD:
256 	FIELD_INFO(ext_fmt.vad);
257 	break;
258     */
259     case PJMEDIA_AUD_DEV_CAP_CNG:
260 	FIELD_INFO(cng_enabled);
261 	break;
262     case PJMEDIA_AUD_DEV_CAP_PLC:
263 	FIELD_INFO(plc_enabled);
264 	break;
265     default:
266 	return PJMEDIA_EAUD_INVCAP;
267     }
268 
269 #undef FIELD_INFO
270 
271     return PJ_SUCCESS;
272 }
273 
274 /* API: set cap value to param */
pjmedia_aud_param_set_cap(pjmedia_aud_param * param,pjmedia_aud_dev_cap cap,const void * pval)275 PJ_DEF(pj_status_t) pjmedia_aud_param_set_cap( pjmedia_aud_param *param,
276 					       pjmedia_aud_dev_cap cap,
277 					       const void *pval)
278 {
279     void *cap_ptr;
280     unsigned cap_size;
281     pj_status_t status;
282 
283     status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
284     if (status != PJ_SUCCESS)
285 	return status;
286 
287     pj_memcpy(cap_ptr, pval, cap_size);
288     param->flags |= cap;
289 
290     return PJ_SUCCESS;
291 }
292 
293 /* API: get cap value from param */
pjmedia_aud_param_get_cap(const pjmedia_aud_param * param,pjmedia_aud_dev_cap cap,void * pval)294 PJ_DEF(pj_status_t) pjmedia_aud_param_get_cap( const pjmedia_aud_param *param,
295 					       pjmedia_aud_dev_cap cap,
296 					       void *pval)
297 {
298     void *cap_ptr;
299     unsigned cap_size;
300     pj_status_t status;
301 
302     status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
303     if (status != PJ_SUCCESS)
304 	return status;
305 
306     if ((param->flags & cap) == 0) {
307 	pj_bzero(cap_ptr, cap_size);
308 	return PJMEDIA_EAUD_INVCAP;
309     }
310 
311     pj_memcpy(pval, cap_ptr, cap_size);
312     return PJ_SUCCESS;
313 }
314 
315 
316 /* API: Refresh the list of sound devices installed in the system. */
pjmedia_aud_dev_refresh(void)317 PJ_DEF(pj_status_t) pjmedia_aud_dev_refresh(void)
318 {
319     unsigned i;
320 
321     aud_subsys.dev_cnt = 0;
322     for (i=0; i<aud_subsys.drv_cnt; ++i) {
323 	pjmedia_aud_driver *drv = &aud_subsys.drv[i];
324 
325 	if (drv->f && drv->f->op->refresh) {
326 	    pj_status_t status = drv->f->op->refresh(drv->f);
327 	    if (status != PJ_SUCCESS) {
328 		PJ_PERROR(4, (THIS_FILE, status, "Unable to refresh device "
329 						 "list for %s", drv->name));
330 	    }
331 	}
332 	pjmedia_aud_driver_init(i, PJ_TRUE);
333     }
334     return PJ_SUCCESS;
335 }
336 
337 /* API: Get the number of sound devices installed in the system. */
pjmedia_aud_dev_count(void)338 PJ_DEF(unsigned) pjmedia_aud_dev_count(void)
339 {
340     return aud_subsys.dev_cnt;
341 }
342 
343 /* Internal: convert local index to global device index */
make_global_index(unsigned drv_idx,pjmedia_aud_dev_index * id)344 static pj_status_t make_global_index(unsigned drv_idx,
345 				     pjmedia_aud_dev_index *id)
346 {
347     if (*id < 0) {
348 	return PJ_SUCCESS;
349     }
350 
351     /* Check that factory still exists */
352     PJ_ASSERT_RETURN(aud_subsys.drv[drv_idx].f, PJ_EBUG);
353 
354     /* Check that device index is valid */
355     PJ_ASSERT_RETURN(*id>=0 && *id<(int)aud_subsys.drv[drv_idx].dev_cnt,
356 		     PJ_EBUG);
357 
358     *id += aud_subsys.drv[drv_idx].start_idx;
359     return PJ_SUCCESS;
360 }
361 
362 /* Internal: lookup device id */
lookup_dev(pjmedia_aud_dev_index id,pjmedia_aud_dev_factory ** p_f,unsigned * p_local_index)363 static pj_status_t lookup_dev(pjmedia_aud_dev_index id,
364 			      pjmedia_aud_dev_factory **p_f,
365 			      unsigned *p_local_index)
366 {
367     int f_id, index;
368 
369     if (id < 0) {
370 	unsigned i;
371 
372 	if (id == PJMEDIA_AUD_INVALID_DEV)
373 	    return PJMEDIA_EAUD_INVDEV;
374 
375 	for (i=0; i<aud_subsys.drv_cnt; ++i) {
376 	    pjmedia_aud_driver *drv = &aud_subsys.drv[i];
377 	    if (drv->dev_idx >= 0) {
378 		id = drv->dev_idx;
379 		make_global_index(i, &id);
380 		break;
381 	    } else if (id==PJMEDIA_AUD_DEFAULT_CAPTURE_DEV &&
382 		drv->rec_dev_idx >= 0)
383 	    {
384 		id = drv->rec_dev_idx;
385 		make_global_index(i, &id);
386 		break;
387 	    } else if (id==PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV &&
388 		drv->play_dev_idx >= 0)
389 	    {
390 		id = drv->play_dev_idx;
391 		make_global_index(i, &id);
392 		break;
393 	    }
394 	}
395 
396 	if (id < 0) {
397 	    return PJMEDIA_EAUD_NODEFDEV;
398 	}
399     }
400 
401     f_id = GET_FID(aud_subsys.dev_list[id]);
402     index = GET_INDEX(aud_subsys.dev_list[id]);
403 
404     if (f_id < 0 || f_id >= (int)aud_subsys.drv_cnt)
405 	return PJMEDIA_EAUD_INVDEV;
406 
407     if (index < 0 || index >= (int)aud_subsys.drv[f_id].dev_cnt)
408 	return PJMEDIA_EAUD_INVDEV;
409 
410     *p_f = aud_subsys.drv[f_id].f;
411     *p_local_index = (unsigned)index;
412 
413     return PJ_SUCCESS;
414 
415 }
416 
417 /* API: Get device information. */
pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,pjmedia_aud_dev_info * info)418 PJ_DEF(pj_status_t) pjmedia_aud_dev_get_info(pjmedia_aud_dev_index id,
419 					     pjmedia_aud_dev_info *info)
420 {
421     pjmedia_aud_dev_factory *f;
422     unsigned index;
423     pj_status_t status;
424 
425     PJ_ASSERT_RETURN(info && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
426     PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
427 
428     status = lookup_dev(id, &f, &index);
429     if (status != PJ_SUCCESS)
430 	return status;
431 
432     return f->op->get_dev_info(f, index, info);
433 }
434 
435 /* API: find device */
pjmedia_aud_dev_lookup(const char * drv_name,const char * dev_name,pjmedia_aud_dev_index * id)436 PJ_DEF(pj_status_t) pjmedia_aud_dev_lookup( const char *drv_name,
437 					    const char *dev_name,
438 					    pjmedia_aud_dev_index *id)
439 {
440     pjmedia_aud_dev_factory *f = NULL;
441     unsigned drv_idx, dev_idx;
442 
443     PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
444     PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
445 
446     for (drv_idx=0; drv_idx<aud_subsys.drv_cnt; ++drv_idx) {
447 	if (!pj_ansi_stricmp(drv_name, aud_subsys.drv[drv_idx].name)) {
448 	    f = aud_subsys.drv[drv_idx].f;
449 	    break;
450 	}
451     }
452 
453     if (!f)
454 	return PJ_ENOTFOUND;
455 
456     for (dev_idx=0; dev_idx<aud_subsys.drv[drv_idx].dev_cnt; ++dev_idx) {
457 	pjmedia_aud_dev_info info;
458 	pj_status_t status;
459 
460 	status = f->op->get_dev_info(f, dev_idx, &info);
461 	if (status != PJ_SUCCESS)
462 	    return status;
463 
464 	if (!pj_ansi_stricmp(dev_name, info.name))
465 	    break;
466     }
467 
468     if (dev_idx==aud_subsys.drv[drv_idx].dev_cnt)
469 	return PJ_ENOTFOUND;
470 
471     *id = dev_idx;
472     make_global_index(drv_idx, id);
473 
474     return PJ_SUCCESS;
475 }
476 
477 /* API: Initialize the audio device parameters with default values for the
478  * specified device.
479  */
pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,pjmedia_aud_param * param)480 PJ_DEF(pj_status_t) pjmedia_aud_dev_default_param(pjmedia_aud_dev_index id,
481 						  pjmedia_aud_param *param)
482 {
483     pjmedia_aud_dev_factory *f;
484     unsigned index;
485     pj_status_t status;
486 
487     PJ_ASSERT_RETURN(param && id!=PJMEDIA_AUD_INVALID_DEV, PJ_EINVAL);
488     PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
489 
490     status = lookup_dev(id, &f, &index);
491     if (status != PJ_SUCCESS)
492 	return status;
493 
494     status = f->op->default_param(f, index, param);
495     if (status != PJ_SUCCESS)
496 	return status;
497 
498     /* Normalize device IDs */
499     make_global_index(f->sys.drv_idx, &param->rec_id);
500     make_global_index(f->sys.drv_idx, &param->play_id);
501 
502     return PJ_SUCCESS;
503 }
504 
505 /* API: Open audio stream object using the specified parameters. */
pjmedia_aud_stream_create(const pjmedia_aud_param * prm,pjmedia_aud_rec_cb rec_cb,pjmedia_aud_play_cb play_cb,void * user_data,pjmedia_aud_stream ** p_aud_strm)506 PJ_DEF(pj_status_t) pjmedia_aud_stream_create(const pjmedia_aud_param *prm,
507 					      pjmedia_aud_rec_cb rec_cb,
508 					      pjmedia_aud_play_cb play_cb,
509 					      void *user_data,
510 					      pjmedia_aud_stream **p_aud_strm)
511 {
512     pjmedia_aud_dev_factory *rec_f=NULL, *play_f=NULL, *f=NULL;
513     pjmedia_aud_param param;
514     pj_status_t status;
515 
516     PJ_ASSERT_RETURN(prm && prm->dir && p_aud_strm, PJ_EINVAL);
517     PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
518     PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE ||
519 		     prm->dir==PJMEDIA_DIR_PLAYBACK ||
520 		     prm->dir==PJMEDIA_DIR_CAPTURE_PLAYBACK,
521 		     PJ_EINVAL);
522 
523     /* Must make copy of param because we're changing device ID */
524     pj_memcpy(&param, prm, sizeof(param));
525 
526     /* Normalize rec_id */
527     if (param.dir & PJMEDIA_DIR_CAPTURE) {
528 	unsigned index;
529 
530 	if (param.rec_id < 0)
531 	    param.rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
532 
533 	status = lookup_dev(param.rec_id, &rec_f, &index);
534 	if (status != PJ_SUCCESS)
535 	    return status;
536 
537 	param.rec_id = index;
538 	f = rec_f;
539     }
540 
541     /* Normalize play_id */
542     if (param.dir & PJMEDIA_DIR_PLAYBACK) {
543 	unsigned index;
544 
545 	if (param.play_id < 0)
546 	    param.play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
547 
548 	status = lookup_dev(param.play_id, &play_f, &index);
549 	if (status != PJ_SUCCESS)
550 	    return status;
551 
552 	param.play_id = index;
553 	f = play_f;
554     }
555 
556     PJ_ASSERT_RETURN(f != NULL, PJ_EBUG);
557 
558     /* For now, rec_id and play_id must belong to the same factory */
559     PJ_ASSERT_RETURN((param.dir != PJMEDIA_DIR_CAPTURE_PLAYBACK) ||
560 		     (rec_f == play_f),
561 		     PJMEDIA_EAUD_INVDEV);
562 
563     /* Create the stream */
564     status = f->op->create_stream(f, &param, rec_cb, play_cb,
565 				  user_data, p_aud_strm);
566     if (status != PJ_SUCCESS)
567 	return status;
568 
569     /* Assign factory id to the stream */
570     (*p_aud_strm)->sys.drv_idx = f->sys.drv_idx;
571     return PJ_SUCCESS;
572 }
573 
574 /* API: Get the running parameters for the specified audio stream. */
pjmedia_aud_stream_get_param(pjmedia_aud_stream * strm,pjmedia_aud_param * param)575 PJ_DEF(pj_status_t) pjmedia_aud_stream_get_param(pjmedia_aud_stream *strm,
576 						 pjmedia_aud_param *param)
577 {
578     pj_status_t status;
579 
580     PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
581     PJ_ASSERT_RETURN(aud_subsys.pf, PJMEDIA_EAUD_INIT);
582 
583     status = strm->op->get_param(strm, param);
584     if (status != PJ_SUCCESS)
585 	return status;
586 
587     /* Normalize device id's */
588     make_global_index(strm->sys.drv_idx, &param->rec_id);
589     make_global_index(strm->sys.drv_idx, &param->play_id);
590 
591     return PJ_SUCCESS;
592 }
593 
594 /* API: Get the value of a specific capability of the audio stream. */
pjmedia_aud_stream_get_cap(pjmedia_aud_stream * strm,pjmedia_aud_dev_cap cap,void * value)595 PJ_DEF(pj_status_t) pjmedia_aud_stream_get_cap(pjmedia_aud_stream *strm,
596 					       pjmedia_aud_dev_cap cap,
597 					       void *value)
598 {
599     return strm->op->get_cap(strm, cap, value);
600 }
601 
602 /* API: Set the value of a specific capability of the audio stream. */
pjmedia_aud_stream_set_cap(pjmedia_aud_stream * strm,pjmedia_aud_dev_cap cap,const void * value)603 PJ_DEF(pj_status_t) pjmedia_aud_stream_set_cap(pjmedia_aud_stream *strm,
604 					       pjmedia_aud_dev_cap cap,
605 					       const void *value)
606 {
607     return strm->op->set_cap(strm, cap, value);
608 }
609 
610 /* API: Start the stream. */
pjmedia_aud_stream_start(pjmedia_aud_stream * strm)611 PJ_DEF(pj_status_t) pjmedia_aud_stream_start(pjmedia_aud_stream *strm)
612 {
613     return strm->op->start(strm);
614 }
615 
616 /* API: Stop the stream. */
pjmedia_aud_stream_stop(pjmedia_aud_stream * strm)617 PJ_DEF(pj_status_t) pjmedia_aud_stream_stop(pjmedia_aud_stream *strm)
618 {
619     return strm->op->stop(strm);
620 }
621 
622 /* API: Destroy the stream. */
pjmedia_aud_stream_destroy(pjmedia_aud_stream * strm)623 PJ_DEF(pj_status_t) pjmedia_aud_stream_destroy(pjmedia_aud_stream *strm)
624 {
625     return strm->op->destroy(strm);
626 }
627 
628 
629