blob: 66fad9b0f05c2fec10be61496f5735c222a848a2 [file] [log] [blame]
Benny Prijono64f91382009-03-05 18:02:28 +00001/* $Id$ */
2/*
3 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjmedia-audiodev/audiodev_imp.h>
20#include <pjmedia/sound.h>
21#include <pj/assert.h>
22
23#if PJMEDIA_AUDIO_DEV_HAS_LEGACY_DEVICE
24
25#define THIS_FILE "legacy_dev.c"
26
27/* Legacy devices factory */
28struct legacy_factory
29{
30 pjmedia_aud_dev_factory base;
31 pj_pool_t *pool;
32 pj_pool_factory *pf;
33};
34
35
36struct legacy_stream
37{
38 pjmedia_aud_stream base;
39
40 pj_pool_t *pool;
41 pjmedia_aud_param param;
42 pjmedia_snd_stream *snd_strm;
43 pjmedia_aud_play_cb user_play_cb;
44 pjmedia_aud_rec_cb user_rec_cb;
45 void *user_user_data;
46 unsigned input_latency;
47 unsigned output_latency;
48};
49
50
51/* Prototypes */
52static pj_status_t factory_init(pjmedia_aud_dev_factory *f);
53static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f);
54static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
55static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
56 unsigned index,
57 pjmedia_aud_dev_info *info);
58static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
59 unsigned index,
60 pjmedia_aud_param *param);
61static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
62 const pjmedia_aud_param *param,
63 pjmedia_aud_rec_cb rec_cb,
64 pjmedia_aud_play_cb play_cb,
65 void *user_data,
66 pjmedia_aud_stream **p_aud_strm);
67
68static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
69 pjmedia_aud_param *param);
70static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
71 pjmedia_aud_dev_cap cap,
72 void *value);
73static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
74 pjmedia_aud_dev_cap cap,
75 const void *value);
76static pj_status_t stream_start(pjmedia_aud_stream *strm);
77static pj_status_t stream_stop(pjmedia_aud_stream *strm);
78static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
79
80
81/* Operations */
82static pjmedia_aud_dev_factory_op factory_op =
83{
84 &factory_init,
85 &factory_destroy,
86 &factory_get_dev_count,
87 &factory_get_dev_info,
88 &factory_default_param,
89 &factory_create_stream
90};
91
92static pjmedia_aud_stream_op stream_op =
93{
94 &stream_get_param,
95 &stream_get_cap,
96 &stream_set_cap,
97 &stream_start,
98 &stream_stop,
99 &stream_destroy
100};
101
102
103/****************************************************************************
104 * Factory operations
105 */
106
107/*
108 * Init legacy audio driver.
109 */
110pjmedia_aud_dev_factory* pjmedia_legacy_factory(pj_pool_factory *pf)
111{
112 struct legacy_factory *f;
113 pj_pool_t *pool;
114
115 pool = pj_pool_create(pf, "legacy-snd", 512, 512, NULL);
116 f = PJ_POOL_ZALLOC_T(pool, struct legacy_factory);
117 f->pf = pf;
118 f->pool = pool;
119 f->base.op = &factory_op;
120
121 return &f->base;
122}
123
124
125/* API: init factory */
126static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
127{
128 struct legacy_factory *wf = (struct legacy_factory*)f;
129
130 return pjmedia_snd_init(wf->pf);
131}
132
133/* API: destroy factory */
134static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
135{
136 struct legacy_factory *wf = (struct legacy_factory*)f;
137 pj_status_t status;
138
139 status = pjmedia_snd_deinit();
140
141 if (status == PJ_SUCCESS) {
142 pj_pool_t *pool = wf->pool;
143 wf->pool = NULL;
144 pj_pool_release(pool);
145 }
146
147 return status;
148}
149
150/* API: get number of devices */
151static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
152{
153 PJ_UNUSED_ARG(f);
154 return pjmedia_snd_get_dev_count();
155}
156
157/* API: get device info */
158static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
159 unsigned index,
160 pjmedia_aud_dev_info *info)
161{
162 const pjmedia_snd_dev_info *si =
163 pjmedia_snd_get_dev_info(index);;
164
165 PJ_UNUSED_ARG(f);
166
167 if (si == NULL)
168 return PJMEDIA_EAUD_INVDEV;
169
170 pj_bzero(info, sizeof(*info));
171 pj_ansi_strncpy(info->name, si->name, sizeof(info->name));
172 info->name[sizeof(info->name)-1] = '\0';
173 info->input_count = si->input_count;
174 info->output_count = si->output_count;
175 info->default_samples_per_sec = si->default_samples_per_sec;
176 pj_ansi_strcpy(info->driver, "legacy");
177 info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
178 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
179
180 return PJ_SUCCESS;
181}
182
183/* API: create default device parameter */
184static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
185 unsigned index,
186 pjmedia_aud_param *param)
187{
188 pjmedia_aud_dev_info di;
189 pj_status_t status;
190
191 status = factory_get_dev_info(f, index, &di);
192 if (status != PJ_SUCCESS)
193 return status;
194
195 pj_bzero(param, sizeof(*param));
196 if (di.input_count && di.output_count) {
197 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
198 param->rec_id = index;
199 param->play_id = index;
200 } else if (di.input_count) {
201 param->dir = PJMEDIA_DIR_CAPTURE;
202 param->rec_id = index;
203 param->play_id = PJMEDIA_AUD_INVALID_DEV;
204 } else if (di.output_count) {
205 param->dir = PJMEDIA_DIR_PLAYBACK;
206 param->play_id = index;
207 param->rec_id = PJMEDIA_AUD_INVALID_DEV;
208 } else {
209 return PJMEDIA_EAUD_INVDEV;
210 }
211
212 param->clock_rate = di.default_samples_per_sec;
213 param->channel_count = 1;
214 param->samples_per_frame = di.default_samples_per_sec * 20 / 1000;
215 param->bits_per_sample = 16;
216 param->flags = di.caps;
217 param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
218 param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
219
220 return PJ_SUCCESS;
221}
222
223/* Callback from legacy sound device */
224static pj_status_t snd_play_cb(/* in */ void *user_data,
225 /* in */ pj_uint32_t timestamp,
226 /* out */ void *output,
227 /* out */ unsigned size)
228{
229 struct legacy_stream *strm = (struct legacy_stream*)user_data;
230 pjmedia_frame frame;
231 pj_status_t status;
232
233 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
234 frame.buf = output;
235 frame.size = size;
236 frame.timestamp.u64 = timestamp;
237
238 status = strm->user_play_cb(strm->user_user_data, &frame);
239 if (status != PJ_SUCCESS)
240 return status;
241
242 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) {
243 pj_bzero(output, size);
244 }
245
246 return PJ_SUCCESS;
247}
248
249/* Callback from legacy sound device */
250static pj_status_t snd_rec_cb( /* in */ void *user_data,
251 /* in */ pj_uint32_t timestamp,
252 /* in */ void *input,
253 /* in*/ unsigned size)
254{
255 struct legacy_stream *strm = (struct legacy_stream*)user_data;
256 pjmedia_frame frame;
257
258 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
259 frame.buf = input;
260 frame.size = size;
261 frame.timestamp.u64 = timestamp;
262
263 return strm->user_rec_cb(strm->user_user_data, &frame);
264}
265
266/* API: create stream */
267static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
268 const pjmedia_aud_param *param,
269 pjmedia_aud_rec_cb rec_cb,
270 pjmedia_aud_play_cb play_cb,
271 void *user_data,
272 pjmedia_aud_stream **p_aud_strm)
273{
274 struct legacy_factory *wf = (struct legacy_factory*)f;
275 pj_pool_t *pool;
276 struct legacy_stream *strm;
277 pj_status_t status;
278
279 /* Initialize our stream data */
280 pool = pj_pool_create(wf->pf, "legacy-snd", 512, 512, NULL);
281 strm = PJ_POOL_ZALLOC_T(pool, struct legacy_stream);
282 strm->pool = pool;
283 strm->user_rec_cb = rec_cb;
284 strm->user_play_cb = play_cb;
285 strm->user_user_data = user_data;
286 pj_memcpy(&strm->param, param, sizeof(*param));
287
288 /* Set the latency if wanted */
289 if (param->dir==PJMEDIA_DIR_CAPTURE_PLAYBACK &&
290 param->flags & (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
291 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY))
292 {
293 PJ_ASSERT_RETURN(param->input_latency_ms &&
294 param->output_latency_ms,
295 PJMEDIA_EAUD_BADLATENCY);
296
297 strm->input_latency = param->input_latency_ms;
298 strm->output_latency = param->output_latency_ms;
299
300 status = pjmedia_snd_set_latency(param->input_latency_ms,
301 param->output_latency_ms);
302 if (status != PJ_SUCCESS) {
303 pj_pool_release(pool);
304 return status;
305 }
306 }
307
308 /* Open the stream */
309 if (param->dir == PJMEDIA_DIR_CAPTURE) {
310 status = pjmedia_snd_open_rec(param->rec_id,
311 param->clock_rate,
312 param->channel_count,
313 param->samples_per_frame,
314 param->bits_per_sample,
315 &snd_rec_cb,
316 strm,
317 &strm->snd_strm);
318 } else if (param->dir == PJMEDIA_DIR_PLAYBACK) {
319 status = pjmedia_snd_open_player(param->play_id,
320 param->clock_rate,
321 param->channel_count,
322 param->samples_per_frame,
323 param->bits_per_sample,
324 &snd_play_cb,
325 strm,
326 &strm->snd_strm);
327
328 } else if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
329 status = pjmedia_snd_open(param->rec_id,
330 param->play_id,
331 param->clock_rate,
332 param->channel_count,
333 param->samples_per_frame,
334 param->bits_per_sample,
335 &snd_rec_cb,
336 &snd_play_cb,
337 strm,
338 &strm->snd_strm);
339 } else {
340 pj_assert(!"Invalid direction!");
341 return PJ_EINVAL;
342 }
343
344 if (status != PJ_SUCCESS) {
345 pj_pool_release(pool);
346 return status;
347 }
348
349 *p_aud_strm = &strm->base;
350 return PJ_SUCCESS;
351}
352
353/* API: Get stream info. */
354static pj_status_t stream_get_param(pjmedia_aud_stream *s,
355 pjmedia_aud_param *pi)
356{
357 struct legacy_stream *strm = (struct legacy_stream*)s;
358 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
359
360 pj_memcpy(pi, &strm->param, sizeof(*pi));
361
362 if (strm->input_latency) {
363 pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
364 pi->input_latency_ms = strm->input_latency;
365 } else {
366 pi->flags &= ~PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
367 }
368
369 if (strm->output_latency) {
370 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
371 pi->output_latency_ms = strm->output_latency;
372 } else {
373 pi->flags &= ~PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
374 }
375
376 return PJ_SUCCESS;
377}
378
379/* API: get capability */
380static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
381 pjmedia_aud_dev_cap cap,
382 void *pval)
383{
384 struct legacy_stream *strm = (struct legacy_stream*)s;
385
386 PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL);
387
388 if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
389 (strm->param.dir & PJMEDIA_DIR_CAPTURE))
390 {
391 /* Recording latency */
392 if (strm->input_latency) {
393 *(unsigned*)pval = strm->input_latency;
394 return PJ_SUCCESS;
395 } else {
396 return PJMEDIA_EAUD_INVCAP;
397 }
398
399 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
400 (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
401 {
402 /* Playback latency */
403 if (strm->output_latency) {
404 *(unsigned*)pval = strm->output_latency;
405 return PJ_SUCCESS;
406 } else {
407 return PJMEDIA_EAUD_INVCAP;
408 }
409 } else {
410 return PJMEDIA_EAUD_INVCAP;
411 }
412}
413
414/* API: set capability */
415static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
416 pjmedia_aud_dev_cap cap,
417 const void *pval)
418{
419 PJ_UNUSED_ARG(s);
420 PJ_UNUSED_ARG(cap);
421 PJ_UNUSED_ARG(pval);
422 return PJMEDIA_EAUD_INVCAP;
423}
424
425/* API: Start stream. */
426static pj_status_t stream_start(pjmedia_aud_stream *s)
427{
428 struct legacy_stream *strm = (struct legacy_stream*)s;
429 return pjmedia_snd_stream_start(strm->snd_strm);
430}
431
432/* API: Stop stream. */
433static pj_status_t stream_stop(pjmedia_aud_stream *s)
434{
435 struct legacy_stream *strm = (struct legacy_stream*)s;
436 return pjmedia_snd_stream_stop(strm->snd_strm);
437}
438
439
440/* API: Destroy stream. */
441static pj_status_t stream_destroy(pjmedia_aud_stream *s)
442{
443 struct legacy_stream *strm = (struct legacy_stream*)s;
444 pj_status_t status;
445
446 status = pjmedia_snd_stream_close(strm->snd_strm);
447
448 if (status == PJ_SUCCESS) {
449 pj_pool_t *pool = strm->pool;
450
451 strm->pool = NULL;
452 pj_pool_release(pool);
453 }
454
455 return status;
456}
457
458#endif /* PJMEDIA_AUDIO_DEV_HAS_LEGACY_DEVICE */
459