1
/* $Id: alsa_dev.c 4125 2012-05-17 03:34:35Z nanang $ */
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>
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.
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.
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
21
#include <pjmedia_audiodev.h>
22
#include <pj/assert.h>
26
#include <pjmedia/errno.h>
28
#if defined(PJMEDIA_AUDIO_DEV_HAS_ALSA) && PJMEDIA_AUDIO_DEV_HAS_ALSA
30
#include <sys/syscall.h>
32
#include <sys/types.h>
34
#include <sys/select.h>
37
#include <alsa/asoundlib.h>
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
48
/* Set to 1 to enable tracing */
50
# define TRACE_(expr) PJ_LOG(5,expr)
58
static pj_status_t alsa_factory_init(pjmedia_aud_dev_factory *f);
59
static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f);
60
static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f);
61
static unsigned alsa_factory_get_dev_count(pjmedia_aud_dev_factory *f);
62
static pj_status_t alsa_factory_get_dev_info(pjmedia_aud_dev_factory *f,
64
pjmedia_aud_dev_info *info);
65
static pj_status_t alsa_factory_default_param(pjmedia_aud_dev_factory *f,
67
pjmedia_aud_param *param);
68
static 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,
73
pjmedia_aud_stream **p_strm);
78
static pj_status_t alsa_stream_get_param(pjmedia_aud_stream *strm,
79
pjmedia_aud_param *param);
80
static pj_status_t alsa_stream_get_cap(pjmedia_aud_stream *strm,
81
pjmedia_aud_dev_cap cap,
83
static pj_status_t alsa_stream_set_cap(pjmedia_aud_stream *strm,
84
pjmedia_aud_dev_cap cap,
86
static pj_status_t alsa_stream_start(pjmedia_aud_stream *strm);
87
static pj_status_t alsa_stream_stop(pjmedia_aud_stream *strm);
88
static pj_status_t alsa_stream_destroy(pjmedia_aud_stream *strm);
93
pjmedia_aud_dev_factory base;
99
pjmedia_aud_dev_info devs[MAX_DEVICES];
104
pjmedia_aud_stream base;
108
struct alsa_factory *af;
110
pjmedia_aud_param param; /* Running parameter */
111
int rec_id; /* Capture device id */
116
snd_pcm_uframes_t pb_frames; /* samples_per_frame */
117
pjmedia_aud_play_cb pb_cb;
118
unsigned pb_buf_size;
120
pj_thread_t *pb_thread;
124
snd_pcm_uframes_t ca_frames; /* samples_per_frame */
125
pjmedia_aud_rec_cb ca_cb;
126
unsigned ca_buf_size;
128
pj_thread_t *ca_thread;
131
static pjmedia_aud_dev_factory_op alsa_factory_op =
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
142
static pjmedia_aud_stream_op alsa_stream_op =
144
&alsa_stream_get_param,
145
&alsa_stream_get_cap,
146
&alsa_stream_set_cap,
152
static void null_alsa_error_handler (const char *file,
154
const char *function,
161
PJ_UNUSED_ARG(function);
166
static void alsa_error_handler (const char *file,
168
const char *function,
178
index = snprintf (err_msg, sizeof(err_msg), "ALSA lib %s:%i:(%s) ",
179
file, line, function);
181
index = snprintf (err_msg, sizeof(err_msg), "ALSA lib: ");
184
if (index < sizeof(err_msg)-1)
185
index += vsnprintf (err_msg+index, sizeof(err_msg)-index, fmt, arg);
187
if (err && index < sizeof(err_msg)-1)
188
index += snprintf (err_msg+index, sizeof(err_msg)-index, ": %s",
190
PJ_LOG (4,(THIS_FILE, "%s", err_msg));
194
static pj_status_t add_dev (struct alsa_factory *af, const char *dev_name)
196
pjmedia_aud_dev_info *adi;
198
int pb_result, ca_result;
200
if (af->dev_cnt >= PJ_ARRAY_SIZE(af->devs))
203
adi = &af->devs[af->dev_cnt];
205
TRACE_((THIS_FILE, "add_dev (%s): Enter", dev_name));
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"));
213
TRACE_((THIS_FILE, "Try to open the device for playback - failure"));
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"));
222
TRACE_((THIS_FILE, "Try to open the device for capture - failure"));
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;
231
/* Reset device info */
232
pj_bzero(adi, sizeof(*adi));
234
/* Set device name */
235
strcpy(adi->name, dev_name);
237
/* Check the number of playback channels */
238
adi->output_count = (pb_result>=0) ? 1 : 0;
240
/* Check the number of capture channels */
241
adi->input_count = (ca_result>=0) ? 1 : 0;
243
/* Set the default sample rate */
244
adi->default_samples_per_sec = 8000;
247
strcpy(adi->driver, "ALSA");
251
PJ_LOG (5,(THIS_FILE, "Added sound device %s", adi->name));
257
/* Create ALSA audio driver. */
258
pjmedia_aud_dev_factory* pjmedia_alsa_factory(pj_pool_factory *pf)
260
struct alsa_factory *af;
263
pool = pj_pool_create(pf, "alsa_aud_base", 256, 256, NULL);
264
af = PJ_POOL_ZALLOC_T(pool, struct alsa_factory);
266
af->base_pool = pool;
267
af->base.op = &alsa_factory_op;
273
/* API: init factory */
274
static pj_status_t alsa_factory_init(pjmedia_aud_dev_factory *f)
276
pj_status_t status = alsa_factory_refresh(f);
277
if (PJ_SUCCESS != status)
280
PJ_LOG(4,(THIS_FILE, "ALSA initialized"));
285
/* API: destroy factory */
286
static pj_status_t alsa_factory_destroy(pjmedia_aud_dev_factory *f)
288
struct alsa_factory *af = (struct alsa_factory*)f;
291
pj_pool_release(af->pool);
294
pj_pool_t *pool = af->base_pool;
295
af->base_pool = NULL;
296
pj_pool_release(pool);
299
/* Restore handler */
300
snd_lib_error_set_handler(NULL);
306
/* API: refresh the device list */
307
static pj_status_t alsa_factory_refresh(pjmedia_aud_dev_factory *f)
309
struct alsa_factory *af = (struct alsa_factory*)f;
313
TRACE_((THIS_FILE, "pjmedia_snd_init: Enumerate sound devices"));
315
if (af->pool != NULL) {
316
pj_pool_release(af->pool);
320
af->pool = pj_pool_create(af->pf, "alsa_aud", 256, 256, NULL);
323
/* Enumerate sound devices */
324
err = snd_device_name_hint(-1, "pcm", (void***)&hints);
326
return PJMEDIA_EAUD_SYSERR;
328
/* Set a null error handler prior to enumeration to suppress errors */
329
snd_lib_error_set_handler(null_alsa_error_handler);
333
char *name = snd_device_name_get_hint(*n, "NAME");
334
if (name != NULL && 0 != strcmp("null", name)) {
341
/* Install error handler after enumeration, otherwise we'll get many
342
* error messages about invalid card/device ID.
344
snd_lib_error_set_handler(alsa_error_handler);
346
err = snd_device_name_free_hint((void**)hints);
348
PJ_LOG(4,(THIS_FILE, "ALSA driver found %d devices", af->dev_cnt));
354
/* API: get device count */
355
static unsigned alsa_factory_get_dev_count(pjmedia_aud_dev_factory *f)
357
struct alsa_factory *af = (struct alsa_factory*)f;
362
/* API: get device info */
363
static pj_status_t alsa_factory_get_dev_info(pjmedia_aud_dev_factory *f,
365
pjmedia_aud_dev_info *info)
367
struct alsa_factory *af = (struct alsa_factory*)f;
369
PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
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;
377
/* API: create default parameter */
378
static pj_status_t alsa_factory_default_param(pjmedia_aud_dev_factory *f,
380
pjmedia_aud_param *param)
382
struct alsa_factory *af = (struct alsa_factory*)f;
383
pjmedia_aud_dev_info *adi;
385
PJ_ASSERT_RETURN(index>=0 && index<af->dev_cnt, PJ_EINVAL);
387
adi = &af->devs[index];
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;
403
return PJMEDIA_EAUD_INVDEV;
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;
418
static int pb_thread_func (void *arg)
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;
429
pj_bzero (buf, size);
432
TRACE_((THIS_FILE, "pb_thread_func(%u): Started",
433
(unsigned)syscall(SYS_gettid)));
435
snd_pcm_prepare (pcm);
437
while (!stream->quit) {
440
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
443
frame.timestamp.u64 = tstamp.u64;
446
result = stream->pb_cb (user_data, &frame);
447
if (result != PJ_SUCCESS || stream->quit)
450
if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
451
pj_bzero (buf, size);
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!"));
461
tstamp.u64 += nframes;
465
TRACE_((THIS_FILE, "pb_thread_func: Stopped"));
471
static int ca_thread_func (void *arg)
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;
481
struct sched_param param;
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, ¶m);
492
PJ_LOG (5,(THIS_FILE, "Unable to increase thread priority, "
493
"root access needed."));
495
PJ_LOG (5,(THIS_FILE, "Unable to increase thread priority, "
500
pj_bzero (buf, size);
503
TRACE_((THIS_FILE, "ca_thread_func(%u): Started",
504
(unsigned)syscall(SYS_gettid)));
506
snd_pcm_prepare (pcm);
508
while (!stream->quit) {
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);
517
} else if (result < 0) {
518
PJ_LOG (4,(THIS_FILE, "ca_thread_func: error reading data!"));
523
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
524
frame.buf = (void*) buf;
526
frame.timestamp.u64 = tstamp.u64;
529
result = stream->ca_cb (user_data, &frame);
530
if (result != PJ_SUCCESS || stream->quit)
533
tstamp.u64 += nframes;
536
TRACE_((THIS_FILE, "ca_thread_func: Stopped"));
542
static pj_status_t open_playback (struct alsa_stream* stream,
543
const pjmedia_aud_param *param)
545
snd_pcm_hw_params_t* params;
546
snd_pcm_format_t format;
549
snd_pcm_uframes_t tmp_buf_size;
550
snd_pcm_uframes_t tmp_period_size;
552
if (param->play_id < 0 || param->play_id >= stream->af->dev_cnt)
553
return PJMEDIA_EAUD_INVDEV;
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,
563
return PJMEDIA_EAUD_SYSERR;
565
/* Allocate a hardware parameters object. */
566
snd_pcm_hw_params_alloca (¶ms);
568
/* Fill it in with default values. */
569
snd_pcm_hw_params_any (stream->pb_pcm, params);
571
/* Set interleaved mode */
572
snd_pcm_hw_params_set_access (stream->pb_pcm, params,
573
SND_PCM_ACCESS_RW_INTERLEAVED);
576
switch (param->bits_per_sample) {
578
TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S8"));
579
format = SND_PCM_FORMAT_S8;
582
TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S16_LE"));
583
format = SND_PCM_FORMAT_S16_LE;
586
TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S24_LE"));
587
format = SND_PCM_FORMAT_S24_LE;
590
TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S32_LE"));
591
format = SND_PCM_FORMAT_S32_LE;
594
TRACE_((THIS_FILE, "open_playback: set format SND_PCM_FORMAT_S16_LE"));
595
format = SND_PCM_FORMAT_S16_LE;
598
snd_pcm_hw_params_set_format (stream->pb_pcm, params, format);
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);
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));
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",
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",
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;
627
tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
628
snd_pcm_hw_params_set_buffer_size_near (stream->pb_pcm, params,
630
stream->param.output_latency_ms = tmp_buf_size / (rate / 1000);
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);
637
TRACE_((THIS_FILE, "open_playback: buffer size set to: %d",
639
TRACE_((THIS_FILE, "open_playback: playback_latency set to: %d ms",
640
(int)stream->param.output_latency_ms));
642
/* Activate the parameters */
643
result = snd_pcm_hw_params (stream->pb_pcm, params);
645
snd_pcm_close (stream->pb_pcm);
646
return PJMEDIA_EAUD_SYSERR;
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));
660
static pj_status_t open_capture (struct alsa_stream* stream,
661
const pjmedia_aud_param *param)
663
snd_pcm_hw_params_t* params;
664
snd_pcm_format_t format;
667
snd_pcm_uframes_t tmp_buf_size;
668
snd_pcm_uframes_t tmp_period_size;
670
if (param->rec_id < 0 || param->rec_id >= stream->af->dev_cnt)
671
return PJMEDIA_EAUD_INVDEV;
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,
681
return PJMEDIA_EAUD_SYSERR;
683
/* Allocate a hardware parameters object. */
684
snd_pcm_hw_params_alloca (¶ms);
686
/* Fill it in with default values. */
687
snd_pcm_hw_params_any (stream->ca_pcm, params);
689
/* Set interleaved mode */
690
snd_pcm_hw_params_set_access (stream->ca_pcm, params,
691
SND_PCM_ACCESS_RW_INTERLEAVED);
694
switch (param->bits_per_sample) {
696
TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S8"));
697
format = SND_PCM_FORMAT_S8;
700
TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S16_LE"));
701
format = SND_PCM_FORMAT_S16_LE;
704
TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S24_LE"));
705
format = SND_PCM_FORMAT_S24_LE;
708
TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S32_LE"));
709
format = SND_PCM_FORMAT_S32_LE;
712
TRACE_((THIS_FILE, "open_capture: set format SND_PCM_FORMAT_S16_LE"));
713
format = SND_PCM_FORMAT_S16_LE;
716
snd_pcm_hw_params_set_format (stream->ca_pcm, params, format);
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);
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));
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",
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",
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;
745
tmp_buf_size = (rate / 1000) * PJMEDIA_SND_DEFAULT_REC_LATENCY;
746
snd_pcm_hw_params_set_buffer_size_near (stream->ca_pcm, params,
748
stream->param.input_latency_ms = tmp_buf_size / (rate / 1000);
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);
755
TRACE_((THIS_FILE, "open_capture: buffer size set to: %d",
757
TRACE_((THIS_FILE, "open_capture: capture_latency set to: %d ms",
758
(int)stream->param.input_latency_ms));
760
/* Activate the parameters */
761
result = snd_pcm_hw_params (stream->ca_pcm, params);
763
snd_pcm_close (stream->ca_pcm);
764
return PJMEDIA_EAUD_SYSERR;
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));
778
/* API: create stream */
779
static 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,
784
pjmedia_aud_stream **p_strm)
786
struct alsa_factory *af = (struct alsa_factory*)f;
789
struct alsa_stream* stream;
791
pool = pj_pool_create (af->pf, "alsa%p", 1024, 1024, NULL);
795
/* Allocate and initialize comon stream data */
796
stream = PJ_POOL_ZALLOC_T (pool, struct alsa_stream);
797
stream->base.op = &alsa_stream_op;
800
stream->user_data = user_data;
801
stream->pb_cb = play_cb;
802
stream->ca_cb = rec_cb;
804
pj_memcpy(&stream->param, param, sizeof(*param));
807
if (param->dir & PJMEDIA_DIR_PLAYBACK) {
808
status = open_playback (stream, param);
809
if (status != PJ_SUCCESS) {
810
pj_pool_release (pool);
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);
826
*p_strm = &stream->base;
831
/* API: get running parameter */
832
static pj_status_t alsa_stream_get_param(pjmedia_aud_stream *s,
833
pjmedia_aud_param *pi)
835
struct alsa_stream *stream = (struct alsa_stream*)s;
837
PJ_ASSERT_RETURN(s && pi, PJ_EINVAL);
839
pj_memcpy(pi, &stream->param, sizeof(*pi));
845
/* API: get capability */
846
static pj_status_t alsa_stream_get_cap(pjmedia_aud_stream *s,
847
pjmedia_aud_dev_cap cap,
850
struct alsa_stream *stream = (struct alsa_stream*)s;
852
PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
854
if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
855
(stream->param.dir & PJMEDIA_DIR_CAPTURE))
857
/* Recording latency */
858
*(unsigned*)pval = stream->param.input_latency_ms;
860
} else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
861
(stream->param.dir & PJMEDIA_DIR_PLAYBACK))
863
/* Playback latency */
864
*(unsigned*)pval = stream->param.output_latency_ms;
867
return PJMEDIA_EAUD_INVCAP;
872
/* API: set capability */
873
static pj_status_t alsa_stream_set_cap(pjmedia_aud_stream *strm,
874
pjmedia_aud_dev_cap cap,
879
PJ_UNUSED_ARG(value);
881
return PJMEDIA_EAUD_INVCAP;
885
/* API: start stream */
886
static pj_status_t alsa_stream_start (pjmedia_aud_stream *s)
888
struct alsa_stream *stream = (struct alsa_stream*)s;
889
pj_status_t status = PJ_SUCCESS;
892
if (stream->param.dir & PJMEDIA_DIR_PLAYBACK) {
893
status = pj_thread_create (stream->pool,
894
"alsasound_playback",
900
if (status != PJ_SUCCESS)
904
if (stream->param.dir & PJMEDIA_DIR_CAPTURE) {
905
status = pj_thread_create (stream->pool,
906
"alsasound_playback",
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;
924
/* API: stop stream */
925
static pj_status_t alsa_stream_stop (pjmedia_aud_stream *s)
927
struct alsa_stream *stream = (struct alsa_stream*)s;
931
if (stream->pb_thread) {
933
"alsa_stream_stop(%u): Waiting for playback to stop.",
934
(unsigned)syscall(SYS_gettid)));
935
pj_thread_join (stream->pb_thread);
937
"alsa_stream_stop(%u): playback stopped.",
938
(unsigned)syscall(SYS_gettid)));
939
pj_thread_destroy(stream->pb_thread);
940
stream->pb_thread = NULL;
943
if (stream->ca_thread) {
945
"alsa_stream_stop(%u): Waiting for capture to stop.",
946
(unsigned)syscall(SYS_gettid)));
947
pj_thread_join (stream->ca_thread);
949
"alsa_stream_stop(%u): capture stopped.",
950
(unsigned)syscall(SYS_gettid)));
951
pj_thread_destroy(stream->ca_thread);
952
stream->ca_thread = NULL;
960
static pj_status_t alsa_stream_destroy (pjmedia_aud_stream *s)
962
struct alsa_stream *stream = (struct alsa_stream*)s;
964
alsa_stream_stop (s);
966
if (stream->param.dir & PJMEDIA_DIR_PLAYBACK) {
967
snd_pcm_close (stream->pb_pcm);
968
stream->pb_pcm = NULL;
970
if (stream->param.dir & PJMEDIA_DIR_CAPTURE) {
971
snd_pcm_close (stream->ca_pcm);
972
stream->ca_pcm = NULL;
975
pj_pool_release (stream->pool);
980
#endif /* PJMEDIA_AUDIO_DEV_HAS_ALSA */