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