summaryrefslogtreecommitdiff
path: root/pjmedia/src
diff options
context:
space:
mode:
authorRiza Sulistyo <riza@teluu.com>2013-03-08 08:02:48 +0000
committerRiza Sulistyo <riza@teluu.com>2013-03-08 08:02:48 +0000
commitb24558f7cbc9fabf6f2c5824c9ae9ef8b0b73f3e (patch)
tree94278ee3bd32303e82a912b8917d5f5b5d7a12c1 /pjmedia/src
parent2585e16cf26de19fc010c37335b13525473caf7d (diff)
Re #1636: add initial support for bdIMAD
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4432 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia/src')
-rw-r--r--pjmedia/src/pjmedia-audiodev/audiodev.c7
-rw-r--r--pjmedia/src/pjmedia-audiodev/bdimad_dev.c1185
-rw-r--r--pjmedia/src/pjmedia-audiodev/errno.c14
3 files changed, 1206 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia-audiodev/audiodev.c b/pjmedia/src/pjmedia-audiodev/audiodev.c
index 53e63209..f9ee4175 100644
--- a/pjmedia/src/pjmedia-audiodev/audiodev.c
+++ b/pjmedia/src/pjmedia-audiodev/audiodev.c
@@ -86,6 +86,10 @@ pjmedia_aud_dev_factory* pjmedia_bb10_factory(pj_pool_factory *pf);
pjmedia_aud_dev_factory* pjmedia_wmme_factory(pj_pool_factory *pf);
#endif
+#if PJMEDIA_AUDIO_DEV_HAS_BDIMAD
+pjmedia_aud_dev_factory* pjmedia_bdimad_factory(pj_pool_factory *pf);
+#endif
+
#if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
pjmedia_aud_dev_factory* pjmedia_symb_vas_factory(pj_pool_factory *pf);
#endif
@@ -406,6 +410,9 @@ PJ_DEF(pj_status_t) pjmedia_aud_subsys_init(pj_pool_factory *pf)
#if PJMEDIA_AUDIO_DEV_HAS_WMME
aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_wmme_factory;
#endif
+#if PJMEDIA_AUDIO_DEV_HAS_BDIMAD
+ aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_bdimad_factory;
+#endif
#if PJMEDIA_AUDIO_DEV_HAS_SYMB_VAS
aud_subsys.drv[aud_subsys.drv_cnt++].create = &pjmedia_symb_vas_factory;
#endif
diff --git a/pjmedia/src/pjmedia-audiodev/bdimad_dev.c b/pjmedia/src/pjmedia-audiodev/bdimad_dev.c
new file mode 100644
index 00000000..1f3c5a04
--- /dev/null
+++ b/pjmedia/src/pjmedia-audiodev/bdimad_dev.c
@@ -0,0 +1,1185 @@
+/*******************************************************
+
+ bdimad_dev.c
+
+ Author: bdSound Development Team (techsupport@bdsound.com)
+
+ Date: 30/10/2012
+ Version 1.0.206
+
+ Copyright (c) 2012 bdSound s.r.l. (www.bdsound.com)
+ All Rights Reserved.
+
+ *******************************************************/
+
+#include <pjmedia-audiodev/audiodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/unicode.h>
+#include <pjmedia/errno.h>
+
+#if defined(PJMEDIA_AUDIO_DEV_HAS_BDIMAD) && PJMEDIA_AUDIO_DEV_HAS_BDIMAD != 0
+
+#include <math.h>
+#include <wchar.h>
+
+/**************************
+* bdIMAD
+***************************/
+#include <bdimad.h>
+
+/* Maximum supported audio devices */
+#define BD_IMAD_MAX_DEV_COUNT 100
+/* Maximum supported device name */
+#define BD_IMAD_MAX_DEV_LENGTH_NAME 256
+/* Only mono mode */
+#define BD_IMAD_MAX_CHANNELS 1
+/* Frequency default value (admitted 8000 Hz, 16000 Hz, 32000 Hz and 48000Hz) */
+#define BD_IMAD_DEFAULT_FREQ 48000
+/* Default milliseconds per buffer */
+#define BD_IMAD_MSECOND_PER_BUFFER 16
+/* Only for supported systems */
+#define BD_IMAD_STARTING_INPUT_VOLUME 50
+/* Only for supported systems */
+#define BD_IMAD_STARTING_OUTPUT_VOLUME 100
+/* Diagnostic Enable/Disable */
+#define BD_IMAD_DIAGNOSTIC BD_IMAD_DIAGNOSTIC_DISABLE
+
+/* Diagnostic folder path */
+wchar_t * bdImadPjDiagnosticFolderPath = L"";
+
+#define THIS_FILE "bdimad_dev.c"
+
+/* BD device info */
+struct bddev_info
+{
+ pjmedia_aud_dev_info info;
+ unsigned deviceId;
+};
+
+/* BD factory */
+struct bd_factory
+{
+ pjmedia_aud_dev_factory base;
+ pj_pool_t *base_pool;
+ pj_pool_t *pool;
+ pj_pool_factory *pf;
+ unsigned dev_count;
+ struct bddev_info *dev_info;
+};
+
+/* Sound stream. */
+struct bd_stream
+{
+ /** Base stream. */
+ pjmedia_aud_stream base;
+ /** Settings. */
+ pjmedia_aud_param param;
+ /** Memory pool. */
+ pj_pool_t *pool;
+
+ /** Capture callback. */
+ pjmedia_aud_rec_cb rec_cb;
+ /** Playback callback. */
+ pjmedia_aud_play_cb play_cb;
+ /** Application data. */
+ void *user_data;
+
+ /** Frame format */
+ pjmedia_format_id fmt_id;
+ /** Silence pattern */
+ pj_uint8_t silence_char;
+ /** Bytes per frame */
+ unsigned bytes_per_frame;
+ /** Samples per frame */
+ unsigned samples_per_frame;
+ /** Channel count */
+ int channel_count;
+
+ /** Extended frame buffer */
+ pjmedia_frame_ext *xfrm;
+ /** Total ext frm size */
+ unsigned xfrm_size;
+
+ /** Check running variable */
+ int go;
+
+ /** Timestamp iterator for capture */
+ pj_timestamp timestampCapture;
+ /** Timestamp iterator for playback */
+ pj_timestamp timestampPlayback;
+
+ /** bdIMAD current session instance */
+ bdIMADpj bdIMADpjInstance;
+ /** bdIMAD current session settings */
+ bdIMADpj_Setting_t *bdIMADpjSettingsPtr;
+ /** bdIMAD current session warnings */
+ bdIMADpj_Warnings_t *bdIMADpjWarningPtr;
+
+ pj_bool_t quit_flag;
+
+ pj_bool_t rec_thread_exited;
+ pj_bool_t rec_thread_initialized;
+ pj_thread_desc rec_thread_desc;
+ pj_thread_t *rec_thread;
+
+ pj_bool_t play_thread_exited;
+ pj_bool_t play_thread_initialized;
+ pj_thread_desc play_thread_desc;
+ pj_thread_t *play_thread;
+
+ /* Sometime the record callback does not return framesize as configured
+ * (e.g: in OSS), while this module must guarantee returning framesize
+ * as configured in the creation settings. In this case, we need a buffer
+ * for the recorded samples.
+ */
+ pj_int16_t *rec_buf;
+ unsigned rec_buf_count;
+
+ /* Sometime the player callback does not request framesize as configured
+ * (e.g: in Linux OSS) while sound device will always get samples from
+ * the other component as many as configured samples_per_frame.
+ */
+ pj_int16_t *play_buf;
+ unsigned play_buf_count;
+};
+
+/* Prototypes */
+
+// pjmedia_aud_dev_factory_op
+static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
+static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
+static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
+static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_dev_info *info);
+static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_param *param);
+static pj_status_t 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);
+static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);
+
+// pjmedia_aud_stream_op
+static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
+ pjmedia_aud_param *param);
+static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
+ pjmedia_aud_dev_cap cap,
+ void *value);
+static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
+ pjmedia_aud_dev_cap cap,
+ const void *value);
+static pj_status_t stream_start(pjmedia_aud_stream *strm);
+static pj_status_t stream_stop(pjmedia_aud_stream *strm);
+static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
+
+/* End Prototypes */
+
+/* Operations */
+static pjmedia_aud_dev_factory_op factory_op =
+{
+ &factory_init,
+ &factory_destroy,
+ &factory_get_dev_count,
+ &factory_get_dev_info,
+ &factory_default_param,
+ &factory_create_stream,
+ &factory_refresh
+};
+
+static pjmedia_aud_stream_op stream_op =
+{
+ &stream_get_param,
+ &stream_get_cap,
+ &stream_set_cap,
+ &stream_start,
+ &stream_stop,
+ &stream_destroy
+};
+
+/* End Operations */
+
+/* Utility functions */
+
+char* BD_IMAD_PJ_WCHARtoCHAR(wchar_t *orig)
+{
+ size_t origsize = wcslen(orig)+1;
+ const size_t newsize = origsize*sizeof(wchar_t);
+ char *nstring = (char*)calloc(newsize, sizeof(char));
+ wcstombs(nstring, orig, newsize);
+ return nstring;
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void manage_code(const unsigned char * pCode, unsigned char *pMsg1,
+ unsigned char * pMsg2);
+#ifdef __cplusplus
+}
+#endif
+
+/* End Utility functions */
+
+/****************************************************************************
+* Factory operations
+*/
+
+/* Init BDIMAD audio driver */
+pjmedia_aud_dev_factory* pjmedia_bdimad_factory(pj_pool_factory *pf)
+{
+ struct bd_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "BDIMAD_DRIVER", 1000, 1000, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct bd_factory);
+ f->pf = pf;
+ f->base_pool = pool;
+ f->base.op = &factory_op;
+ f->pool = NULL;
+ f->dev_count = 0;
+ f->dev_info = NULL;
+
+ return &f->base;
+}
+
+/* API: init factory */
+static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
+{
+ pj_status_t ret = factory_refresh(f);
+ if (ret != PJ_SUCCESS) return ret;
+
+ PJ_LOG(4, (THIS_FILE, "BDIMAD initialized"));
+
+ return PJ_SUCCESS;
+}
+
+/* API: refresh the device list */
+static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
+{
+ struct bd_factory *wf = (struct bd_factory*)f;
+ unsigned int i = 0;
+ wchar_t *deviceNamep=NULL;
+ wchar_t captureDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
+ unsigned int captureDeviceCount = 0;
+ wchar_t playbackDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
+ unsigned int playbackDeviceCount = 0;
+
+
+ if(wf->pool != NULL) {
+ pj_pool_release(wf->pool);
+ wf->pool = NULL;
+ }
+
+ // Enumerate capture sound devices
+ while(bdIMADpj_getDeviceName(BD_IMAD_CAPTURE_DEVICES, &deviceNamep) !=
+ BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
+ {
+ wcscpy(captureDevName[captureDeviceCount], deviceNamep);
+ captureDeviceCount++;
+ }
+
+ // Enumerate playback sound devices
+ while(bdIMADpj_getDeviceName(BD_IMAD_PLAYBACK_DEVICES, &deviceNamep) !=
+ BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
+ {
+ wcscpy(playbackDevName[playbackDeviceCount], deviceNamep);
+ playbackDeviceCount++;
+ }
+
+ // Set devices info
+ wf->dev_count = captureDeviceCount + playbackDeviceCount;
+ wf->pool = pj_pool_create(wf->pf, "BD_IMAD_DEVICES", 1000, 1000, NULL);
+ wf->dev_info = (struct bddev_info*)pj_pool_calloc(wf->pool, wf->dev_count,
+ sizeof(struct bddev_info));
+
+ // Capture device properties
+ for(i=0;i<captureDeviceCount;i++) {
+ wf->dev_info[i].deviceId = i;
+ wf->dev_info[i].info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING |
+ PJMEDIA_AUD_DEV_CAP_EC;
+ wf->dev_info[i].info.default_samples_per_sec = BD_IMAD_DEFAULT_FREQ;
+ strcpy(wf->dev_info[i].info.driver, "BD_IMAD");
+ wf->dev_info[i].info.ext_fmt_cnt = 0;
+ wf->dev_info[i].info.input_count = BD_IMAD_MAX_CHANNELS;
+ wf->dev_info[i].info.output_count = 0;
+ strcpy(wf->dev_info[i].info.name,
+ BD_IMAD_PJ_WCHARtoCHAR(captureDevName[i]));
+ wf->dev_info[i].info.routes = 0;
+ }
+
+ // Playback device properties
+ for(i=0;i<playbackDeviceCount;i++) {
+ wf->dev_info[captureDeviceCount+i].deviceId = captureDeviceCount+i;
+ wf->dev_info[captureDeviceCount+i].info.caps =
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
+ wf->dev_info[captureDeviceCount+i].info.default_samples_per_sec =
+ BD_IMAD_DEFAULT_FREQ;
+ strcpy(wf->dev_info[captureDeviceCount+i].info.driver, "BD_IMAD");
+ wf->dev_info[captureDeviceCount+i].info.ext_fmt_cnt = 0;
+ wf->dev_info[captureDeviceCount+i].info.input_count = 0;
+ wf->dev_info[captureDeviceCount+i].info.output_count =
+ BD_IMAD_MAX_CHANNELS;
+ strcpy(wf->dev_info[captureDeviceCount+i].info.name,
+ BD_IMAD_PJ_WCHARtoCHAR(playbackDevName[i]));
+ wf->dev_info[captureDeviceCount+i].info.routes = 0;
+ }
+
+ PJ_LOG(4, (THIS_FILE, "BDIMAD found %d devices:", wf->dev_count));
+ for(i=0; i<wf->dev_count; i++) {
+ PJ_LOG(4,
+ (THIS_FILE, " dev_id %d: %s (in=%d, out=%d)",
+ i,
+ wf->dev_info[i].info.name,
+ wf->dev_info[i].info.input_count,
+ wf->dev_info[i].info.output_count));
+ }
+ return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
+{
+ struct bd_factory *wf = (struct bd_factory*)f;
+ pj_pool_t *pool = wf->base_pool;
+
+ pj_pool_release(wf->pool);
+ wf->base_pool = NULL;
+ pj_pool_release(pool);
+
+ return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
+{
+ struct bd_factory *wf = (struct bd_factory*)f;
+ return wf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_dev_info *info)
+{
+ struct bd_factory *wf = (struct bd_factory*)f;
+
+ PJ_ASSERT_RETURN(index < wf->dev_count, PJMEDIA_EAUD_INVDEV);
+
+ pj_memcpy(info, &wf->dev_info[index].info, sizeof(*info));
+
+ return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
+ unsigned index,
+ pjmedia_aud_param *param)
+{
+ struct bd_factory *wf = (struct bd_factory*)f;
+ struct bddev_info *di = &wf->dev_info[index];
+
+ pj_bzero(param, sizeof(*param));
+ if (di->info.input_count && di->info.output_count) {
+ param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+ param->rec_id = index;
+ param->play_id = index;
+ param->channel_count = di->info.output_count;
+ } else if(di->info.input_count) {
+ param->dir = PJMEDIA_DIR_CAPTURE;
+ param->rec_id = index;
+ param->play_id = PJMEDIA_AUD_INVALID_DEV;
+ param->channel_count = di->info.input_count;
+ } else if(di->info.output_count) {
+ param->dir = PJMEDIA_DIR_PLAYBACK;
+ param->play_id = index;
+ param->rec_id = PJMEDIA_AUD_INVALID_DEV;
+ param->channel_count = di->info.output_count;
+ } else {
+ return PJMEDIA_EAUD_INVDEV;
+ }
+
+ param->bits_per_sample = BD_IMAD_BITS_X_SAMPLE;
+ param->clock_rate = di->info.default_samples_per_sec;
+ param->flags = di->info.caps;
+ param->samples_per_frame = di->info.default_samples_per_sec *
+ param->channel_count *
+ BD_IMAD_MSECOND_PER_BUFFER /
+ 1000;
+
+ if(di->info.caps & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {
+ param->input_vol = BD_IMAD_STARTING_INPUT_VOLUME;
+ }
+
+ if(di->info.caps & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
+ param->output_vol = BD_IMAD_STARTING_OUTPUT_VOLUME;
+ }
+
+ if(di->info.caps & PJMEDIA_AUD_DEV_CAP_EC) {
+ param->ec_enabled = PJ_TRUE;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* callbacks to set data */
+void bdimad_CaptureCallback(void *buffer, int samples, void *user_data)
+{
+ pj_status_t status = PJ_SUCCESS;
+ pjmedia_frame frame;
+ unsigned nsamples;
+
+ struct bd_stream *strm = (struct bd_stream*)user_data;
+
+ if(!strm->go)
+ goto on_break;
+
+ /* Known cases of callback's thread:
+ * - The thread may be changed in the middle of a session, e.g: in MacOS
+ * it happens when plugging/unplugging headphone.
+ * - The same thread may be reused in consecutive sessions. The first
+ * session will leave TLS set, but release the TLS data address,
+ * so the second session must re-register the callback's thread.
+ */
+ if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered())
+ {
+ pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
+ status = pj_thread_register("bd_CaptureCallback",
+ strm->rec_thread_desc,
+ &strm->rec_thread);
+ if (status != PJ_SUCCESS)
+ goto on_break;
+
+ strm->rec_thread_initialized = 1;
+ PJ_LOG(5,(THIS_FILE, "Recorder thread started"));
+ }
+
+ /* Calculate number of samples we've got */
+ nsamples = samples * strm->channel_count + strm->rec_buf_count;
+
+ /*
+ RECORD
+ */
+ if (strm->fmt_id == PJMEDIA_FORMAT_L16) {
+ if (nsamples >= strm->samples_per_frame) {
+ /* If buffer is not empty, combine the buffer with the just incoming
+ * samples, then call put_frame.
+ */
+ if (strm->rec_buf_count) {
+ unsigned chunk_count = 0;
+
+ chunk_count = strm->samples_per_frame - strm->rec_buf_count;
+ pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
+ (pj_int16_t*)buffer, chunk_count);
+
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame.buf = (void*) strm->rec_buf;
+ frame.size = strm->bytes_per_frame;
+ frame.timestamp.u64 = strm->timestampCapture.u64;
+ frame.bit_info = 0;
+
+ status = (*strm->rec_cb)(strm->user_data, &frame);
+
+ buffer = (pj_int16_t*) buffer + chunk_count;
+ nsamples -= strm->samples_per_frame;
+ strm->rec_buf_count = 0;
+ strm->timestampCapture.u64 += strm->samples_per_frame /
+ strm->channel_count;
+ }
+
+ /* Give all frames we have */
+ while (nsamples >= strm->samples_per_frame && status == 0) {
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame.buf = (void*) buffer;
+ frame.size = strm->bytes_per_frame;
+ frame.timestamp.u64 = strm->timestampCapture.u64;
+ frame.bit_info = 0;
+
+ status = (*strm->rec_cb)(strm->user_data, &frame);
+
+ buffer = (pj_int16_t*) buffer + strm->samples_per_frame;
+ nsamples -= strm->samples_per_frame;
+ strm->timestampCapture.u64 += strm->samples_per_frame /
+ strm->channel_count;
+ }
+
+ /* Store the remaining samples into the buffer */
+ if (nsamples && status == 0) {
+ strm->rec_buf_count = nsamples;
+ pjmedia_copy_samples(strm->rec_buf, (pj_int16_t*)buffer,
+ nsamples);
+ }
+
+ } else {
+ /* Not enough samples, let's just store them in the buffer */
+ pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
+ (pj_int16_t*)buffer,
+ samples * strm->channel_count);
+ strm->rec_buf_count += samples * strm->channel_count;
+ }
+ } else {
+ pj_assert(!"Frame type not supported");
+ }
+
+ strm->timestampCapture.u64 += strm->param.samples_per_frame /
+ strm->param.channel_count;
+
+ if (status==0)
+ return;
+
+on_break:
+ strm->rec_thread_exited = 1;
+}
+
+/* callbacks to get data */
+int bdimad_PlaybackCallback(void *buffer, int samples, void *user_data)
+{
+ pj_status_t status = PJ_SUCCESS;
+ pjmedia_frame frame;
+ struct bd_stream *strm = (struct bd_stream*)user_data;
+ unsigned nsamples_req = samples * strm->channel_count;
+
+ if(!strm->go)
+ goto on_break;
+
+ /* Known cases of callback's thread:
+ * - The thread may be changed in the middle of a session, e.g: in MacOS
+ * it happens when plugging/unplugging headphone.
+ * - The same thread may be reused in consecutive sessions. The first
+ * session will leave TLS set, but release the TLS data address,
+ * so the second session must re-register the callback's thread.
+ */
+ if (strm->play_thread_initialized == 0 || !pj_thread_is_registered())
+ {
+ pj_bzero(strm->play_thread_desc, sizeof(pj_thread_desc));
+ status = pj_thread_register("bd_PlaybackCallback",
+ strm->play_thread_desc,
+ &strm->play_thread);
+ if (status != PJ_SUCCESS)
+ goto on_break;
+
+ strm->play_thread_initialized = 1;
+ PJ_LOG(5,(THIS_FILE, "Player thread started"));
+ }
+
+ /*
+ PLAY
+ */
+ if(strm->fmt_id == PJMEDIA_FORMAT_L16) {
+ /* Check if any buffered samples */
+ if (strm->play_buf_count) {
+ /* samples buffered >= requested by sound device */
+ if (strm->play_buf_count >= nsamples_req) {
+ pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf,
+ nsamples_req);
+ strm->play_buf_count -= nsamples_req;
+ pjmedia_move_samples(strm->play_buf,
+ strm->play_buf + nsamples_req,
+ strm->play_buf_count);
+
+ return nsamples_req;
+ }
+
+ /* samples buffered < requested by sound device */
+ pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf,
+ strm->play_buf_count);
+ nsamples_req -= strm->play_buf_count;
+ buffer = (pj_int16_t*)buffer + strm->play_buf_count;
+ strm->play_buf_count = 0;
+ }
+
+ /* Fill output buffer as requested */
+ while (nsamples_req && status == 0) {
+ if (nsamples_req >= strm->samples_per_frame) {
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame.buf = buffer;
+ frame.size = strm->bytes_per_frame;
+ frame.timestamp.u64 = strm->timestampPlayback.u64;
+ frame.bit_info = 0;
+
+ status = (*strm->play_cb)(strm->user_data, &frame);
+ if (status != PJ_SUCCESS)
+ return 0;
+
+ if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
+ pj_bzero(frame.buf, frame.size);
+
+ nsamples_req -= strm->samples_per_frame;
+ buffer = (pj_int16_t*)buffer + strm->samples_per_frame;
+ } else {
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame.buf = strm->play_buf;
+ frame.size = strm->bytes_per_frame;
+ frame.timestamp.u64 = strm->timestampPlayback.u64;
+ frame.bit_info = 0;
+
+ status = (*strm->play_cb)(strm->user_data, &frame);
+ if (status != PJ_SUCCESS)
+ return 0;
+
+ if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
+ pj_bzero(frame.buf, frame.size);
+
+ pjmedia_copy_samples((pj_int16_t*)buffer, strm->play_buf,
+ nsamples_req);
+ strm->play_buf_count = strm->samples_per_frame -
+ nsamples_req;
+ pjmedia_move_samples(strm->play_buf,
+ strm->play_buf+nsamples_req,
+ strm->play_buf_count);
+ nsamples_req = 0;
+ }
+
+ strm->timestampPlayback.u64 += strm->samples_per_frame /
+ strm->channel_count;
+ }
+ } else {
+ pj_assert(!"Frame type not supported");
+ }
+
+ if(status != PJ_SUCCESS) {
+ return 0;
+ }
+
+ strm->timestampPlayback.u64 += strm->param.samples_per_frame /
+ strm->param.channel_count;
+
+ if (status == 0)
+ return samples;
+
+on_break:
+ strm->play_thread_exited = 1;
+ return 0;
+}
+
+/* Internal: Get format name */
+static const char *get_fmt_name(pj_uint32_t id)
+{
+ static char name[8];
+
+ if (id == PJMEDIA_FORMAT_L16)
+ return "PCM";
+ pj_memcpy(name, &id, 4);
+ name[4] = '\0';
+ return name;
+}
+
+/* Internal: create BD device. */
+static pj_status_t init_streams(struct bd_factory *wf,
+ struct bd_stream *strm,
+ const pjmedia_aud_param *prm)
+{
+ unsigned ptime;
+ enum bdIMADpj_Status errorInitAEC;
+ wchar_t *deviceNamep=NULL;
+ wchar_t captureDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
+ int captureDeviceCount = 0;
+ wchar_t playbackDevName[BD_IMAD_MAX_DEV_COUNT][BD_IMAD_MAX_DEV_LENGTH_NAME];
+ int playbackDeviceCount = 0;
+
+ PJ_ASSERT_RETURN(prm->play_id < (int)wf->dev_count, PJ_EINVAL);
+ PJ_ASSERT_RETURN(prm->rec_id < (int)wf->dev_count, PJ_EINVAL);
+
+ ptime = prm->samples_per_frame *
+ 1000 /
+ (prm->clock_rate * prm->channel_count);
+ strm->bytes_per_frame = (prm->clock_rate *
+ ((prm->channel_count * prm->bits_per_sample) / 8)) *
+ ptime /
+ 1000;
+ strm->timestampCapture.u64 = 0;
+ strm->timestampPlayback.u64 = 0;
+
+ //BD_IMAD_PJ
+ bdIMADpj_CreateStructures(&strm->bdIMADpjSettingsPtr,
+ &strm->bdIMADpjWarningPtr);
+
+ strm->bdIMADpjSettingsPtr->FrameSize_ms = ptime;
+ strm->bdIMADpjSettingsPtr->DiagnosticEnable = BD_IMAD_DIAGNOSTIC;
+ strm->bdIMADpjSettingsPtr->DiagnosticFolderPath =
+ bdImadPjDiagnosticFolderPath;
+ strm->bdIMADpjSettingsPtr->validate = (void *)manage_code;
+
+ if(prm->clock_rate != 8000 && prm->clock_rate != 16000
+ && prm->clock_rate != 32000 && prm->clock_rate != 48000) {
+ PJ_LOG(4, (THIS_FILE,
+ "BDIMAD support 8000 Hz, 16000 Hz, 32000 Hz and 48000 Hz "
+ "frequency."));
+ }
+ strm->bdIMADpjSettingsPtr->SamplingFrequency = prm->clock_rate;
+
+ if(prm->channel_count > BD_IMAD_MAX_CHANNELS) {
+ PJ_LOG(4, (THIS_FILE,
+ "BDIMAD doesn't support a number of channels upper than %d.",
+ BD_IMAD_MAX_CHANNELS));
+ }
+
+ // Enumerate capture sound devices
+ while(bdIMADpj_getDeviceName(BD_IMAD_CAPTURE_DEVICES, &deviceNamep) !=
+ BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
+ {
+ wcscpy(captureDevName[captureDeviceCount], deviceNamep);
+ captureDeviceCount++;
+ }
+ strm->bdIMADpjSettingsPtr->CaptureDevice = captureDevName[(int)prm->rec_id];
+
+ // Enumerate playback sound devices
+ while(bdIMADpj_getDeviceName(BD_IMAD_PLAYBACK_DEVICES, &deviceNamep) !=
+ BD_PJ_ERROR_IMAD_DEVICE_LIST_EMPTY)
+ {
+ wcscpy(playbackDevName[playbackDeviceCount], deviceNamep);
+ playbackDeviceCount++;
+ }
+ strm->bdIMADpjSettingsPtr->PlayDevice =
+ playbackDevName[(int)(prm->play_id-captureDeviceCount)];
+
+ strm->bdIMADpjSettingsPtr->cb_emptyCaptureBuffer = &bdimad_CaptureCallback;
+ strm->bdIMADpjSettingsPtr->cb_emptyCaptureBuffer_user_data = (void*)strm;
+ strm->bdIMADpjSettingsPtr->cb_fillPlayBackBuffer = &bdimad_PlaybackCallback;
+ strm->bdIMADpjSettingsPtr->cb_fillPlayBackBuffer_user_data = (void*)strm;
+
+ if(strm->bdIMADpjInstance != NULL)
+ bdIMADpj_FreeAEC(&strm->bdIMADpjInstance);
+ strm->bdIMADpjInstance = NULL;
+
+ errorInitAEC = bdIMADpj_InitAEC(&strm->bdIMADpjInstance,
+ &strm->bdIMADpjSettingsPtr,
+ &strm->bdIMADpjWarningPtr);
+
+ {
+ int auxInt = (prm->ec_enabled == PJ_TRUE ? 1 : 0);
+ bdIMADpj_setParameter(strm->bdIMADpjInstance,
+ BD_PARAM_IMAD_PJ_AEC_ENABLE,
+ &auxInt);
+ auxInt = 1;
+ //Mic control On by default
+ bdIMADpj_setParameter(strm->bdIMADpjInstance,
+ BD_PARAM_IMAD_PJ_MIC_CONTROL_ENABLE,
+ &auxInt);
+ }
+
+ if(errorInitAEC != BD_PJ_OK &&
+ errorInitAEC != BD_PJ_WARN_BDIMAD_WARNING_ASSERTED)
+ {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(errorInitAEC);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/****************************************
+ API: create stream
+*****************************************/
+static pj_status_t stream_stopBDIMAD(pjmedia_aud_stream *s)
+{
+ struct bd_stream *strm = (struct bd_stream*)s;
+ pj_status_t status = PJ_SUCCESS;
+ int i, err = 0;
+
+ PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
+
+ strm->go = 0;
+
+ for (i=0; !strm->rec_thread_exited && i<100; ++i)
+ pj_thread_sleep(10);
+ for (i=0; !strm->play_thread_exited && i<100; ++i)
+ pj_thread_sleep(10);
+
+ pj_thread_sleep(1);
+
+ PJ_LOG(5,(THIS_FILE, "Stopping stream.."));
+
+ strm->play_thread_initialized = 0;
+ strm->rec_thread_initialized = 0;
+
+ PJ_LOG(5,(THIS_FILE, "Done, status=%d", err));
+
+ return status;
+}
+
+static pj_status_t stream_stop(pjmedia_aud_stream *s)
+{
+ struct bd_stream *strm = (struct bd_stream*)s;
+
+ PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
+
+ if(strm->bdIMADpjInstance != NULL) {
+ return stream_stopBDIMAD(s);
+ } else {
+ return PJMEDIA_EAUD_ERR;
+ }
+}
+
+static pj_status_t stream_destroyBDIMAD(pjmedia_aud_stream *s)
+{
+ struct bd_stream *strm = (struct bd_stream*)s;
+ int i = 0;
+
+ PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
+
+ stream_stopBDIMAD(s);
+
+ // DeInit BDIMAD
+ bdIMADpj_FreeAEC(&strm->bdIMADpjInstance);
+ PJ_LOG(4, (THIS_FILE, "Free AEC"));
+
+ bdIMADpj_FreeStructures(&strm->bdIMADpjSettingsPtr,
+ &strm->bdIMADpjWarningPtr);
+ PJ_LOG(4, (THIS_FILE, "Free AEC Structure"));
+
+ strm->bdIMADpjInstance = NULL;
+ strm->bdIMADpjSettingsPtr = NULL;
+ strm->bdIMADpjWarningPtr = NULL;
+
+ strm->quit_flag = 1;
+ for (i=0; !strm->rec_thread_exited && i<100; ++i) {
+ pj_thread_sleep(1);
+ }
+ for (i=0; !strm->play_thread_exited && i<100; ++i) {
+ pj_thread_sleep(1);
+ }
+
+ PJ_LOG(5,(THIS_FILE, "Destroying stream.."));
+
+ pj_pool_release(strm->pool);
+ return PJ_SUCCESS;
+}
+
+/* API: Destroy stream. */
+static pj_status_t stream_destroy(pjmedia_aud_stream *s)
+{
+ struct bd_stream *strm = (struct bd_stream*)s;
+
+ PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
+
+ if(strm->bdIMADpjInstance != NULL) {
+ return stream_destroyBDIMAD(s);
+ } else {
+ return PJMEDIA_EAUD_ERR;
+ }
+}
+
+/* API: set capability */
+static pj_status_t stream_set_capBDIMAD(pjmedia_aud_stream *s,
+ pjmedia_aud_dev_cap cap,
+ const void *pval)
+{
+ struct bd_stream *strm = (struct bd_stream*)s;
+ bdIMADpj_Status res = BD_PJ_OK;
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if(cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
+ /* Output volume setting */
+ float vol = (float)*(unsigned*)pval;
+
+ if(vol > 100.0f) vol = 100.0f;
+ if(vol < 0.0f) vol = 0.0f;
+
+ vol = vol / 100.0f;
+ res = bdIMADpj_setParameter(strm->bdIMADpjInstance,
+ BD_PARAM_IMAD_PJ_SPK_VOLUME, &vol);
+
+
+ if(res == BD_PJ_OK) {
+ strm->param.output_vol = *(unsigned*)pval;
+ return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
+ }
+ }
+
+ if(cap == PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {
+ /* Input volume setting */
+ float vol = (float)*(unsigned*)pval;
+
+ if(vol > 100.0f) vol = 100.0f;
+ if(vol < 0.0f) vol = 0.0f;
+
+ vol = vol / 100.0f;
+ res = bdIMADpj_setParameter(strm->bdIMADpjInstance,
+ BD_PARAM_IMAD_PJ_MIC_VOLUME, &vol);
+ if(res == BD_PJ_OK) {
+ strm->param.input_vol = *(unsigned*)pval;
+ return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
+ }
+ }
+
+ if(cap == PJMEDIA_AUD_DEV_CAP_EC) {
+ int aecOnOff = (*(pj_bool_t*)pval == PJ_TRUE ? 1 : 0);
+
+ /* AEC setting */
+ res = bdIMADpj_setParameter(strm->bdIMADpjInstance,
+ BD_PARAM_IMAD_PJ_AEC_ENABLE,
+ &aecOnOff);
+ if(res == BD_PJ_OK) {
+ strm->param.ec_enabled = (aecOnOff == 1 ? PJ_TRUE : PJ_FALSE);
+ return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
+ }
+ }
+
+ return PJMEDIA_EAUD_INVCAP;
+}
+
+static pj_status_t 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)
+{
+ struct bd_factory *wf = (struct bd_factory*)f;
+ pj_pool_t *pool;
+ struct bd_stream *strm;
+ pj_uint8_t silence_char;
+ pj_status_t status;
+
+ switch (param->ext_fmt.id) {
+ case PJMEDIA_FORMAT_L16:
+ silence_char = '\0';
+ break;
+ default:
+ return PJMEDIA_EAUD_BADFORMAT;
+ }
+
+ /* Create and Initialize stream descriptor */
+ pool = pj_pool_create(wf->pf, "BDIMAD_STREAM", 1000, 1000, NULL);
+ PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+ strm = PJ_POOL_ZALLOC_T(pool, struct bd_stream);
+ pj_memcpy(&strm->param, param, sizeof(*param));
+ strm->pool = pool;
+ strm->rec_cb = rec_cb;
+ strm->play_cb = play_cb;
+ strm->user_data = user_data;
+ strm->fmt_id = (pjmedia_format_id)param->ext_fmt.id;
+ strm->silence_char = silence_char;
+ strm->channel_count = param->channel_count;
+ strm->samples_per_frame = param->samples_per_frame;
+
+ if (param->dir & PJMEDIA_DIR_CAPTURE_PLAYBACK) {
+ status = init_streams(wf, strm, param);
+
+ if (status != PJ_SUCCESS) {
+ stream_destroyBDIMAD(&strm->base);
+ return status;
+ }
+ } else {
+ stream_destroyBDIMAD(&strm->base);
+ return PJMEDIA_EAUD_ERR;
+ }
+
+ strm->rec_buf = (pj_int16_t*)pj_pool_alloc(pool,
+ strm->bytes_per_frame);
+ if (!strm->rec_buf) {
+ pj_pool_release(pool);
+ return PJ_ENOMEM;
+ }
+ strm->rec_buf_count = 0;
+
+ strm->play_buf = (pj_int16_t*)pj_pool_alloc(pool,
+ strm->bytes_per_frame);
+ if (!strm->play_buf) {
+ pj_pool_release(pool);
+ return PJ_ENOMEM;
+ }
+ strm->play_buf_count = 0;
+
+ /* Apply the remaining settings */
+ if(param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
+ stream_set_capBDIMAD(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+ &param->output_vol);
+ }
+ if(param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING) {
+ stream_set_capBDIMAD(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
+ &param->input_vol);
+ }
+ if(param->flags & PJMEDIA_AUD_DEV_CAP_EC) {
+ stream_set_capBDIMAD(&strm->base,
+ PJMEDIA_AUD_DEV_CAP_EC,
+ &param->ec_enabled);
+ }
+
+ strm->base.op = &stream_op;
+ *p_aud_strm = &strm->base;
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t 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)
+{
+ return factory_create_streamBDIMAD(f, param, rec_cb,
+ play_cb, user_data, p_aud_strm);
+}
+// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
+// ----------------------------------------------------------------------
+
+/* API: Get stream info. */
+static pj_status_t stream_get_param(pjmedia_aud_stream *s,
+ pjmedia_aud_param *pi)
+{
+ struct bd_stream *strm = (struct bd_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+ // Get the output volume setting
+ if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
+ &pi->output_vol) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
+ }
+
+ // Get the input volume setting
+ if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING,
+ &pi->input_vol) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING;
+ }
+
+ // Get the AEC setting
+ if(stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_EC, &pi->ec_enabled) == PJ_SUCCESS)
+ {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_EC;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t stream_get_capBDIMAD(pjmedia_aud_stream *s,
+ pjmedia_aud_dev_cap cap,
+ void *pval)
+{
+ struct bd_stream *strm = (struct bd_stream*)s;
+ bdIMADpj_Status res = BD_PJ_OK;
+
+ PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+
+ if(cap == PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING)
+ {
+ /* Input volume setting */
+ float vol;
+ res = bdIMADpj_getParameter(strm->bdIMADpjInstance,
+ BD_PARAM_IMAD_PJ_MIC_VOLUME, &vol);
+ if(res == BD_PJ_OK) {
+ vol = vol * 100;
+ if(vol > 100.0f) vol = 100.0f;
+ if(vol < 0.0f) vol = 0.0f;
+ *(unsigned int *)pval = (unsigned int)vol;
+ return PJ_SUCCESS;
+ } else{
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
+ }
+ } else if(cap == PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
+ /* Output volume setting */
+ float vol;
+ res = bdIMADpj_getParameter(strm->bdIMADpjInstance,
+ BD_PARAM_IMAD_PJ_SPK_VOLUME, &vol);
+ if(res == BD_PJ_OK) {
+ vol = vol * 100;
+ if(vol > 100.0f) vol = 100.0f;
+ if(vol < 0.0f) vol = 0.0f;
+ *(unsigned int *)pval = (unsigned int)vol;
+ return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
+ }
+ }
+ else if(cap == PJMEDIA_AUD_DEV_CAP_EC) {
+ int aecIsOn;
+ res = bdIMADpj_getParameter(strm->bdIMADpjInstance,
+ BD_PARAM_IMAD_PJ_AEC_ENABLE, &aecIsOn);
+ if(res == BD_PJ_OK) {
+ *(pj_bool_t*)pval = (aecIsOn == 1 ? PJ_TRUE : PJ_FALSE);
+ return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_AUDIODEV_ERRNO_FROM_BDIMAD(res);
+ }
+ } else {
+ return PJMEDIA_EAUD_INVCAP;
+ }
+}
+
+static pj_status_t stream_startBDIMAD(pjmedia_aud_stream *s)
+{
+ struct bd_stream *strm = (struct bd_stream*)s;
+
+ PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
+
+ strm->go = 1;
+
+ return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
+ pjmedia_aud_dev_cap cap,
+ void *pval)
+{
+ struct bd_stream *strm = (struct bd_stream*)s;
+
+ PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
+
+ if(strm->bdIMADpjInstance != NULL) {
+ return stream_get_capBDIMAD(s, cap, pval);
+ } else {
+ return PJMEDIA_EAUD_ERR;
+ }
+}
+
+/* API: set capability */
+static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
+ pjmedia_aud_dev_cap cap,
+ const void *pval)
+{
+ struct bd_stream *strm = (struct bd_stream*)s;
+
+ PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
+
+ if(strm->bdIMADpjInstance != NULL) {
+ return stream_set_capBDIMAD(s, cap, pval);
+ } else {
+ return PJMEDIA_EAUD_ERR;
+ }
+}
+
+/* API: Start stream. */
+static pj_status_t stream_start(pjmedia_aud_stream *s)
+{
+ struct bd_stream *strm = (struct bd_stream*)s;
+
+ PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
+
+ if(strm->bdIMADpjInstance != NULL) {
+ return stream_startBDIMAD(s);
+ } else {
+ return PJMEDIA_EAUD_ERR;
+ }
+}
+
+#if defined (_MSC_VER)
+#pragma comment ( lib, "bdClientValidation.lib" )
+#pragma comment ( lib, "bdIMADpj.lib" )
+#endif
+
+
+#endif /* PJMEDIA_AUDIO_DEV_HAS_BDIMAD */
+
diff --git a/pjmedia/src/pjmedia-audiodev/errno.c b/pjmedia/src/pjmedia-audiodev/errno.c
index 49f837bb..498e43d9 100644
--- a/pjmedia/src/pjmedia-audiodev/errno.c
+++ b/pjmedia/src/pjmedia-audiodev/errno.c
@@ -155,6 +155,20 @@ PJ_DEF(pj_str_t) pjmedia_audiodev_strerror(pj_status_t statcode,
} else
#endif
+/* See if the error comes from BDIMAD */
+#if PJMEDIA_AUDIO_DEV_HAS_BDIMAD
+
+ if (statcode >= PJMEDIA_AUDIODEV_BDIMAD_ERROR_START &&
+ statcode < PJMEDIA_AUDIODEV_BDIMAD_ERROR_END)
+ {
+ pj_status_t native_err;
+ native_err = statcode - PJMEDIA_AUDIODEV_BDIMAD_ERROR_START;
+
+ pj_ansi_snprintf(buf, bufsize, "BDIMAD native error %d", native_err);
+ return pj_str(buf);
+ } else
+#endif
+
/* Audiodev error */
if (statcode >= PJMEDIA_AUDIODEV_ERRNO_START &&
statcode < PJMEDIA_AUDIODEV_ERRNO_END)