summaryrefslogtreecommitdiff
path: root/pjmedia/src/pjmedia-audiodev/legacy_dev.c
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2009-03-12 18:11:37 +0000
committerBenny Prijono <bennylp@teluu.com>2009-03-12 18:11:37 +0000
commit1dacdee696b7591a6dcc0b3c1d0f41573e473168 (patch)
tree302b09dcd989c0c05cf09f6aebaa63d870b421b9 /pjmedia/src/pjmedia-audiodev/legacy_dev.c
parentba9d8ca28eb209571c0bd6a080a8bb03d0fa2d33 (diff)
(Major) Task #737 and #738: integration of APS-Direct and Audiodev from aps-direct branch to trunk.
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2506 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia/src/pjmedia-audiodev/legacy_dev.c')
-rw-r--r--pjmedia/src/pjmedia-audiodev/legacy_dev.c459
1 files changed, 459 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia-audiodev/legacy_dev.c b/pjmedia/src/pjmedia-audiodev/legacy_dev.c
new file mode 100644
index 00000000..66fad9b0
--- /dev/null
+++ b/pjmedia/src/pjmedia-audiodev/legacy_dev.c
@@ -0,0 +1,459 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia-audiodev/audiodev_imp.h>
+#include <pjmedia/sound.h>
+#include <pj/assert.h>
+
+#if PJMEDIA_AUDIO_DEV_HAS_LEGACY_DEVICE
+
+#define THIS_FILE "legacy_dev.c"
+
+/* Legacy devices factory */
+struct legacy_factory
+{
+ pjmedia_aud_dev_factory base;
+ pj_pool_t *pool;
+ pj_pool_factory *pf;
+};
+
+
+struct legacy_stream
+{
+ pjmedia_aud_stream base;
+
+ pj_pool_t *pool;
+ pjmedia_aud_param param;
+ pjmedia_snd_stream *snd_strm;
+ pjmedia_aud_play_cb user_play_cb;
+ pjmedia_aud_rec_cb user_rec_cb;
+ void *user_user_data;
+ unsigned input_latency;
+ unsigned output_latency;
+};
+
+
+/* Prototypes */
+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 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);
+
+
+/* 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
+};
+
+static pjmedia_aud_stream_op stream_op =
+{
+ &stream_get_param,
+ &stream_get_cap,
+ &stream_set_cap,
+ &stream_start,
+ &stream_stop,
+ &stream_destroy
+};
+
+
+/****************************************************************************
+ * Factory operations
+ */
+
+/*
+ * Init legacy audio driver.
+ */
+pjmedia_aud_dev_factory* pjmedia_legacy_factory(pj_pool_factory *pf)
+{
+ struct legacy_factory *f;
+ pj_pool_t *pool;
+
+ pool = pj_pool_create(pf, "legacy-snd", 512, 512, NULL);
+ f = PJ_POOL_ZALLOC_T(pool, struct legacy_factory);
+ f->pf = pf;
+ f->pool = pool;
+ f->base.op = &factory_op;
+
+ return &f->base;
+}
+
+
+/* API: init factory */
+static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
+{
+ struct legacy_factory *wf = (struct legacy_factory*)f;
+
+ return pjmedia_snd_init(wf->pf);
+}
+
+/* API: destroy factory */
+static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
+{
+ struct legacy_factory *wf = (struct legacy_factory*)f;
+ pj_status_t status;
+
+ status = pjmedia_snd_deinit();
+
+ if (status == PJ_SUCCESS) {
+ pj_pool_t *pool = wf->pool;
+ wf->pool = NULL;
+ pj_pool_release(pool);
+ }
+
+ return status;
+}
+
+/* API: get number of devices */
+static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
+{
+ PJ_UNUSED_ARG(f);
+ return pjmedia_snd_get_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)
+{
+ const pjmedia_snd_dev_info *si =
+ pjmedia_snd_get_dev_info(index);;
+
+ PJ_UNUSED_ARG(f);
+
+ if (si == NULL)
+ return PJMEDIA_EAUD_INVDEV;
+
+ pj_bzero(info, sizeof(*info));
+ pj_ansi_strncpy(info->name, si->name, sizeof(info->name));
+ info->name[sizeof(info->name)-1] = '\0';
+ info->input_count = si->input_count;
+ info->output_count = si->output_count;
+ info->default_samples_per_sec = si->default_samples_per_sec;
+ pj_ansi_strcpy(info->driver, "legacy");
+ info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
+
+ 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)
+{
+ pjmedia_aud_dev_info di;
+ pj_status_t status;
+
+ status = factory_get_dev_info(f, index, &di);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_bzero(param, sizeof(*param));
+ if (di.input_count && di.output_count) {
+ param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
+ param->rec_id = index;
+ param->play_id = index;
+ } else if (di.input_count) {
+ param->dir = PJMEDIA_DIR_CAPTURE;
+ param->rec_id = index;
+ param->play_id = PJMEDIA_AUD_INVALID_DEV;
+ } else if (di.output_count) {
+ param->dir = PJMEDIA_DIR_PLAYBACK;
+ param->play_id = index;
+ param->rec_id = PJMEDIA_AUD_INVALID_DEV;
+ } else {
+ return PJMEDIA_EAUD_INVDEV;
+ }
+
+ param->clock_rate = di.default_samples_per_sec;
+ param->channel_count = 1;
+ param->samples_per_frame = di.default_samples_per_sec * 20 / 1000;
+ param->bits_per_sample = 16;
+ param->flags = di.caps;
+ param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
+ param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
+
+ return PJ_SUCCESS;
+}
+
+/* Callback from legacy sound device */
+static pj_status_t snd_play_cb(/* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* out */ void *output,
+ /* out */ unsigned size)
+{
+ struct legacy_stream *strm = (struct legacy_stream*)user_data;
+ pjmedia_frame frame;
+ pj_status_t status;
+
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame.buf = output;
+ frame.size = size;
+ frame.timestamp.u64 = timestamp;
+
+ status = strm->user_play_cb(strm->user_user_data, &frame);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) {
+ pj_bzero(output, size);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* Callback from legacy sound device */
+static pj_status_t snd_rec_cb( /* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* in */ void *input,
+ /* in*/ unsigned size)
+{
+ struct legacy_stream *strm = (struct legacy_stream*)user_data;
+ pjmedia_frame frame;
+
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ frame.buf = input;
+ frame.size = size;
+ frame.timestamp.u64 = timestamp;
+
+ return strm->user_rec_cb(strm->user_user_data, &frame);
+}
+
+/* API: create stream */
+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)
+{
+ struct legacy_factory *wf = (struct legacy_factory*)f;
+ pj_pool_t *pool;
+ struct legacy_stream *strm;
+ pj_status_t status;
+
+ /* Initialize our stream data */
+ pool = pj_pool_create(wf->pf, "legacy-snd", 512, 512, NULL);
+ strm = PJ_POOL_ZALLOC_T(pool, struct legacy_stream);
+ strm->pool = pool;
+ strm->user_rec_cb = rec_cb;
+ strm->user_play_cb = play_cb;
+ strm->user_user_data = user_data;
+ pj_memcpy(&strm->param, param, sizeof(*param));
+
+ /* Set the latency if wanted */
+ if (param->dir==PJMEDIA_DIR_CAPTURE_PLAYBACK &&
+ param->flags & (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
+ PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY))
+ {
+ PJ_ASSERT_RETURN(param->input_latency_ms &&
+ param->output_latency_ms,
+ PJMEDIA_EAUD_BADLATENCY);
+
+ strm->input_latency = param->input_latency_ms;
+ strm->output_latency = param->output_latency_ms;
+
+ status = pjmedia_snd_set_latency(param->input_latency_ms,
+ param->output_latency_ms);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
+ }
+
+ /* Open the stream */
+ if (param->dir == PJMEDIA_DIR_CAPTURE) {
+ status = pjmedia_snd_open_rec(param->rec_id,
+ param->clock_rate,
+ param->channel_count,
+ param->samples_per_frame,
+ param->bits_per_sample,
+ &snd_rec_cb,
+ strm,
+ &strm->snd_strm);
+ } else if (param->dir == PJMEDIA_DIR_PLAYBACK) {
+ status = pjmedia_snd_open_player(param->play_id,
+ param->clock_rate,
+ param->channel_count,
+ param->samples_per_frame,
+ param->bits_per_sample,
+ &snd_play_cb,
+ strm,
+ &strm->snd_strm);
+
+ } else if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
+ status = pjmedia_snd_open(param->rec_id,
+ param->play_id,
+ param->clock_rate,
+ param->channel_count,
+ param->samples_per_frame,
+ param->bits_per_sample,
+ &snd_rec_cb,
+ &snd_play_cb,
+ strm,
+ &strm->snd_strm);
+ } else {
+ pj_assert(!"Invalid direction!");
+ return PJ_EINVAL;
+ }
+
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
+
+ *p_aud_strm = &strm->base;
+ return PJ_SUCCESS;
+}
+
+/* API: Get stream info. */
+static pj_status_t stream_get_param(pjmedia_aud_stream *s,
+ pjmedia_aud_param *pi)
+{
+ struct legacy_stream *strm = (struct legacy_stream*)s;
+ PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+
+ pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+ if (strm->input_latency) {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
+ pi->input_latency_ms = strm->input_latency;
+ } else {
+ pi->flags &= ~PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
+ }
+
+ if (strm->output_latency) {
+ pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
+ pi->output_latency_ms = strm->output_latency;
+ } else {
+ pi->flags &= ~PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
+ }
+
+ 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 legacy_stream *strm = (struct legacy_stream*)s;
+
+ PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL);
+
+ if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
+ (strm->param.dir & PJMEDIA_DIR_CAPTURE))
+ {
+ /* Recording latency */
+ if (strm->input_latency) {
+ *(unsigned*)pval = strm->input_latency;
+ return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_EAUD_INVCAP;
+ }
+
+ } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
+ (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
+ {
+ /* Playback latency */
+ if (strm->output_latency) {
+ *(unsigned*)pval = strm->output_latency;
+ return PJ_SUCCESS;
+ } else {
+ return PJMEDIA_EAUD_INVCAP;
+ }
+ } else {
+ return PJMEDIA_EAUD_INVCAP;
+ }
+}
+
+/* API: set capability */
+static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
+ pjmedia_aud_dev_cap cap,
+ const void *pval)
+{
+ PJ_UNUSED_ARG(s);
+ PJ_UNUSED_ARG(cap);
+ PJ_UNUSED_ARG(pval);
+ return PJMEDIA_EAUD_INVCAP;
+}
+
+/* API: Start stream. */
+static pj_status_t stream_start(pjmedia_aud_stream *s)
+{
+ struct legacy_stream *strm = (struct legacy_stream*)s;
+ return pjmedia_snd_stream_start(strm->snd_strm);
+}
+
+/* API: Stop stream. */
+static pj_status_t stream_stop(pjmedia_aud_stream *s)
+{
+ struct legacy_stream *strm = (struct legacy_stream*)s;
+ return pjmedia_snd_stream_stop(strm->snd_strm);
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t stream_destroy(pjmedia_aud_stream *s)
+{
+ struct legacy_stream *strm = (struct legacy_stream*)s;
+ pj_status_t status;
+
+ status = pjmedia_snd_stream_close(strm->snd_strm);
+
+ if (status == PJ_SUCCESS) {
+ pj_pool_t *pool = strm->pool;
+
+ strm->pool = NULL;
+ pj_pool_release(pool);
+ }
+
+ return status;
+}
+
+#endif /* PJMEDIA_AUDIO_DEV_HAS_LEGACY_DEVICE */
+