blob: 90eca5425ad34832825b3543bcd2bf976e40d550 [file] [log] [blame]
Benny Prijono19a87c72010-01-27 17:22:17 +00001/* $Id$ */
2/*
3 * Copyright (C) 2009-2010 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2007-2009 Keystream AB and Konftel AB, All rights reserved.
5 * Author: <dan.aberg@keystream.se>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21#include <pjmedia_audiodev.h>
22#include <pj/assert.h>
23#include <pj/log.h>
24#include <pj/os.h>
25#include <pj/pool.h>
26#include <pjmedia/errno.h>
27
28#if defined(PJMEDIA_AUDIO_DEV_HAS_ALSA) && PJMEDIA_AUDIO_DEV_HAS_ALSA
29
30#include <sys/syscall.h>
31#include <sys/time.h>
32#include <sys/types.h>
33#include <unistd.h>
34#include <sys/select.h>
35#include <pthread.h>
36#include <errno.h>
37#include <alsa/asoundlib.h>
38
39
40#define THIS_FILE "alsa_dev.c"
41#define ALSA_DEVICE_NAME "plughw:%d,%d"
42#define ALSASOUND_PLAYBACK 1
43#define ALSASOUND_CAPTURE 2
44#define MAX_SOUND_CARDS 5
45#define MAX_SOUND_DEVICES_PER_CARD 5
46#define MAX_DEVICES 16
47
48/* Set to 1 to enable tracing */
49#if 0
50# define TRACE_(expr) PJ_LOG(5,expr)
51#else
52# define TRACE_(expr)
53#endif
54
55/*
56 * Factory prototypes
57 */
58static pj_status_t alsa_factory_init(pjmedia_aud_dev_factory *f);
59static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f);
60static unsigned alsa_factory_get_dev_count(pjmedia_aud_dev_factory *f);
61static pj_status_t alsa_factory_get_dev_info(pjmedia_aud_dev_factory *f,
62 unsigned index,
63 pjmedia_aud_dev_info *info);
64static pj_status_t alsa_factory_default_param(pjmedia_aud_dev_factory *f,
65 unsigned index,
66 pjmedia_aud_param *param);
67static pj_status_t alsa_factory_create_stream(pjmedia_aud_dev_factory *f,
68 const pjmedia_aud_param *param,
69 pjmedia_aud_rec_cb rec_cb,
70 pjmedia_aud_play_cb play_cb,
71 void *user_data,
72 pjmedia_aud_stream **p_strm);
73
74/*
75 * Stream prototypes
76 */
77static pj_status_t alsa_stream_get_param(pjmedia_aud_stream *strm,
78 pjmedia_aud_param *param);
79static pj_status_t alsa_stream_get_cap(pjmedia_aud_stream *strm,
80 pjmedia_aud_dev_cap cap,
81 void *value);
82static pj_status_t alsa_stream_set_cap(pjmedia_aud_stream *strm,
83 pjmedia_aud_dev_cap cap,
84 const void *value);
85static pj_status_t alsa_stream_start(pjmedia_aud_stream *strm);
86static pj_status_t alsa_stream_stop(pjmedia_aud_stream *strm);
87static pj_status_t alsa_stream_destroy(pjmedia_aud_stream *strm);
88
89
90struct alsa_factory
91{
92 pjmedia_aud_dev_factory base;
93 pj_pool_factory *pf;
94 pj_pool_t *pool;
95
96 unsigned dev_cnt;
97 pjmedia_aud_dev_info devs[MAX_DEVICES];
98};
99
100struct alsa_stream
101{
102 pjmedia_aud_stream base;
103
104 /* Common */
105 pj_pool_t *pool;
106 struct alsa_factory *af;
107 void *user_data;
108 pjmedia_aud_param param; /* Running parameter */
109 int rec_id; /* Capture device id */
110 int quit;
111
112 /* Playback */
113 snd_pcm_t *pb_pcm;
114 snd_pcm_uframes_t pb_frames; /* samples_per_frame */
115 pjmedia_aud_play_cb pb_cb;
116 unsigned pb_buf_size;
117 char *pb_buf;
118 pj_thread_t *pb_thread;
119
120 /* Capture */
121 snd_pcm_t *ca_pcm;
122 snd_pcm_uframes_t ca_frames; /* samples_per_frame */
123 pjmedia_aud_rec_cb ca_cb;
124 unsigned ca_buf_size;
125 char *ca_buf;
126 pj_thread_t *ca_thread;
127};
128
129static pjmedia_aud_dev_factory_op alsa_factory_op =
130{
131 &alsa_factory_init,
132 &alsa_factory_destroy,
133 &alsa_factory_get_dev_count,
134 &alsa_factory_get_dev_info,
135 &alsa_factory_default_param,
136 &alsa_factory_create_stream
137};
138
139static pjmedia_aud_stream_op alsa_stream_op =
140{
141 &alsa_stream_get_param,
142 &alsa_stream_get_cap,
143 &alsa_stream_set_cap,
144 &alsa_stream_start,
145 &alsa_stream_stop,
146 &alsa_stream_destroy
147};
148
149static void alsa_error_handler (const char *file,
150 int line,
151 const char *function,
152 int err,
153 const char *fmt,
154 ...)
155{
156 char err_msg[128];
157 int index;
158 va_list arg;
159
160#ifndef NDEBUG
161 index = snprintf (err_msg, sizeof(err_msg), "ALSA lib %s:%i:(%s) ",
162 file, line, function);
163#else
164 index = snprintf (err_msg, sizeof(err_msg), "ALSA lib: ");
165#endif
166 va_start (arg, fmt);
167 if (index < sizeof(err_msg)-1)
168 index += vsnprintf (err_msg+index, sizeof(err_msg)-index, fmt, arg);
169 va_end(arg);
170 if (err && index < sizeof(err_msg)-1)
171 index += snprintf (err_msg+index, sizeof(err_msg)-index, ": %s",
172 snd_strerror(err));
173 PJ_LOG (4,(THIS_FILE, "%s", err_msg));
174}
175
176
177static pj_status_t add_dev (struct alsa_factory *af, int card, int device)
178{
179 pjmedia_aud_dev_info *adi;
180 char dev_name[32];
181 snd_pcm_t* pcm;
182 int pb_result, ca_result;
183
184 if (af->dev_cnt >= PJ_ARRAY_SIZE(af->devs))
185 return PJ_ETOOMANY;
186
187 adi = &af->devs[af->dev_cnt];
188
189 TRACE_((THIS_FILE, "add_dev (%d, %d): Enter", card, device));
190 sprintf (dev_name, ALSA_DEVICE_NAME, card, device);
191
192 /* Try to open the device in playback mode */
193 pb_result = snd_pcm_open (&pcm, dev_name, SND_PCM_STREAM_PLAYBACK, 0);
194 if (pb_result >= 0) {
195 TRACE_((THIS_FILE, "Try to open the device for playback - success"));
196 snd_pcm_close (pcm);
197 } else {
198 TRACE_((THIS_FILE, "Try to open the device for playback - failure"));
199 }
200
201 /* Try to open the device in capture mode */
202 ca_result = snd_pcm_open (&pcm, dev_name, SND_PCM_STREAM_CAPTURE, 0);
203 if (ca_result >= 0) {
204 TRACE_((THIS_FILE, "Try to open the device for capture - success"));
205 snd_pcm_close (pcm);
206 } else {
207 TRACE_((THIS_FILE, "Try to open the device for capture - failure"));
208 }
209
210 /* Check if the device could be opened in playback or capture mode */
211 if (pb_result<0 && ca_result<0) {
212 TRACE_((THIS_FILE, "Unable to open sound device %s", dev_name));
213 return PJMEDIA_EAUD_NODEV;
214 }
215
216 /* Reset device info */
217 pj_bzero(adi, sizeof(*adi));
218
219 /* Set device name */
220 strcpy(adi->name, dev_name);
221
222 /* Check the number of playback channels */
223 adi->output_count = (pb_result>=0) ? 1 : 0;
224
225 /* Check the number of capture channels */
226 adi->input_count = (ca_result>=0) ? 1 : 0;
227
228 /* Set the default sample rate */
229 adi->default_samples_per_sec = 8000;
230
231 /* Driver name */
232 strcpy(adi->driver, "ALSA");
233
234 ++af->dev_cnt;
235
236 PJ_LOG (5,(THIS_FILE, "Added sound device %s", adi->name));
237
238 return PJ_SUCCESS;
239}
240
241
242/* Create ALSA audio driver. */
243pjmedia_aud_dev_factory* pjmedia_alsa_factory(pj_pool_factory *pf)
244{
245 struct alsa_factory *af;
246 pj_pool_t *pool;
247
248 pool = pj_pool_create(pf, "alsa_aud", 256, 256, NULL);
249 af = PJ_POOL_ZALLOC_T(pool, struct alsa_factory);
250 af->pf = pf;
251 af->pool = pool;
252 af->base.op = &alsa_factory_op;
253
254 return &af->base;
255}
256
257
258/* API: init factory */
259static pj_status_t alsa_factory_init(pjmedia_aud_dev_factory *f)
260{
261 struct alsa_factory *af = (struct alsa_factory*)f;
262 int card, device;
263
264 TRACE_((THIS_FILE, "pjmedia_snd_init: Enumerate sound devices"));
265 /* Enumerate sound devices */
266 for (card=0; card<MAX_SOUND_CARDS; card++) {
267 for (device=0; device<MAX_SOUND_DEVICES_PER_CARD; device++) {
268 add_dev(af, card, device);
269 }
270 }
271
272 /* Install error handler after enumeration, otherwise we'll get many
273 * error messages about invalid card/device ID.
274 */
275 snd_lib_error_set_handler (alsa_error_handler);
276
277 PJ_LOG(4,(THIS_FILE, "ALSA driver found %d devices", af->dev_cnt));
278 return PJ_SUCCESS;
279}
280
281
282/* API: destroy factory */
283static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f)
284{
285 struct alsa_factory *af = (struct alsa_factory*)f;
286
287 if (af->pool) {
288 pj_pool_t *pool = af->pool;
289 af->pool = NULL;
290 pj_pool_release(pool);
291 }
292
293 /* Restore handler */
294 snd_lib_error_set_handler(NULL);
295
296 return PJ_SUCCESS;
297}
298
299
300/* API: get device count */
301static unsigned alsa_factory_get_dev_count(pjmedia_aud_dev_factory *f)
302{
303 struct alsa_factory *af = (struct alsa_factory*)f;
304 return af->dev_cnt;
305}
306
307
308/* API: get device info */
309static pj_status_t alsa_factory_get_dev_info(pjmedia_aud_dev_factory *f,
310 unsigned index,
311 pjmedia_aud_dev_info *info)
312{
313 struct alsa_factory *af = (struct alsa_factory*)f;
314
315 PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
316
317 pj_memcpy(info, &af->devs[index], sizeof(*info));
318 info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
319 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
320 return PJ_SUCCESS;
321}
322
323/* API: create default parameter */
324static pj_status_t alsa_factory_default_param(pjmedia_aud_dev_factory *f,
325 unsigned index,
326 pjmedia_aud_param *param)
327{
328 struct alsa_factory *af = (struct alsa_factory*)f;
329 pjmedia_aud_dev_info *adi;
330
331 PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
332
333 adi = &af->devs[index];
334
335 pj_bzero(param, sizeof(*param));
336 if (adi->input_count && adi->output_count) {
337 param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
338 param->rec_id = index;
339 param->play_id = index;
340 } else if (adi->input_count) {
341 param->dir = PJMEDIA_DIR_CAPTURE;
342 param->rec_id = index;
343 param->play_id = PJMEDIA_AUD_INVALID_DEV;
344 } else if (adi->output_count) {
345 param->dir = PJMEDIA_DIR_PLAYBACK;
346 param->play_id = index;
347 param->rec_id = PJMEDIA_AUD_INVALID_DEV;
348 } else {
349 return PJMEDIA_EAUD_INVDEV;
350 }
351
352 param->clock_rate = adi->default_samples_per_sec;
353 param->channel_count = 1;
354 param->samples_per_frame = adi->default_samples_per_sec * 20 / 1000;
355 param->bits_per_sample = 16;
356 param->flags = adi->caps;
357 param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
358 param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
359
360 return PJ_SUCCESS;
361}
362
363
364static int pb_thread_func (void *arg)
365{
366 struct alsa_stream* stream = (struct alsa_stream*) arg;
367 snd_pcm_t* pcm = stream->pb_pcm;
368 int size = stream->pb_buf_size;
369 snd_pcm_uframes_t nframes = stream->pb_frames;
370 void* user_data = stream->user_data;
371 char* buf = stream->pb_buf;
372 pj_timestamp tstamp;
373 int result;
374
375 pj_bzero (buf, size);
376 tstamp.u64 = 0;
377
378 TRACE_((THIS_FILE, "pb_thread_func(%u): Started",
379 (unsigned)syscall(SYS_gettid)));
380
381 snd_pcm_prepare (pcm);
382
383 while (!stream->quit) {
384 pjmedia_frame frame;
385
386 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
387 frame.buf = buf;
388 frame.size = size;
389 frame.timestamp.u64 = tstamp.u64;
390 frame.bit_info = 0;
391
392 result = stream->pb_cb (user_data, &frame);
393 if (result != PJ_SUCCESS || stream->quit)
394 break;
395
396 if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
397 pj_bzero (buf, size);
398
399 result = snd_pcm_writei (pcm, buf, nframes);
400 if (result == -EPIPE) {
401 PJ_LOG (4,(THIS_FILE, "pb_thread_func: underrun!"));
402 snd_pcm_prepare (pcm);
403 } else if (result < 0) {
404 PJ_LOG (4,(THIS_FILE, "pb_thread_func: error writing data!"));
405 }
406
407 tstamp.u64 += nframes;
408 }
409
410 snd_pcm_drain (pcm);
411 TRACE_((THIS_FILE, "pb_thread_func: Stopped"));
412 return PJ_SUCCESS;
413}
414
415
416
417static int ca_thread_func (void *arg)
418{
419 struct alsa_stream* stream = (struct alsa_stream*) arg;
420 snd_pcm_t* pcm = stream->ca_pcm;
421 int size = stream->ca_buf_size;
422 snd_pcm_uframes_t nframes = stream->ca_frames;
423 void* user_data = stream->user_data;
424 char* buf = stream->ca_buf;
425 pj_timestamp tstamp;
426 int result;
427 struct sched_param param;
428 pthread_t* thid;
429
430 thid = (pthread_t*) pj_thread_get_os_handle (pj_thread_this());
431 param.sched_priority = sched_get_priority_max (SCHED_RR);
432 PJ_LOG (5,(THIS_FILE, "ca_thread_func(%u): Set thread priority "
433 "for audio capture thread.",
434 (unsigned)syscall(SYS_gettid)));
435 result = pthread_setschedparam (*thid, SCHED_RR, &param);
436 if (result) {
437 if (result == EPERM)
438 PJ_LOG (5,(THIS_FILE, "Unable to increase thread priority, "
439 "root access needed."));
440 else
441 PJ_LOG (5,(THIS_FILE, "Unable to increase thread priority, "
442 "error: %d",
443 result));
444 }
445
446 pj_bzero (buf, size);
447 tstamp.u64 = 0;
448
449 TRACE_((THIS_FILE, "ca_thread_func(%u): Started",
450 (unsigned)syscall(SYS_gettid)));
451
452 snd_pcm_prepare (pcm);
453
454 while (!stream->quit) {
455 pjmedia_frame frame;
456
457 pj_bzero (buf, size);
458 result = snd_pcm_readi (pcm, buf, nframes);
459 if (result == -EPIPE) {
460 PJ_LOG (4,(THIS_FILE, "ca_thread_func: overrun!"));
461 snd_pcm_prepare (pcm);
462 continue;
463 } else if (result < 0) {
464 PJ_LOG (4,(THIS_FILE, "ca_thread_func: error reading data!"));
465 }
466 if (stream->quit)
467 break;
468
469 frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
470 frame.buf = (void*) buf;
471 frame.size = size;
472 frame.timestamp.u64 = tstamp.u64;
473 frame.bit_info = 0;
474
475 result = stream->ca_cb (user_data, &frame);
476 if (result != PJ_SUCCESS || stream->quit)
477 break;
478
479 tstamp.u64 += nframes;
480 }
481 snd_pcm_drain (pcm);
482 TRACE_((THIS_FILE, "ca_thread_func: Stopped"));
483
484 return PJ_SUCCESS;
485}
486
487
488static pj_status_t open_playback (struct alsa_stream* stream,
489 const pjmedia_aud_param *param)
490{
491 snd_pcm_hw_params_t* params;
492 snd_pcm_format_t format;
493 int result;
494 unsigned int rate;
495 snd_pcm_uframes_t tmp_buf_size;
496
497 if (param->play_id < 0 || param->play_id >= stream->af->dev_cnt)
498 return PJMEDIA_EAUD_INVDEV;
499
500 /* Open PCM for playback */
501 PJ_LOG (5,(THIS_FILE, "open_playback: Open playback device '%s'",
502 stream->af->devs[param->play_id].name));
503 result = snd_pcm_open (&stream->pb_pcm,
504 stream->af->devs[param->play_id].name,
505 SND_PCM_STREAM_PLAYBACK,
506 0);
507 if (result < 0)
508 return PJMEDIA_EAUD_SYSERR;
509
510 /* Allocate a hardware parameters object. */
511 snd_pcm_hw_params_alloca (&params);
512
513 /* Fill it in with default values. */
514 snd_pcm_hw_params_any (stream->pb_pcm, params);
515
516 /* Set interleaved mode */
517 snd_pcm_hw_params_set_access (stream->pb_pcm, params,
518 SND_PCM_ACCESS_RW_INTERLEAVED);
519
520 /* Set format */
521 switch (param->bits_per_sample) {
522 case 8:
523 TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S8"));
524 format = SND_PCM_FORMAT_S8;
525 break;
526 case 16:
527 TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S16_LE"));
528 format = SND_PCM_FORMAT_S16_LE;
529 break;
530 case 24:
531 TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S24_LE"));
532 format = SND_PCM_FORMAT_S24_LE;
533 break;
534 case 32:
535 TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S32_LE"));
536 format = SND_PCM_FORMAT_S32_LE;
537 break;
538 default:
539 TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S16_LE"));
540 format = SND_PCM_FORMAT_S16_LE;
541 break;
542 }
543 snd_pcm_hw_params_set_format (stream->pb_pcm, params, format);
544
545 /* Set number of channels */
546 TRACE_((THIS_FILE, "open_playback: set channels: %d",
547 param->channel_count));
548 snd_pcm_hw_params_set_channels (stream->pb_pcm, params,
549 param->channel_count);
550
551 /* Set clock rate */
552 rate = param->clock_rate;
553 TRACE_((THIS_FILE, "open_playback: set clock rate: %d", rate));
554 snd_pcm_hw_params_set_rate_near (stream->pb_pcm, params, &rate, NULL);
555 TRACE_((THIS_FILE, "open_playback: clock rate set to: %d", rate));
556
557 /* Set period size to samples_per_frame frames. */
558 stream->pb_frames = (snd_pcm_uframes_t) param->samples_per_frame /
559 param->channel_count;
560 TRACE_((THIS_FILE, "open_playback: set period size: %d",
561 stream->pb_frames));
562 snd_pcm_hw_params_set_period_size_near (stream->pb_pcm, params,
563 &stream->pb_frames, NULL);
564 TRACE_((THIS_FILE, "open_playback: period size set to: %d",
565 stream->pb_frames));
566
567 /* Set the sound device buffer size and latency */
568 if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)
569 tmp_buf_size = (rate / 1000) * param->output_latency_ms;
570 else
571 tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
572 snd_pcm_hw_params_set_buffer_size_near (stream->pb_pcm, params,
573 &tmp_buf_size);
574 stream->param.output_latency_ms = tmp_buf_size / (rate / 1000);
575
576 /* Set our buffer */
577 stream->pb_buf_size = stream->pb_frames * param->channel_count *
578 (param->bits_per_sample/8);
579 stream->pb_buf = (char*) pj_pool_alloc(stream->pool, stream->pb_buf_size);
580
581 TRACE_((THIS_FILE, "open_playback: buffer size set to: %d",
582 (int)tmp_buf_size));
583 TRACE_((THIS_FILE, "open_playback: playback_latency set to: %d ms",
584 (int)stream->param.output_latency_ms));
585
586 /* Activate the parameters */
587 result = snd_pcm_hw_params (stream->pb_pcm, params);
588 if (result < 0) {
589 snd_pcm_close (stream->pb_pcm);
590 return PJMEDIA_EAUD_SYSERR;
591 }
592
593 PJ_LOG (5,(THIS_FILE, "Opened device alsa(%s) for playing, sample rate=%d"
594 ", ch=%d, bits=%d, period size=%d frames, latency=%d ms",
595 stream->af->devs[param->play_id].name,
596 rate, param->channel_count,
597 param->bits_per_sample, stream->pb_frames,
598 (int)stream->param.output_latency_ms));
599
600 return PJ_SUCCESS;
601}
602
603
604static pj_status_t open_capture (struct alsa_stream* stream,
605 const pjmedia_aud_param *param)
606{
607 snd_pcm_hw_params_t* params;
608 snd_pcm_format_t format;
609 int result;
610 unsigned int rate;
611 snd_pcm_uframes_t tmp_buf_size;
612
613 if (param->rec_id < 0 || param->rec_id >= stream->af->dev_cnt)
614 return PJMEDIA_EAUD_INVDEV;
615
616 /* Open PCM for capture */
617 PJ_LOG (5,(THIS_FILE, "open_capture: Open capture device '%s'",
618 stream->af->devs[param->rec_id].name));
619 result = snd_pcm_open (&stream->ca_pcm,
620 stream->af->devs[param->rec_id].name,
621 SND_PCM_STREAM_CAPTURE,
622 0);
623 if (result < 0)
624 return PJMEDIA_EAUD_SYSERR;
625
626 /* Allocate a hardware parameters object. */
627 snd_pcm_hw_params_alloca (&params);
628
629 /* Fill it in with default values. */
630 snd_pcm_hw_params_any (stream->ca_pcm, params);
631
632 /* Set interleaved mode */
633 snd_pcm_hw_params_set_access (stream->ca_pcm, params,
634 SND_PCM_ACCESS_RW_INTERLEAVED);
635
636 /* Set format */
637 switch (param->bits_per_sample) {
638 case 8:
639 TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S8"));
640 format = SND_PCM_FORMAT_S8;
641 break;
642 case 16:
643 TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S16_LE"));
644 format = SND_PCM_FORMAT_S16_LE;
645 break;
646 case 24:
647 TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S24_LE"));
648 format = SND_PCM_FORMAT_S24_LE;
649 break;
650 case 32:
651 TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S32_LE"));
652 format = SND_PCM_FORMAT_S32_LE;
653 break;
654 default:
655 TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S16_LE"));
656 format = SND_PCM_FORMAT_S16_LE;
657 break;
658 }
659 snd_pcm_hw_params_set_format (stream->ca_pcm, params, format);
660
661 /* Set number of channels */
662 TRACE_((THIS_FILE, "open_capture: set channels: %d",
663 param->channel_count));
664 snd_pcm_hw_params_set_channels (stream->ca_pcm, params,
665 param->channel_count);
666
667 /* Set clock rate */
668 rate = param->clock_rate;
669 TRACE_((THIS_FILE, "open_capture: set clock rate: %d", rate));
670 snd_pcm_hw_params_set_rate_near (stream->ca_pcm, params, &rate, NULL);
671 TRACE_((THIS_FILE, "open_capture: clock rate set to: %d", rate));
672
673 /* Set period size to samples_per_frame frames. */
674 stream->ca_frames = (snd_pcm_uframes_t) param->samples_per_frame /
675 param->channel_count;
676 TRACE_((THIS_FILE, "open_capture: set period size: %d",
677 stream->ca_frames));
678 snd_pcm_hw_params_set_period_size_near (stream->ca_pcm, params,
679 &stream->ca_frames, NULL);
680 TRACE_((THIS_FILE, "open_capture: period size set to: %d",
681 stream->ca_frames));
682
683 /* Set the sound device buffer size and latency */
684 if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)
685 tmp_buf_size = (rate / 1000) * param->input_latency_ms;
686 else
687 tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_REC_LATENCY;
688 snd_pcm_hw_params_set_buffer_size_near (stream->ca_pcm, params,
689 &tmp_buf_size);
690 stream->param.input_latency_ms = tmp_buf_size / (rate / 1000);
691
692 /* Set our buffer */
693 stream->ca_buf_size = stream->ca_frames * param->channel_count *
694 (param->bits_per_sample/8);
695 stream->ca_buf = (char*) pj_pool_alloc (stream->pool, stream->ca_buf_size);
696
697 TRACE_((THIS_FILE, "open_capture: buffer size set to: %d",
698 (int)tmp_buf_size));
699 TRACE_((THIS_FILE, "open_capture: capture_latency set to: %d ms",
700 (int)stream->param.input_latency_ms));
701
702 /* Activate the parameters */
703 result = snd_pcm_hw_params (stream->ca_pcm, params);
704 if (result < 0) {
705 snd_pcm_close (stream->ca_pcm);
706 return PJMEDIA_EAUD_SYSERR;
707 }
708
709 PJ_LOG (5,(THIS_FILE, "Opened device alsa(%s) for capture, sample rate=%d"
710 ", ch=%d, bits=%d, period size=%d frames, latency=%d ms",
711 stream->af->devs[param->rec_id].name,
712 rate, param->channel_count,
713 param->bits_per_sample, stream->ca_frames,
714 (int)stream->param.input_latency_ms));
715
716 return PJ_SUCCESS;
717}
718
719
720/* API: create stream */
721static pj_status_t alsa_factory_create_stream(pjmedia_aud_dev_factory *f,
722 const pjmedia_aud_param *param,
723 pjmedia_aud_rec_cb rec_cb,
724 pjmedia_aud_play_cb play_cb,
725 void *user_data,
726 pjmedia_aud_stream **p_strm)
727{
728 struct alsa_factory *af = (struct alsa_factory*)f;
729 pj_status_t status;
730 pj_pool_t* pool;
731 struct alsa_stream* stream;
732
733 pool = pj_pool_create (af->pf, "alsa%p", 1024, 1024, NULL);
734 if (!pool)
735 return PJ_ENOMEM;
736
737 /* Allocate and initialize comon stream data */
738 stream = PJ_POOL_ZALLOC_T (pool, struct alsa_stream);
739 stream->base.op = &alsa_stream_op;
740 stream->pool = pool;
741 stream->af = af;
742 stream->user_data = user_data;
743 stream->pb_cb = play_cb;
744 stream->ca_cb = rec_cb;
745 stream->quit = 0;
746 pj_memcpy(&stream->param, param, sizeof(*param));
747
748 /* Init playback */
749 if (param->dir & PJMEDIA_DIR_PLAYBACK) {
750 status = open_playback (stream, param);
751 if (status != PJ_SUCCESS) {
752 pj_pool_release (pool);
753 return status;
754 }
755 }
756
757 /* Init capture */
758 if (param->dir & PJMEDIA_DIR_CAPTURE) {
759 status = open_capture (stream, param);
760 if (status != PJ_SUCCESS) {
761 if (param->dir & PJMEDIA_DIR_PLAYBACK)
762 snd_pcm_close (stream->pb_pcm);
763 pj_pool_release (pool);
764 return status;
765 }
766 }
767
768 *p_strm = &stream->base;
769 return PJ_SUCCESS;
770}
771
772
773/* API: get running parameter */
774static pj_status_t alsa_stream_get_param(pjmedia_aud_stream *s,
775 pjmedia_aud_param *pi)
776{
777 struct alsa_stream *stream = (struct alsa_stream*)s;
778
779 PJ_ASSERT_RETURN(s && pi, PJ_EINVAL);
780
781 pj_memcpy(pi, &stream->param, sizeof(*pi));
782
783 return PJ_SUCCESS;
784}
785
786
787/* API: get capability */
788static pj_status_t alsa_stream_get_cap(pjmedia_aud_stream *s,
789 pjmedia_aud_dev_cap cap,
790 void *pval)
791{
792 struct alsa_stream *stream = (struct alsa_stream*)s;
793
794 PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
795
796 if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
797 (stream->param.dir & PJMEDIA_DIR_CAPTURE))
798 {
799 /* Recording latency */
800 *(unsigned*)pval = stream->param.input_latency_ms;
801 return PJ_SUCCESS;
802 } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
803 (stream->param.dir & PJMEDIA_DIR_PLAYBACK))
804 {
805 /* Playback latency */
806 *(unsigned*)pval = stream->param.output_latency_ms;
807 return PJ_SUCCESS;
808 } else {
809 return PJMEDIA_EAUD_INVCAP;
810 }
811}
812
813
814/* API: set capability */
815static pj_status_t alsa_stream_set_cap(pjmedia_aud_stream *strm,
816 pjmedia_aud_dev_cap cap,
817 const void *value)
818{
819 PJ_UNUSED_ARG(strm);
820 PJ_UNUSED_ARG(cap);
821 PJ_UNUSED_ARG(value);
822
823 return PJMEDIA_EAUD_INVCAP;
824}
825
826
827/* API: start stream */
828static pj_status_t alsa_stream_start (pjmedia_aud_stream *s)
829{
830 struct alsa_stream *stream = (struct alsa_stream*)s;
831 pj_status_t status = PJ_SUCCESS;
832
833 stream->quit = 0;
834 if (stream->param.dir & PJMEDIA_DIR_PLAYBACK) {
835 status = pj_thread_create (stream->pool,
836 "alsasound_playback",
837 pb_thread_func,
838 stream,
839 0, //ZERO,
840 0,
841 &stream->pb_thread);
842 if (status != PJ_SUCCESS)
843 return status;
844 }
845
846 if (stream->param.dir & PJMEDIA_DIR_CAPTURE) {
847 status = pj_thread_create (stream->pool,
848 "alsasound_playback",
849 ca_thread_func,
850 stream,
851 0, //ZERO,
852 0,
853 &stream->ca_thread);
854 if (status != PJ_SUCCESS) {
855 stream->quit = PJ_TRUE;
856 pj_thread_join(stream->pb_thread);
857 pj_thread_destroy(stream->pb_thread);
858 stream->pb_thread = NULL;
859 }
860 }
861
862 return status;
863}
864
865
866/* API: stop stream */
867static pj_status_t alsa_stream_stop (pjmedia_aud_stream *s)
868{
869 struct alsa_stream *stream = (struct alsa_stream*)s;
870
871 stream->quit = 1;
872
873 if (stream->pb_thread) {
874 TRACE_((THIS_FILE,
875 "alsa_stream_stop(%u): Waiting for playback to stop.",
876 (unsigned)syscall(SYS_gettid)));
877 pj_thread_join (stream->pb_thread);
878 TRACE_((THIS_FILE,
879 "alsa_stream_stop(%u): playback stopped.",
880 (unsigned)syscall(SYS_gettid)));
881 pj_thread_destroy(stream->pb_thread);
882 stream->pb_thread = NULL;
883 }
884
885 if (stream->ca_thread) {
886 TRACE_((THIS_FILE,
887 "alsa_stream_stop(%u): Waiting for capture to stop.",
888 (unsigned)syscall(SYS_gettid)));
889 pj_thread_join (stream->ca_thread);
890 TRACE_((THIS_FILE,
891 "alsa_stream_stop(%u): capture stopped.",
892 (unsigned)syscall(SYS_gettid)));
893 pj_thread_destroy(stream->ca_thread);
894 stream->ca_thread = NULL;
895 }
896
897 return PJ_SUCCESS;
898}
899
900
901
902static pj_status_t alsa_stream_destroy (pjmedia_aud_stream *s)
903{
904 struct alsa_stream *stream = (struct alsa_stream*)s;
905
906 alsa_stream_stop (s);
907
908 if (stream->param.dir & PJMEDIA_DIR_PLAYBACK) {
909 snd_pcm_close (stream->pb_pcm);
910 stream->pb_pcm = NULL;
911 }
912 if (stream->param.dir & PJMEDIA_DIR_CAPTURE) {
913 snd_pcm_close (stream->ca_pcm);
914 stream->ca_pcm = NULL;
915 }
916
917 pj_pool_release (stream->pool);
918
919 return PJ_SUCCESS;
920}
921
922#endif /* PJMEDIA_AUDIO_DEV_HAS_ALSA */