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