~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): Francois Marier, Francois Marier, Mark Purcell
  • Date: 2014-10-18 15:08:50 UTC
  • mfrom: (1.1.12)
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20141018150850-2exfk34ckb15pcwi
Tags: 1.4.1-0.1
[ Francois Marier ]
* Non-maintainer upload
* New upstream release (closes: #759576, #741130)
  - debian/rules +PJPROJECT_VERSION := 2.2.1
  - add upstream patch to fix broken TLS support
  - add patch to fix pjproject regression

[ Mark Purcell ]
* Build-Depends:
  - sflphone-daemon + libavformat-dev, libavcodec-dev, libswscale-dev,
  libavdevice-dev, libavutil-dev
  - sflphone-gnome + libclutter-gtk-1.0-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