diff options
author | Benny Prijono <bennylp@teluu.com> | 2009-03-12 18:11:37 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2009-03-12 18:11:37 +0000 |
commit | 1dacdee696b7591a6dcc0b3c1d0f41573e473168 (patch) | |
tree | 302b09dcd989c0c05cf09f6aebaa63d870b421b9 /pjmedia/src/pjmedia-audiodev/legacy_dev.c | |
parent | ba9d8ca28eb209571c0bd6a080a8bb03d0fa2d33 (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.c | 459 |
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 */ + |