1 /*******************************************************
2
3 bdimad_dev.c
4
5 Author: bdSound Development Team (techsupport@bdsound.com)
6
7 Date: 12/03/2015
8 Version 2.0.0 rev.1618
9
10 Copyright (c) 2015 bdSound s.r.l. (www.bdsound.com)
11 All Rights Reserved.
12
13 *******************************************************/
14
15 #include <pjmedia-audiodev/audiodev_imp.h>
16 #include <pj/assert.h>
17 #include <pj/log.h>
18 #include <pj/os.h>
19 #include <pj/string.h>
20 #include <pj/unicode.h>
21 #include <pjmedia/errno.h>
22
23 #if defined(PJMEDIA_AUDIO_DEV_HAS_BDIMAD) && PJMEDIA_AUDIO_DEV_HAS_BDIMAD != 0
24
25 #include <math.h>
26 #include <wchar.h>
27
28 /**************************
29 * bdIMAD
30 ***************************/
31 #include "../../third_party/bdsound/include/bdimad.h"
32
33 /* Maximum supported audio devices */
34 #define BD_IMAD_MAX_DEV_COUNT 100
35 /* Maximum supported device name */
36 #define BD_IMAD_MAX_DEV_LENGTH_NAME 256
37 /* Only mono mode */
38 #define BD_IMAD_MAX_CHANNELS 1
39 /* Frequency default value (admitted 8000 Hz, 16000 Hz, 32000 Hz, 44100 Hz and 48000 Hz) */
40 #define BD_IMAD_DEFAULT_FREQ 48000
41 /* Default milliseconds per buffer */
42 #define BD_IMAD_MSECOND_PER_BUFFER 16
43 /* Only for supported systems */
44 #define BD_IMAD_STARTING_INPUT_VOLUME 50
45 /* Only for supported systems */
46 #define BD_IMAD_STARTING_OUTPUT_VOLUME 100
47 /* Diagnostic Enable/Disable */
48 #define BD_IMAD_DIAGNOSTIC BD_IMAD_DIAGNOSTIC_DISABLE
49
50 /* Diagnostic folder path */
51 #define BD_IMAD_DIAGNOSTIC_PATH "/mnt/sdcard/MUSIC/"
52
53 static wchar_t bdImadPjDiagnosticFolderPath[200];
54
55 #define THIS_FILE "bdimad_dev.c"
56
57 /* BD device info */
58 struct bddev_info
59 {
60 pjmedia_aud_dev_info info;
61 unsigned deviceId;
62 };
63
64 /* BD factory */
65 struct bd_factory
66 {
67 pjmedia_aud_dev_factory base;
68 pj_pool_t *base_pool;
69 pj_pool_t *pool;
70 pj_pool_factory *pf;
71 unsigned dev_count;
72 struct bddev_info *dev_info;
73 };
74
75 /* Sound stream. */
76 struct bd_stream
77 {
78 /** Base stream. */
79 pjmedia_aud_stream base;
80 /** Settings. */
81 pjmedia_aud_param param;
82 /** Memory pool. */
83 pj_pool_t *pool;
84
85 /** Capture callback. */
86 pjmedia_aud_rec_cb rec_cb;
87 /** Playback callback. */
88 pjmedia_aud_play_cb play_cb;
89 /** Application data. */
90 void *user_data;
91
92 /** Frame format */
93 pjmedia_format_id fmt_id;
94 /** Silence pattern */
95 pj_uint8_t silence_char;
96 /** Bytes per frame */
97 unsigned bytes_per_frame;
98 /** Samples per frame */
99 unsigned samples_per_frame;
100 /** Channel count */
101 int channel_count;
102
103 /** Extended frame buffer */
104 pjmedia_frame_ext *xfrm;
105 /** Total ext frm size */
106 unsigned xfrm_size;
107
108 /** Check running variable */
109 int go;
110
111 /** Timestamp iterator for capture */
112 pj_timestamp timestampCapture;
113 /** Timestamp iterator for playback */
114 pj_timestamp timestampPlayback;
115
116 /** bdIMAD current session instance */
117 bdIMADpj bdIMADpjInstance;
118 /** bdIMAD current session settings */
119 bdIMADpj_Setting_t *bdIMADpjSettingsPtr;
120 /** bdIMAD current session warnings */
121 bdIMADpj_Warnings_t *bdIMADpjWarningPtr;
122
123 pj_bool_t quit_flag;
124
125 pj_bool_t rec_thread_exited;
126 pj_bool_t rec_thread_initialized;
127 pj_thread_desc rec_thread_desc;
128 pj_thread_t *rec_thread;
129
130 pj_bool_t play_thread_exited;
131 pj_bool_t play_thread_initialized;
132 pj_thread_desc play_thread_desc;
133 pj_thread_t *play_thread;
134
135 /* Sometime the record callback does not return framesize as configured
136 * (e.g: in OSS), while this module must guarantee returning framesize
137 * as configured in the creation settings. In this case, we need a buffer
138 * for the recorded samples.
139 */
140 pj_int16_t *rec_buf;
141 unsigned rec_buf_count;
142
143 /* Sometime the player callback does not request framesize as configured
144 * (e.g: in Linux OSS) while sound device will always get samples from
145 * the other component as many as configured samples_per_frame.
146 */
147 pj_int16_t *play_buf;
148 unsigned play_buf_count;
149 };
150
151 /* Prototypes */
152
153 // pjmedia_aud_dev_factory_op
154 static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
155 static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
156 static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
157 static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
158 unsigned index,
159 pjmedia_aud_dev_info *info);
160 static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
161 unsigned index,
162 pjmedia_aud_param *param);
163 static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
164 const pjmedia_aud_param *param,
165 pjmedia_aud_rec_cb rec_cb,
166 pjmedia_aud_play_cb play_cb,
167 void *user_data,
168 pjmedia_aud_stream **p_aud_strm);
169 static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);
170
171 // pjmedia_aud_stream_op
172 static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
173 pjmedia_aud_param *param);
174 static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
175 pjmedia_aud_dev_cap cap,
176 void *value);
177 static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
178 pjmedia_aud_dev_cap cap,
179 const void *value);
180 static pj_status_t stream_start(pjmedia_aud_stream *strm);
181 static pj_status_t stream_stop(pjmedia_aud_stream *strm);
182 static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
183
184 /* End Prototypes */
185
186 /* Operations */
187 static pjmedia_aud_dev_factory_op factory_op =
188 {
189 &factory_init,
190 &factory_destroy,
191 &factory_get_dev_count,
192 &factory_get_dev_info,
193 &factory_default_param,
194 &factory_create_stream,
195 &factory_refresh
196 };
197
198 static pjmedia_aud_stream_op stream_op =
199 {
200 &stream_get_param,
201 &stream_get_cap,
202 &stream_set_cap,
203 &stream_start,
204 &stream_stop,
205 &stream_destroy
206 };
207
208 /* End Operations */
209
210 /* Utility functions */
211
BD_IMAD_PJ_WCHARtoCHAR(wchar_t * orig)212 char* BD_IMAD_PJ_WCHARtoCHAR(wchar_t *orig)
213 {
214 size_t origsize = wcslen(orig)+1;
215 const size_t newsize = origsize*sizeof(wchar_t);
216 char *nstring = (char*)calloc(newsize, sizeof(char));
217 wcstombs(nstring, orig, newsize);
218 return nstring;
219 }
220
221 #ifdef __cplusplus
222 extern "C" {
223 #endif
224 void manage_code(const unsigned char * pCode, unsigned char *pMsg1,
225 unsigned char * pMsg2);
226 #ifdef __cplusplus
227 }
228 #endif
229
230 /* End Utility functions */
231
232 /****************************************************************************
233 * Factory operations
234 */
235
236 /* Init BDIMAD audio driver */
pjmedia_bdimad_factory(pj_pool_factory * pf)237 pjmedia_aud_dev_factory* pjmedia_bdimad_factory(pj_pool_factory *pf)
238 {
239 struct bd_factory *f;
240 pj_pool_t *pool;
241
242 pool = pj_pool_create(pf, "BDIMAD_DRIVER", 1000, 1000, NULL);
243 f = PJ_POOL_ZALLOC_T(pool, struct bd_factory);
244 f->pf = pf;
245 f->base_pool = pool;
246 f->base.op = &factory_op;
247 f->pool = NULL;
248 f->dev_count = 0;
249 f->dev_info = NULL;
250
251 return &f->base;
252 }
253
254 /* API: init factory */
factory_init(pjmedia_aud_dev_factory * f)255 static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
256 {
257 pj_status_t ret = factory_refresh(f);
258 if (ret != PJ_SUCCESS) return ret;
259
260 PJ_LOG(4, (THIS_FILE, "BDIMAD initialized"));
261
262 return PJ_SUCCESS;
263 }
264
265 /* API: refresh the device list */
factory_refresh(pjmedia_aud_dev_factory * f)266 static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
267 {
268 struct bd_factory *wf = (struct bd_factory*)f;
269 unsigned int i = 0;
270 wchar_t *deviceNamep=NULL;
271 wchar_t captureDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
272 unsigned int captureDeviceCount = 0;
273 wchar_t playbackDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
274 unsigned int playbackDeviceCount = 0;
275
276
277 if(wf->pool != NULL) {
278 pj_pool_release(wf->pool);
279 wf->pool = NULL;
280 }
281
282 // Enumerate capture sound devices
283 while(bdIMADpj_getDeviceName(BD_IMAD_CAPTURE_DEVICES, &deviceNamep) !=
284 BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
285 {
286 wcscpy(captureDevName[captureDeviceCount], deviceNamep);
287 captureDeviceCount++;
288 }
289
290 // Enumerate playback sound devices
291 while(bdIMADpj_getDeviceName(BD_IMAD_PLAYBACK_DEVICES, &deviceNamep) !=
292 BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
293 {
294 wcscpy(playbackDevName[playbackDeviceCount], deviceNamep);
295 playbackDeviceCount++;
296 }
297
298 // Set devices info
299 wf->dev_count = captureDeviceCount + playbackDeviceCount;
300 wf->pool = pj_pool_create(wf->pf, "BD_IMAD_DEVICES", 1000, 1000, NULL);
301 wf->dev_info = (struct bddev_info*)pj_pool_calloc(wf->pool, wf->dev_count,
302 sizeof(struct bddev_info));
303
304 // Capture device properties
305 for(i=0;i<captureDeviceCount;i++) {
306 wf->dev_info[i].deviceId = i;
307 bdIMADpj_getDeviceCapabilities(BD_IMAD_CAPTURE_DEVICES,&wf->dev_info[i].info.caps);
308 wf->dev_info[i].info.default_samples_per_sec = BD_IMAD_DEFAULT_FREQ;
309 strcpy(wf->dev_info[i].info.driver, "BD_IMAD");
310 wf->dev_info[i].info.ext_fmt_cnt = 0;
311 wf->dev_info[i].info.input_count = BD_IMAD_MAX_CHANNELS;
312 wf->dev_info[i].info.output_count = 0;
313 strcpy(wf->dev_info[i].info.name,
314 BD_IMAD_PJ_WCHARtoCHAR(captureDevName[i]));
315 wf->dev_info[i].info.routes = 0;
316 }
317
318 // Playback device properties
319 for(i=0;i<playbackDeviceCount;i++) {
320 wf->dev_info[captureDeviceCount+i].deviceId = captureDeviceCount+i;
321 bdIMADpj_getDeviceCapabilities(BD_IMAD_PLAYBACK_DEVICES,&wf->dev_info[captureDeviceCount+i].info.caps);
322 wf->dev_info[captureDeviceCount+i].info.default_samples_per_sec =
323 BD_IMAD_DEFAULT_FREQ;
324 strcpy(wf->dev_info[captureDeviceCount+i].info.driver, "BD_IMAD");
325 wf->dev_info[captureDeviceCount+i].info.ext_fmt_cnt = 0;
326 wf->dev_info[captureDeviceCount+i].info.input_count = 0;
327 wf->dev_info[captureDeviceCount+i].info.output_count =
328 BD_IMAD_MAX_CHANNELS;
329 strcpy(wf->dev_info[captureDeviceCount+i].info.name,
330 BD_IMAD_PJ_WCHARtoCHAR(playbackDevName[i]));
331 wf->dev_info[captureDeviceCount+i].info.routes = 0;
332 }
333
334 PJ_LOG(4, (THIS_FILE, "BDIMAD found %d devices:", wf->dev_count));
335 for(i=0; i<wf->dev_count; i++) {
336 PJ_LOG(4,
337 (THIS_FILE, " dev_id %d: %s (in=%d, out=%d)",
338 i,
339 wf->dev_info[i].info.name,
340 wf->dev_info[i].info.input_count,
341 wf->dev_info[i].info.output_count));
342 }
343 return PJ_SUCCESS;
344 }
345
346 /* API: destroy factory */
factory_destroy(pjmedia_aud_dev_factory * f)347 static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
348 {
349 struct bd_factory *wf = (struct bd_factory*)f;
350 pj_pool_t *pool = wf->base_pool;
351
352 pj_pool_release(wf->pool);
353 wf->base_pool = NULL;
354 pj_pool_release(pool);
355
356 return PJ_SUCCESS;
357 }
358
359 /* API: get number of devices */
factory_get_dev_count(pjmedia_aud_dev_factory * f)360 static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
361 {
362 struct bd_factory *wf = (struct bd_factory*)f;
363 return wf->dev_count;
364 }
365
366 /* API: get device info */
factory_get_dev_info(pjmedia_aud_dev_factory * f,unsigned index,pjmedia_aud_dev_info * info)367 static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
368 unsigned index,
369 pjmedia_aud_dev_info *info)
370 {
371 struct bd_factory *wf = (struct bd_factory*)f;
372
373 PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV);
374
375 pj_memcpy(info, &wf->dev_info[index].info, sizeof(*info));
376
377 return PJ_SUCCESS;
378 }
379
380 /* API: create default device parameter */
factory_default_param(pjmedia_aud_dev_factory * f,unsigned index,pjmedia_aud_param * param)381 static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
382 unsigned index,
383 pjmedia_aud_param *param)
384 {
385 struct bd_factory *wf = (struct bd_factory*)f;
386 struct bddev_info *di = &wf->dev_info[index];
387
388 pj_bzero(param, sizeof(*param));
389 if (di->info.input_count && di->info.output_count) {
390 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
391 param->rec_id = index;
392 param->play_id = index;
393 param->channel_count = di->info.output_count;
394 } else if(di->info.input_count) {
395 param->dir = PJMEDIA_DIR_CAPTURE;
396 param->rec_id = index;
397 param->play_id = PJMEDIA_AUD_INVALID_DEV;
398 param->channel_count = di->info.input_count;
399 } else if(di->info.output_count) {
400 param->dir = PJMEDIA_DIR_PLAYBACK;
401 param->play_id = index;
402 param->rec_id = PJMEDIA_AUD_INVALID_DEV;
403 param->channel_count = di->info.output_count;
404 } else {
405 return PJMEDIA_EAUD_INVDEV;
406 }
407
408 param->bits_per_sample = BD_IMAD_BITS_X_SAMPLE;
409 param->clock_rate = di->info.default_samples_per_sec;
410 param->flags = di->info.caps;
411 param->samples_per_frame = di->info.default_samples_per_sec *
412 param->channel_count *
413 BD_IMAD_MSECOND_PER_BUFFER /
414 1000;
415
416 if(di->info.caps & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {
417 param->input_vol = BD_IMAD_STARTING_INPUT_VOLUME;
418 }
419
420 if(di->info.caps & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
421 param->output_vol = BD_IMAD_STARTING_OUTPUT_VOLUME;
422 }
423
424 if(di->info.caps & PJMEDIA_AUD_DEV_CAP_EC) {
425 param->ec_enabled = PJ_TRUE;
426 }
427
428 if(di->info.caps & PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE) {
429 param->output_route = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;
430 }
431
432 return PJ_SUCCESS;
433 }
434
435 /* callbacks to set data */
bdimad_CaptureCallback(void * buffer,int samples,void * user_data)436 void bdimad_CaptureCallback(void *buffer, int samples, void *user_data)
437 {
438 pj_status_t status = PJ_SUCCESS;
439 pjmedia_frame frame;
440 unsigned nsamples;
441
442 struct bd_stream *strm = (struct bd_stream*)user_data;
443
444 if(!strm->go)
445 goto on_break;
446
447 /* Known cases of callback's thread:
448 * - The thread may be changed in the middle of a session, e.g: in MacOS
449 * it happens when plugging/unplugging headphone.
450 * - The same thread may be reused in consecutive sessions. The first
451 * session will leave TLS set, but release the TLS data address,
452 * so the second session must re-register the callback's thread.
453 */
454 if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered())
455 {
456 pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
457 status = pj_thread_register("bd_CaptureCallback",
458 strm->rec_thread_desc,
459 &strm->rec_thread);
460 if (status != PJ_SUCCESS)
461 goto on_break;
462
463 strm->rec_thread_initialized = 1;
464 PJ_LOG(5,(THIS_FILE, "Recorder thread started"));
465 }
466
467 /* Calculate number of samples we've got */
468 nsamples = samples * strm->channel_count + strm->rec_buf_count;
469
470 /*
471 RECORD
472 */
473 if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
474 if (nsamples >= strm->samples_per_frame) {
475 /* If buffer is not empty, combine the buffer with the just incoming
476 * samples, then call put_frame.
477 */
478 if (strm->rec_buf_count) {
479 unsigned chunk_count = 0;
480
481 chunk_count = strm->samples_per_frame - strm->rec_buf_count;
482 pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
483 (pj_int16_t*)buffer, chunk_count);
484
485 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
486 frame.buf = (void*) strm->rec_buf;
487 frame.size = strm->bytes_per_frame;
488 frame.timestamp.u64 = strm->timestampCapture.u64;
489 frame.bit_info = 0;
490
491 status = (*strm->rec_cb)(strm->user_data, &frame);
492
493 buffer = (pj_int16_t*) buffer + chunk_count;
494 nsamples -= strm->samples_per_frame;
495 strm->rec_buf_count = 0;
496 strm->timestampCapture.u64 += strm->samples_per_frame /
497 strm->channel_count;
498 }
499
500 /* Give all frames we have */
501 while (nsamples >= strm->samples_per_frame && status == 0) {
502 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
503 frame.buf = (void*) buffer;
504 frame.size = strm->bytes_per_frame;
505 frame.timestamp.u64 = strm->timestampCapture.u64;
506 frame.bit_info = 0;
507
508 status = (*strm->rec_cb)(strm->user_data, &frame);
509
510 buffer = (pj_int16_t*) buffer + strm->samples_per_frame;
511 nsamples -= strm->samples_per_frame;
512 strm->timestampCapture.u64 += strm->samples_per_frame /
513 strm->channel_count;
514 }
515
516 /* Store the remaining samples into the buffer */
517 if (nsamples && status == 0) {
518 strm->rec_buf_count = nsamples;
519 pjmedia_copy_samples(strm->rec_buf, (pj_int16_t*)buffer,
520 nsamples);
521 }
522
523 } else {
524 /* Not enough samples, let's just store them in the buffer */
525 pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
526 (pj_int16_t*)buffer,
527 samples * strm->channel_count);
528 strm->rec_buf_count += samples * strm->channel_count;
529 }
530 } else {
531 pj_assert(!"Frame type not supported");
532 }
533
534 strm->timestampCapture.u64 += strm->param.samples_per_frame /
535 strm->param.channel_count;
536
537 if (status==0)
538 return;
539
540 on_break:
541 strm->rec_thread_exited = 1;
542 }
543
544 /* callbacks to get data */
bdimad_PlaybackCallback(void * buffer,int samples,void * user_data)545 int bdimad_PlaybackCallback(void *buffer, int samples, void *user_data)
546 {
547 pj_status_t status = PJ_SUCCESS;
548 pjmedia_frame frame;
549 struct bd_stream *strm = (struct bd_stream*)user_data;
550 unsigned nsamples_req = samples * strm->channel_count;
551
552 if(!strm->go)
553 goto on_break;
554
555 /* Known cases of callback's thread:
556 * - The thread may be changed in the middle of a session, e.g: in MacOS
557 * it happens when plugging/unplugging headphone.
558 * - The same thread may be reused in consecutive sessions. The first
559 * session will leave TLS set, but release the TLS data address,
560 * so the second session must re-register the callback's thread.
561 */
562 if (strm->play_thread_initialized == 0 || !pj_thread_is_registered())
563 {
564 pj_bzero(strm->play_thread_desc, sizeof(pj_thread_desc));
565 status = pj_thread_register("bd_PlaybackCallback",
566 strm->play_thread_desc,
567 &strm->play_thread);
568 if (status != PJ_SUCCESS)
569 goto on_break;
570
571 strm->play_thread_initialized = 1;
572 PJ_LOG(5,(THIS_FILE, "Player thread started"));
573 }
574
575 /*
576 PLAY
577 */
578 if(strm->fmt_id == PJMEDIA_FORMAT_L16) {
579 /* Check if any buffered samples */
580 if (strm->play_buf_count) {
581 /* samples buffered >= requested by sound device */
582 if (strm->play_buf_count >= nsamples_req) {
583 pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf,
584 nsamples_req);
585 strm->play_buf_count -= nsamples_req;
586 pjmedia_move_samples(strm->play_buf,
587 strm->play_buf + nsamples_req,
588 strm->play_buf_count);
589
590 return nsamples_req;
591 }
592
593 /* samples buffered < requested by sound device */
594 pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf,
595 strm->play_buf_count);
596 nsamples_req -= strm->play_buf_count;
597 buffer = (pj_int16_t*)buffer + strm->play_buf_count;
598 strm->play_buf_count = 0;
599 }
600
601 /* Fill output buffer as requested */
602 while (nsamples_req && status == 0) {
603 if (nsamples_req >= strm->samples_per_frame) {
604 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
605 frame.buf = buffer;
606 frame.size = strm->bytes_per_frame;
607 frame.timestamp.u64 = strm->timestampPlayback.u64;
608 frame.bit_info = 0;
609
610 status = (*strm->play_cb)(strm->user_data, &frame);
611 if (status != PJ_SUCCESS)
612 return 0;
613
614 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
615 pj_bzero(frame.buf, frame.size);
616
617 nsamples_req -= strm->samples_per_frame;
618 buffer = (pj_int16_t*)buffer + strm->samples_per_frame;
619 } else {
620 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
621 frame.buf = strm->play_buf;
622 frame.size = strm->bytes_per_frame;
623 frame.timestamp.u64 = strm->timestampPlayback.u64;
624 frame.bit_info = 0;
625
626 status = (*strm->play_cb)(strm->user_data, &frame);
627 if (status != PJ_SUCCESS)
628 return 0;
629
630 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
631 pj_bzero(frame.buf, frame.size);
632
633 pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf,
634 nsamples_req);
635 strm->play_buf_count = strm->samples_per_frame -
636 nsamples_req;
637 pjmedia_move_samples(strm->play_buf,
638 strm->play_buf+nsamples_req,
639 strm->play_buf_count);
640 nsamples_req = 0;
641 }
642
643 strm->timestampPlayback.u64 += strm->samples_per_frame /
644 strm->channel_count;
645 }
646 } else {
647 pj_assert(!"Frame type not supported");
648 }
649
650 if(status != PJ_SUCCESS) {
651 return 0;
652 }
653
654 strm->timestampPlayback.u64 += strm->param.samples_per_frame /
655 strm->param.channel_count;
656
657 if (status == 0)
658 return samples;
659
660 on_break:
661 strm->play_thread_exited = 1;
662 return 0;
663 }
664
665 /* Internal: Get format name */
get_fmt_name(pj_uint32_t id)666 static const char *get_fmt_name(pj_uint32_t id)
667 {
668 static char name[8];
669
670 if (id == PJMEDIA_FORMAT_L16)
671 return "PCM";
672 pj_memcpy(name, &id, 4);
673 name[4] = '\0';
674 return name;
675 }
676
677 /* Internal: create BD device. */
init_streams(struct bd_factory * wf,struct bd_stream * strm,const pjmedia_aud_param * prm)678 static pj_status_t init_streams(struct bd_factory *wf,
679 struct bd_stream *strm,
680 const pjmedia_aud_param *prm)
681 {
682 unsigned ptime;
683 enum bdIMADpj_Status errorInitAEC;
684 wchar_t *deviceNamep=NULL;
685 wchar_t captureDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
686 int captureDeviceCount = 0;
687 wchar_t playbackDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
688 int playbackDeviceCount = 0;
689
690 PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL);
691 PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL);
692
693 ptime = prm->samples_per_frame *
694 1000 /
695 (prm->clock_rate * prm->channel_count);
696 strm->bytes_per_frame = (prm->clock_rate *
697 ((prm->channel_count * prm->bits_per_sample) / 8)) *
698 ptime /
699 1000;
700 strm->timestampCapture.u64 = 0;
701 strm->timestampPlayback.u64 = 0;
702
703 //BD_IMAD_PJ
704 bdIMADpj_CreateStructures(&strm->bdIMADpjSettingsPtr,
705 &strm->bdIMADpjWarningPtr);
706
707 strm->bdIMADpjSettingsPtr->FrameSize_ms = ptime;
708 strm->bdIMADpjSettingsPtr->DiagnosticEnable = BD_IMAD_DIAGNOSTIC;
709 mbstowcs(bdImadPjDiagnosticFolderPath, BD_IMAD_DIAGNOSTIC_PATH, strlen(BD_IMAD_DIAGNOSTIC_PATH));
710 strm->bdIMADpjSettingsPtr->DiagnosticFolderPath = bdImadPjDiagnosticFolderPath;
711 strm->bdIMADpjSettingsPtr->validate = (void *)manage_code;
712
713 if(prm->clock_rate != 8000 && prm->clock_rate != 16000
714 && prm->clock_rate != 32000 && prm->clock_rate != 44100 && prm->clock_rate != 48000) {
715 PJ_LOG(4, (THIS_FILE,
716 "BDIMAD support 8000 Hz, 16000 Hz, 32000 Hz, 44100 Hz and 48000 Hz "
717 "frequency."));
718 }
719 strm->bdIMADpjSettingsPtr->SamplingFrequency = prm->clock_rate;
720
721 if(prm->channel_count > BD_IMAD_MAX_CHANNELS) {
722 PJ_LOG(4, (THIS_FILE,
723 "BDIMAD doesn't support a number of channels upper than %d.",
724 BD_IMAD_MAX_CHANNELS));
725 }
726
727 // Enumerate capture sound devices
728 while(bdIMADpj_getDeviceName(BD_IMAD_CAPTURE_DEVICES, &deviceNamep) !=
729 BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
730 {
731 wcscpy(captureDevName[captureDeviceCount], deviceNamep);
732 captureDeviceCount++;
733 }
734 strm->bdIMADpjSettingsPtr->CaptureDevice = captureDevName[(int)prm->rec_id];
735
736 // Enumerate playback sound devices
737 while(bdIMADpj_getDeviceName(BD_IMAD_PLAYBACK_DEVICES, &deviceNamep) !=
738 BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
739 {
740 wcscpy(playbackDevName[playbackDeviceCount], deviceNamep);
741 playbackDeviceCount++;
742 }
743 strm->bdIMADpjSettingsPtr->PlayDevice =
744 playbackDevName[(int)(prm->play_id-captureDeviceCount)];
745
746 strm->bdIMADpjSettingsPtr->cb_emptyCaptureBuffer = &bdimad_CaptureCallback;
747 strm->bdIMADpjSettingsPtr->cb_emptyCaptureBuffer_user_data = (void*)strm;
748 strm->bdIMADpjSettingsPtr->cb_fillPlayBackBuffer = &bdimad_PlaybackCallback;
749 strm->bdIMADpjSettingsPtr->cb_fillPlayBackBuffer_user_data = (void*)strm;
750
751 if(strm->bdIMADpjInstance != NULL)
752 bdIMADpj_FreeAEC(&strm->bdIMADpjInstance);
753 strm->bdIMADpjInstance = NULL;
754
755 errorInitAEC = bdIMADpj_InitAEC(&strm->bdIMADpjInstance,
756 &strm->bdIMADpjSettingsPtr,
757 &strm->bdIMADpjWarningPtr);
758
759 {
760 int auxInt = (prm->ec_enabled == PJ_TRUE ? 1 : 0);
761 bdIMADpj_setParameter(strm->bdIMADpjInstance,
762 BD_PARAM_IMAD_PJ_AEC_ENABLE,
763 &auxInt);
764 auxInt = 1;
765 //Mic control On by default
766 bdIMADpj_setParameter(strm->bdIMADpjInstance,
767 BD_PARAM_IMAD_PJ_MIC_CONTROL_ENABLE,
768 &auxInt);
769
770 // Enable GUI Socket Communication [default->disabled]
771 bdIMADpj_enableGuiSocketCommunication(strm->bdIMADpjInstance,27000,0);
772 }
773
774 if(errorInitAEC != BD_PJ_OK &&
775 errorInitAEC != BD_PJ_WARN_BDIMAD_WARNING_ASSERTED)
776 {
777 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(errorInitAEC);
778 }
779
780 return PJ_SUCCESS;
781 }
782
783 /****************************************
784 API: create stream
785 *****************************************/
stream_stopBDIMAD(pjmedia_aud_stream * s)786 static pj_status_t stream_stopBDIMAD(pjmedia_aud_stream *s)
787 {
788 struct bd_stream *strm = (struct bd_stream*)s;
789 pj_status_t status = PJ_SUCCESS;
790 int i, err = 0;
791
792 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
793
794 strm->go = 0;
795
796 for (i=0; !strm->rec_thread_exited && i<100; ++i)
797 pj_thread_sleep(10);
798 for (i=0; !strm->play_thread_exited && i<100; ++i)
799 pj_thread_sleep(10);
800
801 pj_thread_sleep(1);
802
803 PJ_LOG(5,(THIS_FILE, "Stopping stream.."));
804
805 strm->play_thread_initialized = 0;
806 strm->rec_thread_initialized = 0;
807
808 PJ_LOG(5,(THIS_FILE, "Done, status=%d", err));
809
810 return status;
811 }
812
stream_stop(pjmedia_aud_stream * s)813 static pj_status_t stream_stop(pjmedia_aud_stream *s)
814 {
815 struct bd_stream *strm = (struct bd_stream*)s;
816
817 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
818
819 if(strm->bdIMADpjInstance != NULL) {
820 return stream_stopBDIMAD(s);
821 } else {
822 return PJMEDIA_EAUD_ERR;
823 }
824 }
825
stream_destroyBDIMAD(pjmedia_aud_stream * s)826 static pj_status_t stream_destroyBDIMAD(pjmedia_aud_stream *s)
827 {
828 struct bd_stream *strm = (struct bd_stream*)s;
829 int i = 0;
830
831 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
832
833 stream_stopBDIMAD(s);
834
835 // DeInit BDIMAD
836 bdIMADpj_FreeAEC(&strm->bdIMADpjInstance);
837 PJ_LOG(4, (THIS_FILE, "Free AEC"));
838
839 bdIMADpj_FreeStructures(&strm->bdIMADpjSettingsPtr,
840 &strm->bdIMADpjWarningPtr);
841 PJ_LOG(4, (THIS_FILE, "Free AEC Structure"));
842
843 strm->bdIMADpjInstance = NULL;
844 strm->bdIMADpjSettingsPtr = NULL;
845 strm->bdIMADpjWarningPtr = NULL;
846
847 strm->quit_flag = 1;
848 for (i=0; !strm->rec_thread_exited && i<100; ++i) {
849 pj_thread_sleep(1);
850 }
851 for (i=0; !strm->play_thread_exited && i<100; ++i) {
852 pj_thread_sleep(1);
853 }
854
855 PJ_LOG(5,(THIS_FILE, "Destroying stream.."));
856
857 pj_pool_release(strm->pool);
858 return PJ_SUCCESS;
859 }
860
861 /* API: Destroy stream. */
stream_destroy(pjmedia_aud_stream * s)862 static pj_status_t stream_destroy(pjmedia_aud_stream *s)
863 {
864 struct bd_stream *strm = (struct bd_stream*)s;
865
866 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
867
868 if(strm->bdIMADpjInstance != NULL) {
869 return stream_destroyBDIMAD(s);
870 } else {
871 return PJMEDIA_EAUD_ERR;
872 }
873 }
874
875 /* API: set capability */
stream_set_capBDIMAD(pjmedia_aud_stream * s,pjmedia_aud_dev_cap cap,const void * pval)876 static pj_status_t stream_set_capBDIMAD(pjmedia_aud_stream *s,
877 pjmedia_aud_dev_cap cap,
878 const void *pval)
879 {
880 struct bd_stream *strm = (struct bd_stream*)s;
881 bdIMADpj_Status res = BD_PJ_OK;
882 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
883
884 if(cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
885 /* Output volume setting */
886 float vol = (float)*(unsigned*)pval;
887
888 if(vol > 100.0f) vol = 100.0f;
889 if(vol < 0.0f) vol = 0.0f;
890
891 vol = vol / 100.0f;
892 res = bdIMADpj_setParameter(strm->bdIMADpjInstance,
893 BD_PARAM_IMAD_PJ_SPK_VOLUME, &vol);
894
895
896 if(res == BD_PJ_OK) {
897 strm->param.output_vol = *(unsigned*)pval;
898 return PJ_SUCCESS;
899 } else {
900 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
901 }
902 }
903
904 if(cap == PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {
905 /* Input volume setting */
906 float vol = (float)*(unsigned*)pval;
907
908 if(vol > 100.0f) vol = 100.0f;
909 if(vol < 0.0f) vol = 0.0f;
910
911 vol = vol / 100.0f;
912 res = bdIMADpj_setParameter(strm->bdIMADpjInstance,
913 BD_PARAM_IMAD_PJ_MIC_VOLUME, &vol);
914 if(res == BD_PJ_OK) {
915 strm->param.input_vol = *(unsigned*)pval;
916 return PJ_SUCCESS;
917 } else {
918 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
919 }
920 }
921
922 if(cap == PJMEDIA_AUD_DEV_CAP_EC) {
923 int aecOnOff = (*(pj_bool_t*)pval == PJ_TRUE ? 1 : 0);
924
925 /* AEC setting */
926 res = bdIMADpj_setParameter(strm->bdIMADpjInstance,
927 BD_PARAM_IMAD_PJ_AEC_ENABLE,
928 &aecOnOff);
929 if(res == BD_PJ_OK) {
930 strm->param.ec_enabled = (aecOnOff == 1 ? PJ_TRUE : PJ_FALSE);
931 return PJ_SUCCESS;
932 } else {
933 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
934 }
935 }
936
937 if(cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE) {
938 pjmedia_aud_dev_route outputRoute = *(pjmedia_aud_dev_route*)pval;
939 res = bdIMADpj_setRouteOutputDevice(strm->bdIMADpjInstance, (bdIMADpj_out_dev_route) outputRoute, &strm->bdIMADpjWarningPtr);
940 if(res == BD_PJ_OK) {
941 strm->param.output_route = outputRoute;
942 return PJ_SUCCESS;
943 } else {
944 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
945 }
946 }
947
948 return PJMEDIA_EAUD_INVCAP;
949 }
950
factory_create_streamBDIMAD(pjmedia_aud_dev_factory * f,const pjmedia_aud_param * param,pjmedia_aud_rec_cb rec_cb,pjmedia_aud_play_cb play_cb,void * user_data,pjmedia_aud_stream ** p_aud_strm)951 static pj_status_t factory_create_streamBDIMAD(pjmedia_aud_dev_factory *f,
952 const pjmedia_aud_param *param,
953 pjmedia_aud_rec_cb rec_cb,
954 pjmedia_aud_play_cb play_cb,
955 void *user_data,
956 pjmedia_aud_stream **p_aud_strm)
957 {
958 struct bd_factory *wf = (struct bd_factory*)f;
959 pj_pool_t *pool;
960 struct bd_stream *strm;
961 pj_uint8_t silence_char;
962 pj_status_t status;
963
964 switch (param->ext_fmt.id) {
965 case PJMEDIA_FORMAT_L16:
966 silence_char = '\0';
967 break;
968 default:
969 return PJMEDIA_EAUD_BADFORMAT;
970 }
971
972 /* Create and Initialize stream descriptor */
973 pool = pj_pool_create(wf->pf, "BDIMAD_STREAM", 1000, 1000, NULL);
974 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
975
976 strm = PJ_POOL_ZALLOC_T(pool, struct bd_stream);
977 pj_memcpy(&strm->param, param, sizeof(*param));
978 strm->pool = pool;
979 strm->rec_cb = rec_cb;
980 strm->play_cb = play_cb;
981 strm->user_data = user_data;
982 strm->fmt_id = (pjmedia_format_id)param->ext_fmt.id;
983 strm->silence_char = silence_char;
984 strm->channel_count = param->channel_count;
985 strm->samples_per_frame = param->samples_per_frame;
986
987 if (param->dir & PJMEDIA_DIR_CAPTURE_PLAYBACK) {
988 status = init_streams(wf, strm, param);
989
990 if (status != PJ_SUCCESS) {
991 stream_destroyBDIMAD(&strm->base);
992 return status;
993 }
994 } else {
995 stream_destroyBDIMAD(&strm->base);
996 return PJMEDIA_EAUD_ERR;
997 }
998
999 strm->rec_buf = (pj_int16_t*)pj_pool_alloc(pool,
1000 strm->bytes_per_frame);
1001 if (!strm->rec_buf) {
1002 pj_pool_release(pool);
1003 return PJ_ENOMEM;
1004 }
1005 strm->rec_buf_count = 0;
1006
1007 strm->play_buf = (pj_int16_t*)pj_pool_alloc(pool,
1008 strm->bytes_per_frame);
1009 if (!strm->play_buf) {
1010 pj_pool_release(pool);
1011 return PJ_ENOMEM;
1012 }
1013 strm->play_buf_count = 0;
1014
1015 /* Apply the remaining settings */
1016 if(param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1017 stream_set_capBDIMAD(&strm->base,
1018 PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1019 ¶m->output_vol);
1020 }
1021 if(param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {
1022 stream_set_capBDIMAD(&strm->base,
1023 PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
1024 ¶m->input_vol);
1025 }
1026 if(param->flags & PJMEDIA_AUD_DEV_CAP_EC) {
1027 stream_set_capBDIMAD(&strm->base,
1028 PJMEDIA_AUD_DEV_CAP_EC,
1029 ¶m->ec_enabled);
1030 }
1031
1032 if(param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE) {
1033 stream_set_capBDIMAD(&strm->base,
1034 PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
1035 ¶m->output_route);
1036 }
1037
1038 strm->base.op = &stream_op;
1039 *p_aud_strm = &strm->base;
1040
1041 return PJ_SUCCESS;
1042 }
1043
factory_create_stream(pjmedia_aud_dev_factory * f,const pjmedia_aud_param * param,pjmedia_aud_rec_cb rec_cb,pjmedia_aud_play_cb play_cb,void * user_data,pjmedia_aud_stream ** p_aud_strm)1044 static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
1045 const pjmedia_aud_param *param,
1046 pjmedia_aud_rec_cb rec_cb,
1047 pjmedia_aud_play_cb play_cb,
1048 void *user_data,
1049 pjmedia_aud_stream **p_aud_strm)
1050 {
1051 return factory_create_streamBDIMAD(f, param, rec_cb,
1052 play_cb, user_data, p_aud_strm);
1053 }
1054 // ----------------------------------------------------------------------
1055 // ----------------------------------------------------------------------
1056 // ----------------------------------------------------------------------
1057
1058 /* API: Get stream info. */
stream_get_param(pjmedia_aud_stream * s,pjmedia_aud_param * pi)1059 static pj_status_t stream_get_param(pjmedia_aud_stream *s,
1060 pjmedia_aud_param *pi)
1061 {
1062 struct bd_stream *strm = (struct bd_stream*)s;
1063
1064 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
1065
1066 pj_memcpy(pi, &strm->param, sizeof(*pi));
1067
1068 // Get the output volume setting
1069 if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
1070 &pi->output_vol) == PJ_SUCCESS)
1071 {
1072 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
1073 }
1074
1075 // Get the input volume setting
1076 if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
1077 &pi->input_vol) == PJ_SUCCESS)
1078 {
1079 pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING;
1080 }
1081
1082 // Get the AEC setting
1083 if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_EC, &pi->ec_enabled) == PJ_SUCCESS)
1084 {
1085 pi->flags |= PJMEDIA_AUD_DEV_CAP_EC;
1086 }
1087 if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_EC, &pi->ec_enabled) == PJ_SUCCESS)
1088 {
1089 pi->flags |= PJMEDIA_AUD_DEV_CAP_EC;
1090 }
1091
1092
1093 // Get the Route Output Device setting
1094 if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, &pi->output_route) == PJ_SUCCESS)
1095 {
1096 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
1097 }
1098
1099 return PJ_SUCCESS;
1100 }
1101
stream_get_capBDIMAD(pjmedia_aud_stream * s,pjmedia_aud_dev_cap cap,void * pval)1102 static pj_status_t stream_get_capBDIMAD(pjmedia_aud_stream *s,
1103 pjmedia_aud_dev_cap cap,
1104 void *pval)
1105 {
1106 struct bd_stream *strm = (struct bd_stream*)s;
1107 bdIMADpj_Status res = BD_PJ_OK;
1108
1109 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
1110
1111 if(cap == PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING)
1112 {
1113 /* Input volume setting */
1114 float vol;
1115 res = bdIMADpj_getParameter(strm->bdIMADpjInstance,
1116 BD_PARAM_IMAD_PJ_MIC_VOLUME, &vol);
1117 if(res == BD_PJ_OK) {
1118 vol = vol * 100;
1119 if(vol > 100.0f) vol = 100.0f;
1120 if(vol < 0.0f) vol = 0.0f;
1121 *(unsigned int *)pval = (unsigned int)vol;
1122 return PJ_SUCCESS;
1123 } else{
1124 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
1125 }
1126 } else if(cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
1127 /* Output volume setting */
1128 float vol;
1129 res = bdIMADpj_getParameter(strm->bdIMADpjInstance,
1130 BD_PARAM_IMAD_PJ_SPK_VOLUME, &vol);
1131 if(res == BD_PJ_OK) {
1132 vol = vol * 100;
1133 if(vol > 100.0f) vol = 100.0f;
1134 if(vol < 0.0f) vol = 0.0f;
1135 *(unsigned int *)pval = (unsigned int)vol;
1136 return PJ_SUCCESS;
1137 } else {
1138 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
1139 }
1140 }
1141 else if(cap == PJMEDIA_AUD_DEV_CAP_EC) {
1142 int aecIsOn;
1143 res = bdIMADpj_getParameter(strm->bdIMADpjInstance,
1144 BD_PARAM_IMAD_PJ_AEC_ENABLE, &aecIsOn);
1145 if(res == BD_PJ_OK) {
1146 *(pj_bool_t*)pval = (aecIsOn == 1 ? PJ_TRUE : PJ_FALSE);
1147 return PJ_SUCCESS;
1148 } else {
1149 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
1150 }
1151 }
1152 else if(cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE) {
1153 pjmedia_aud_dev_route routeOutDev;
1154 res = bdIMADpj_getRouteOutputDevice(strm->bdIMADpjInstance,(bdIMADpj_out_dev_route*)&routeOutDev);
1155 if(res == BD_PJ_OK) {
1156 *(pjmedia_aud_dev_route*)pval = routeOutDev;
1157 return PJ_SUCCESS;
1158 } else {
1159 return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
1160 }
1161 } else {
1162 return PJMEDIA_EAUD_INVCAP;
1163 }
1164 }
1165
stream_startBDIMAD(pjmedia_aud_stream * s)1166 static pj_status_t stream_startBDIMAD(pjmedia_aud_stream *s)
1167 {
1168 struct bd_stream *strm = (struct bd_stream*)s;
1169
1170 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
1171
1172 strm->go = 1;
1173
1174 return PJ_SUCCESS;
1175 }
1176
1177 /* API: get capability */
stream_get_cap(pjmedia_aud_stream * s,pjmedia_aud_dev_cap cap,void * pval)1178 static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
1179 pjmedia_aud_dev_cap cap,
1180 void *pval)
1181 {
1182 struct bd_stream *strm = (struct bd_stream*)s;
1183
1184 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
1185
1186 if(strm->bdIMADpjInstance != NULL) {
1187 return stream_get_capBDIMAD(s, cap, pval);
1188 } else {
1189 return PJMEDIA_EAUD_ERR;
1190 }
1191 }
1192
1193 /* API: set capability */
stream_set_cap(pjmedia_aud_stream * s,pjmedia_aud_dev_cap cap,const void * pval)1194 static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
1195 pjmedia_aud_dev_cap cap,
1196 const void *pval)
1197 {
1198 struct bd_stream *strm = (struct bd_stream*)s;
1199
1200 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
1201
1202 if(strm->bdIMADpjInstance != NULL) {
1203 return stream_set_capBDIMAD(s, cap, pval);
1204 } else {
1205 return PJMEDIA_EAUD_ERR;
1206 }
1207 }
1208
1209 /* API: Start stream. */
stream_start(pjmedia_aud_stream * s)1210 static pj_status_t stream_start(pjmedia_aud_stream *s)
1211 {
1212 struct bd_stream *strm = (struct bd_stream*)s;
1213
1214 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
1215
1216 if(strm->bdIMADpjInstance != NULL) {
1217 return stream_startBDIMAD(s);
1218 } else {
1219 return PJMEDIA_EAUD_ERR;
1220 }
1221 }
1222
1223 #if defined (_MSC_VER)
1224 #pragma comment ( lib, "bdClientValidation.lib" )
1225 #pragma comment ( lib, "bdIMADpj.lib" )
1226 #endif
1227
1228
1229 #endif /* PJMEDIA_AUDIO_DEV_HAS_BDIMAD */
1230
1231