~noskcaj/ubuntu/saucy/sflphone/merge-1.2.3-2

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject/pjmedia/src/pjmedia-audiodev/alsa_dev.c

  • Committer: Jackson Doak
  • Date: 2013-07-10 21:04:46 UTC
  • mfrom: (20.1.3 sid)
  • Revision ID: noskcaj@ubuntu.com-20130710210446-y8f587vza807icr9
Properly merged from upstream.

Show diffs side-by-side

added added

removed removed

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