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*)¶m->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, ¶m->rec_id);
500 make_global_index(f->sys.drv_idx, ¶m->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(¶m, 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, ¶m, 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, ¶m->rec_id);
589 make_global_index(strm->sys.drv_idx, ¶m->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