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