~ubuntu-branches/ubuntu/vivid/sflphone/vivid

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2013-06-30 11:40:56 UTC
  • mfrom: (4.1.18 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130630114056-0np50jkyqo6vnmii
Tags: 1.2.3-2
* changeset_r92d62cfc54732bbbcfff2b1d36c096b120b981a5.diff 
  - fixes automatic endian detection 
* Update Vcs: fixes vcs-field-not-canonical

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: alsa_dev.c 4130 2012-05-17 08:35:51Z 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 */