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, &param);
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 (&params);
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 (&params);
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