~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/coreaudio_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: coreaudio_dev.c 4082 2012-04-24 13:09:14Z bennylp $ */
 
2
/*
 
3
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
 */
 
19
#include <pjmedia-audiodev/audiodev_imp.h>
 
20
#include <pj/assert.h>
 
21
#include <pj/log.h>
 
22
#include <pj/os.h>
 
23
 
 
24
#if PJMEDIA_AUDIO_DEV_HAS_COREAUDIO
 
25
 
 
26
#include "TargetConditionals.h"
 
27
#if TARGET_OS_IPHONE
 
28
    #define COREAUDIO_MAC 0
 
29
#else
 
30
    #define COREAUDIO_MAC 1
 
31
#endif
 
32
 
 
33
#include <AudioUnit/AudioUnit.h>
 
34
#include <AudioToolbox/AudioConverter.h>
 
35
#if !COREAUDIO_MAC
 
36
    #include <AudioToolbox/AudioServices.h>
 
37
 
 
38
    #define AudioDeviceID unsigned
 
39
 
 
40
    /**
 
41
     * As in iOS SDK 4 or later, audio route change property listener is
 
42
     * no longer necessary. Just make surethat your application can receive
 
43
     * remote control events by adding the code:
 
44
     *     [[UIApplication sharedApplication] 
 
45
     *      beginReceivingRemoteControlEvents];
 
46
     * Otherwise audio route change (such as headset plug/unplug) will not be
 
47
     * processed while your application is in the background mode.
 
48
     */
 
49
    #define USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER 0
 
50
 
 
51
#endif
 
52
 
 
53
/* For Mac OS 10.5.x and earlier */
 
54
#if AUDIO_UNIT_VERSION < 1060
 
55
    #define AudioComponent Component
 
56
    #define AudioComponentDescription ComponentDescription
 
57
    #define AudioComponentInstance ComponentInstance
 
58
    #define AudioComponentFindNext FindNextComponent
 
59
    #define AudioComponentInstanceNew OpenAComponent
 
60
    #define AudioComponentInstanceDispose CloseComponent
 
61
#endif
 
62
 
 
63
 
 
64
#define THIS_FILE               "coreaudio_dev.c"
 
65
 
 
66
/* coreaudio device info */
 
67
struct coreaudio_dev_info
 
68
{
 
69
    pjmedia_aud_dev_info         info;
 
70
    AudioDeviceID                dev_id;
 
71
};
 
72
 
 
73
/* linked list of streams */
 
74
struct stream_list
 
75
{
 
76
    PJ_DECL_LIST_MEMBER(struct stream_list);
 
77
    struct coreaudio_stream     *stream;
 
78
};
 
79
 
 
80
/* coreaudio factory */
 
81
struct coreaudio_factory
 
82
{
 
83
    pjmedia_aud_dev_factory      base;
 
84
    pj_pool_t                   *base_pool;
 
85
    pj_pool_t                   *pool;
 
86
    pj_pool_factory             *pf;
 
87
    pj_mutex_t                  *mutex;
 
88
 
 
89
    unsigned                     dev_count;
 
90
    struct coreaudio_dev_info   *dev_info;
 
91
 
 
92
    AudioComponent               io_comp;
 
93
    struct stream_list           streams;
 
94
};
 
95
 
 
96
/* Sound stream. */
 
97
struct coreaudio_stream
 
98
{
 
99
    pjmedia_aud_stream           base;           /**< Base stream         */
 
100
    pjmedia_aud_param            param;          /**< Settings            */
 
101
    pj_pool_t                   *pool;           /**< Memory pool.        */
 
102
    struct coreaudio_factory    *cf;
 
103
    struct stream_list           list_entry;
 
104
 
 
105
    pjmedia_aud_rec_cb           rec_cb;         /**< Capture callback.   */
 
106
    pjmedia_aud_play_cb          play_cb;        /**< Playback callback.  */
 
107
    void                        *user_data;      /**< Application data.   */
 
108
 
 
109
    pj_timestamp                 play_timestamp;
 
110
    pj_timestamp                 rec_timestamp;
 
111
 
 
112
    pj_int16_t                  *rec_buf;
 
113
    unsigned                     rec_buf_count;
 
114
    pj_int16_t                  *play_buf;
 
115
    unsigned                     play_buf_count;
 
116
 
 
117
    pj_bool_t                    interrupted;
 
118
    pj_bool_t                    quit_flag;
 
119
    pj_bool_t                    running;
 
120
 
 
121
    pj_bool_t                    rec_thread_initialized;
 
122
    pj_thread_desc               rec_thread_desc;
 
123
    pj_thread_t                 *rec_thread;
 
124
 
 
125
    pj_bool_t                    play_thread_initialized;
 
126
    pj_thread_desc               play_thread_desc;
 
127
    pj_thread_t                 *play_thread;
 
128
 
 
129
    AudioUnit                    io_units[2];
 
130
    AudioStreamBasicDescription  streamFormat;
 
131
    AudioBufferList             *audio_buf;
 
132
 
 
133
    AudioConverterRef            resample;
 
134
    pj_int16_t                  *resample_buf;
 
135
    void                        *resample_buf_ptr;
 
136
    unsigned                     resample_buf_count;
 
137
    unsigned                     resample_buf_size;
 
138
};
 
139
 
 
140
/* Static variable */
 
141
static struct coreaudio_factory *cf_instance = NULL;
 
142
 
 
143
/* Prototypes */
 
144
static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f);
 
145
static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f);
 
146
static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f);
 
147
static unsigned    ca_factory_get_dev_count(pjmedia_aud_dev_factory *f);
 
148
static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f,
 
149
                                           unsigned index,
 
150
                                           pjmedia_aud_dev_info *info);
 
151
static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f,
 
152
                                            unsigned index,
 
153
                                            pjmedia_aud_param *param);
 
154
static pj_status_t ca_factory_create_stream(pjmedia_aud_dev_factory *f,
 
155
                                            const pjmedia_aud_param *param,
 
156
                                            pjmedia_aud_rec_cb rec_cb,
 
157
                                            pjmedia_aud_play_cb play_cb,
 
158
                                            void *user_data,
 
159
                                            pjmedia_aud_stream **p_aud_strm);
 
160
 
 
161
static pj_status_t ca_stream_get_param(pjmedia_aud_stream *strm,
 
162
                                       pjmedia_aud_param *param);
 
163
static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *strm,
 
164
                                     pjmedia_aud_dev_cap cap,
 
165
                                     void *value);
 
166
static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *strm,
 
167
                                     pjmedia_aud_dev_cap cap,
 
168
                                     const void *value);
 
169
static pj_status_t ca_stream_start(pjmedia_aud_stream *strm);
 
170
static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm);
 
171
static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm);
 
172
static pj_status_t create_audio_unit(AudioComponent io_comp,
 
173
                                     AudioDeviceID dev_id,
 
174
                                     pjmedia_dir dir,
 
175
                                     struct coreaudio_stream *strm,
 
176
                                     AudioUnit *io_unit);
 
177
#if !COREAUDIO_MAC
 
178
static void interruptionListener(void *inClientData, UInt32 inInterruption);
 
179
static void propListener(void *                 inClientData,
 
180
                         AudioSessionPropertyID inID,
 
181
                         UInt32                 inDataSize,
 
182
                         const void *           inData);
 
183
#endif
 
184
 
 
185
/* Operations */
 
186
static pjmedia_aud_dev_factory_op factory_op =
 
187
{
 
188
    &ca_factory_init,
 
189
    &ca_factory_destroy,
 
190
    &ca_factory_get_dev_count,
 
191
    &ca_factory_get_dev_info,
 
192
    &ca_factory_default_param,
 
193
    &ca_factory_create_stream,
 
194
    &ca_factory_refresh
 
195
};
 
196
 
 
197
static pjmedia_aud_stream_op stream_op =
 
198
{
 
199
    &ca_stream_get_param,
 
200
    &ca_stream_get_cap,
 
201
    &ca_stream_set_cap,
 
202
    &ca_stream_start,
 
203
    &ca_stream_stop,
 
204
    &ca_stream_destroy
 
205
};
 
206
 
 
207
 
 
208
/****************************************************************************
 
209
 * Factory operations
 
210
 */
 
211
/*
 
212
 * Init coreaudio audio driver.
 
213
 */
 
214
pjmedia_aud_dev_factory* pjmedia_coreaudio_factory(pj_pool_factory *pf)
 
215
{
 
216
    struct coreaudio_factory *f;
 
217
    pj_pool_t *pool;
 
218
 
 
219
    pool = pj_pool_create(pf, "core audio base", 1000, 1000, NULL);
 
220
    f = PJ_POOL_ZALLOC_T(pool, struct coreaudio_factory);
 
221
    f->pf = pf;
 
222
    f->base_pool = pool;
 
223
    f->base.op = &factory_op;
 
224
 
 
225
    return &f->base;
 
226
}
 
227
 
 
228
 
 
229
/* API: init factory */
 
230
static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f)
 
231
{
 
232
    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
 
233
    AudioComponentDescription desc;
 
234
    pj_status_t status;
 
235
#if !COREAUDIO_MAC
 
236
    unsigned i;
 
237
    OSStatus ostatus;
 
238
#endif
 
239
 
 
240
    pj_list_init(&cf->streams);
 
241
    status = pj_mutex_create_recursive(cf->base_pool,
 
242
                                       "coreaudio",
 
243
                                       &cf->mutex);
 
244
    if (status != PJ_SUCCESS)
 
245
        return status;
 
246
 
 
247
    desc.componentType = kAudioUnitType_Output;
 
248
#if COREAUDIO_MAC
 
249
    desc.componentSubType = kAudioUnitSubType_HALOutput;
 
250
#else
 
251
    desc.componentSubType = kAudioUnitSubType_RemoteIO;
 
252
#endif
 
253
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
 
254
    desc.componentFlags = 0;
 
255
    desc.componentFlagsMask = 0;
 
256
 
 
257
    cf->io_comp = AudioComponentFindNext(NULL, &desc);
 
258
    if (cf->io_comp == NULL)
 
259
        return PJMEDIA_EAUD_INIT; // cannot find IO unit;
 
260
 
 
261
    status = ca_factory_refresh(f);
 
262
    if (status != PJ_SUCCESS)
 
263
        return status;
 
264
 
 
265
#if !COREAUDIO_MAC
 
266
    cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL);
 
267
    cf->dev_count = 1;
 
268
    cf->dev_info = (struct coreaudio_dev_info*)
 
269
                   pj_pool_calloc(cf->pool, cf->dev_count,
 
270
                   sizeof(struct coreaudio_dev_info));
 
271
    for (i = 0; i < cf->dev_count; i++) {
 
272
        struct coreaudio_dev_info *cdi;
 
273
 
 
274
        cdi = &cf->dev_info[i];
 
275
        pj_bzero(cdi, sizeof(*cdi));
 
276
        cdi->dev_id = 0;
 
277
        strcpy(cdi->info.name, "iPhone IO device");
 
278
        strcpy(cdi->info.driver, "apple");
 
279
        cdi->info.input_count = 1;
 
280
        cdi->info.output_count = 1;
 
281
        cdi->info.default_samples_per_sec = 8000;
 
282
 
 
283
        /* Set the device capabilities here */
 
284
        cdi->info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
 
285
                         PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY |
 
286
                         PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING |
 
287
                         PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE |
 
288
                         PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE |
 
289
                         PJMEDIA_AUD_DEV_CAP_EC;
 
290
        cdi->info.routes = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER |
 
291
                           PJMEDIA_AUD_DEV_ROUTE_EARPIECE |
 
292
                           PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH;
 
293
 
 
294
        PJ_LOG(4, (THIS_FILE, " dev_id %d: %s  (in=%d, out=%d) %dHz",
 
295
                   i,
 
296
                   cdi->info.name,
 
297
                   cdi->info.input_count,
 
298
                   cdi->info.output_count,
 
299
                   cdi->info.default_samples_per_sec));
 
300
    }
 
301
 
 
302
    /* Initialize the Audio Session */
 
303
    ostatus = AudioSessionInitialize(NULL, NULL, interruptionListener, NULL);
 
304
    if (ostatus != kAudioSessionNoError) {
 
305
        PJ_LOG(4, (THIS_FILE,
 
306
                   "Warning: cannot initialize audio session services (%i)",
 
307
                   ostatus));
 
308
    }
 
309
 
 
310
    /* Listen for audio routing change notifications. */
 
311
#if USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0
 
312
    ostatus = AudioSessionAddPropertyListener(
 
313
                  kAudioSessionProperty_AudioRouteChange,
 
314
                  propListener, cf);
 
315
    if (ostatus != kAudioSessionNoError) {
 
316
        PJ_LOG(4, (THIS_FILE,
 
317
                   "Warning: cannot listen for audio route change "
 
318
                   "notifications (%i)", ostatus));
 
319
    }
 
320
#endif
 
321
 
 
322
    cf_instance = cf;
 
323
#endif
 
324
 
 
325
    PJ_LOG(4, (THIS_FILE, "core audio initialized"));
 
326
 
 
327
    return PJ_SUCCESS;
 
328
}
 
329
 
 
330
/* API: destroy factory */
 
331
static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f)
 
332
{
 
333
    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
 
334
    pj_pool_t *pool;
 
335
 
 
336
    pj_assert(cf);
 
337
    pj_assert(cf->base_pool);
 
338
    pj_assert(pj_list_empty(&cf->streams));
 
339
 
 
340
#if !COREAUDIO_MAC
 
341
#if USE_AUDIO_ROUTE_CHANGE_PROP_LISTENER != 0
 
342
    AudioSessionRemovePropertyListenerWithUserData(
 
343
        kAudioSessionProperty_AudioRouteChange, propListener, cf);
 
344
#endif
 
345
#endif
 
346
    
 
347
    if (cf->pool) {
 
348
        pj_pool_release(cf->pool);
 
349
        cf->pool = NULL;
 
350
    }
 
351
 
 
352
    if (cf->mutex) {
 
353
        pj_mutex_lock(cf->mutex);
 
354
        cf_instance = NULL;
 
355
        pj_mutex_unlock(cf->mutex);
 
356
        pj_mutex_destroy(cf->mutex);
 
357
        cf->mutex = NULL;
 
358
    }
 
359
 
 
360
    pool = cf->base_pool;
 
361
    cf->base_pool = NULL;
 
362
    pj_pool_release(pool);
 
363
 
 
364
    return PJ_SUCCESS;
 
365
}
 
366
 
 
367
/* API: refresh the device list */
 
368
static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f)
 
369
{
 
370
#if !COREAUDIO_MAC
 
371
    /* iPhone doesn't support refreshing the device list */
 
372
    PJ_UNUSED_ARG(f);
 
373
    return PJ_SUCCESS;
 
374
#else
 
375
    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
 
376
    unsigned i;
 
377
    unsigned dev_count;
 
378
    AudioObjectPropertyAddress addr;
 
379
    AudioDeviceID *dev_ids;
 
380
    UInt32 buf_size, dev_size, size = sizeof(AudioDeviceID);
 
381
    AudioBufferList *buf = NULL;
 
382
    OSStatus ostatus;
 
383
 
 
384
    if (cf->pool != NULL) {
 
385
        pj_pool_release(cf->pool);
 
386
        cf->pool = NULL;
 
387
    }
 
388
 
 
389
    cf->dev_count = 0;
 
390
    cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL);
 
391
 
 
392
    /* Find out how many audio devices there are */
 
393
    addr.mSelector = kAudioHardwarePropertyDevices;
 
394
    addr.mScope = kAudioObjectPropertyScopeGlobal;
 
395
    addr.mElement = kAudioObjectPropertyElementMaster;
 
396
    ostatus = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
 
397
                                             0, NULL, &dev_size);
 
398
    if (ostatus != noErr) {
 
399
        dev_size = 0;
 
400
    }
 
401
 
 
402
    /* Calculate the number of audio devices available */
 
403
    dev_count = dev_size / size;
 
404
    if (dev_count==0) {
 
405
        PJ_LOG(4,(THIS_FILE, "core audio found no sound devices"));
 
406
        /* Enabling this will cause pjsua-lib initialization to fail when
 
407
         * there is no sound device installed in the system, even when pjsua
 
408
         * has been run with --null-audio. Moreover, it might be better to
 
409
         * think that the core audio backend initialization is successful,
 
410
         * regardless there is no audio device installed, as later application
 
411
         * can check it using get_dev_count().
 
412
        return PJMEDIA_EAUD_NODEV;
 
413
         */
 
414
        return PJ_SUCCESS;
 
415
    }
 
416
    PJ_LOG(4, (THIS_FILE, "core audio detected %d devices",
 
417
               dev_count));
 
418
 
 
419
    /* Get all the audio device IDs */
 
420
    dev_ids = (AudioDeviceID *)pj_pool_calloc(cf->pool, dev_size, size);
 
421
    if (!dev_ids)
 
422
        return PJ_ENOMEM;
 
423
    pj_bzero(dev_ids, dev_count);
 
424
    ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
 
425
                                         0, NULL,
 
426
                                         &dev_size, (void *)dev_ids);
 
427
    if (ostatus != noErr ) {
 
428
        /* This should not happen since we have successfully retrieved
 
429
         * the property data size before
 
430
         */
 
431
        return PJMEDIA_EAUD_INIT;
 
432
    }
 
433
    
 
434
    if (dev_size > 1) {
 
435
        AudioDeviceID dev_id = kAudioObjectUnknown;
 
436
        unsigned idx = 0;
 
437
        
 
438
        /* Find default audio input device */
 
439
        addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
 
440
        addr.mScope = kAudioObjectPropertyScopeGlobal;
 
441
        addr.mElement = kAudioObjectPropertyElementMaster;
 
442
        size = sizeof(dev_id);
 
443
        
 
444
        ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
 
445
                                             &addr, 0, NULL,
 
446
                                             &size, (void *)&dev_id);
 
447
        if (ostatus != noErr && dev_id != dev_ids[idx]) {
 
448
            AudioDeviceID temp_id = dev_ids[idx];
 
449
            
 
450
            for (i = idx + 1; i < dev_size; i++) {
 
451
                if (dev_ids[i] == dev_id) {
 
452
                    dev_ids[idx++] = dev_id;
 
453
                    dev_ids[i] = temp_id;
 
454
                    break;
 
455
                }
 
456
            }
 
457
        }
 
458
 
 
459
        /* Find default audio output device */
 
460
        addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;     
 
461
        ostatus = AudioObjectGetPropertyData(kAudioObjectSystemObject,
 
462
                                             &addr, 0, NULL,
 
463
                                             &size, (void *)&dev_id);
 
464
        if (ostatus != noErr && dev_id != dev_ids[idx]) {
 
465
            AudioDeviceID temp_id = dev_ids[idx];
 
466
            
 
467
            for (i = idx + 1; i < dev_size; i++) {
 
468
                if (dev_ids[i] == dev_id) {
 
469
                    dev_ids[idx] = dev_id;
 
470
                    dev_ids[i] = temp_id;
 
471
                    break;
 
472
                }
 
473
            }
 
474
        }
 
475
    }
 
476
 
 
477
    /* Build the devices' info */
 
478
    cf->dev_info = (struct coreaudio_dev_info*)
 
479
                   pj_pool_calloc(cf->pool, dev_count,
 
480
                   sizeof(struct coreaudio_dev_info));
 
481
    buf_size = 0;
 
482
    for (i = 0; i < dev_count; i++) {
 
483
        struct coreaudio_dev_info *cdi;
 
484
        Float64 sampleRate;
 
485
 
 
486
        cdi = &cf->dev_info[i];
 
487
        pj_bzero(cdi, sizeof(*cdi));
 
488
        cdi->dev_id = dev_ids[i];
 
489
 
 
490
        /* Get device name */
 
491
        addr.mSelector = kAudioDevicePropertyDeviceName;
 
492
        addr.mScope = kAudioObjectPropertyScopeGlobal;
 
493
        addr.mElement = kAudioObjectPropertyElementMaster;
 
494
        size = sizeof(cdi->info.name);
 
495
        AudioObjectGetPropertyData(cdi->dev_id, &addr,
 
496
                                   0, NULL,
 
497
                                   &size, (void *)cdi->info.name);
 
498
 
 
499
        strcpy(cdi->info.driver, "core audio");
 
500
 
 
501
        /* Get the number of input channels */
 
502
        addr.mSelector = kAudioDevicePropertyStreamConfiguration;
 
503
        addr.mScope = kAudioDevicePropertyScopeInput;
 
504
        size = 0;
 
505
        ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr,
 
506
                                                 0, NULL, &size);
 
507
        if (ostatus == noErr && size > 0) {
 
508
 
 
509
            if (size > buf_size) {
 
510
                buf = pj_pool_alloc(cf->pool, size);
 
511
                buf_size = size;
 
512
            }
 
513
            if (buf) {
 
514
                UInt32 idx;
 
515
 
 
516
                /* Get the input stream configuration */
 
517
                ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr,
 
518
                                                     0, NULL,
 
519
                                                     &size, buf);
 
520
                if (ostatus == noErr) {
 
521
                    /* Count the total number of input channels in
 
522
                     * the stream
 
523
                     */
 
524
                    for (idx = 0; idx < buf->mNumberBuffers; idx++) {
 
525
                        cdi->info.input_count +=
 
526
                            buf->mBuffers[idx].mNumberChannels;
 
527
                    }
 
528
                }
 
529
            }
 
530
        }
 
531
 
 
532
        /* Get the number of output channels */
 
533
        addr.mScope = kAudioDevicePropertyScopeOutput;
 
534
        size = 0;
 
535
        ostatus = AudioObjectGetPropertyDataSize(cdi->dev_id, &addr,
 
536
                                                 0, NULL, &size);
 
537
        if (ostatus == noErr && size > 0) {
 
538
 
 
539
            if (size > buf_size) {
 
540
                buf = pj_pool_alloc(cf->pool, size);
 
541
                buf_size = size;
 
542
            }
 
543
            if (buf) {
 
544
                UInt32 idx;
 
545
 
 
546
                /* Get the output stream configuration */
 
547
                ostatus = AudioObjectGetPropertyData(cdi->dev_id, &addr,
 
548
                                                     0, NULL,
 
549
                                                     &size, buf);
 
550
                if (ostatus == noErr) {
 
551
                    /* Count the total number of output channels in
 
552
                     * the stream
 
553
                     */
 
554
                    for (idx = 0; idx < buf->mNumberBuffers; idx++) {
 
555
                        cdi->info.output_count +=
 
556
                            buf->mBuffers[idx].mNumberChannels;
 
557
                    }
 
558
                }
 
559
            }
 
560
        }
 
561
 
 
562
        /* Get default sample rate */
 
563
        addr.mSelector = kAudioDevicePropertyNominalSampleRate;
 
564
        addr.mScope = kAudioObjectPropertyScopeGlobal;
 
565
        size = sizeof(Float64);
 
566
        ostatus = AudioObjectGetPropertyData (cdi->dev_id, &addr,
 
567
                                              0, NULL,
 
568
                                              &size, &sampleRate);
 
569
        cdi->info.default_samples_per_sec = (ostatus == noErr ?
 
570
                                            sampleRate:
 
571
                                            16000);
 
572
 
 
573
        /* Set device capabilities here */
 
574
        if (cdi->info.input_count > 0) {
 
575
            cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
 
576
        }
 
577
        if (cdi->info.output_count > 0) {
 
578
            cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
 
579
            addr.mSelector = kAudioDevicePropertyVolumeScalar;
 
580
            addr.mScope = kAudioDevicePropertyScopeOutput;
 
581
            if (AudioObjectHasProperty(cdi->dev_id, &addr)) {
 
582
                cdi->info.caps |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
 
583
            }
 
584
        }
 
585
 
 
586
        cf->dev_count++;
 
587
 
 
588
        PJ_LOG(4, (THIS_FILE, " dev_id %d: %s  (in=%d, out=%d) %dHz",
 
589
               i,
 
590
               cdi->info.name,
 
591
               cdi->info.input_count,
 
592
               cdi->info.output_count,
 
593
               cdi->info.default_samples_per_sec));
 
594
    }
 
595
 
 
596
    return PJ_SUCCESS;
 
597
#endif
 
598
}
 
599
 
 
600
/* API: get number of devices */
 
601
static unsigned ca_factory_get_dev_count(pjmedia_aud_dev_factory *f)
 
602
{
 
603
    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
 
604
    return cf->dev_count;
 
605
}
 
606
 
 
607
/* API: get device info */
 
608
static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f,
 
609
                                           unsigned index,
 
610
                                           pjmedia_aud_dev_info *info)
 
611
{
 
612
    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
 
613
 
 
614
    PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV);
 
615
 
 
616
    pj_memcpy(info, &cf->dev_info[index].info, sizeof(*info));
 
617
 
 
618
    return PJ_SUCCESS;
 
619
}
 
620
 
 
621
/* API: create default device parameter */
 
622
static pj_status_t ca_factory_default_param(pjmedia_aud_dev_factory *f,
 
623
                                            unsigned index,
 
624
                                            pjmedia_aud_param *param)
 
625
{
 
626
    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
 
627
    struct coreaudio_dev_info *di = &cf->dev_info[index];
 
628
 
 
629
    PJ_ASSERT_RETURN(index < cf->dev_count, PJMEDIA_EAUD_INVDEV);
 
630
 
 
631
    pj_bzero(param, sizeof(*param));
 
632
    if (di->info.input_count && di->info.output_count) {
 
633
        param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
 
634
        param->rec_id = index;
 
635
        param->play_id = index;
 
636
    } else if (di->info.input_count) {
 
637
        param->dir = PJMEDIA_DIR_CAPTURE;
 
638
        param->rec_id = index;
 
639
        param->play_id = PJMEDIA_AUD_INVALID_DEV;
 
640
    } else if (di->info.output_count) {
 
641
        param->dir = PJMEDIA_DIR_PLAYBACK;
 
642
        param->play_id = index;
 
643
        param->rec_id = PJMEDIA_AUD_INVALID_DEV;
 
644
    } else {
 
645
        return PJMEDIA_EAUD_INVDEV;
 
646
    }
 
647
 
 
648
    /* Set the mandatory settings here */
 
649
    param->clock_rate = di->info.default_samples_per_sec;
 
650
    param->channel_count = 1;
 
651
    param->samples_per_frame = di->info.default_samples_per_sec * 20 / 1000;
 
652
    param->bits_per_sample = 16;
 
653
 
 
654
    /* Set the param for device capabilities here */
 
655
    param->flags = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |
 
656
                   PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
 
657
    param->input_latency_ms = PJMEDIA_SND_DEFAULT_REC_LATENCY;
 
658
    param->output_latency_ms = PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
 
659
 
 
660
    return PJ_SUCCESS;
 
661
}
 
662
 
 
663
OSStatus resampleProc(AudioConverterRef             inAudioConverter,
 
664
                      UInt32                        *ioNumberDataPackets,
 
665
                      AudioBufferList               *ioData,
 
666
                      AudioStreamPacketDescription  **outDataPacketDescription,
 
667
                      void                          *inUserData)
 
668
{
 
669
    struct coreaudio_stream *strm = (struct coreaudio_stream*)inUserData;
 
670
 
 
671
    if (*ioNumberDataPackets > strm->resample_buf_size)
 
672
        *ioNumberDataPackets = strm->resample_buf_size;
 
673
 
 
674
    ioData->mNumberBuffers = 1;
 
675
    ioData->mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame;
 
676
    ioData->mBuffers[0].mData = strm->resample_buf_ptr;
 
677
    ioData->mBuffers[0].mDataByteSize = *ioNumberDataPackets *
 
678
                                        strm->streamFormat.mChannelsPerFrame *
 
679
                                        strm->param.bits_per_sample >> 3;
 
680
 
 
681
    return noErr;
 
682
}
 
683
 
 
684
static OSStatus resample_callback(void                       *inRefCon,
 
685
                                  AudioUnitRenderActionFlags *ioActionFlags,
 
686
                                  const AudioTimeStamp       *inTimeStamp,
 
687
                                  UInt32                      inBusNumber,
 
688
                                  UInt32                      inNumberFrames,
 
689
                                  AudioBufferList            *ioData)
 
690
{
 
691
    struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon;
 
692
    OSStatus ostatus;
 
693
    pj_status_t status = 0;
 
694
    unsigned nsamples;
 
695
    AudioBufferList *buf = strm->audio_buf;
 
696
    pj_int16_t *input;
 
697
    UInt32 resampleSize;
 
698
 
 
699
    pj_assert(!strm->quit_flag);
 
700
 
 
701
    /* Known cases of callback's thread:
 
702
     * - The thread may be changed in the middle of a session
 
703
     *   it happens when plugging/unplugging headphone.
 
704
     * - The same thread may be reused in consecutive sessions. The first
 
705
     *   session will leave TLS set, but release the TLS data address,
 
706
     *   so the second session must re-register the callback's thread.
 
707
     */
 
708
    if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered())
 
709
    {
 
710
        pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
 
711
        status = pj_thread_register("ca_rec", strm->rec_thread_desc,
 
712
                                    &strm->rec_thread);
 
713
        strm->rec_thread_initialized = 1;
 
714
        PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)", 
 
715
                  inNumberFrames));
 
716
    }
 
717
 
 
718
    buf->mBuffers[0].mData = NULL;
 
719
    buf->mBuffers[0].mDataByteSize = inNumberFrames *
 
720
                                     strm->streamFormat.mChannelsPerFrame;
 
721
    /* Render the unit to get input data */
 
722
    ostatus = AudioUnitRender(strm->io_units[0],
 
723
                              ioActionFlags,
 
724
                              inTimeStamp,
 
725
                              inBusNumber,
 
726
                              inNumberFrames,
 
727
                              buf);
 
728
 
 
729
    if (ostatus != noErr) {
 
730
        PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus));
 
731
        goto on_break;
 
732
    }
 
733
    input = (pj_int16_t *)buf->mBuffers[0].mData;
 
734
 
 
735
    resampleSize = strm->resample_buf_size;
 
736
    nsamples = inNumberFrames * strm->param.channel_count +
 
737
               strm->resample_buf_count;
 
738
 
 
739
    if (nsamples >= resampleSize) {
 
740
        pjmedia_frame frame;
 
741
        UInt32 resampleOutput = strm->param.samples_per_frame /
 
742
                                strm->streamFormat.mChannelsPerFrame;
 
743
        AudioBufferList ab;
 
744
 
 
745
        frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
746
        frame.buf = (void*) strm->rec_buf;
 
747
        frame.size = strm->param.samples_per_frame *
 
748
                     strm->param.bits_per_sample >> 3;
 
749
        frame.bit_info = 0;
 
750
        
 
751
        ab.mNumberBuffers = 1;
 
752
        ab.mBuffers[0].mNumberChannels = strm->streamFormat.mChannelsPerFrame;
 
753
        ab.mBuffers[0].mData = strm->rec_buf;
 
754
        ab.mBuffers[0].mDataByteSize = frame.size;
 
755
 
 
756
        /* If buffer is not empty, combine the buffer with the just incoming
 
757
         * samples, then call put_frame.
 
758
         */
 
759
        if (strm->resample_buf_count) {
 
760
            unsigned chunk_count = resampleSize - strm->resample_buf_count;
 
761
            pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count,
 
762
                                 input, chunk_count);
 
763
 
 
764
            /* Do the resample */
 
765
 
 
766
            strm->resample_buf_ptr = strm->resample_buf;
 
767
            ostatus = AudioConverterFillComplexBuffer(strm->resample,
 
768
                                                      resampleProc,
 
769
                                                      strm,
 
770
                                                      &resampleOutput,
 
771
                                                      &ab,
 
772
                                                      NULL);
 
773
            if (ostatus != noErr) {
 
774
                goto on_break;
 
775
            }
 
776
            frame.timestamp.u64 = strm->rec_timestamp.u64;
 
777
 
 
778
            status = (*strm->rec_cb)(strm->user_data, &frame);
 
779
 
 
780
            input = input + chunk_count;
 
781
            nsamples -= resampleSize;
 
782
            strm->resample_buf_count = 0;
 
783
            strm->rec_timestamp.u64 += strm->param.samples_per_frame /
 
784
                                       strm->param.channel_count;
 
785
        }
 
786
        
 
787
        
 
788
        /* Give all frames we have */
 
789
        while (nsamples >= resampleSize && status == 0) {
 
790
            frame.timestamp.u64 = strm->rec_timestamp.u64;
 
791
            
 
792
            /* Do the resample */
 
793
            strm->resample_buf_ptr = input;
 
794
            ab.mBuffers[0].mDataByteSize = frame.size;
 
795
            resampleOutput = strm->param.samples_per_frame /
 
796
                             strm->streamFormat.mChannelsPerFrame;
 
797
            ostatus = AudioConverterFillComplexBuffer(strm->resample,
 
798
                                                      resampleProc,
 
799
                                                      strm,
 
800
                                                      &resampleOutput,
 
801
                                                      &ab,
 
802
                                                      NULL);
 
803
            if (ostatus != noErr) {
 
804
                goto on_break;
 
805
            }       
 
806
            
 
807
            status = (*strm->rec_cb)(strm->user_data, &frame);
 
808
            
 
809
            input = (pj_int16_t*) input + resampleSize;
 
810
            nsamples -= resampleSize;
 
811
            strm->rec_timestamp.u64 += strm->param.samples_per_frame /
 
812
                                       strm->param.channel_count;
 
813
        }
 
814
 
 
815
        /* Store the remaining samples into the buffer */
 
816
        if (nsamples && status == 0) {
 
817
            strm->resample_buf_count = nsamples;
 
818
            pjmedia_copy_samples(strm->resample_buf, input,
 
819
                                 nsamples);
 
820
        }
 
821
 
 
822
    } else {
 
823
        /* Not enough samples, let's just store them in the buffer */
 
824
        pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count,
 
825
                             input,
 
826
                             inNumberFrames * strm->param.channel_count);
 
827
        strm->resample_buf_count += inNumberFrames *
 
828
                                    strm->param.channel_count;
 
829
    }
 
830
 
 
831
    return noErr;
 
832
 
 
833
on_break:
 
834
    return -1;
 
835
}
 
836
 
 
837
static OSStatus input_callback(void                       *inRefCon,
 
838
                               AudioUnitRenderActionFlags *ioActionFlags,
 
839
                               const AudioTimeStamp       *inTimeStamp,
 
840
                               UInt32                      inBusNumber,
 
841
                               UInt32                      inNumberFrames,
 
842
                               AudioBufferList            *ioData)
 
843
{
 
844
    struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon;
 
845
    OSStatus ostatus;
 
846
    pj_status_t status = 0;
 
847
    unsigned nsamples;
 
848
    AudioBufferList *buf = strm->audio_buf;
 
849
    pj_int16_t *input;
 
850
 
 
851
    pj_assert(!strm->quit_flag);
 
852
 
 
853
    /* Known cases of callback's thread:
 
854
     * - The thread may be changed in the middle of a session
 
855
     *   it happens when plugging/unplugging headphone.
 
856
     * - The same thread may be reused in consecutive sessions. The first
 
857
     *   session will leave TLS set, but release the TLS data address,
 
858
     *   so the second session must re-register the callback's thread.
 
859
     */
 
860
    if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered())
 
861
    {
 
862
        pj_bzero(strm->rec_thread_desc, sizeof(pj_thread_desc));
 
863
        status = pj_thread_register("ca_rec", strm->rec_thread_desc,
 
864
                                    &strm->rec_thread);
 
865
        strm->rec_thread_initialized = 1;
 
866
        PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)",
 
867
                  inNumberFrames));
 
868
    }
 
869
 
 
870
    buf->mBuffers[0].mData = NULL;
 
871
    buf->mBuffers[0].mDataByteSize = inNumberFrames *
 
872
                                     strm->streamFormat.mChannelsPerFrame;
 
873
    /* Render the unit to get input data */
 
874
    ostatus = AudioUnitRender(strm->io_units[0],
 
875
                              ioActionFlags,
 
876
                              inTimeStamp,
 
877
                              inBusNumber,
 
878
                              inNumberFrames,
 
879
                              buf);
 
880
 
 
881
    if (ostatus != noErr) {
 
882
        PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus));
 
883
        goto on_break;
 
884
    }
 
885
    input = (pj_int16_t *)buf->mBuffers[0].mData;
 
886
 
 
887
    /* Calculate number of samples we've got */
 
888
    nsamples = inNumberFrames * strm->param.channel_count +
 
889
               strm->rec_buf_count;
 
890
    if (nsamples >= strm->param.samples_per_frame) {
 
891
        pjmedia_frame frame;
 
892
 
 
893
        frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
894
        frame.size = strm->param.samples_per_frame *
 
895
                     strm->param.bits_per_sample >> 3;
 
896
        frame.bit_info = 0;
 
897
 
 
898
        /* If buffer is not empty, combine the buffer with the just incoming
 
899
         * samples, then call put_frame.
 
900
         */
 
901
        if (strm->rec_buf_count) {
 
902
            unsigned chunk_count = 0;
 
903
 
 
904
            chunk_count = strm->param.samples_per_frame - strm->rec_buf_count;
 
905
            pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
 
906
                                 input, chunk_count);
 
907
 
 
908
            frame.buf = (void*) strm->rec_buf;
 
909
            frame.timestamp.u64 = strm->rec_timestamp.u64;
 
910
 
 
911
            status = (*strm->rec_cb)(strm->user_data, &frame);
 
912
 
 
913
            input = input + chunk_count;
 
914
            nsamples -= strm->param.samples_per_frame;
 
915
            strm->rec_buf_count = 0;
 
916
            strm->rec_timestamp.u64 += strm->param.samples_per_frame /
 
917
                                       strm->param.channel_count;
 
918
        }
 
919
 
 
920
        /* Give all frames we have */
 
921
        while (nsamples >= strm->param.samples_per_frame && status == 0) {
 
922
            frame.buf = (void*) input;
 
923
            frame.timestamp.u64 = strm->rec_timestamp.u64;
 
924
 
 
925
            status = (*strm->rec_cb)(strm->user_data, &frame);
 
926
 
 
927
            input = (pj_int16_t*) input + strm->param.samples_per_frame;
 
928
            nsamples -= strm->param.samples_per_frame;
 
929
            strm->rec_timestamp.u64 += strm->param.samples_per_frame /
 
930
                                       strm->param.channel_count;
 
931
        }
 
932
 
 
933
        /* Store the remaining samples into the buffer */
 
934
        if (nsamples && status == 0) {
 
935
            strm->rec_buf_count = nsamples;
 
936
            pjmedia_copy_samples(strm->rec_buf, input,
 
937
                                 nsamples);
 
938
        }
 
939
 
 
940
     } else {
 
941
        /* Not enough samples, let's just store them in the buffer */
 
942
        pjmedia_copy_samples(strm->rec_buf + strm->rec_buf_count,
 
943
                             input,
 
944
                             inNumberFrames * strm->param.channel_count);
 
945
        strm->rec_buf_count += inNumberFrames * strm->param.channel_count;
 
946
     }
 
947
 
 
948
    return noErr;
 
949
 
 
950
on_break:
 
951
    return -1;
 
952
}
 
953
 
 
954
static OSStatus output_renderer(void                       *inRefCon,
 
955
                                AudioUnitRenderActionFlags *ioActionFlags,
 
956
                                const AudioTimeStamp       *inTimeStamp,
 
957
                                UInt32                      inBusNumber,
 
958
                                UInt32                      inNumberFrames,
 
959
                                AudioBufferList            *ioData)
 
960
{
 
961
    struct coreaudio_stream *stream = (struct coreaudio_stream*)inRefCon;
 
962
    pj_status_t status = 0;
 
963
    unsigned nsamples_req = inNumberFrames * stream->param.channel_count;
 
964
    pj_int16_t *output = ioData->mBuffers[0].mData;
 
965
 
 
966
    pj_assert(!stream->quit_flag);
 
967
 
 
968
    /* Known cases of callback's thread:
 
969
     * - The thread may be changed in the middle of a session
 
970
     *   it happens when plugging/unplugging headphone.
 
971
     * - The same thread may be reused in consecutive sessions. The first
 
972
     *   session will leave TLS set, but release the TLS data address,
 
973
     *   so the second session must re-register the callback's thread.
 
974
     */
 
975
    if (stream->play_thread_initialized == 0 || !pj_thread_is_registered())
 
976
    {
 
977
        pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
 
978
        status = pj_thread_register("coreaudio", stream->play_thread_desc,
 
979
                                    &stream->play_thread);
 
980
        stream->play_thread_initialized = 1;
 
981
        PJ_LOG(5,(THIS_FILE, "Player thread started, (%i frames)",
 
982
                  inNumberFrames));
 
983
    }
 
984
 
 
985
 
 
986
    /* Check if any buffered samples */
 
987
    if (stream->play_buf_count) {
 
988
        /* samples buffered >= requested by sound device */
 
989
        if (stream->play_buf_count >= nsamples_req) {
 
990
            pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
 
991
                                 nsamples_req);
 
992
            stream->play_buf_count -= nsamples_req;
 
993
            pjmedia_move_samples(stream->play_buf,
 
994
                                 stream->play_buf + nsamples_req,
 
995
                                 stream->play_buf_count);
 
996
            nsamples_req = 0;
 
997
 
 
998
            return noErr;
 
999
        }
 
1000
 
 
1001
        /* samples buffered < requested by sound device */
 
1002
        pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
 
1003
                             stream->play_buf_count);
 
1004
        nsamples_req -= stream->play_buf_count;
 
1005
        output = (pj_int16_t*)output + stream->play_buf_count;
 
1006
        stream->play_buf_count = 0;
 
1007
    }
 
1008
 
 
1009
    /* Fill output buffer as requested */
 
1010
    while (nsamples_req && status == 0) {
 
1011
        pjmedia_frame frame;
 
1012
 
 
1013
        frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
 
1014
        frame.size = stream->param.samples_per_frame *
 
1015
                     stream->param.bits_per_sample >> 3;
 
1016
        frame.timestamp.u64 = stream->play_timestamp.u64;
 
1017
        frame.bit_info = 0;
 
1018
 
 
1019
        if (nsamples_req >= stream->param.samples_per_frame) {
 
1020
            frame.buf = output;
 
1021
            status = (*stream->play_cb)(stream->user_data, &frame);
 
1022
            if (status != PJ_SUCCESS)
 
1023
                goto on_break;
 
1024
 
 
1025
            if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
 
1026
                pj_bzero(frame.buf, frame.size);
 
1027
 
 
1028
            nsamples_req -= stream->param.samples_per_frame;
 
1029
            output = (pj_int16_t*)output + stream->param.samples_per_frame;
 
1030
        } else {
 
1031
            frame.buf = stream->play_buf;
 
1032
            status = (*stream->play_cb)(stream->user_data, &frame);
 
1033
            if (status != PJ_SUCCESS)
 
1034
                goto on_break;
 
1035
 
 
1036
            if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO)
 
1037
                pj_bzero(frame.buf, frame.size);
 
1038
 
 
1039
            pjmedia_copy_samples((pj_int16_t*)output, stream->play_buf,
 
1040
                                 nsamples_req);
 
1041
            stream->play_buf_count = stream->param.samples_per_frame -
 
1042
                                     nsamples_req;
 
1043
            pjmedia_move_samples(stream->play_buf,
 
1044
                                 stream->play_buf+nsamples_req,
 
1045
                                 stream->play_buf_count);
 
1046
            nsamples_req = 0;
 
1047
        }
 
1048
 
 
1049
        stream->play_timestamp.u64 += stream->param.samples_per_frame /
 
1050
                                      stream->param.channel_count;
 
1051
    }
 
1052
 
 
1053
    return noErr;
 
1054
 
 
1055
on_break:
 
1056
    return -1;
 
1057
}
 
1058
 
 
1059
#if !COREAUDIO_MAC
 
1060
static void propListener(void                   *inClientData,
 
1061
                         AudioSessionPropertyID inID,
 
1062
                         UInt32                 inDataSize,
 
1063
                         const void *           inData)
 
1064
{
 
1065
    struct coreaudio_factory *cf = (struct coreaudio_factory*)inClientData;
 
1066
    struct stream_list *it, *itBegin;
 
1067
    CFDictionaryRef routeDictionary;
 
1068
    CFNumberRef reason;
 
1069
    SInt32 reasonVal;
 
1070
    pj_assert(cf);
 
1071
 
 
1072
    if (inID != kAudioSessionProperty_AudioRouteChange)
 
1073
        return;
 
1074
 
 
1075
    routeDictionary = (CFDictionaryRef)inData;
 
1076
    reason = (CFNumberRef)
 
1077
             CFDictionaryGetValue(
 
1078
                 routeDictionary, 
 
1079
                 CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
 
1080
    CFNumberGetValue(reason, kCFNumberSInt32Type, &reasonVal);
 
1081
 
 
1082
    if (reasonVal != kAudioSessionRouteChangeReason_OldDeviceUnavailable) {
 
1083
        PJ_LOG(3, (THIS_FILE, "ignoring audio route change..."));
 
1084
        return;
 
1085
    }
 
1086
 
 
1087
    PJ_LOG(3, (THIS_FILE, "audio route changed"));
 
1088
 
 
1089
    pj_mutex_lock(cf->mutex);
 
1090
    itBegin = &cf->streams;
 
1091
    for (it = itBegin->next; it != itBegin; it = it->next) {
 
1092
        if (it->stream->interrupted)
 
1093
            continue;
 
1094
 
 
1095
        /*
 
1096
        status = ca_stream_stop((pjmedia_aud_stream *)it->stream);
 
1097
        status = ca_stream_start((pjmedia_aud_stream *)it->stream);
 
1098
        if (status != PJ_SUCCESS) {
 
1099
            PJ_LOG(3, (THIS_FILE,
 
1100
                       "Error: failed to restart the audio unit (%i)",
 
1101
                       status));
 
1102
            continue;
 
1103
        }
 
1104
        PJ_LOG(3, (THIS_FILE, "core audio unit successfully restarted"));
 
1105
        */
 
1106
    }
 
1107
    pj_mutex_unlock(cf->mutex);
 
1108
}
 
1109
 
 
1110
static void interruptionListener(void *inClientData, UInt32 inInterruption)
 
1111
{
 
1112
    struct stream_list *it, *itBegin;
 
1113
    pj_status_t status;
 
1114
    pj_thread_desc thread_desc;
 
1115
    pj_thread_t *thread;
 
1116
    
 
1117
    /* Register the thread with PJLIB, this is must for any external threads
 
1118
     * which need to use the PJLIB framework.
 
1119
     */
 
1120
    if (!pj_thread_is_registered()) {
 
1121
        pj_bzero(thread_desc, sizeof(pj_thread_desc));
 
1122
        status = pj_thread_register("intListener", thread_desc, &thread);
 
1123
    }
 
1124
    
 
1125
    PJ_LOG(3, (THIS_FILE, "Session interrupted! --- %s ---",
 
1126
           inInterruption == kAudioSessionBeginInterruption ?
 
1127
           "Begin Interruption" : "End Interruption"));
 
1128
 
 
1129
    if (!cf_instance)
 
1130
        return;
 
1131
    
 
1132
    pj_mutex_lock(cf_instance->mutex);
 
1133
    itBegin = &cf_instance->streams;
 
1134
    for (it = itBegin->next; it != itBegin; it = it->next) {
 
1135
        if (inInterruption == kAudioSessionEndInterruption &&
 
1136
            it->stream->interrupted == PJ_TRUE)
 
1137
        {
 
1138
            UInt32 audioCategory;
 
1139
            OSStatus ostatus;
 
1140
 
 
1141
            /* Make sure that your application can receive remote control
 
1142
             * events by adding the code:
 
1143
             *     [[UIApplication sharedApplication] 
 
1144
             *      beginReceivingRemoteControlEvents];
 
1145
             * Otherwise audio unit will fail to restart while your
 
1146
             * application is in the background mode.
 
1147
             */
 
1148
            /* Make sure we set the correct audio category before restarting */
 
1149
            audioCategory = kAudioSessionCategory_PlayAndRecord;
 
1150
            ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
 
1151
                                              sizeof(audioCategory),
 
1152
                                              &audioCategory);
 
1153
            if (ostatus != kAudioSessionNoError) {
 
1154
                PJ_LOG(4, (THIS_FILE,
 
1155
                           "Warning: cannot set the audio session category (%i)",
 
1156
                           ostatus));
 
1157
            }
 
1158
            
 
1159
            /* Restart the stream */
 
1160
            status = ca_stream_start((pjmedia_aud_stream*)it->stream);
 
1161
            if (status != PJ_SUCCESS) {
 
1162
                PJ_LOG(3, (THIS_FILE,
 
1163
                           "Error: failed to restart the audio unit (%i)",
 
1164
                           status));
 
1165
                continue;
 
1166
            }
 
1167
            PJ_LOG(3, (THIS_FILE, "core audio unit successfully resumed"
 
1168
                       " after interruption"));
 
1169
        } else if (inInterruption == kAudioSessionBeginInterruption &&
 
1170
                   it->stream->running == PJ_TRUE)
 
1171
        {
 
1172
            status = ca_stream_stop((pjmedia_aud_stream*)it->stream);
 
1173
            it->stream->interrupted = PJ_TRUE;
 
1174
        }
 
1175
    }
 
1176
    pj_mutex_unlock(cf_instance->mutex);
 
1177
}
 
1178
 
 
1179
#endif
 
1180
 
 
1181
#if COREAUDIO_MAC
 
1182
/* Internal: create audio converter for resampling the recorder device */
 
1183
static pj_status_t create_audio_resample(struct coreaudio_stream     *strm,
 
1184
                                         AudioStreamBasicDescription *desc)
 
1185
{
 
1186
    OSStatus ostatus;
 
1187
 
 
1188
    pj_assert(strm->streamFormat.mSampleRate != desc->mSampleRate);
 
1189
    pj_assert(NULL == strm->resample);
 
1190
    pj_assert(NULL == strm->resample_buf);
 
1191
 
 
1192
    /* Create the audio converter */
 
1193
    ostatus = AudioConverterNew(desc, &strm->streamFormat, &strm->resample);
 
1194
    if (ostatus != noErr) {
 
1195
        return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1196
    }
 
1197
    
 
1198
    /*
 
1199
     * Allocate the buffer required to hold enough input data
 
1200
     */
 
1201
    strm->resample_buf_size =  (unsigned)(desc->mSampleRate *
 
1202
                                          strm->param.samples_per_frame /
 
1203
                                          strm->param.clock_rate);
 
1204
    strm->resample_buf = (pj_int16_t*)
 
1205
                         pj_pool_alloc(strm->pool,
 
1206
                                       strm->resample_buf_size *
 
1207
                                       strm->param.bits_per_sample >> 3);
 
1208
    if (!strm->resample_buf)
 
1209
        return PJ_ENOMEM;
 
1210
    strm->resample_buf_count = 0;
 
1211
 
 
1212
    return PJ_SUCCESS;
 
1213
}
 
1214
#endif
 
1215
 
 
1216
/* Internal: create audio unit for recorder/playback device */
 
1217
static pj_status_t create_audio_unit(AudioComponent io_comp,
 
1218
                                     AudioDeviceID dev_id,
 
1219
                                     pjmedia_dir dir,
 
1220
                                     struct coreaudio_stream *strm,
 
1221
                                     AudioUnit *io_unit)
 
1222
{
 
1223
    OSStatus ostatus;
 
1224
#if !COREAUDIO_MAC
 
1225
    UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;
 
1226
    /* We want to be able to open playback and recording streams */
 
1227
    ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
 
1228
                                      sizeof(audioCategory),
 
1229
                                      &audioCategory);
 
1230
    if (ostatus != kAudioSessionNoError) {
 
1231
        PJ_LOG(4, (THIS_FILE,
 
1232
                   "Warning: cannot set the audio session category (%i)",
 
1233
                   ostatus));
 
1234
    }    
 
1235
#endif
 
1236
    
 
1237
    /* Create an audio unit to interface with the device */
 
1238
    ostatus = AudioComponentInstanceNew(io_comp, io_unit);
 
1239
    if (ostatus != noErr) {
 
1240
        return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1241
    }
 
1242
 
 
1243
    /* Set audio unit's properties for capture device */
 
1244
    if (dir & PJMEDIA_DIR_CAPTURE) {
 
1245
        UInt32 enable = 1;
 
1246
 
 
1247
        /* Enable input */
 
1248
        ostatus = AudioUnitSetProperty(*io_unit,
 
1249
                                       kAudioOutputUnitProperty_EnableIO,
 
1250
                                       kAudioUnitScope_Input,
 
1251
                                       1,
 
1252
                                       &enable,
 
1253
                                       sizeof(enable));
 
1254
        if (ostatus != noErr) {
 
1255
            PJ_LOG(4, (THIS_FILE,
 
1256
                   "Warning: cannot enable IO of capture device %d",
 
1257
                   dev_id));
 
1258
        }
 
1259
 
 
1260
        /* Disable output */
 
1261
        if (!(dir & PJMEDIA_DIR_PLAYBACK)) {
 
1262
            enable = 0;
 
1263
            ostatus = AudioUnitSetProperty(*io_unit,
 
1264
                                           kAudioOutputUnitProperty_EnableIO,
 
1265
                                           kAudioUnitScope_Output,
 
1266
                                           0,
 
1267
                                           &enable,
 
1268
                                           sizeof(enable));
 
1269
            if (ostatus != noErr) {
 
1270
                PJ_LOG(4, (THIS_FILE,
 
1271
                       "Warning: cannot disable IO of capture device %d",
 
1272
                       dev_id));
 
1273
            }
 
1274
        }
 
1275
    }
 
1276
 
 
1277
    /* Set audio unit's properties for playback device */
 
1278
    if (dir & PJMEDIA_DIR_PLAYBACK) {
 
1279
        UInt32 enable = 1;
 
1280
 
 
1281
        /* Enable output */
 
1282
        ostatus = AudioUnitSetProperty(*io_unit,
 
1283
                                       kAudioOutputUnitProperty_EnableIO,
 
1284
                                       kAudioUnitScope_Output,
 
1285
                                       0,
 
1286
                                       &enable,
 
1287
                                       sizeof(enable));
 
1288
        if (ostatus != noErr) {
 
1289
            PJ_LOG(4, (THIS_FILE,
 
1290
                   "Warning: cannot enable IO of playback device %d",
 
1291
                   dev_id));
 
1292
        }
 
1293
 
 
1294
    }
 
1295
 
 
1296
#if COREAUDIO_MAC
 
1297
    PJ_LOG(5, (THIS_FILE, "Opening device %d", dev_id));
 
1298
    ostatus = AudioUnitSetProperty(*io_unit,
 
1299
                                   kAudioOutputUnitProperty_CurrentDevice,
 
1300
                                   kAudioUnitScope_Global,
 
1301
                                   0,
 
1302
                                   &dev_id,
 
1303
                                   sizeof(dev_id));
 
1304
    if (ostatus != noErr) {
 
1305
        return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1306
    }
 
1307
#endif
 
1308
 
 
1309
    if (dir & PJMEDIA_DIR_CAPTURE) {
 
1310
#if COREAUDIO_MAC
 
1311
        AudioStreamBasicDescription deviceFormat;
 
1312
        UInt32 size;
 
1313
 
 
1314
        /*
 
1315
         * Keep the sample rate from the device, otherwise we will confuse
 
1316
         * AUHAL
 
1317
         */
 
1318
        size = sizeof(AudioStreamBasicDescription);
 
1319
        ostatus = AudioUnitGetProperty(*io_unit,
 
1320
                                       kAudioUnitProperty_StreamFormat,
 
1321
                                       kAudioUnitScope_Input,
 
1322
                                       1,
 
1323
                                       &deviceFormat,
 
1324
                                       &size);
 
1325
        if (ostatus != noErr) {
 
1326
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1327
        }
 
1328
        strm->streamFormat.mSampleRate = deviceFormat.mSampleRate;
 
1329
#endif
 
1330
 
 
1331
        /* When setting the stream format, we have to make sure the sample
 
1332
         * rate is supported. Setting an unsupported sample rate will cause
 
1333
         * AudioUnitRender() to fail later.
 
1334
         */
 
1335
        ostatus = AudioUnitSetProperty(*io_unit,
 
1336
                                       kAudioUnitProperty_StreamFormat,
 
1337
                                       kAudioUnitScope_Output,
 
1338
                                       1,
 
1339
                                       &strm->streamFormat,
 
1340
                                       sizeof(strm->streamFormat));
 
1341
        if (ostatus != noErr) {
 
1342
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1343
        }
 
1344
 
 
1345
#if COREAUDIO_MAC
 
1346
        strm->streamFormat.mSampleRate = strm->param.clock_rate;
 
1347
        size = sizeof(AudioStreamBasicDescription);
 
1348
        ostatus = AudioUnitGetProperty (*io_unit,
 
1349
                                        kAudioUnitProperty_StreamFormat,
 
1350
                                        kAudioUnitScope_Output,
 
1351
                                        1,
 
1352
                                        &deviceFormat,
 
1353
                                        &size);
 
1354
        if (ostatus == noErr) {
 
1355
            if (strm->streamFormat.mSampleRate != deviceFormat.mSampleRate) {
 
1356
                pj_status_t rc = create_audio_resample(strm, &deviceFormat);
 
1357
                if (PJ_SUCCESS != rc)
 
1358
                    return rc;
 
1359
            }
 
1360
        } else {
 
1361
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1362
        }
 
1363
#endif
 
1364
    }
 
1365
 
 
1366
    if (dir & PJMEDIA_DIR_PLAYBACK) {
 
1367
        AURenderCallbackStruct output_cb;
 
1368
 
 
1369
        /* Set the stream format */
 
1370
        ostatus = AudioUnitSetProperty(*io_unit,
 
1371
                                       kAudioUnitProperty_StreamFormat,
 
1372
                                       kAudioUnitScope_Input,
 
1373
                                       0,
 
1374
                                       &strm->streamFormat,
 
1375
                                       sizeof(strm->streamFormat));
 
1376
        if (ostatus != noErr) {
 
1377
            PJ_LOG(4, (THIS_FILE,
 
1378
                   "Warning: cannot set playback stream format of dev %d",
 
1379
                   dev_id));
 
1380
        }
 
1381
 
 
1382
        /* Set render callback */
 
1383
        output_cb.inputProc = output_renderer;
 
1384
        output_cb.inputProcRefCon = strm;
 
1385
        ostatus = AudioUnitSetProperty(*io_unit,
 
1386
                                       kAudioUnitProperty_SetRenderCallback,
 
1387
                                       kAudioUnitScope_Input,
 
1388
                                       0,
 
1389
                                       &output_cb,
 
1390
                                       sizeof(output_cb));
 
1391
        if (ostatus != noErr) {
 
1392
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1393
        }
 
1394
 
 
1395
        /* Allocate playback buffer */
 
1396
        strm->play_buf = (pj_int16_t*)pj_pool_alloc(strm->pool,
 
1397
                         strm->param.samples_per_frame *
 
1398
                         strm->param.bits_per_sample >> 3);
 
1399
        if (!strm->play_buf)
 
1400
            return PJ_ENOMEM;
 
1401
        strm->play_buf_count = 0;
 
1402
    }
 
1403
 
 
1404
    if (dir & PJMEDIA_DIR_CAPTURE) {
 
1405
        AURenderCallbackStruct input_cb;
 
1406
#if COREAUDIO_MAC
 
1407
        AudioBuffer *ab;
 
1408
        UInt32 size, buf_size;
 
1409
#endif
 
1410
 
 
1411
        /* Set input callback */
 
1412
        input_cb.inputProc = strm->resample ? resample_callback :
 
1413
                             input_callback;
 
1414
        input_cb.inputProcRefCon = strm;
 
1415
        ostatus = AudioUnitSetProperty(
 
1416
                      *io_unit,
 
1417
                      kAudioOutputUnitProperty_SetInputCallback,
 
1418
                      kAudioUnitScope_Global,
 
1419
                      0,
 
1420
                      &input_cb,
 
1421
                      sizeof(input_cb));
 
1422
        if (ostatus != noErr) {
 
1423
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1424
        }
 
1425
 
 
1426
#if COREAUDIO_MAC
 
1427
        /* Get device's buffer frame size */
 
1428
        size = sizeof(UInt32);
 
1429
        ostatus = AudioUnitGetProperty(*io_unit,
 
1430
                                       kAudioDevicePropertyBufferFrameSize,
 
1431
                                       kAudioUnitScope_Global,
 
1432
                                       0,
 
1433
                                       &buf_size,
 
1434
                                       &size);
 
1435
        if (ostatus != noErr)
 
1436
        {
 
1437
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1438
        }
 
1439
 
 
1440
        /* Allocate audio buffer */
 
1441
        strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool,
 
1442
                          sizeof(AudioBufferList) + sizeof(AudioBuffer));
 
1443
        if (!strm->audio_buf)
 
1444
            return PJ_ENOMEM;
 
1445
 
 
1446
        strm->audio_buf->mNumberBuffers = 1;
 
1447
        ab = &strm->audio_buf->mBuffers[0];
 
1448
        ab->mNumberChannels = strm->streamFormat.mChannelsPerFrame;
 
1449
        ab->mDataByteSize = buf_size * ab->mNumberChannels *
 
1450
                            strm->param.bits_per_sample >> 3;
 
1451
        ab->mData = pj_pool_alloc(strm->pool,
 
1452
                                  ab->mDataByteSize);
 
1453
        if (!ab->mData)
 
1454
            return PJ_ENOMEM;
 
1455
 
 
1456
#else
 
1457
        /* We will let AudioUnitRender() to allocate the buffer
 
1458
         * for us later
 
1459
         */
 
1460
        strm->audio_buf = (AudioBufferList*)pj_pool_alloc(strm->pool,
 
1461
                          sizeof(AudioBufferList) + sizeof(AudioBuffer));
 
1462
        if (!strm->audio_buf)
 
1463
            return PJ_ENOMEM;
 
1464
 
 
1465
        strm->audio_buf->mNumberBuffers = 1;
 
1466
        strm->audio_buf->mBuffers[0].mNumberChannels =
 
1467
                strm->streamFormat.mChannelsPerFrame;
 
1468
        
 
1469
#endif
 
1470
 
 
1471
        /* Allocate recording buffer */
 
1472
        strm->rec_buf = (pj_int16_t*)pj_pool_alloc(strm->pool,
 
1473
                        strm->param.samples_per_frame *
 
1474
                        strm->param.bits_per_sample >> 3);
 
1475
        if (!strm->rec_buf)
 
1476
            return PJ_ENOMEM;
 
1477
        strm->rec_buf_count = 0;
 
1478
    }
 
1479
 
 
1480
    /* Initialize the audio unit */
 
1481
    ostatus = AudioUnitInitialize(*io_unit);
 
1482
    if (ostatus != noErr) {
 
1483
        return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1484
    }
 
1485
 
 
1486
    return PJ_SUCCESS;
 
1487
}
 
1488
 
 
1489
/* API: create stream */
 
1490
static pj_status_t ca_factory_create_stream(pjmedia_aud_dev_factory *f,
 
1491
                                            const pjmedia_aud_param *param,
 
1492
                                            pjmedia_aud_rec_cb rec_cb,
 
1493
                                            pjmedia_aud_play_cb play_cb,
 
1494
                                            void *user_data,
 
1495
                                            pjmedia_aud_stream **p_aud_strm)
 
1496
{
 
1497
    struct coreaudio_factory *cf = (struct coreaudio_factory*)f;
 
1498
    pj_pool_t *pool;
 
1499
    struct coreaudio_stream *strm;
 
1500
    pj_status_t status;
 
1501
 
 
1502
    /* Create and Initialize stream descriptor */
 
1503
    pool = pj_pool_create(cf->pf, "coreaudio-dev", 1000, 1000, NULL);
 
1504
    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
 
1505
 
 
1506
    strm = PJ_POOL_ZALLOC_T(pool, struct coreaudio_stream);
 
1507
    pj_list_init(&strm->list_entry);
 
1508
    strm->list_entry.stream = strm;
 
1509
    strm->cf = cf;
 
1510
    pj_memcpy(&strm->param, param, sizeof(*param));
 
1511
    strm->pool = pool;
 
1512
    strm->rec_cb = rec_cb;
 
1513
    strm->play_cb = play_cb;
 
1514
    strm->user_data = user_data;
 
1515
 
 
1516
    /* Set the stream format */
 
1517
    strm->streamFormat.mSampleRate       = param->clock_rate;
 
1518
    strm->streamFormat.mFormatID         = kAudioFormatLinearPCM;
 
1519
    strm->streamFormat.mFormatFlags      = kLinearPCMFormatFlagIsSignedInteger
 
1520
                                           | kLinearPCMFormatFlagIsPacked;
 
1521
    strm->streamFormat.mBitsPerChannel   = strm->param.bits_per_sample;
 
1522
    strm->streamFormat.mChannelsPerFrame = param->channel_count;
 
1523
    strm->streamFormat.mBytesPerFrame    = strm->streamFormat.mChannelsPerFrame
 
1524
                                           * strm->param.bits_per_sample >> 3;
 
1525
    strm->streamFormat.mFramesPerPacket  = 1;
 
1526
    strm->streamFormat.mBytesPerPacket   = strm->streamFormat.mBytesPerFrame *
 
1527
                                           strm->streamFormat.mFramesPerPacket;
 
1528
 
 
1529
    /* Apply input/output routes settings before we create the audio units */
 
1530
    if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE) {
 
1531
        ca_stream_set_cap(&strm->base,
 
1532
                          PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
 
1533
                          &param->input_route);
 
1534
    }
 
1535
    if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE) {
 
1536
        ca_stream_set_cap(&strm->base,
 
1537
                          PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
 
1538
                          &param->output_route);
 
1539
    }
 
1540
    if (param->flags & PJMEDIA_AUD_DEV_CAP_EC) {
 
1541
        ca_stream_set_cap(&strm->base,
 
1542
                          PJMEDIA_AUD_DEV_CAP_EC,
 
1543
                          &param->ec_enabled);
 
1544
    } else {
 
1545
        pj_bool_t ec = PJ_FALSE;
 
1546
        ca_stream_set_cap(&strm->base,
 
1547
                          PJMEDIA_AUD_DEV_CAP_EC, &ec);
 
1548
    }
 
1549
 
 
1550
    strm->io_units[0] = strm->io_units[1] = NULL;
 
1551
    if (param->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK &&
 
1552
        param->rec_id == param->play_id)
 
1553
    {
 
1554
        /* If both input and output are on the same device, only create
 
1555
         * one audio unit to interface with the device.
 
1556
         */
 
1557
        status = create_audio_unit(cf->io_comp,
 
1558
                                   cf->dev_info[param->rec_id].dev_id,
 
1559
                                   param->dir, strm, &strm->io_units[0]);
 
1560
        if (status != PJ_SUCCESS)
 
1561
            goto on_error;
 
1562
    } else {
 
1563
        unsigned nunits = 0;
 
1564
 
 
1565
        if (param->dir & PJMEDIA_DIR_CAPTURE) {
 
1566
            status = create_audio_unit(cf->io_comp,
 
1567
                                       cf->dev_info[param->rec_id].dev_id,
 
1568
                                       PJMEDIA_DIR_CAPTURE,
 
1569
                                       strm, &strm->io_units[nunits++]);
 
1570
            if (status != PJ_SUCCESS)
 
1571
                goto on_error;
 
1572
        }
 
1573
        if (param->dir & PJMEDIA_DIR_PLAYBACK) {
 
1574
 
 
1575
            status = create_audio_unit(cf->io_comp,
 
1576
                                       cf->dev_info[param->play_id].dev_id,
 
1577
                                       PJMEDIA_DIR_PLAYBACK,
 
1578
                                       strm, &strm->io_units[nunits++]);
 
1579
            if (status != PJ_SUCCESS)
 
1580
                goto on_error;
 
1581
        }
 
1582
    }
 
1583
 
 
1584
    /* Apply the remaining settings */
 
1585
    if (param->flags & PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY) {
 
1586
        ca_stream_get_cap(&strm->base,
 
1587
                          PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
 
1588
                          &strm->param.input_latency_ms);
 
1589
    }
 
1590
    if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY) {
 
1591
        ca_stream_get_cap(&strm->base,
 
1592
                          PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
 
1593
                          &strm->param.output_latency_ms);
 
1594
    }
 
1595
    if (param->flags & PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING) {
 
1596
        ca_stream_set_cap(&strm->base,
 
1597
                          PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
 
1598
                          &param->output_vol);
 
1599
    }
 
1600
 
 
1601
    pj_mutex_lock(strm->cf->mutex);
 
1602
    pj_assert(pj_list_empty(&strm->list_entry));
 
1603
    pj_list_insert_after(&strm->cf->streams, &strm->list_entry);
 
1604
    pj_mutex_unlock(strm->cf->mutex);
 
1605
 
 
1606
    /* Done */
 
1607
    strm->base.op = &stream_op;
 
1608
    *p_aud_strm = &strm->base;
 
1609
 
 
1610
    return PJ_SUCCESS;
 
1611
 
 
1612
 on_error:
 
1613
    ca_stream_destroy((pjmedia_aud_stream *)strm);
 
1614
    return status;
 
1615
}
 
1616
 
 
1617
/* API: Get stream info. */
 
1618
static pj_status_t ca_stream_get_param(pjmedia_aud_stream *s,
 
1619
                                       pjmedia_aud_param *pi)
 
1620
{
 
1621
    struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
 
1622
 
 
1623
    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
 
1624
 
 
1625
    pj_memcpy(pi, &strm->param, sizeof(*pi));
 
1626
 
 
1627
    /* Update the device capabilities' values */
 
1628
    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY,
 
1629
                          &pi->input_latency_ms) == PJ_SUCCESS)
 
1630
    {
 
1631
        pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY;
 
1632
    }
 
1633
    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY,
 
1634
                          &pi->output_latency_ms) == PJ_SUCCESS)
 
1635
    {
 
1636
        pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY;
 
1637
    }
 
1638
    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING,
 
1639
                          &pi->output_vol) == PJ_SUCCESS)
 
1640
    {
 
1641
        pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING;
 
1642
    }
 
1643
    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE,
 
1644
                          &pi->input_route) == PJ_SUCCESS)
 
1645
    {
 
1646
        pi->flags |= PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE;
 
1647
    }
 
1648
    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
 
1649
                          &pi->output_route) == PJ_SUCCESS)
 
1650
    {
 
1651
        pi->flags |= PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE;
 
1652
    }
 
1653
    if (ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_EC,
 
1654
                          &pi->ec_enabled) == PJ_SUCCESS)
 
1655
    {
 
1656
        pi->flags |= PJMEDIA_AUD_DEV_CAP_EC;
 
1657
    }
 
1658
 
 
1659
    return PJ_SUCCESS;
 
1660
}
 
1661
 
 
1662
/* API: get capability */
 
1663
static pj_status_t ca_stream_get_cap(pjmedia_aud_stream *s,
 
1664
                                     pjmedia_aud_dev_cap cap,
 
1665
                                     void *pval)
 
1666
{
 
1667
    struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
 
1668
 
 
1669
    PJ_UNUSED_ARG(strm);
 
1670
 
 
1671
    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
 
1672
 
 
1673
    if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
 
1674
        (strm->param.dir & PJMEDIA_DIR_CAPTURE))
 
1675
    {
 
1676
#if COREAUDIO_MAC
 
1677
        UInt32 latency, size = sizeof(UInt32);
 
1678
 
 
1679
        /* Recording latency */
 
1680
        if (AudioUnitGetProperty (strm->io_units[0],
 
1681
                                  kAudioDevicePropertyLatency,
 
1682
                                  kAudioUnitScope_Input,
 
1683
                                  1,
 
1684
                                  &latency,
 
1685
                                  &size) == noErr)
 
1686
        {
 
1687
            UInt32 latency2;
 
1688
            if (AudioUnitGetProperty (strm->io_units[0],
 
1689
                                      kAudioDevicePropertyBufferFrameSize,
 
1690
                                      kAudioUnitScope_Input,
 
1691
                                      1,
 
1692
                                      &latency2,
 
1693
                                      &size) == noErr)
 
1694
            {
 
1695
                strm->param.input_latency_ms = (latency + latency2) * 1000 /
 
1696
                                               strm->param.clock_rate;
 
1697
                strm->param.input_latency_ms++;
 
1698
            }
 
1699
        }
 
1700
#else
 
1701
        Float32 latency, latency2;
 
1702
        UInt32 size = sizeof(Float32);
 
1703
 
 
1704
        if ((AudioSessionGetProperty(
 
1705
            kAudioSessionProperty_CurrentHardwareInputLatency,
 
1706
            &size, &latency) == kAudioSessionNoError) &&
 
1707
            (AudioSessionGetProperty(
 
1708
            kAudioSessionProperty_CurrentHardwareIOBufferDuration,
 
1709
            &size, &latency2) == kAudioSessionNoError))
 
1710
        {
 
1711
            strm->param.input_latency_ms = (unsigned)
 
1712
                                           ((latency + latency2) * 1000);
 
1713
            strm->param.input_latency_ms++;
 
1714
        }
 
1715
#endif
 
1716
 
 
1717
        *(unsigned*)pval = strm->param.input_latency_ms;
 
1718
        return PJ_SUCCESS;
 
1719
    } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY  &&
 
1720
               (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
 
1721
    {
 
1722
#if COREAUDIO_MAC
 
1723
        UInt32 latency, size = sizeof(UInt32);
 
1724
        AudioUnit *io_unit = strm->io_units[1] ? &strm->io_units[1] :
 
1725
                             &strm->io_units[0];
 
1726
 
 
1727
        /* Playback latency */
 
1728
        if (AudioUnitGetProperty (*io_unit,
 
1729
                                  kAudioDevicePropertyLatency,
 
1730
                                  kAudioUnitScope_Output,
 
1731
                                  0,
 
1732
                                  &latency,
 
1733
                                  &size) == noErr)
 
1734
        {
 
1735
            UInt32 latency2;
 
1736
            if (AudioUnitGetProperty (*io_unit,
 
1737
                                      kAudioDevicePropertyBufferFrameSize,
 
1738
                                      kAudioUnitScope_Output,
 
1739
                                      0,
 
1740
                                      &latency2,
 
1741
                                      &size) == noErr)
 
1742
            {
 
1743
                strm->param.output_latency_ms = (latency + latency2) * 1000 /
 
1744
                                                strm->param.clock_rate;
 
1745
                strm->param.output_latency_ms++;
 
1746
            }
 
1747
        }
 
1748
#else
 
1749
        Float32 latency, latency2;
 
1750
        UInt32 size = sizeof(Float32);
 
1751
 
 
1752
        if ((AudioSessionGetProperty(
 
1753
            kAudioSessionProperty_CurrentHardwareOutputLatency,
 
1754
            &size, &latency) == kAudioSessionNoError) &&
 
1755
            (AudioSessionGetProperty(
 
1756
            kAudioSessionProperty_CurrentHardwareIOBufferDuration,
 
1757
            &size, &latency2) == kAudioSessionNoError))
 
1758
        {
 
1759
            strm->param.output_latency_ms = (unsigned)
 
1760
                                            ((latency + latency2) * 1000);
 
1761
            strm->param.output_latency_ms++;
 
1762
        }
 
1763
#endif
 
1764
        *(unsigned*)pval = (++strm->param.output_latency_ms * 2);
 
1765
        return PJ_SUCCESS;
 
1766
    } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
 
1767
               (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
 
1768
    {
 
1769
        OSStatus ostatus;
 
1770
        Float32 volume;
 
1771
        UInt32 size = sizeof(Float32);
 
1772
 
 
1773
        /* Output volume setting */
 
1774
#if COREAUDIO_MAC
 
1775
        ostatus = AudioUnitGetProperty (strm->io_units[1] ? strm->io_units[1] :
 
1776
                                        strm->io_units[0],
 
1777
                                        kAudioDevicePropertyVolumeScalar,
 
1778
                                        kAudioUnitScope_Output,
 
1779
                                        0,
 
1780
                                        &volume,
 
1781
                                        &size);
 
1782
        if (ostatus != noErr)
 
1783
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1784
#else
 
1785
        ostatus = AudioSessionGetProperty(
 
1786
                  kAudioSessionProperty_CurrentHardwareOutputVolume,
 
1787
                  &size, &volume);
 
1788
        if (ostatus != kAudioSessionNoError) {
 
1789
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1790
        }
 
1791
#endif
 
1792
 
 
1793
        *(unsigned*)pval = (unsigned)(volume * 100);
 
1794
        return PJ_SUCCESS;
 
1795
#if !COREAUDIO_MAC
 
1796
    } else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE &&
 
1797
               (strm->param.dir & PJMEDIA_DIR_CAPTURE))
 
1798
    {
 
1799
        UInt32 btooth, size = sizeof(UInt32);
 
1800
        OSStatus ostatus;
 
1801
 
 
1802
        ostatus = AudioSessionGetProperty (
 
1803
            kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
 
1804
            &size, &btooth);
 
1805
        if (ostatus != kAudioSessionNoError) {
 
1806
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1807
        }
 
1808
 
 
1809
        *(pjmedia_aud_dev_route*)pval = btooth?
 
1810
                                        PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH:
 
1811
                                        PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
 
1812
        return PJ_SUCCESS;
 
1813
    } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
 
1814
               (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
 
1815
    {
 
1816
        CFStringRef route;
 
1817
        UInt32 size = sizeof(CFStringRef);
 
1818
        OSStatus ostatus;
 
1819
 
 
1820
        ostatus = AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
 
1821
                                           &size, &route);
 
1822
        if (ostatus != kAudioSessionNoError) {
 
1823
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1824
        }
 
1825
 
 
1826
        if (!route) {
 
1827
            *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
 
1828
        } else if (CFStringHasPrefix(route, CFSTR("Headset"))) {
 
1829
            *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;
 
1830
        } else {
 
1831
            *(pjmedia_aud_dev_route*)pval = PJMEDIA_AUD_DEV_ROUTE_DEFAULT;
 
1832
        }
 
1833
 
 
1834
        CFRelease(route);
 
1835
 
 
1836
        return PJ_SUCCESS;
 
1837
    } else if (cap==PJMEDIA_AUD_DEV_CAP_EC) {
 
1838
        AudioComponentDescription desc;
 
1839
        OSStatus ostatus;
 
1840
 
 
1841
        ostatus = AudioComponentGetDescription(strm->cf->io_comp, &desc);
 
1842
        if (ostatus != noErr) {
 
1843
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1844
        }
 
1845
 
 
1846
        *(pj_bool_t*)pval = (desc.componentSubType ==
 
1847
                            kAudioUnitSubType_VoiceProcessingIO);
 
1848
        return PJ_SUCCESS;
 
1849
#endif
 
1850
    } else {
 
1851
        return PJMEDIA_EAUD_INVCAP;
 
1852
    }
 
1853
}
 
1854
 
 
1855
/* API: set capability */
 
1856
static pj_status_t ca_stream_set_cap(pjmedia_aud_stream *s,
 
1857
                                     pjmedia_aud_dev_cap cap,
 
1858
                                     const void *pval)
 
1859
{
 
1860
    struct coreaudio_stream *strm = (struct coreaudio_stream*)s;
 
1861
 
 
1862
    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
 
1863
 
 
1864
#if COREAUDIO_MAC
 
1865
    if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING &&
 
1866
        (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
 
1867
    {
 
1868
        OSStatus ostatus;
 
1869
        Float32 volume = *(unsigned*)pval;
 
1870
 
 
1871
        /* Output volume setting */
 
1872
        volume /= 100.0;
 
1873
        ostatus = AudioUnitSetProperty (strm->io_units[1] ? strm->io_units[1] :
 
1874
                                        strm->io_units[0],
 
1875
                                        kAudioDevicePropertyVolumeScalar,
 
1876
                                        kAudioUnitScope_Output,
 
1877
                                        0,
 
1878
                                        &volume,
 
1879
                                        sizeof(Float32));
 
1880
        if (ostatus != noErr) {
 
1881
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1882
        }
 
1883
        strm->param.output_vol = *(unsigned*)pval;
 
1884
        return PJ_SUCCESS;
 
1885
    }
 
1886
 
 
1887
#else
 
1888
 
 
1889
    if ((cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY &&
 
1890
         (strm->param.dir & PJMEDIA_DIR_CAPTURE)) ||
 
1891
        (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY &&
 
1892
         (strm->param.dir & PJMEDIA_DIR_PLAYBACK)))
 
1893
    {
 
1894
        Float32 bufferDuration = *(unsigned *)pval;
 
1895
        OSStatus ostatus;
 
1896
        unsigned latency;
 
1897
        
 
1898
        /* For low-latency audio streaming, you can set this value to
 
1899
         * as low as 5 ms (the default is 23ms). However, lowering the
 
1900
         * latency may cause a decrease in audio quality.
 
1901
         */
 
1902
        bufferDuration /= 1000;
 
1903
        ostatus = AudioSessionSetProperty(
 
1904
                      kAudioSessionProperty_PreferredHardwareIOBufferDuration,
 
1905
                      sizeof(bufferDuration), &bufferDuration);
 
1906
        if (ostatus != kAudioSessionNoError) {
 
1907
            PJ_LOG(4, (THIS_FILE,
 
1908
                       "Error: cannot set the preferred buffer duration (%i)",
 
1909
                       ostatus));
 
1910
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1911
        }
 
1912
        
 
1913
        ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, &latency);
 
1914
        ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, &latency);
 
1915
        
 
1916
        return PJ_SUCCESS;
 
1917
    } else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE &&
 
1918
               (strm->param.dir & PJMEDIA_DIR_CAPTURE))
 
1919
    {
 
1920
        UInt32 btooth = *(pjmedia_aud_dev_route*)pval ==
 
1921
                        PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH ? 1 : 0;
 
1922
        OSStatus ostatus;
 
1923
 
 
1924
        ostatus = AudioSessionSetProperty (
 
1925
            kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
 
1926
            sizeof(btooth), &btooth);
 
1927
        if (ostatus != kAudioSessionNoError) {
 
1928
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1929
        }
 
1930
        strm->param.input_route = *(pjmedia_aud_dev_route*)pval;
 
1931
        return PJ_SUCCESS;
 
1932
    } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE &&
 
1933
               (strm->param.dir & PJMEDIA_DIR_PLAYBACK))
 
1934
    {
 
1935
        OSStatus ostatus;
 
1936
        UInt32 route = *(pjmedia_aud_dev_route*)pval ==
 
1937
                       PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER ?
 
1938
                       kAudioSessionOverrideAudioRoute_Speaker :
 
1939
                       kAudioSessionOverrideAudioRoute_None;
 
1940
 
 
1941
        ostatus = AudioSessionSetProperty (
 
1942
            kAudioSessionProperty_OverrideAudioRoute,
 
1943
            sizeof(route), &route);
 
1944
        if (ostatus != kAudioSessionNoError) {
 
1945
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1946
        }
 
1947
        strm->param.output_route = *(pjmedia_aud_dev_route*)pval;
 
1948
        return PJ_SUCCESS;
 
1949
    } else if (cap==PJMEDIA_AUD_DEV_CAP_EC) {
 
1950
        AudioComponentDescription desc;
 
1951
        AudioComponent io_comp;
 
1952
 
 
1953
        desc.componentType = kAudioUnitType_Output;
 
1954
        desc.componentSubType = (*(pj_bool_t*)pval)?
 
1955
                                kAudioUnitSubType_VoiceProcessingIO :
 
1956
                                kAudioUnitSubType_RemoteIO;
 
1957
        desc.componentManufacturer = kAudioUnitManufacturer_Apple;
 
1958
        desc.componentFlags = 0;
 
1959
        desc.componentFlagsMask = 0;
 
1960
 
 
1961
        io_comp = AudioComponentFindNext(NULL, &desc);
 
1962
        if (io_comp == NULL)
 
1963
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(-1);
 
1964
        strm->cf->io_comp = io_comp;
 
1965
        strm->param.ec_enabled = *(pj_bool_t*)pval;
 
1966
 
 
1967
        PJ_LOG(4, (THIS_FILE, "Using %s audio unit", 
 
1968
                              (desc.componentSubType ==
 
1969
                               kAudioUnitSubType_RemoteIO? "RemoteIO":
 
1970
                               "VoiceProcessingIO")));
 
1971
        
 
1972
        return PJ_SUCCESS;
 
1973
    }
 
1974
#endif
 
1975
 
 
1976
    return PJMEDIA_EAUD_INVCAP;
 
1977
}
 
1978
 
 
1979
/* API: Start stream. */
 
1980
static pj_status_t ca_stream_start(pjmedia_aud_stream *strm)
 
1981
{
 
1982
    struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
 
1983
    OSStatus ostatus;
 
1984
    UInt32 i;
 
1985
 
 
1986
    if (stream->running)
 
1987
        return PJ_SUCCESS;
 
1988
 
 
1989
    stream->quit_flag = 0;
 
1990
    stream->interrupted = PJ_FALSE;
 
1991
    stream->rec_buf_count = 0;
 
1992
    stream->play_buf_count = 0;
 
1993
    stream->resample_buf_count = 0;
 
1994
 
 
1995
    if (stream->resample) {
 
1996
        ostatus = AudioConverterReset(stream->resample);
 
1997
        if (ostatus != noErr)
 
1998
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
1999
    }
 
2000
 
 
2001
#if !COREAUDIO_MAC
 
2002
    AudioSessionSetActive(true);
 
2003
#endif
 
2004
    
 
2005
    for (i = 0; i < 2; i++) {
 
2006
        if (stream->io_units[i] == NULL) break;
 
2007
        ostatus = AudioOutputUnitStart(stream->io_units[i]);
 
2008
        if (ostatus != noErr) {
 
2009
            if (i == 1)
 
2010
                AudioOutputUnitStop(stream->io_units[0]);
 
2011
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
2012
        }
 
2013
    }
 
2014
 
 
2015
    stream->running = PJ_TRUE;
 
2016
 
 
2017
    PJ_LOG(4, (THIS_FILE, "core audio stream started"));
 
2018
 
 
2019
    return PJ_SUCCESS;
 
2020
}
 
2021
 
 
2022
/* API: Stop stream. */
 
2023
static pj_status_t ca_stream_stop(pjmedia_aud_stream *strm)
 
2024
{
 
2025
    struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
 
2026
    OSStatus ostatus;
 
2027
    unsigned i;
 
2028
    int should_deactivate;
 
2029
    struct stream_list *it, *itBegin;
 
2030
 
 
2031
    if (!stream->running)
 
2032
        return PJ_SUCCESS;
 
2033
 
 
2034
    for (i = 0; i < 2; i++) {
 
2035
        if (stream->io_units[i] == NULL) break;
 
2036
        ostatus = AudioOutputUnitStop(stream->io_units[i]);
 
2037
        if (ostatus != noErr) {
 
2038
            if (i == 0 && stream->io_units[1])
 
2039
                AudioOutputUnitStop(stream->io_units[1]);
 
2040
            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus);
 
2041
        }
 
2042
    }
 
2043
 
 
2044
    /* Check whether we need to deactivate the audio session. */
 
2045
    pj_mutex_lock(stream->cf->mutex);
 
2046
    pj_assert(!pj_list_empty(&stream->cf->streams));
 
2047
    pj_assert(!pj_list_empty(&stream->list_entry));
 
2048
    stream->running = PJ_FALSE;
 
2049
    should_deactivate = PJ_TRUE;
 
2050
    itBegin = &stream->cf->streams;
 
2051
    for (it = itBegin->next; it != itBegin; it = it->next) {
 
2052
        if (it->stream->running) {
 
2053
            should_deactivate = PJ_FALSE;
 
2054
            break;
 
2055
        }
 
2056
    }
 
2057
    pj_mutex_unlock(stream->cf->mutex);
 
2058
 
 
2059
#if !COREAUDIO_MAC
 
2060
    if (should_deactivate)
 
2061
        AudioSessionSetActive(false);
 
2062
#endif
 
2063
 
 
2064
    stream->quit_flag = 1;
 
2065
    stream->play_thread_initialized = 0;
 
2066
    stream->rec_thread_initialized = 0;
 
2067
    pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc));
 
2068
    pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc));
 
2069
 
 
2070
    PJ_LOG(4, (THIS_FILE, "core audio stream stopped"));
 
2071
 
 
2072
    return PJ_SUCCESS;
 
2073
}
 
2074
 
 
2075
 
 
2076
/* API: Destroy stream. */
 
2077
static pj_status_t ca_stream_destroy(pjmedia_aud_stream *strm)
 
2078
{
 
2079
    struct coreaudio_stream *stream = (struct coreaudio_stream*)strm;
 
2080
    unsigned i;
 
2081
 
 
2082
    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
 
2083
 
 
2084
    ca_stream_stop(strm);
 
2085
 
 
2086
    for (i = 0; i < 2; i++) {
 
2087
        if (stream->io_units[i]) {
 
2088
            AudioUnitUninitialize(stream->io_units[i]);
 
2089
            AudioComponentInstanceDispose(stream->io_units[i]);
 
2090
            stream->io_units[i] = NULL;
 
2091
        }
 
2092
    }
 
2093
 
 
2094
    if (stream->resample)
 
2095
        AudioConverterDispose(stream->resample);
 
2096
 
 
2097
    pj_mutex_lock(stream->cf->mutex);
 
2098
    if (!pj_list_empty(&stream->list_entry))
 
2099
        pj_list_erase(&stream->list_entry);
 
2100
    pj_mutex_unlock(stream->cf->mutex);
 
2101
 
 
2102
    pj_pool_release(stream->pool);
 
2103
 
 
2104
    return PJ_SUCCESS;
 
2105
}
 
2106
 
 
2107
#endif  /* PJMEDIA_AUDIO_DEV_HAS_COREAUDIO */