blob: 45c6c5de4aa52445598ff425a35ca88ceace9e56 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id: legacy_dev.c 3553 2011-05-05 06:14:19Z nanang $ */
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002/*
3 * Copyright (C) 2008-2011 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 pj_status_t factory_refresh(pjmedia_aud_dev_factory *f);
55static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f);
56static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
57 unsigned index,
58 pjmedia_aud_dev_info *info);
59static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
60 unsigned index,
61 pjmedia_aud_param *param);
62static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
63 const pjmedia_aud_param *param,
64 pjmedia_aud_rec_cb rec_cb,
65 pjmedia_aud_play_cb play_cb,
66 void *user_data,
67 pjmedia_aud_stream **p_aud_strm);
68
69static pj_status_t stream_get_param(pjmedia_aud_stream *strm,
70 pjmedia_aud_param *param);
71static pj_status_t stream_get_cap(pjmedia_aud_stream *strm,
72 pjmedia_aud_dev_cap cap,
73 void *value);
74static pj_status_t stream_set_cap(pjmedia_aud_stream *strm,
75 pjmedia_aud_dev_cap cap,
76 const void *value);
77static pj_status_t stream_start(pjmedia_aud_stream *strm);
78static pj_status_t stream_stop(pjmedia_aud_stream *strm);
79static pj_status_t stream_destroy(pjmedia_aud_stream *strm);
80
81
82/* Operations */
83static pjmedia_aud_dev_factory_op factory_op =
84{
85 &factory_init,
86 &factory_destroy,
87 &factory_get_dev_count,
88 &factory_get_dev_info,
89 &factory_default_param,
90 &factory_create_stream,
91 &factory_refresh
92};
93
94static pjmedia_aud_stream_op stream_op =
95{
96 &stream_get_param,
97 &stream_get_cap,
98 &stream_set_cap,
99 &stream_start,
100 &stream_stop,
101 &stream_destroy
102};
103
104
105/****************************************************************************
106 * Factory operations
107 */
108
109/*
110 * Init legacy audio driver.
111 */
112pjmedia_aud_dev_factory* pjmedia_legacy_factory(pj_pool_factory *pf)
113{
114 struct legacy_factory *f;
115 pj_pool_t *pool;
116
117 pool = pj_pool_create(pf, "legacy-snd", 512, 512, NULL);
118 f = PJ_POOL_ZALLOC_T(pool, struct legacy_factory);
119 f->pf = pf;
120 f->pool = pool;
121 f->base.op = &factory_op;
122
123 return &f->base;
124}
125
126
127/* API: init factory */
128static pj_status_t factory_init(pjmedia_aud_dev_factory *f)
129{
130 struct legacy_factory *wf = (struct legacy_factory*)f;
131
132 return pjmedia_snd_init(wf->pf);
133}
134
135/* API: destroy factory */
136static pj_status_t factory_destroy(pjmedia_aud_dev_factory *f)
137{
138 struct legacy_factory *wf = (struct legacy_factory*)f;
139 pj_status_t status;
140
141 status = pjmedia_snd_deinit();
142
143 if (status == PJ_SUCCESS) {
144 pj_pool_t *pool = wf->pool;
145 wf->pool = NULL;
146 pj_pool_release(pool);
147 }
148
149 return status;
150}
151
152/* API: refresh the list of devices */
153static pj_status_t factory_refresh(pjmedia_aud_dev_factory *f)
154{
155 PJ_UNUSED_ARG(f);
156 return PJ_ENOTSUP;
157}
158
159/* API: get number of devices */
160static unsigned factory_get_dev_count(pjmedia_aud_dev_factory *f)
161{
162 PJ_UNUSED_ARG(f);
163 return pjmedia_snd_get_dev_count();
164}
165
166/* API: get device info */
167static pj_status_t factory_get_dev_info(pjmedia_aud_dev_factory *f,
168 unsigned index,
169 pjmedia_aud_dev_info *info)
170{
171 const pjmedia_snd_dev_info *si =
172 pjmedia_snd_get_dev_info(index);;
173
174 PJ_UNUSED_ARG(f);
175
176 if (si == NULL)
177 return PJMEDIA_EAUD_INVDEV;
178
179 pj_bzero(info, sizeof(*info));
180 pj_ansi_strncpy(info->name, si->name, sizeof(info->name));
181 info->name[sizeof(info->name)-1] = '\0';
182 info->input_count = si->input_count;
183 info->output_count = si->output_count;
184 info->default_samples_per_sec = si->default_samples_per_sec;
185 pj_ansi_strcpy(info->driver, "legacy");
186 info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
187 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
188
189 return PJ_SUCCESS;
190}
191
192/* API: create default device parameter */
193static pj_status_t factory_default_param(pjmedia_aud_dev_factory *f,
194 unsigned index,
195 pjmedia_aud_param *param)
196{
197 pjmedia_aud_dev_info di;
198 pj_status_t status;
199
200 status = factory_get_dev_info(f, index, &di);
201 if (status != PJ_SUCCESS)
202 return status;
203
204 pj_bzero(param, sizeof(*param));
205 if (di.input_count && di.output_count) {
206 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
207 param->rec_id = index;
208 param->play_id = index;
209 } else if (di.input_count) {
210 param->dir = PJMEDIA_DIR_CAPTURE;
211 param->rec_id = index;
212 param->play_id = PJMEDIA_AUD_INVALID_DEV;
213 } else if (di.output_count) {
214 param->dir = PJMEDIA_DIR_PLAYBACK;
215 param->play_id = index;
216 param->rec_id = PJMEDIA_AUD_INVALID_DEV;
217 } else {
218 return PJMEDIA_EAUD_INVDEV;
219 }
220
221 param->clock_rate = di.default_samples_per_sec;
222 param->channel_count = 1;
223 param->samples_per_frame = di.default_samples_per_sec * 20 / 1000;
224 param->bits_per_sample = 16;
225 param->flags = di.caps;
226 param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
227 param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
228
229 return PJ_SUCCESS;
230}
231
232/* Callback from legacy sound device */
233static pj_status_t snd_play_cb(/* in */ void *user_data,
234 /* in */ pj_uint32_t timestamp,
235 /* out */ void *output,
236 /* out */ unsigned size)
237{
238 struct legacy_stream *strm = (struct legacy_stream*)user_data;
239 pjmedia_frame frame;
240 pj_status_t status;
241
242 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
243 frame.buf = output;
244 frame.size = size;
245 frame.timestamp.u64 = timestamp;
246
247 status = strm->user_play_cb(strm->user_user_data, &frame);
248 if (status != PJ_SUCCESS)
249 return status;
250
251 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) {
252 pj_bzero(output, size);
253 }
254
255 return PJ_SUCCESS;
256}
257
258/* Callback from legacy sound device */
259static pj_status_t snd_rec_cb( /* in */ void *user_data,
260 /* in */ pj_uint32_t timestamp,
261 /* in */ void *input,
262 /* in*/ unsigned size)
263{
264 struct legacy_stream *strm = (struct legacy_stream*)user_data;
265 pjmedia_frame frame;
266
267 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
268 frame.buf = input;
269 frame.size = size;
270 frame.timestamp.u64 = timestamp;
271
272 return strm->user_rec_cb(strm->user_user_data, &frame);
273}
274
275/* API: create stream */
276static pj_status_t factory_create_stream(pjmedia_aud_dev_factory *f,
277 const pjmedia_aud_param *param,
278 pjmedia_aud_rec_cb rec_cb,
279 pjmedia_aud_play_cb play_cb,
280 void *user_data,
281 pjmedia_aud_stream **p_aud_strm)
282{
283 struct legacy_factory *wf = (struct legacy_factory*)f;
284 pj_pool_t *pool;
285 struct legacy_stream *strm;
286 pj_status_t status;
287
288 /* Initialize our stream data */
289 pool = pj_pool_create(wf->pf, "legacy-snd", 512, 512, NULL);
290 strm = PJ_POOL_ZALLOC_T(pool, struct legacy_stream);
291 strm->pool = pool;
292 strm->user_rec_cb = rec_cb;
293 strm->user_play_cb = play_cb;
294 strm->user_user_data = user_data;
295 pj_memcpy(&strm->param, param, sizeof(*param));
296
297 /* Set the latency if wanted */
298 if (param->dir==PJMEDIA_DIR_CAPTURE_PLAYBACK &&
299 param->flags & (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
300 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY))
301 {
302 PJ_ASSERT_RETURN(param->input_latency_ms &&
303 param->output_latency_ms,
304 PJMEDIA_EAUD_BADLATENCY);
305
306 strm->input_latency = param->input_latency_ms;
307 strm->output_latency = param->output_latency_ms;
308
309 status = pjmedia_snd_set_latency(param->input_latency_ms,
310 param->output_latency_ms);
311 if (status != PJ_SUCCESS) {
312 pj_pool_release(pool);
313 return status;
314 }
315 }
316
317 /* Open the stream */
318 if (param->dir == PJMEDIA_DIR_CAPTURE) {
319 status = pjmedia_snd_open_rec(param->rec_id,
320 param->clock_rate,
321 param->channel_count,
322 param->samples_per_frame,
323 param->bits_per_sample,
324 &snd_rec_cb,
325 strm,
326 &strm->snd_strm);
327 } else if (param->dir == PJMEDIA_DIR_PLAYBACK) {
328 status = pjmedia_snd_open_player(param->play_id,
329 param->clock_rate,
330 param->channel_count,
331 param->samples_per_frame,
332 param->bits_per_sample,
333 &snd_play_cb,
334 strm,
335 &strm->snd_strm);
336
337 } else if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
338 status = pjmedia_snd_open(param->rec_id,
339 param->play_id,
340 param->clock_rate,
341 param->channel_count,
342 param->samples_per_frame,
343 param->bits_per_sample,
344 &snd_rec_cb,
345 &snd_play_cb,
346 strm,
347 &strm->snd_strm);
348 } else {
349 pj_assert(!"Invalid direction!");
350 return PJ_EINVAL;
351 }
352
353 if (status != PJ_SUCCESS) {
354 pj_pool_release(pool);
355 return status;
356 }
357
358 *p_aud_strm = &strm->base;
359 return PJ_SUCCESS;
360}
361
362/* API: Get stream info. */
363static pj_status_t stream_get_param(pjmedia_aud_stream *s,
364 pjmedia_aud_param *pi)
365{
366 struct legacy_stream *strm = (struct legacy_stream*)s;
367 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
368
369 pj_memcpy(pi, &strm->param, sizeof(*pi));
370
371 if (strm->input_latency) {
372 pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
373 pi->input_latency_ms = strm->input_latency;
374 } else {
375 pi->flags &= ~PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
376 }
377
378 if (strm->output_latency) {
379 pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
380 pi->output_latency_ms = strm->output_latency;
381 } else {
382 pi->flags &= ~PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
383 }
384
385 return PJ_SUCCESS;
386}
387
388/* API: get capability */
389static pj_status_t stream_get_cap(pjmedia_aud_stream *s,
390 pjmedia_aud_dev_cap cap,
391 void *pval)
392{
393 struct legacy_stream *strm = (struct legacy_stream*)s;
394
395 PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL);
396
397 if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
398 (strm->param.dir & PJMEDIA_DIR_CAPTURE))
399 {
400 /* Recording latency */
401 if (strm->input_latency) {
402 *(unsigned*)pval = strm->input_latency;
403 return PJ_SUCCESS;
404 } else {
405 return PJMEDIA_EAUD_INVCAP;
406 }
407
408 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
409 (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
410 {
411 /* Playback latency */
412 if (strm->output_latency) {
413 *(unsigned*)pval = strm->output_latency;
414 return PJ_SUCCESS;
415 } else {
416 return PJMEDIA_EAUD_INVCAP;
417 }
418 } else {
419 return PJMEDIA_EAUD_INVCAP;
420 }
421}
422
423/* API: set capability */
424static pj_status_t stream_set_cap(pjmedia_aud_stream *s,
425 pjmedia_aud_dev_cap cap,
426 const void *pval)
427{
428 PJ_UNUSED_ARG(s);
429 PJ_UNUSED_ARG(cap);
430 PJ_UNUSED_ARG(pval);
431 return PJMEDIA_EAUD_INVCAP;
432}
433
434/* API: Start stream. */
435static pj_status_t stream_start(pjmedia_aud_stream *s)
436{
437 struct legacy_stream *strm = (struct legacy_stream*)s;
438 return pjmedia_snd_stream_start(strm->snd_strm);
439}
440
441/* API: Stop stream. */
442static pj_status_t stream_stop(pjmedia_aud_stream *s)
443{
444 struct legacy_stream *strm = (struct legacy_stream*)s;
445 return pjmedia_snd_stream_stop(strm->snd_strm);
446}
447
448
449/* API: Destroy stream. */
450static pj_status_t stream_destroy(pjmedia_aud_stream *s)
451{
452 struct legacy_stream *strm = (struct legacy_stream*)s;
453 pj_status_t status;
454
455 status = pjmedia_snd_stream_close(strm->snd_strm);
456
457 if (status == PJ_SUCCESS) {
458 pj_pool_t *pool = strm->pool;
459
460 strm->pool = NULL;
461 pj_pool_release(pool);
462 }
463
464 return status;
465}
466
467#endif /* PJMEDIA_AUDIO_DEV_HAS_LEGACY_DEVICE */
468