/* $Id$ */ /* * Copyright (C) 2008-2011 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 */ /* * This is implementation of legacy sound device API, for applications * that still use the old/deprecated sound device API. This implementation * uses the new Audio Device API. * * Please see http://trac.pjsip.org/repos/wiki/Audio_Dev_API for more * information. */ #include #include #if PJMEDIA_HAS_LEGACY_SOUND_API static struct legacy_subsys { pjmedia_snd_dev_info info[4]; unsigned info_counter; unsigned user_rec_latency; unsigned user_play_latency; } g_sys; struct pjmedia_snd_stream { pj_pool_t *pool; pjmedia_aud_stream *aud_strm; pjmedia_snd_rec_cb user_rec_cb; pjmedia_snd_play_cb user_play_cb; void *user_user_data; }; PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index) { pjmedia_snd_dev_info *oi = &g_sys.info[g_sys.info_counter]; pjmedia_aud_dev_info di; g_sys.info_counter = (g_sys.info_counter+1) % PJ_ARRAY_SIZE(g_sys.info); if (pjmedia_aud_dev_get_info(index, &di) != PJ_SUCCESS) return NULL; pj_bzero(oi, sizeof(*oi)); pj_ansi_strncpy(oi->name, di.name, sizeof(oi->name)); oi->name[sizeof(oi->name)-1] = '\0'; oi->input_count = di.input_count; oi->output_count = di.output_count; oi->default_samples_per_sec = di.default_samples_per_sec; return oi; } static pj_status_t snd_play_cb(void *user_data, pjmedia_frame *frame) { pjmedia_snd_stream *strm = (pjmedia_snd_stream*)user_data; frame->type = PJMEDIA_FRAME_TYPE_AUDIO; return strm->user_play_cb(strm->user_user_data, frame->timestamp.u32.lo, frame->buf, (unsigned)frame->size); } static pj_status_t snd_rec_cb(void *user_data, pjmedia_frame *frame) { pjmedia_snd_stream *strm = (pjmedia_snd_stream*)user_data; return strm->user_rec_cb(strm->user_user_data, frame->timestamp.u32.lo, frame->buf, (unsigned)frame->size); } static pj_status_t open_stream( pjmedia_dir dir, int rec_id, int play_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_rec_cb rec_cb, pjmedia_snd_play_cb play_cb, void *user_data, pjmedia_snd_stream **p_snd_strm) { pj_pool_t *pool; pjmedia_snd_stream *snd_strm; pjmedia_aud_param param; pj_status_t status; /* Normalize rec_id & play_id */ if (dir & PJMEDIA_DIR_CAPTURE && rec_id < 0) rec_id = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV; if (dir & PJMEDIA_DIR_PLAYBACK && play_id < 0) play_id = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV; /* Initialize parameters */ if (dir & PJMEDIA_DIR_CAPTURE) { status = pjmedia_aud_dev_default_param(rec_id, ¶m); } else { status = pjmedia_aud_dev_default_param(play_id, ¶m); } if (status != PJ_SUCCESS) return status; param.dir = dir; param.rec_id = rec_id; param.play_id = play_id; param.clock_rate = clock_rate; param.channel_count = channel_count; param.samples_per_frame = samples_per_frame; param.bits_per_sample = bits_per_sample; /* Latencies setting */ if ((dir & PJMEDIA_DIR_CAPTURE) && g_sys.user_rec_latency) { param.flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY; param.input_latency_ms = g_sys.user_rec_latency; } if ((dir & PJMEDIA_DIR_PLAYBACK) && g_sys.user_play_latency) { param.flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY; param.output_latency_ms = g_sys.user_play_latency; } /* Create sound wrapper */ pool = pj_pool_create(pjmedia_get_aud_subsys()->pf, "legacy-snd", 512, 512, NULL); snd_strm = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_stream); snd_strm->pool = pool; snd_strm->user_rec_cb = rec_cb; snd_strm->user_play_cb = play_cb; snd_strm->user_user_data = user_data; /* Create the stream */ status = pjmedia_aud_stream_create(¶m, &snd_rec_cb, &snd_play_cb, snd_strm, &snd_strm->aud_strm); if (status != PJ_SUCCESS) { pj_pool_release(pool); return status; } *p_snd_strm = snd_strm; return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_snd_open_rec( int index, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_rec_cb rec_cb, void *user_data, pjmedia_snd_stream **p_snd_strm) { return open_stream(PJMEDIA_DIR_CAPTURE, index, PJMEDIA_AUD_INVALID_DEV, clock_rate, channel_count, samples_per_frame, bits_per_sample, rec_cb, NULL, user_data, p_snd_strm); } PJ_DEF(pj_status_t) pjmedia_snd_open_player( int index, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_play_cb play_cb, void *user_data, pjmedia_snd_stream **p_snd_strm ) { return open_stream(PJMEDIA_DIR_PLAYBACK, PJMEDIA_AUD_INVALID_DEV, index, clock_rate, channel_count, samples_per_frame, bits_per_sample, NULL, play_cb, user_data, p_snd_strm); } PJ_DEF(pj_status_t) pjmedia_snd_open( int rec_id, int play_id, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_snd_rec_cb rec_cb, pjmedia_snd_play_cb play_cb, void *user_data, pjmedia_snd_stream **p_snd_strm) { return open_stream(PJMEDIA_DIR_CAPTURE_PLAYBACK, rec_id, play_id, clock_rate, channel_count, samples_per_frame, bits_per_sample, rec_cb, play_cb, user_data, p_snd_strm); } PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream) { return pjmedia_aud_stream_start(stream->aud_strm); } PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream) { return pjmedia_aud_stream_stop(stream->aud_strm); } PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm, pjmedia_snd_stream_info *pi) { pjmedia_aud_param param; pj_status_t status; status = pjmedia_aud_stream_get_param(strm->aud_strm, ¶m); if (status != PJ_SUCCESS) return status; pj_bzero(pi, sizeof(*pi)); pi->dir = param.dir; pi->play_id = param.play_id; pi->rec_id = param.rec_id; pi->clock_rate = param.clock_rate; pi->channel_count = param.channel_count; pi->samples_per_frame = param.samples_per_frame; pi->bits_per_sample = param.bits_per_sample; if (param.flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) { pi->rec_latency = param.input_latency_ms; } if (param.flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) { pi->play_latency = param.output_latency_ms; } return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream) { pj_status_t status; status = pjmedia_aud_stream_destroy(stream->aud_strm); if (status != PJ_SUCCESS) return status; pj_pool_release(stream->pool); return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency, unsigned output_latency) { g_sys.user_rec_latency = input_latency; g_sys.user_play_latency = output_latency; return PJ_SUCCESS; } #endif /* PJMEDIA_HAS_LEGACY_SOUND_API */