~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: pa_dev.c 3553 2011-05-05 06:14:19Z nanang $ */
 
2
/* 
 
3
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 
4
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 
19
 */
 
20
#include <pjmedia-audiodev/audiodev_imp.h>
 
21
#include <pj/assert.h>
 
22
#include <pj/log.h>
 
23
#include <pj/os.h>
 
24
#include <pj/string.h>
 
25
 
 
26
#if PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO
 
27
 
 
28
#include <portaudio.h>
 
29
 
 
30
#define THIS_FILE       "pa_dev.c"
 
31
#define DRIVER_NAME     "PA"
 
32
 
 
33
/* Enable call to PaUtil_SetDebugPrintFunction, but this is not always
 
34
 * available across all PortAudio versions (?)
 
35
 */
 
36
/*#define USE_PA_DEBUG_PRINT */
 
37
 
 
38
struct pa_aud_factory
 
39
{
 
40
    pjmedia_aud_dev_factory      base;
 
41
    pj_pool_factory             *pf;
 
42
    pj_pool_t                   *pool;
 
43
};
 
44
 
 
45
 
 
46
/* 
 
47
 * Sound stream descriptor.
 
48
 * This struct may be used for both unidirectional or bidirectional sound
 
49
 * streams.
 
50
 */
 
51
struct pa_aud_stream
 
52
{
 
53
    pjmedia_aud_stream   base;
 
54
 
 
55
    pj_pool_t           *pool;
 
56
    pj_str_t             name;
 
57
    pjmedia_dir          dir;
 
58
    int                  play_id;
 
59
    int                  rec_id;
 
60
    int                  bytes_per_sample;
 
61
    pj_uint32_t          samples_per_sec;
 
62
    unsigned             samples_per_frame;
 
63
    int                  channel_count;
 
64
 
 
65
    PaStream            *rec_strm;
 
66
    PaStream            *play_strm;
 
67
 
 
68
    void                *user_data;
 
69
    pjmedia_aud_rec_cb   rec_cb;
 
70
    pjmedia_aud_play_cb  play_cb;
 
71
 
 
72
    pj_timestamp         play_timestamp;
 
73
    pj_timestamp         rec_timestamp;
 
74
    pj_uint32_t          underflow;
 
75
    pj_uint32_t          overflow;
 
76
 
 
77
    pj_bool_t            quit_flag;
 
78
 
 
79
    pj_bool_t            rec_thread_exited;
 
80
    pj_bool_t            rec_thread_initialized;
 
81
    pj_thread_desc       rec_thread_desc;
 
82
    pj_thread_t         *rec_thread;
 
83
 
 
84
    pj_bool_t            play_thread_exited;
 
85
    pj_bool_t            play_thread_initialized;
 
86
    pj_thread_desc       play_thread_desc;
 
87
    pj_thread_t         *play_thread;
 
88
 
 
89
    /* Sometime the record callback does not return framesize as configured
 
90
     * (e.g: in OSS), while this module must guarantee returning framesize
 
91
     * as configured in the creation settings. In this case, we need a buffer 
 
92
     * for the recorded samples.
 
93
     */
 
94
    pj_int16_t          *rec_buf;
 
95
    unsigned             rec_buf_count;
 
96
 
 
97
    /* Sometime the player callback does not request framesize as configured
 
98
     * (e.g: in Linux OSS) while sound device will always get samples from 
 
99
     * the other component as many as configured samples_per_frame. 
 
100
     */
 
101
    pj_int16_t          *play_buf;
 
102
    unsigned             play_buf_count;
 
103
};
 
104
 
 
105
 
 
106
/* Factory prototypes */
 
107
static pj_status_t  pa_init(pjmedia_aud_dev_factory *f);
 
108
static pj_status_t  pa_destroy(pjmedia_aud_dev_factory *f);
 
109
static pj_status_t  pa_refresh(pjmedia_aud_dev_factory *f);
 
110
static unsigned     pa_get_dev_count(pjmedia_aud_dev_factory *f);
 
111
static pj_status_t  pa_get_dev_info(pjmedia_aud_dev_factory *f, 
 
112
                                    unsigned index,
 
113
                                    pjmedia_aud_dev_info *info);
 
114
static pj_status_t  pa_default_param(pjmedia_aud_dev_factory *f,
 
115
                                     unsigned index,
 
116
                                     pjmedia_aud_param *param);
 
117
static pj_status_t  pa_create_stream(pjmedia_aud_dev_factory *f,
 
118
                                     const pjmedia_aud_param *param,
 
119
                                     pjmedia_aud_rec_cb rec_cb,
 
120
                                     pjmedia_aud_play_cb play_cb,
 
121
                                     void *user_data,
 
122
                                     pjmedia_aud_stream **p_aud_strm);
 
123
 
 
124
/* Stream prototypes */
 
125
static pj_status_t strm_get_param(pjmedia_aud_stream *strm,
 
126
                                  pjmedia_aud_param *param);
 
127
static pj_status_t strm_get_cap(pjmedia_aud_stream *strm,
 
128
                                pjmedia_aud_dev_cap cap,
 
129
                                void *value);
 
130
static pj_status_t strm_set_cap(pjmedia_aud_stream *strm,
 
131
                                pjmedia_aud_dev_cap cap,
 
132
                                const void *value);
 
133
static pj_status_t strm_start(pjmedia_aud_stream *strm);
 
134
static pj_status_t strm_stop(pjmedia_aud_stream *strm);
 
135
static pj_status_t strm_destroy(pjmedia_aud_stream *strm);
 
136
 
 
137
 
 
138
static pjmedia_aud_dev_factory_op pa_op = 
 
139
{
 
140
    &pa_init,
 
141
    &pa_destroy,
 
142
    &pa_get_dev_count,
 
143
    &pa_get_dev_info,
 
144
    &pa_default_param,
 
145
    &pa_create_stream,
 
146
    &pa_refresh    
 
147
};
 
148
 
 
149
static pjmedia_aud_stream_op pa_strm_op = 
 
150
{
 
151
    &strm_get_param,
 
152
    &strm_get_cap,
 
153
    &strm_set_cap,
 
154
    &strm_start,
 
155
    &strm_stop,
 
156
    &strm_destroy
 
157
};
 
158
 
 
159
 
 
160
 
 
161
static int PaRecorderCallback(const void *input, 
 
162
                              void *output,
 
163
                              unsigned long frameCount,
 
164
                              const PaStreamCallbackTimeInfo* timeInfo,
 
165
                              PaStreamCallbackFlags statusFlags,
 
166
                              void *userData )
 
167
{
 
168
    struct pa_aud_stream *stream = (struct pa_aud_stream*) userData;
 
169
    pj_status_t status = 0;
 
170
    unsigned nsamples;
 
171
 
 
172
    PJ_UNUSED_ARG(output);
 
173
    PJ_UNUSED_ARG(timeInfo);
 
174
 
 
175
    if (stream->quit_flag)
 
176
        goto on_break;
 
177
 
 
178
    if (input == NULL)
 
179
        return paContinue;
 
180
 
 
181
    /* Known cases of callback's thread:
 
182
     * - The thread may be changed in the middle of a session, e.g: in MacOS 
 
183
     *   it happens when plugging/unplugging headphone.
 
184
     * - The same thread may be reused in consecutive sessions. The first
 
185
     *   session will leave TLS set, but release the TLS data address,
 
186
     *   so the second session must re-register the callback's thread.
 
187
     */
 
188
    if (stream->rec_thread_initialized == 0 || !pj_thread_is_registered()) 
 
189
    {
 
190
        pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc));
 
191
        status = pj_thread_register("pa_rec", stream->rec_thread_desc, 
 
192
                                    &stream->rec_thread);
 
193
        stream->rec_thread_initialized = 1;
 
194
        PJ_LOG(5,(THIS_FILE, "Recorder thread started"));
 
195
    }
 
196
 
 
197
    if (statusFlags & paInputUnderflow)
 
198
        ++stream->underflow;
 
199
    if (statusFlags & paInputOverflow)
 
200
        ++stream->overflow;
 
201
 
 
202
    /* Calculate number of samples we've got */
 
203
    nsamples = frameCount * stream->channel_count + stream->rec_buf_count;
 
204
 
 
205
    if (nsamples >= stream->samples_per_frame) 
 
206
    {
 
207
        /* If buffer is not empty, combine the buffer with the just incoming
 
208
         * samples, then call put_frame.
 
209
         */
 
210
        if (stream->rec_buf_count) {
 
211
            unsigned chunk_count = 0;
 
212
            pjmedia_frame frame;
 
213
        
 
214
            chunk_count = stream->samples_per_frame - stream->rec_buf_count;
 
215
            pjmedia_copy_samples(stream->rec_buf + stream->rec_buf_count,
 
216
                                 (pj_int16_t*)input, chunk_count);
 
217
 
 
218
            frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
219
            frame.buf = (void*) stream->rec_buf;
 
220
            frame.size = stream->samples_per_frame * stream->bytes_per_sample;
 
221
            frame.timestamp.u64 = stream->rec_timestamp.u64;
 
222
            frame.bit_info = 0;
 
223
 
 
224
            status = (*stream->rec_cb)(stream->user_data, &frame);
 
225
 
 
226
            input = (pj_int16_t*) input + chunk_count;
 
227
            nsamples -= stream->samples_per_frame;
 
228
            stream->rec_buf_count = 0;
 
229
            stream->rec_timestamp.u64 += stream->samples_per_frame /
 
230
                                         stream->channel_count;
 
231
        }
 
232
 
 
233
        /* Give all frames we have */
 
234
        while (nsamples >= stream->samples_per_frame && status == 0) {
 
235
            pjmedia_frame frame;
 
236
 
 
237
            frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
238
            frame.buf = (void*) input;
 
239
            frame.size = stream->samples_per_frame * stream->bytes_per_sample;
 
240
            frame.timestamp.u64 = stream->rec_timestamp.u64;
 
241
            frame.bit_info = 0;
 
242
 
 
243
            status = (*stream->rec_cb)(stream->user_data, &frame);
 
244
 
 
245
            input = (pj_int16_t*) input + stream->samples_per_frame;
 
246
            nsamples -= stream->samples_per_frame;
 
247
            stream->rec_timestamp.u64 += stream->samples_per_frame /
 
248
                                         stream->channel_count;
 
249
        }
 
250
 
 
251
        /* Store the remaining samples into the buffer */
 
252
        if (nsamples && status == 0) {
 
253
            stream->rec_buf_count = nsamples;
 
254
            pjmedia_copy_samples(stream->rec_buf, (pj_int16_t*)input, 
 
255
                                 nsamples);
 
256
        }
 
257
 
 
258
    } else {
 
259
        /* Not enough samples, let's just store them in the buffer */
 
260
        pjmedia_copy_samples(stream->rec_buf + stream->rec_buf_count,
 
261
                             (pj_int16_t*)input, 
 
262
                             frameCount * stream->channel_count);
 
263
        stream->rec_buf_count += frameCount * stream->channel_count;
 
264
    }
 
265
 
 
266
    if (status==0) 
 
267
        return paContinue;
 
268
 
 
269
on_break:
 
270
    stream->rec_thread_exited = 1;
 
271
    return paAbort;
 
272
}
 
273
 
 
274
static int PaPlayerCallback( const void *input, 
 
275
                             void *output,
 
276
                             unsigned long frameCount,
 
277
                             const PaStreamCallbackTimeInfo* timeInfo,
 
278
                             PaStreamCallbackFlags statusFlags,
 
279
                             void *userData )
 
280
{
 
281
    struct pa_aud_stream *stream = (struct pa_aud_stream*) userData;
 
282
    pj_status_t status = 0;
 
283
    unsigned nsamples_req = frameCount * stream->channel_count;
 
284
 
 
285
    PJ_UNUSED_ARG(input);
 
286
    PJ_UNUSED_ARG(timeInfo);
 
287
 
 
288
    if (stream->quit_flag)
 
289
        goto on_break;
 
290
 
 
291
    if (output == NULL)
 
292
        return paContinue;
 
293
 
 
294
    /* Known cases of callback's thread:
 
295
     * - The thread may be changed in the middle of a session, e.g: in MacOS 
 
296
     *   it happens when plugging/unplugging headphone.
 
297
     * - The same thread may be reused in consecutive sessions. The first
 
298
     *   session will leave TLS set, but release the TLS data address,
 
299
     *   so the second session must re-register the callback's thread.
 
300
     */
 
301
    if (stream->play_thread_initialized == 0 || !pj_thread_is_registered()) 
 
302
    {
 
303
        pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
 
304
        status = pj_thread_register("portaudio", stream->play_thread_desc,
 
305
                                    &stream->play_thread);
 
306
        stream->play_thread_initialized = 1;
 
307
        PJ_LOG(5,(THIS_FILE, "Player thread started"));
 
308
    }
 
309
 
 
310
    if (statusFlags & paOutputUnderflow)
 
311
        ++stream->underflow;
 
312
    if (statusFlags & paOutputOverflow)
 
313
        ++stream->overflow;
 
314
 
 
315
 
 
316
    /* Check if any buffered samples */
 
317
    if (stream->play_buf_count) {
 
318
        /* samples buffered >= requested by sound device */
 
319
        if (stream->play_buf_count >= nsamples_req) {
 
320
            pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, 
 
321
                                 nsamples_req);
 
322
            stream->play_buf_count -= nsamples_req;
 
323
            pjmedia_move_samples(stream->play_buf, 
 
324
                                 stream->play_buf + nsamples_req,
 
325
                                 stream->play_buf_count);
 
326
            nsamples_req = 0;
 
327
            
 
328
            return paContinue;
 
329
        }
 
330
 
 
331
        /* samples buffered < requested by sound device */
 
332
        pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, 
 
333
                             stream->play_buf_count);
 
334
        nsamples_req -= stream->play_buf_count;
 
335
        output = (pj_int16_t*)output + stream->play_buf_count;
 
336
        stream->play_buf_count = 0;
 
337
    }
 
338
 
 
339
    /* Fill output buffer as requested */
 
340
    while (nsamples_req && status == 0) {
 
341
        if (nsamples_req >= stream->samples_per_frame) {
 
342
            pjmedia_frame frame;
 
343
 
 
344
            frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
345
            frame.buf = output;
 
346
            frame.size = stream->samples_per_frame *  stream->bytes_per_sample;
 
347
            frame.timestamp.u64 = stream->play_timestamp.u64;
 
348
            frame.bit_info = 0;
 
349
 
 
350
            status = (*stream->play_cb)(stream->user_data, &frame);
 
351
            if (status != PJ_SUCCESS)
 
352
                goto on_break;
 
353
 
 
354
            if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
 
355
                pj_bzero(frame.buf, frame.size);
 
356
 
 
357
            nsamples_req -= stream->samples_per_frame;
 
358
            output = (pj_int16_t*)output + stream->samples_per_frame;
 
359
        } else {
 
360
            pjmedia_frame frame;
 
361
 
 
362
            frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
363
            frame.buf = stream->play_buf;
 
364
            frame.size = stream->samples_per_frame *  stream->bytes_per_sample;
 
365
            frame.timestamp.u64 = stream->play_timestamp.u64;
 
366
            frame.bit_info = 0;
 
367
 
 
368
            status = (*stream->play_cb)(stream->user_data, &frame);
 
369
            if (status != PJ_SUCCESS)
 
370
                goto on_break;
 
371
 
 
372
            if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
 
373
                pj_bzero(frame.buf, frame.size);
 
374
 
 
375
            pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf, 
 
376
                                 nsamples_req);
 
377
            stream->play_buf_count = stream->samples_per_frame - nsamples_req;
 
378
            pjmedia_move_samples(stream->play_buf, 
 
379
                                 stream->play_buf+nsamples_req,
 
380
                                 stream->play_buf_count);
 
381
            nsamples_req = 0;
 
382
        }
 
383
 
 
384
        stream->play_timestamp.u64 += stream->samples_per_frame /
 
385
                                      stream->channel_count;
 
386
    }
 
387
    
 
388
    if (status==0) 
 
389
        return paContinue;
 
390
 
 
391
on_break:
 
392
    stream->play_thread_exited = 1;
 
393
    return paAbort;
 
394
}
 
395
 
 
396
 
 
397
static int PaRecorderPlayerCallback( const void *input, 
 
398
                                     void *output,
 
399
                                     unsigned long frameCount,
 
400
                                     const PaStreamCallbackTimeInfo* timeInfo,
 
401
                                     PaStreamCallbackFlags statusFlags,
 
402
                                     void *userData )
 
403
{
 
404
    int rc;
 
405
 
 
406
    rc = PaRecorderCallback(input, output, frameCount, timeInfo,
 
407
                            statusFlags, userData);
 
408
    if (rc != paContinue)
 
409
        return rc;
 
410
 
 
411
    rc = PaPlayerCallback(input, output, frameCount, timeInfo,
 
412
                          statusFlags, userData);
 
413
    return rc;
 
414
}
 
415
 
 
416
#ifdef USE_PA_DEBUG_PRINT
 
417
/* Logging callback from PA */
 
418
static void pa_log_cb(const char *log)
 
419
{
 
420
    PJ_LOG(5,(THIS_FILE, "PA message: %s", log));
 
421
}
 
422
 
 
423
/* We should include pa_debugprint.h for this, but the header
 
424
 * is not available publicly. :(
 
425
 */
 
426
typedef void (*PaUtilLogCallback ) (const char *log);
 
427
void PaUtil_SetDebugPrintFunction(PaUtilLogCallback  cb);
 
428
#endif
 
429
 
 
430
 
 
431
/*
 
432
 * Init PortAudio audio driver.
 
433
 */
 
434
pjmedia_aud_dev_factory* pjmedia_pa_factory(pj_pool_factory *pf)
 
435
{
 
436
    struct pa_aud_factory *f;
 
437
    pj_pool_t *pool;
 
438
 
 
439
    pool = pj_pool_create(pf, "portaudio", 64, 64, NULL);
 
440
    f = PJ_POOL_ZALLOC_T(pool, struct pa_aud_factory);
 
441
    f->pf = pf;
 
442
    f->pool = pool;
 
443
    f->base.op = &pa_op;
 
444
 
 
445
    return &f->base;
 
446
}
 
447
 
 
448
 
 
449
/* API: Init factory */
 
450
static pj_status_t pa_init(pjmedia_aud_dev_factory *f)
 
451
{
 
452
    int err;
 
453
 
 
454
    PJ_UNUSED_ARG(f);
 
455
 
 
456
#ifdef USE_PA_DEBUG_PRINT
 
457
    PaUtil_SetDebugPrintFunction(&pa_log_cb);
 
458
#endif
 
459
 
 
460
    err = Pa_Initialize();
 
461
 
 
462
    PJ_LOG(4,(THIS_FILE, 
 
463
              "PortAudio sound library initialized, status=%d", err));
 
464
    PJ_LOG(4,(THIS_FILE, "PortAudio host api count=%d",
 
465
                         Pa_GetHostApiCount()));
 
466
    PJ_LOG(4,(THIS_FILE, "Sound device count=%d",
 
467
                         pa_get_dev_count(f)));
 
468
 
 
469
    return err ? PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
 
470
}
 
471
 
 
472
 
 
473
/* API: Destroy factory */
 
474
static pj_status_t pa_destroy(pjmedia_aud_dev_factory *f)
 
475
{
 
476
    struct pa_aud_factory *pa = (struct pa_aud_factory*)f;
 
477
    pj_pool_t *pool;
 
478
    int err;
 
479
 
 
480
    PJ_LOG(4,(THIS_FILE, "PortAudio sound library shutting down.."));
 
481
 
 
482
    err = Pa_Terminate();
 
483
 
 
484
    pool = pa->pool;
 
485
    pa->pool = NULL;
 
486
    pj_pool_release(pool);
 
487
    
 
488
    return err ? PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
 
489
}
 
490
 
 
491
 
 
492
/* API: Refresh the device list. */
 
493
static pj_status_t pa_refresh(pjmedia_aud_dev_factory *f)
 
494
{
 
495
    PJ_UNUSED_ARG(f);
 
496
    return PJ_ENOTSUP;
 
497
}
 
498
 
 
499
 
 
500
/* API: Get device count. */
 
501
static unsigned pa_get_dev_count(pjmedia_aud_dev_factory *f)
 
502
{
 
503
    int count = Pa_GetDeviceCount();
 
504
    PJ_UNUSED_ARG(f);
 
505
    return count < 0 ? 0 : count;
 
506
}
 
507
 
 
508
 
 
509
/* API: Get device info. */
 
510
static pj_status_t  pa_get_dev_info(pjmedia_aud_dev_factory *f, 
 
511
                                    unsigned index,
 
512
                                    pjmedia_aud_dev_info *info)
 
513
{
 
514
    const PaDeviceInfo *pa_info;
 
515
 
 
516
    PJ_UNUSED_ARG(f);
 
517
 
 
518
    pa_info = Pa_GetDeviceInfo(index);
 
519
    if (!pa_info)
 
520
        return PJMEDIA_EAUD_INVDEV;
 
521
 
 
522
    pj_bzero(info, sizeof(*info));
 
523
    strncpy(info->name, pa_info->name, sizeof(info->name));
 
524
    info->name[sizeof(info->name)-1] = '\0';
 
525
    info->input_count = pa_info->maxInputChannels;
 
526
    info->output_count = pa_info->maxOutputChannels;
 
527
    info->default_samples_per_sec = (unsigned)pa_info->defaultSampleRate;
 
528
    strncpy(info->driver, DRIVER_NAME, sizeof(info->driver));
 
529
    info->driver[sizeof(info->driver)-1] = '\0';
 
530
    info->caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
 
531
                 PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
 
532
 
 
533
    return PJ_SUCCESS;
 
534
}
 
535
 
 
536
 
 
537
/* API: fill in with default parameter. */
 
538
static pj_status_t  pa_default_param(pjmedia_aud_dev_factory *f,
 
539
                                     unsigned index,
 
540
                                     pjmedia_aud_param *param)
 
541
{
 
542
    pjmedia_aud_dev_info adi;
 
543
    pj_status_t status;
 
544
 
 
545
    PJ_UNUSED_ARG(f);
 
546
 
 
547
    status = pa_get_dev_info(f, index, &adi);
 
548
    if (status != PJ_SUCCESS)
 
549
        return status;
 
550
 
 
551
    pj_bzero(param, sizeof(*param));
 
552
    if (adi.input_count && adi.output_count) {
 
553
        param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
 
554
        param->rec_id = index;
 
555
        param->play_id = index;
 
556
    } else if (adi.input_count) {
 
557
        param->dir = PJMEDIA_DIR_CAPTURE;
 
558
        param->rec_id = index;
 
559
        param->play_id = PJMEDIA_AUD_INVALID_DEV;
 
560
    } else if (adi.output_count) {
 
561
        param->dir = PJMEDIA_DIR_PLAYBACK;
 
562
        param->play_id = index;
 
563
        param->rec_id = PJMEDIA_AUD_INVALID_DEV;
 
564
    } else {
 
565
        return PJMEDIA_EAUD_INVDEV;
 
566
    }
 
567
 
 
568
    param->clock_rate = adi.default_samples_per_sec;
 
569
    param->channel_count = 1;
 
570
    param->samples_per_frame = adi.default_samples_per_sec * 20 / 1000;
 
571
    param->bits_per_sample = 16;
 
572
    param->flags = adi.caps;
 
573
    param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
 
574
    param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
 
575
 
 
576
    return PJ_SUCCESS;
 
577
}
 
578
 
 
579
 
 
580
/* Internal: Get PortAudio default input device ID */
 
581
static int pa_get_default_input_dev(int channel_count)
 
582
{
 
583
    int i, count;
 
584
 
 
585
    /* Special for Windows - try to use the DirectSound implementation
 
586
     * first since it provides better latency.
 
587
     */
 
588
#if PJMEDIA_PREFER_DIRECT_SOUND
 
589
    if (Pa_HostApiTypeIdToHostApiIndex(paDirectSound) >= 0) {
 
590
        const PaHostApiInfo *pHI;
 
591
        int index = Pa_HostApiTypeIdToHostApiIndex(paDirectSound);
 
592
        pHI = Pa_GetHostApiInfo(index);
 
593
        if (pHI) {
 
594
            const PaDeviceInfo *paDevInfo = NULL;
 
595
            paDevInfo = Pa_GetDeviceInfo(pHI->defaultInputDevice);
 
596
            if (paDevInfo && paDevInfo->maxInputChannels >= channel_count)
 
597
                return pHI->defaultInputDevice;
 
598
        }
 
599
    }
 
600
#endif
 
601
 
 
602
    /* Enumerate the host api's for the default devices, and return
 
603
     * the device with suitable channels.
 
604
     */
 
605
    count = Pa_GetHostApiCount();
 
606
    for (i=0; i < count; ++i) {
 
607
        const PaHostApiInfo *pHAInfo;
 
608
 
 
609
        pHAInfo = Pa_GetHostApiInfo(i);
 
610
        if (!pHAInfo)
 
611
            continue;
 
612
 
 
613
        if (pHAInfo->defaultInputDevice >= 0) {
 
614
            const PaDeviceInfo *paDevInfo;
 
615
 
 
616
            paDevInfo = Pa_GetDeviceInfo(pHAInfo->defaultInputDevice);
 
617
 
 
618
            if (paDevInfo->maxInputChannels >= channel_count)
 
619
                return pHAInfo->defaultInputDevice;
 
620
        }
 
621
    }
 
622
 
 
623
    /* If still no device is found, enumerate all devices */
 
624
    count = Pa_GetDeviceCount();
 
625
    for (i=0; i<count; ++i) {
 
626
        const PaDeviceInfo *paDevInfo;
 
627
 
 
628
        paDevInfo = Pa_GetDeviceInfo(i);
 
629
        if (paDevInfo->maxInputChannels >= channel_count)
 
630
            return i;
 
631
    }
 
632
    
 
633
    return -1;
 
634
}
 
635
 
 
636
/* Internal: Get PortAudio default output device ID */
 
637
static int pa_get_default_output_dev(int channel_count)
 
638
{
 
639
    int i, count;
 
640
 
 
641
    /* Special for Windows - try to use the DirectSound implementation
 
642
     * first since it provides better latency.
 
643
     */
 
644
#if PJMEDIA_PREFER_DIRECT_SOUND
 
645
    if (Pa_HostApiTypeIdToHostApiIndex(paDirectSound) >= 0) {
 
646
        const PaHostApiInfo *pHI;
 
647
        int index = Pa_HostApiTypeIdToHostApiIndex(paDirectSound);
 
648
        pHI = Pa_GetHostApiInfo(index);
 
649
        if (pHI) {
 
650
            const PaDeviceInfo *paDevInfo = NULL;
 
651
            paDevInfo = Pa_GetDeviceInfo(pHI->defaultOutputDevice);
 
652
            if (paDevInfo && paDevInfo->maxOutputChannels >= channel_count)
 
653
                return pHI->defaultOutputDevice;
 
654
        }
 
655
    }
 
656
#endif
 
657
 
 
658
    /* Enumerate the host api's for the default devices, and return
 
659
     * the device with suitable channels.
 
660
     */
 
661
    count = Pa_GetHostApiCount();
 
662
    for (i=0; i < count; ++i) {
 
663
        const PaHostApiInfo *pHAInfo;
 
664
 
 
665
        pHAInfo = Pa_GetHostApiInfo(i);
 
666
        if (!pHAInfo)
 
667
            continue;
 
668
 
 
669
        if (pHAInfo->defaultOutputDevice >= 0) {
 
670
            const PaDeviceInfo *paDevInfo;
 
671
 
 
672
            paDevInfo = Pa_GetDeviceInfo(pHAInfo->defaultOutputDevice);
 
673
 
 
674
            if (paDevInfo->maxOutputChannels >= channel_count)
 
675
                return pHAInfo->defaultOutputDevice;
 
676
        }
 
677
    }
 
678
 
 
679
    /* If still no device is found, enumerate all devices */
 
680
    count = Pa_GetDeviceCount();
 
681
    for (i=0; i<count; ++i) {
 
682
        const PaDeviceInfo *paDevInfo;
 
683
 
 
684
        paDevInfo = Pa_GetDeviceInfo(i);
 
685
        if (paDevInfo->maxOutputChannels >= channel_count)
 
686
            return i;
 
687
    }
 
688
 
 
689
    return -1;
 
690
}
 
691
 
 
692
 
 
693
/* Internal: create capture/recorder stream */
 
694
static pj_status_t create_rec_stream( struct pa_aud_factory *pa,
 
695
                                      const pjmedia_aud_param *param,
 
696
                                      pjmedia_aud_rec_cb rec_cb,
 
697
                                      void *user_data,
 
698
                                      pjmedia_aud_stream **p_snd_strm)
 
699
{
 
700
    pj_pool_t *pool;
 
701
    pjmedia_aud_dev_index rec_id;
 
702
    struct pa_aud_stream *stream;
 
703
    PaStreamParameters inputParam;
 
704
    int sampleFormat;
 
705
    const PaDeviceInfo *paDevInfo = NULL;
 
706
    const PaHostApiInfo *paHostApiInfo = NULL;
 
707
    unsigned paFrames, paRate, paLatency;
 
708
    const PaStreamInfo *paSI;
 
709
    PaError err;
 
710
 
 
711
    PJ_ASSERT_RETURN(rec_cb && p_snd_strm, PJ_EINVAL);
 
712
 
 
713
    rec_id = param->rec_id;
 
714
    if (rec_id < 0) {
 
715
        rec_id = pa_get_default_input_dev(param->channel_count);
 
716
        if (rec_id < 0) {
 
717
            /* No such device. */
 
718
            return PJMEDIA_EAUD_NODEFDEV;
 
719
        }
 
720
    }
 
721
 
 
722
    paDevInfo = Pa_GetDeviceInfo(rec_id);
 
723
    if (!paDevInfo) {
 
724
        /* Assumed it is "No such device" error. */
 
725
        return PJMEDIA_EAUD_INVDEV;
 
726
    }
 
727
 
 
728
    if (param->bits_per_sample == 8)
 
729
        sampleFormat = paUInt8;
 
730
    else if (param->bits_per_sample == 16)
 
731
        sampleFormat = paInt16;
 
732
    else if (param->bits_per_sample == 32)
 
733
        sampleFormat = paInt32;
 
734
    else
 
735
        return PJMEDIA_EAUD_SAMPFORMAT;
 
736
    
 
737
    pool = pj_pool_create(pa->pf, "recstrm", 1024, 1024, NULL);
 
738
    if (!pool)
 
739
        return PJ_ENOMEM;
 
740
 
 
741
    stream = PJ_POOL_ZALLOC_T(pool, struct pa_aud_stream);
 
742
    stream->pool = pool;
 
743
    pj_strdup2_with_null(pool, &stream->name, paDevInfo->name);
 
744
    stream->dir = PJMEDIA_DIR_CAPTURE;
 
745
    stream->rec_id = rec_id;
 
746
    stream->play_id = -1;
 
747
    stream->user_data = user_data;
 
748
    stream->samples_per_sec = param->clock_rate;
 
749
    stream->samples_per_frame = param->samples_per_frame;
 
750
    stream->bytes_per_sample = param->bits_per_sample / 8;
 
751
    stream->channel_count = param->channel_count;
 
752
    stream->rec_cb = rec_cb;
 
753
 
 
754
    stream->rec_buf = (pj_int16_t*)pj_pool_alloc(pool, 
 
755
                      stream->samples_per_frame * stream->bytes_per_sample);
 
756
    stream->rec_buf_count = 0;
 
757
 
 
758
    pj_bzero(&inputParam, sizeof(inputParam));
 
759
    inputParam.device = rec_id;
 
760
    inputParam.channelCount = param->channel_count;
 
761
    inputParam.hostApiSpecificStreamInfo = NULL;
 
762
    inputParam.sampleFormat = sampleFormat;
 
763
    if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)
 
764
        inputParam.suggestedLatency = param->input_latency_ms / 1000.0;
 
765
    else
 
766
        inputParam.suggestedLatency = PJMEDIA_SND_DEFAULT_REC_LATENCY / 1000.0;
 
767
 
 
768
    paHostApiInfo = Pa_GetHostApiInfo(paDevInfo->hostApi);
 
769
 
 
770
    /* Frames in PortAudio is number of samples in a single channel */
 
771
    paFrames = param->samples_per_frame / param->channel_count;
 
772
 
 
773
    err = Pa_OpenStream( &stream->rec_strm, &inputParam, NULL,
 
774
                         param->clock_rate, paFrames, 
 
775
                         paClipOff, &PaRecorderCallback, stream );
 
776
    if (err != paNoError) {
 
777
        pj_pool_release(pool);
 
778
        return PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err);
 
779
    }
 
780
 
 
781
    paSI = Pa_GetStreamInfo(stream->rec_strm);
 
782
    paRate = (unsigned)paSI->sampleRate;
 
783
    paLatency = (unsigned)(paSI->inputLatency * 1000);
 
784
 
 
785
    PJ_LOG(5,(THIS_FILE, "Opened device %s (%s) for recording, sample "
 
786
                         "rate=%d, ch=%d, "
 
787
                         "bits=%d, %d samples per frame, latency=%d ms",
 
788
                         paDevInfo->name, paHostApiInfo->name,
 
789
                         paRate, param->channel_count,
 
790
                         param->bits_per_sample, param->samples_per_frame,
 
791
                         paLatency));
 
792
 
 
793
    *p_snd_strm = &stream->base;
 
794
    return PJ_SUCCESS;
 
795
}
 
796
 
 
797
 
 
798
/* Internal: create playback stream */
 
799
static pj_status_t create_play_stream(struct pa_aud_factory *pa,
 
800
                                      const pjmedia_aud_param *param,
 
801
                                      pjmedia_aud_play_cb play_cb,
 
802
                                      void *user_data,
 
803
                                      pjmedia_aud_stream **p_snd_strm)
 
804
{
 
805
    pj_pool_t *pool;
 
806
    pjmedia_aud_dev_index play_id;
 
807
    struct pa_aud_stream *stream;
 
808
    PaStreamParameters outputParam;
 
809
    int sampleFormat;
 
810
    const PaDeviceInfo *paDevInfo = NULL;
 
811
    const PaHostApiInfo *paHostApiInfo = NULL;
 
812
    const PaStreamInfo *paSI;
 
813
    unsigned paFrames, paRate, paLatency;
 
814
    PaError err;
 
815
 
 
816
    PJ_ASSERT_RETURN(play_cb && p_snd_strm, PJ_EINVAL);
 
817
 
 
818
    play_id = param->play_id;
 
819
    if (play_id < 0) {
 
820
        play_id = pa_get_default_output_dev(param->channel_count);
 
821
        if (play_id < 0) {
 
822
            /* No such device. */
 
823
            return PJMEDIA_EAUD_NODEFDEV;
 
824
        }
 
825
    } 
 
826
 
 
827
    paDevInfo = Pa_GetDeviceInfo(play_id);
 
828
    if (!paDevInfo) {
 
829
        /* Assumed it is "No such device" error. */
 
830
        return PJMEDIA_EAUD_INVDEV;
 
831
    }
 
832
 
 
833
    if (param->bits_per_sample == 8)
 
834
        sampleFormat = paUInt8;
 
835
    else if (param->bits_per_sample == 16)
 
836
        sampleFormat = paInt16;
 
837
    else if (param->bits_per_sample == 32)
 
838
        sampleFormat = paInt32;
 
839
    else
 
840
        return PJMEDIA_EAUD_SAMPFORMAT;
 
841
    
 
842
    pool = pj_pool_create(pa->pf, "playstrm", 1024, 1024, NULL);
 
843
    if (!pool)
 
844
        return PJ_ENOMEM;
 
845
 
 
846
    stream = PJ_POOL_ZALLOC_T(pool, struct pa_aud_stream);
 
847
    stream->pool = pool;
 
848
    pj_strdup2_with_null(pool, &stream->name, paDevInfo->name);
 
849
    stream->dir = PJMEDIA_DIR_PLAYBACK;
 
850
    stream->play_id = play_id;
 
851
    stream->rec_id = -1;
 
852
    stream->user_data = user_data;
 
853
    stream->samples_per_sec = param->clock_rate;
 
854
    stream->samples_per_frame = param->samples_per_frame;
 
855
    stream->bytes_per_sample = param->bits_per_sample / 8;
 
856
    stream->channel_count = param->channel_count;
 
857
    stream->play_cb = play_cb;
 
858
 
 
859
    stream->play_buf = (pj_int16_t*)pj_pool_alloc(pool, 
 
860
                                            stream->samples_per_frame * 
 
861
                                            stream->bytes_per_sample);
 
862
    stream->play_buf_count = 0;
 
863
 
 
864
    pj_bzero(&outputParam, sizeof(outputParam));
 
865
    outputParam.device = play_id;
 
866
    outputParam.channelCount = param->channel_count;
 
867
    outputParam.hostApiSpecificStreamInfo = NULL;
 
868
    outputParam.sampleFormat = sampleFormat;
 
869
    if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)
 
870
        outputParam.suggestedLatency=param->output_latency_ms / 1000.0;
 
871
    else
 
872
        outputParam.suggestedLatency=PJMEDIA_SND_DEFAULT_PLAY_LATENCY/1000.0;
 
873
 
 
874
    paHostApiInfo = Pa_GetHostApiInfo(paDevInfo->hostApi);
 
875
 
 
876
    /* Frames in PortAudio is number of samples in a single channel */
 
877
    paFrames = param->samples_per_frame / param->channel_count;
 
878
 
 
879
    err = Pa_OpenStream( &stream->play_strm, NULL, &outputParam,
 
880
                         param->clock_rate,  paFrames, 
 
881
                         paClipOff, &PaPlayerCallback, stream );
 
882
    if (err != paNoError) {
 
883
        pj_pool_release(pool);
 
884
        return PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err);
 
885
    }
 
886
 
 
887
    paSI = Pa_GetStreamInfo(stream->play_strm);
 
888
    paRate = (unsigned)(paSI->sampleRate);
 
889
    paLatency = (unsigned)(paSI->outputLatency * 1000);
 
890
 
 
891
    PJ_LOG(5,(THIS_FILE, "Opened device %d: %s(%s) for playing, sample rate=%d"
 
892
                         ", ch=%d, "
 
893
                         "bits=%d, %d samples per frame, latency=%d ms",
 
894
                         play_id, paDevInfo->name, paHostApiInfo->name, 
 
895
                         paRate, param->channel_count,
 
896
                         param->bits_per_sample, param->samples_per_frame, 
 
897
                         paLatency));
 
898
 
 
899
    *p_snd_strm = &stream->base;
 
900
 
 
901
    return PJ_SUCCESS;
 
902
}
 
903
 
 
904
 
 
905
/* Internal: Create both player and recorder stream */
 
906
static pj_status_t create_bidir_stream(struct pa_aud_factory *pa,
 
907
                                       const pjmedia_aud_param *param,
 
908
                                       pjmedia_aud_rec_cb rec_cb,
 
909
                                       pjmedia_aud_play_cb play_cb,
 
910
                                       void *user_data,
 
911
                                       pjmedia_aud_stream **p_snd_strm)
 
912
{
 
913
    pj_pool_t *pool;
 
914
    pjmedia_aud_dev_index rec_id, play_id;
 
915
    struct pa_aud_stream *stream;
 
916
    PaStream *paStream = NULL;
 
917
    PaStreamParameters inputParam;
 
918
    PaStreamParameters outputParam;
 
919
    int sampleFormat;
 
920
    const PaDeviceInfo *paRecDevInfo = NULL;
 
921
    const PaDeviceInfo *paPlayDevInfo = NULL;
 
922
    const PaHostApiInfo *paRecHostApiInfo = NULL;
 
923
    const PaHostApiInfo *paPlayHostApiInfo = NULL;
 
924
    const PaStreamInfo *paSI;
 
925
    unsigned paFrames, paRate, paInputLatency, paOutputLatency;
 
926
    PaError err;
 
927
 
 
928
    PJ_ASSERT_RETURN(play_cb && rec_cb && p_snd_strm, PJ_EINVAL);
 
929
 
 
930
    rec_id = param->rec_id;
 
931
    if (rec_id < 0) {
 
932
        rec_id = pa_get_default_input_dev(param->channel_count);
 
933
        if (rec_id < 0) {
 
934
            /* No such device. */
 
935
            return PJMEDIA_EAUD_NODEFDEV;
 
936
        }
 
937
    }
 
938
 
 
939
    paRecDevInfo = Pa_GetDeviceInfo(rec_id);
 
940
    if (!paRecDevInfo) {
 
941
        /* Assumed it is "No such device" error. */
 
942
        return PJMEDIA_EAUD_INVDEV;
 
943
    }
 
944
 
 
945
    play_id = param->play_id;
 
946
    if (play_id < 0) {
 
947
        play_id = pa_get_default_output_dev(param->channel_count);
 
948
        if (play_id < 0) {
 
949
            /* No such device. */
 
950
            return PJMEDIA_EAUD_NODEFDEV;
 
951
        }
 
952
    } 
 
953
 
 
954
    paPlayDevInfo = Pa_GetDeviceInfo(play_id);
 
955
    if (!paPlayDevInfo) {
 
956
        /* Assumed it is "No such device" error. */
 
957
        return PJMEDIA_EAUD_INVDEV;
 
958
    }
 
959
 
 
960
 
 
961
    if (param->bits_per_sample == 8)
 
962
        sampleFormat = paUInt8;
 
963
    else if (param->bits_per_sample == 16)
 
964
        sampleFormat = paInt16;
 
965
    else if (param->bits_per_sample == 32)
 
966
        sampleFormat = paInt32;
 
967
    else
 
968
        return PJMEDIA_EAUD_SAMPFORMAT;
 
969
    
 
970
    pool = pj_pool_create(pa->pf, "sndstream", 1024, 1024, NULL);
 
971
    if (!pool)
 
972
        return PJ_ENOMEM;
 
973
 
 
974
    stream = PJ_POOL_ZALLOC_T(pool, struct pa_aud_stream);
 
975
    stream->pool = pool;
 
976
    pj_strdup2_with_null(pool, &stream->name, paRecDevInfo->name);
 
977
    stream->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
 
978
    stream->play_id = play_id;
 
979
    stream->rec_id = rec_id;
 
980
    stream->user_data = user_data;
 
981
    stream->samples_per_sec = param->clock_rate;
 
982
    stream->samples_per_frame = param->samples_per_frame;
 
983
    stream->bytes_per_sample = param->bits_per_sample / 8;
 
984
    stream->channel_count = param->channel_count;
 
985
    stream->rec_cb = rec_cb;
 
986
    stream->play_cb = play_cb;
 
987
 
 
988
    stream->rec_buf = (pj_int16_t*)pj_pool_alloc(pool, 
 
989
                      stream->samples_per_frame * stream->bytes_per_sample);
 
990
    stream->rec_buf_count = 0;
 
991
 
 
992
    stream->play_buf = (pj_int16_t*)pj_pool_alloc(pool, 
 
993
                       stream->samples_per_frame * stream->bytes_per_sample);
 
994
    stream->play_buf_count = 0;
 
995
 
 
996
    pj_bzero(&inputParam, sizeof(inputParam));
 
997
    inputParam.device = rec_id;
 
998
    inputParam.channelCount = param->channel_count;
 
999
    inputParam.hostApiSpecificStreamInfo = NULL;
 
1000
    inputParam.sampleFormat = sampleFormat;
 
1001
    if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY)
 
1002
        inputParam.suggestedLatency = param->input_latency_ms / 1000.0;
 
1003
    else
 
1004
        inputParam.suggestedLatency = PJMEDIA_SND_DEFAULT_REC_LATENCY / 1000.0;
 
1005
 
 
1006
    paRecHostApiInfo = Pa_GetHostApiInfo(paRecDevInfo->hostApi);
 
1007
 
 
1008
    pj_bzero(&outputParam, sizeof(outputParam));
 
1009
    outputParam.device = play_id;
 
1010
    outputParam.channelCount = param->channel_count;
 
1011
    outputParam.hostApiSpecificStreamInfo = NULL;
 
1012
    outputParam.sampleFormat = sampleFormat;
 
1013
    if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY)
 
1014
        outputParam.suggestedLatency=param->output_latency_ms / 1000.0;
 
1015
    else
 
1016
        outputParam.suggestedLatency=PJMEDIA_SND_DEFAULT_PLAY_LATENCY/1000.0;
 
1017
 
 
1018
    paPlayHostApiInfo = Pa_GetHostApiInfo(paPlayDevInfo->hostApi);
 
1019
 
 
1020
    /* Frames in PortAudio is number of samples in a single channel */
 
1021
    paFrames = param->samples_per_frame / param->channel_count;
 
1022
 
 
1023
    /* If both input and output are on the same device, open a single stream
 
1024
     * for both input and output.
 
1025
     */
 
1026
    if (rec_id == play_id) {
 
1027
        err = Pa_OpenStream( &paStream, &inputParam, &outputParam,
 
1028
                             param->clock_rate, paFrames, 
 
1029
                             paClipOff, &PaRecorderPlayerCallback, stream );
 
1030
        if (err == paNoError) {
 
1031
            /* Set play stream and record stream to the same stream */
 
1032
            stream->play_strm = stream->rec_strm = paStream;
 
1033
        }
 
1034
    } else {
 
1035
        err = -1;
 
1036
    }
 
1037
 
 
1038
    /* .. otherwise if input and output are on the same device, OR if we're
 
1039
     * unable to open a bidirectional stream, then open two separate
 
1040
     * input and output stream.
 
1041
     */
 
1042
    if (paStream == NULL) {
 
1043
        /* Open input stream */
 
1044
        err = Pa_OpenStream( &stream->rec_strm, &inputParam, NULL,
 
1045
                             param->clock_rate, paFrames, 
 
1046
                             paClipOff, &PaRecorderCallback, stream );
 
1047
        if (err == paNoError) {
 
1048
            /* Open output stream */
 
1049
            err = Pa_OpenStream( &stream->play_strm, NULL, &outputParam,
 
1050
                                 param->clock_rate, paFrames, 
 
1051
                                 paClipOff, &PaPlayerCallback, stream );
 
1052
            if (err != paNoError)
 
1053
                Pa_CloseStream(stream->rec_strm);
 
1054
        }
 
1055
    }
 
1056
 
 
1057
    if (err != paNoError) {
 
1058
        pj_pool_release(pool);
 
1059
        return PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err);
 
1060
    }
 
1061
 
 
1062
    paSI = Pa_GetStreamInfo(stream->rec_strm);
 
1063
    paRate = (unsigned)(paSI->sampleRate);
 
1064
    paInputLatency = (unsigned)(paSI->inputLatency * 1000);
 
1065
    paSI = Pa_GetStreamInfo(stream->play_strm);
 
1066
    paOutputLatency = (unsigned)(paSI->outputLatency * 1000);
 
1067
 
 
1068
    PJ_LOG(5,(THIS_FILE, "Opened device %s(%s)/%s(%s) for recording and "
 
1069
                         "playback, sample rate=%d, ch=%d, "
 
1070
                         "bits=%d, %d samples per frame, input latency=%d ms, "
 
1071
                         "output latency=%d ms",
 
1072
                         paRecDevInfo->name, paRecHostApiInfo->name,
 
1073
                         paPlayDevInfo->name, paPlayHostApiInfo->name,
 
1074
                         paRate, param->channel_count,
 
1075
                         param->bits_per_sample, param->samples_per_frame,
 
1076
                         paInputLatency, paOutputLatency));
 
1077
 
 
1078
    *p_snd_strm = &stream->base;
 
1079
 
 
1080
    return PJ_SUCCESS;
 
1081
}
 
1082
 
 
1083
 
 
1084
/* API: create stream */
 
1085
static pj_status_t  pa_create_stream(pjmedia_aud_dev_factory *f,
 
1086
                                     const pjmedia_aud_param *param,
 
1087
                                     pjmedia_aud_rec_cb rec_cb,
 
1088
                                     pjmedia_aud_play_cb play_cb,
 
1089
                                     void *user_data,
 
1090
                                     pjmedia_aud_stream **p_aud_strm)
 
1091
{
 
1092
    struct pa_aud_factory *pa = (struct pa_aud_factory*)f;
 
1093
    pj_status_t status;
 
1094
 
 
1095
    if (param->dir == PJMEDIA_DIR_CAPTURE) {
 
1096
        status = create_rec_stream(pa, param, rec_cb, user_data, p_aud_strm);
 
1097
    } else if (param->dir == PJMEDIA_DIR_PLAYBACK) {
 
1098
        status = create_play_stream(pa, param, play_cb, user_data, p_aud_strm);
 
1099
    } else if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) {
 
1100
        status = create_bidir_stream(pa, param, rec_cb, play_cb, user_data, 
 
1101
                                     p_aud_strm);
 
1102
    } else {
 
1103
        return PJ_EINVAL;
 
1104
    }
 
1105
 
 
1106
    if (status != PJ_SUCCESS)
 
1107
        return status;
 
1108
 
 
1109
    (*p_aud_strm)->op = &pa_strm_op;
 
1110
 
 
1111
    return PJ_SUCCESS;
 
1112
}
 
1113
 
 
1114
 
 
1115
/* API: Get stream parameters */
 
1116
static pj_status_t strm_get_param(pjmedia_aud_stream *s,
 
1117
                                  pjmedia_aud_param *pi)
 
1118
{
 
1119
    struct pa_aud_stream *strm = (struct pa_aud_stream*)s;
 
1120
    const PaStreamInfo *paPlaySI = NULL, *paRecSI = NULL;
 
1121
 
 
1122
    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
 
1123
    PJ_ASSERT_RETURN(strm->play_strm || strm->rec_strm, PJ_EINVALIDOP);
 
1124
 
 
1125
    if (strm->play_strm) {
 
1126
        paPlaySI = Pa_GetStreamInfo(strm->play_strm);
 
1127
    }
 
1128
    if (strm->rec_strm) {
 
1129
        paRecSI = Pa_GetStreamInfo(strm->rec_strm);
 
1130
    }
 
1131
 
 
1132
    pj_bzero(pi, sizeof(*pi));
 
1133
    pi->dir = strm->dir;
 
1134
    pi->play_id = strm->play_id;
 
1135
    pi->rec_id = strm->rec_id;
 
1136
    pi->clock_rate = (unsigned)(paPlaySI ? paPlaySI->sampleRate : 
 
1137
                                paRecSI->sampleRate);
 
1138
    pi->channel_count = strm->channel_count;
 
1139
    pi->samples_per_frame = strm->samples_per_frame;
 
1140
    pi->bits_per_sample = strm->bytes_per_sample * 8;
 
1141
    if (paRecSI) {
 
1142
        pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
 
1143
        pi->input_latency_ms = (unsigned)(paRecSI ? paRecSI->inputLatency * 
 
1144
                                                    1000 : 0);
 
1145
    }
 
1146
    if (paPlaySI) {
 
1147
        pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
 
1148
        pi->output_latency_ms = (unsigned)(paPlaySI? paPlaySI->outputLatency * 
 
1149
                                                     1000 : 0);
 
1150
    }
 
1151
 
 
1152
    return PJ_SUCCESS;
 
1153
}
 
1154
 
 
1155
 
 
1156
/* API: get capability */
 
1157
static pj_status_t strm_get_cap(pjmedia_aud_stream *s,
 
1158
                                pjmedia_aud_dev_cap cap,
 
1159
                                void *pval)
 
1160
{
 
1161
    struct pa_aud_stream *strm = (struct pa_aud_stream*)s;
 
1162
 
 
1163
    PJ_ASSERT_RETURN(strm && pval, PJ_EINVAL);
 
1164
 
 
1165
    if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && strm->rec_strm) {
 
1166
        const PaStreamInfo *si = Pa_GetStreamInfo(strm->rec_strm);
 
1167
        if (!si)
 
1168
            return PJMEDIA_EAUD_SYSERR;
 
1169
 
 
1170
        *(unsigned*)pval = (unsigned)(si->inputLatency * 1000);
 
1171
        return PJ_SUCCESS;
 
1172
    } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && strm->play_strm) {
 
1173
        const PaStreamInfo *si = Pa_GetStreamInfo(strm->play_strm);
 
1174
        if (!si)
 
1175
            return PJMEDIA_EAUD_SYSERR;
 
1176
 
 
1177
        *(unsigned*)pval = (unsigned)(si->outputLatency * 1000);
 
1178
        return PJ_SUCCESS;
 
1179
    } else {
 
1180
        return PJMEDIA_EAUD_INVCAP;
 
1181
    }
 
1182
}
 
1183
 
 
1184
 
 
1185
/* API: set capability */
 
1186
static pj_status_t strm_set_cap(pjmedia_aud_stream *strm,
 
1187
                                pjmedia_aud_dev_cap cap,
 
1188
                                const void *value)
 
1189
{
 
1190
    PJ_UNUSED_ARG(strm);
 
1191
    PJ_UNUSED_ARG(cap);
 
1192
    PJ_UNUSED_ARG(value);
 
1193
 
 
1194
    /* Nothing is supported */
 
1195
    return PJMEDIA_EAUD_INVCAP;
 
1196
}
 
1197
 
 
1198
 
 
1199
/* API: start stream. */
 
1200
static pj_status_t strm_start(pjmedia_aud_stream *s)
 
1201
{
 
1202
    struct pa_aud_stream *stream = (struct pa_aud_stream*)s;
 
1203
    int err = 0;
 
1204
 
 
1205
    PJ_LOG(5,(THIS_FILE, "Starting %s stream..", stream->name.ptr));
 
1206
 
 
1207
    if (stream->play_strm)
 
1208
        err = Pa_StartStream(stream->play_strm);
 
1209
 
 
1210
    if (err==0 && stream->rec_strm && stream->rec_strm != stream->play_strm) {
 
1211
        err = Pa_StartStream(stream->rec_strm);
 
1212
        if (err != 0)
 
1213
            Pa_StopStream(stream->play_strm);
 
1214
    }
 
1215
 
 
1216
    PJ_LOG(5,(THIS_FILE, "Done, status=%d", err));
 
1217
 
 
1218
    return err ? PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
 
1219
}
 
1220
 
 
1221
 
 
1222
/* API: stop stream. */
 
1223
static pj_status_t strm_stop(pjmedia_aud_stream *s)
 
1224
{
 
1225
    struct pa_aud_stream *stream = (struct pa_aud_stream*)s;
 
1226
    int i, err = 0;
 
1227
 
 
1228
    stream->quit_flag = 1;
 
1229
    for (i=0; !stream->rec_thread_exited && i<100; ++i)
 
1230
        pj_thread_sleep(10);
 
1231
    for (i=0; !stream->play_thread_exited && i<100; ++i)
 
1232
        pj_thread_sleep(10);
 
1233
 
 
1234
    pj_thread_sleep(1);
 
1235
 
 
1236
    PJ_LOG(5,(THIS_FILE, "Stopping stream.."));
 
1237
 
 
1238
    if (stream->play_strm)
 
1239
        err = Pa_StopStream(stream->play_strm);
 
1240
 
 
1241
    if (stream->rec_strm && stream->rec_strm != stream->play_strm)
 
1242
        err = Pa_StopStream(stream->rec_strm);
 
1243
 
 
1244
    stream->play_thread_initialized = 0;
 
1245
    stream->rec_thread_initialized = 0;
 
1246
 
 
1247
    PJ_LOG(5,(THIS_FILE, "Done, status=%d", err));
 
1248
 
 
1249
    return err ? PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
 
1250
}
 
1251
 
 
1252
 
 
1253
/* API: destroy stream. */
 
1254
static pj_status_t strm_destroy(pjmedia_aud_stream *s)
 
1255
{
 
1256
    struct pa_aud_stream *stream = (struct pa_aud_stream*)s;
 
1257
    int i, err = 0;
 
1258
 
 
1259
    stream->quit_flag = 1;
 
1260
    for (i=0; !stream->rec_thread_exited && i<100; ++i) {
 
1261
        pj_thread_sleep(1);
 
1262
    }
 
1263
    for (i=0; !stream->play_thread_exited && i<100; ++i) {
 
1264
        pj_thread_sleep(1);
 
1265
    }
 
1266
 
 
1267
    PJ_LOG(5,(THIS_FILE, "Closing %.*s: %lu underflow, %lu overflow",
 
1268
                         (int)stream->name.slen,
 
1269
                         stream->name.ptr,
 
1270
                         stream->underflow, stream->overflow));
 
1271
 
 
1272
    if (stream->play_strm)
 
1273
        err = Pa_CloseStream(stream->play_strm);
 
1274
 
 
1275
    if (stream->rec_strm && stream->rec_strm != stream->play_strm)
 
1276
        err = Pa_CloseStream(stream->rec_strm);
 
1277
 
 
1278
    pj_pool_release(stream->pool);
 
1279
 
 
1280
    return err ? PJMEDIA_AUDIODEV_ERRNO_FROM_PORTAUDIO(err) : PJ_SUCCESS;
 
1281
}
 
1282
 
 
1283
#endif  /* PJMEDIA_AUDIO_DEV_HAS_PORTAUDIO */
 
1284