Changeset 3398


Ignore:
Timestamp:
Dec 17, 2010 7:10:13 AM (13 years ago)
Author:
ming
Message:

Fixed #1174, #1191, #1192

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/src/pjmedia-audiodev/coreaudio_dev.c

    r3386 r3398  
    3232 
    3333#include <AudioUnit/AudioUnit.h> 
     34#include <AudioToolbox/AudioConverter.h> 
    3435#if !COREAUDIO_MAC 
    3536    #include <AudioToolbox/AudioServices.h> 
     
    6566}; 
    6667 
     68/* linked list of streams */ 
     69struct stream_list 
     70{ 
     71    PJ_DECL_LIST_MEMBER(struct stream_list); 
     72    struct coreaudio_stream     *stream; 
     73}; 
     74 
    6775/* coreaudio factory */ 
    6876struct coreaudio_factory 
    6977{ 
    7078    pjmedia_aud_dev_factory      base; 
     79    pj_pool_t                   *base_pool; 
    7180    pj_pool_t                   *pool; 
    7281    pj_pool_factory             *pf; 
     82    pj_mutex_t                  *mutex; 
    7383 
    7484    unsigned                     dev_count; 
     
    7686 
    7787    AudioComponent               io_comp; 
    78     struct coreaudio_stream     *stream; 
     88    struct stream_list           streams; 
    7989}; 
    8090 
     
    8292struct coreaudio_stream 
    8393{ 
    84     pjmedia_aud_stream          base;            /**< Base stream         */ 
    85     pjmedia_aud_param           param;           /**< Settings            */ 
    86     pj_pool_t                   *pool;           /**< Memory pool.        */ 
     94    pjmedia_aud_stream           base;           /**< Base stream         */ 
     95    pjmedia_aud_param            param;          /**< Settings            */ 
     96    pj_pool_t                   *pool;           /**< Memory pool.        */ 
    8797    struct coreaudio_factory    *cf; 
    88  
    89     pjmedia_aud_rec_cb          rec_cb;          /**< Capture callback.   */ 
    90     pjmedia_aud_play_cb         play_cb;         /**< Playback callback.  */ 
     98    struct stream_list           list_entry; 
     99 
     100    pjmedia_aud_rec_cb           rec_cb;          /**< Capture callback.   */ 
     101    pjmedia_aud_play_cb          play_cb;         /**< Playback callback.  */ 
    91102    void                        *user_data;      /**< Application data.   */ 
    92103 
    93     pj_timestamp                play_timestamp; 
    94     pj_timestamp                rec_timestamp; 
     104    pj_timestamp                 play_timestamp; 
     105    pj_timestamp                 rec_timestamp; 
    95106 
    96107    pj_int16_t                  *rec_buf; 
    97     unsigned                    rec_buf_count; 
     108    unsigned                     rec_buf_count; 
    98109    pj_int16_t                  *play_buf; 
    99     unsigned                    play_buf_count; 
    100  
    101     pj_bool_t                   interrupted; 
    102     pj_bool_t                   quit_flag; 
    103  
    104     pj_bool_t                   rec_thread_exited; 
    105     pj_bool_t                   rec_thread_initialized; 
    106     pj_thread_desc              rec_thread_desc; 
     110    unsigned                     play_buf_count; 
     111 
     112    pj_bool_t                    interrupted; 
     113    pj_bool_t                    quit_flag; 
     114    pj_bool_t                    running; 
     115 
     116    pj_bool_t                    rec_thread_initialized; 
     117    pj_thread_desc               rec_thread_desc; 
    107118    pj_thread_t                 *rec_thread; 
    108119 
    109     pj_bool_t                   play_thread_exited; 
    110     pj_bool_t                   play_thread_initialized; 
    111     pj_thread_desc              play_thread_desc; 
     120    pj_bool_t                    play_thread_initialized; 
     121    pj_thread_desc               play_thread_desc; 
    112122    pj_thread_t                 *play_thread; 
    113123 
    114     AudioUnit                   io_units[2]; 
    115     AudioStreamBasicDescription streamFormat; 
     124    AudioUnit                    io_units[2]; 
     125    AudioStreamBasicDescription  streamFormat; 
    116126    AudioBufferList             *audio_buf; 
     127 
     128    AudioConverterRef            resample; 
     129    pj_int16_t                  *resample_buf; 
     130    unsigned                     resample_buf_count; 
     131    unsigned                     resample_buf_size; 
    117132}; 
    118133 
     
    121136static pj_status_t ca_factory_init(pjmedia_aud_dev_factory *f); 
    122137static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f); 
     138static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f); 
    123139static unsigned    ca_factory_get_dev_count(pjmedia_aud_dev_factory *f); 
    124140static pj_status_t ca_factory_get_dev_info(pjmedia_aud_dev_factory *f, 
     
    153169#if !COREAUDIO_MAC 
    154170static void interruptionListener(void *inClientData, UInt32 inInterruption); 
     171static void propListener(void *                 inClientData, 
     172                         AudioSessionPropertyID inID, 
     173                         UInt32                 inDataSize, 
     174                         const void *           inData); 
    155175#endif 
    156176 
     
    188208    pj_pool_t *pool; 
    189209 
    190     pool = pj_pool_create(pf, "core audio", 1000, 1000, NULL); 
     210    pool = pj_pool_create(pf, "core audio base", 1000, 1000, NULL); 
    191211    f = PJ_POOL_ZALLOC_T(pool, struct coreaudio_factory); 
    192212    f->pf = pf; 
    193     f->pool = pool; 
     213    f->base_pool = pool; 
    194214    f->base.op = &factory_op; 
    195215 
     
    202222{ 
    203223    struct coreaudio_factory *cf = (struct coreaudio_factory*)f; 
     224    AudioComponentDescription desc; 
     225    pj_status_t status; 
     226#if !COREAUDIO_MAC 
    204227    unsigned i; 
    205     AudioComponentDescription desc; 
     228    OSStatus ostatus; 
     229    UInt32 audioCategory; 
     230#endif 
     231 
     232    pj_list_init(&cf->streams); 
     233    status = pj_mutex_create_recursive(cf->base_pool, 
     234                                       "coreaudio", 
     235                                       &cf->mutex); 
     236    if (status != PJ_SUCCESS) 
     237        return status; 
     238 
     239    desc.componentType = kAudioUnitType_Output; 
    206240#if COREAUDIO_MAC 
     241    desc.componentSubType = kAudioUnitSubType_HALOutput; 
     242#else 
     243    desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO; 
     244#endif 
     245    desc.componentManufacturer = kAudioUnitManufacturer_Apple; 
     246    desc.componentFlags = 0; 
     247    desc.componentFlagsMask = 0; 
     248 
     249    cf->io_comp = AudioComponentFindNext(NULL, &desc); 
     250    if (cf->io_comp == NULL) 
     251        return PJMEDIA_EAUD_INIT; // cannot find IO unit; 
     252 
     253    status = ca_factory_refresh(f); 
     254    if (status != PJ_SUCCESS) 
     255        return status; 
     256 
     257#if !COREAUDIO_MAC 
     258    cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL); 
     259    cf->dev_count = 1; 
     260    cf->dev_info = (struct coreaudio_dev_info*) 
     261                   pj_pool_calloc(cf->pool, cf->dev_count, 
     262                   sizeof(struct coreaudio_dev_info)); 
     263    for (i = 0; i < cf->dev_count; i++) { 
     264        struct coreaudio_dev_info *cdi; 
     265 
     266        cdi = &cf->dev_info[i]; 
     267        pj_bzero(cdi, sizeof(*cdi)); 
     268        cdi->dev_id = 0; 
     269        strcpy(cdi->info.name, "iPhone IO device"); 
     270        strcpy(cdi->info.driver, "apple"); 
     271        cdi->info.input_count = 1; 
     272        cdi->info.output_count = 1; 
     273        cdi->info.default_samples_per_sec = 8000; 
     274 
     275        /* Set the device capabilities here */ 
     276        cdi->info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | 
     277                         PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY | 
     278                         PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING | 
     279                         PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE | 
     280                         PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE | 
     281                         PJMEDIA_AUD_DEV_CAP_EC; 
     282        cdi->info.routes = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER | 
     283                           PJMEDIA_AUD_DEV_ROUTE_EARPIECE | 
     284                           PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH; 
     285 
     286        PJ_LOG(4, (THIS_FILE, " dev_id %d: %s  (in=%d, out=%d) %dHz", 
     287                   i, 
     288                   cdi->info.name, 
     289                   cdi->info.input_count, 
     290                   cdi->info.output_count, 
     291                   cdi->info.default_samples_per_sec)); 
     292    } 
     293 
     294    /* Initialize the Audio Session */ 
     295    ostatus = AudioSessionInitialize(NULL, NULL, interruptionListener, cf); 
     296    if (ostatus != kAudioSessionNoError) { 
     297        PJ_LOG(4, (THIS_FILE, 
     298                   "Error: cannot initialize audio session services (%i)", 
     299                   ostatus)); 
     300        return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 
     301    } 
     302 
     303    /* We want to be able to open playback and recording streams */ 
     304    audioCategory = kAudioSessionCategory_PlayAndRecord; 
     305    ostatus = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, 
     306                                      sizeof(audioCategory), 
     307                                      &audioCategory); 
     308    if (ostatus != kAudioSessionNoError) { 
     309        PJ_LOG(4, (THIS_FILE, 
     310                   "Error: cannot set the audio session category (%i)", 
     311                   ostatus)); 
     312        return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 
     313    } 
     314 
     315    /* Listen for audio routing change notifications */ 
     316    ostatus = AudioSessionAddPropertyListener( 
     317                  kAudioSessionProperty_AudioRouteChange, 
     318                  propListener, cf); 
     319    if (ostatus != kAudioSessionNoError) { 
     320        PJ_LOG(4, (THIS_FILE, 
     321                   "Error: cannot listen for audio route change " 
     322                   "notifications (%i)", ostatus)); 
     323        return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 
     324    } 
     325#endif 
     326 
     327    PJ_LOG(4, (THIS_FILE, "core audio initialized")); 
     328 
     329    return PJ_SUCCESS; 
     330} 
     331 
     332/* API: destroy factory */ 
     333static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f) 
     334{ 
     335    struct coreaudio_factory *cf = (struct coreaudio_factory*)f; 
     336    pj_pool_t *pool; 
     337 
     338    pj_assert(cf); 
     339    pj_assert(cf->base_pool); 
     340    pj_assert(pj_list_empty(&cf->streams)); 
     341 
     342#if !COREAUDIO_MAC 
     343    AudioSessionRemovePropertyListenerWithUserData( 
     344        kAudioSessionProperty_AudioRouteChange, propListener, cf); 
     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_destroy(cf->mutex); 
     354        cf->mutex = NULL; 
     355    } 
     356 
     357    pool = cf->base_pool; 
     358    cf->base_pool = NULL; 
     359    pj_pool_release(pool); 
     360 
     361    return PJ_SUCCESS; 
     362} 
     363 
     364/* API: refresh the device list */ 
     365static pj_status_t ca_factory_refresh(pjmedia_aud_dev_factory *f) 
     366{ 
     367#if !COREAUDIO_MAC 
     368    /* iPhone doesn't support refreshing the device list */ 
     369    PJ_UNUSED_ARG(f); 
     370    return PJ_SUCCESS; 
     371#else 
     372    struct coreaudio_factory *cf = (struct coreaudio_factory*)f; 
     373    unsigned i; 
    207374    unsigned dev_count; 
    208375    AudioObjectPropertyAddress addr; 
     
    211378    AudioBufferList *buf = NULL; 
    212379    OSStatus ostatus; 
    213 #endif 
    214  
    215     desc.componentType = kAudioUnitType_Output; 
    216 #if COREAUDIO_MAC 
    217     desc.componentSubType = kAudioUnitSubType_HALOutput; 
    218 #else 
    219     desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO; 
    220 #endif 
    221     desc.componentManufacturer = kAudioUnitManufacturer_Apple; 
    222     desc.componentFlags = 0; 
    223     desc.componentFlagsMask = 0; 
    224  
    225     cf->io_comp = AudioComponentFindNext(NULL, &desc); 
    226     if (cf->io_comp == NULL) 
    227         return PJMEDIA_EAUD_INIT; // cannot find IO unit; 
    228  
    229     cf->stream = NULL; 
    230  
    231 #if COREAUDIO_MAC 
     380 
     381    if (cf->pool != NULL) { 
     382        pj_pool_release(cf->pool); 
     383        cf->pool = NULL; 
     384    } 
     385 
     386    cf->dev_count = 0; 
     387    cf->pool = pj_pool_create(cf->pf, "core audio", 1000, 1000, NULL); 
     388 
    232389    /* Find out how many audio devices there are */ 
    233390    addr.mSelector = kAudioHardwarePropertyDevices; 
     
    254411        return PJ_SUCCESS; 
    255412    } 
    256     PJ_LOG(4, (THIS_FILE, "core audio initialized with %d devices", 
    257            dev_count)); 
     413    PJ_LOG(4, (THIS_FILE, "core audio detected %d devices", 
     414               dev_count)); 
    258415 
    259416    /* Get all the audio device IDs */ 
     
    390547               cdi->info.default_samples_per_sec)); 
    391548    } 
    392 #else 
    393     cf->dev_count = 1; 
    394     cf->dev_info = (struct coreaudio_dev_info*) 
    395                    pj_pool_calloc(cf->pool, cf->dev_count, 
    396                    sizeof(struct coreaudio_dev_info)); 
    397     for (i = 0; i < cf->dev_count; i++) { 
    398         struct coreaudio_dev_info *cdi; 
    399  
    400         cdi = &cf->dev_info[i]; 
    401         pj_bzero(cdi, sizeof(*cdi)); 
    402         cdi->dev_id = 0; 
    403         strcpy(cdi->info.name, "iPhone IO device"); 
    404         strcpy(cdi->info.driver, "apple"); 
    405         cdi->info.input_count = 1; 
    406         cdi->info.output_count = 1; 
    407         cdi->info.default_samples_per_sec = 8000; 
    408  
    409         /* Set the device capabilities here */ 
    410         cdi->info.caps = PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY | 
    411                          PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY | 
    412                          PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING | 
    413                          PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE | 
    414                          PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE | 
    415                          PJMEDIA_AUD_DEV_CAP_EC; 
    416         cdi->info.routes = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER | 
    417                            PJMEDIA_AUD_DEV_ROUTE_EARPIECE | 
    418                            PJMEDIA_AUD_DEV_ROUTE_BLUETOOTH; 
    419     } 
    420  
    421     if (AudioSessionInitialize(NULL, NULL, interruptionListener, cf) != 
    422         kAudioSessionNoError) 
    423     { 
    424         PJ_LOG(4, (THIS_FILE, 
    425                "Warning: cannot initialize audio session services")); 
    426     } 
    427  
    428     PJ_LOG(4, (THIS_FILE, "core audio initialized")); 
    429  
    430 #endif 
    431549 
    432550    return PJ_SUCCESS; 
    433 } 
    434  
    435 /* API: destroy factory */ 
    436 static pj_status_t ca_factory_destroy(pjmedia_aud_dev_factory *f) 
    437 { 
    438     struct coreaudio_factory *cf = (struct coreaudio_factory*)f; 
    439     pj_pool_t *pool = cf->pool; 
    440  
    441     cf->pool = NULL; 
    442     pj_pool_release(pool); 
    443  
    444     return PJ_SUCCESS; 
     551#endif 
    445552} 
    446553 
     
    508615} 
    509616 
    510 static OSStatus input_callback(void                       *inRefCon, 
    511                                AudioUnitRenderActionFlags *ioActionFlags, 
    512                                const AudioTimeStamp       *inTimeStamp, 
    513                                UInt32                      inBusNumber, 
    514                                UInt32                      inNumberFrames, 
    515                                AudioBufferList            *ioData) 
     617OSStatus resampleProc(AudioConverterRef             inAudioConverter, 
     618                      UInt32                        *ioNumberDataPackets, 
     619                      AudioBufferList               *ioData, 
     620                      AudioStreamPacketDescription  **outDataPacketDescription, 
     621                      void                          *inUserData) 
     622{ 
     623    struct coreaudio_stream *strm = (struct coreaudio_stream*)inUserData; 
     624 
     625    pj_assert(*ioNumberDataPackets == strm->resample_buf_count); 
     626 
     627    ioData->mNumberBuffers = 1; 
     628    ioData->mBuffers[0].mNumberChannels = 1; 
     629    ioData->mBuffers[0].mData = strm->resample_buf; 
     630    ioData->mBuffers[0].mDataByteSize = strm->resample_buf_count * 
     631                                        strm->param.bits_per_sample >> 3; 
     632    return 0; 
     633} 
     634 
     635static OSStatus resample_callback(void                       *inRefCon, 
     636                                  AudioUnitRenderActionFlags *ioActionFlags, 
     637                                  const AudioTimeStamp       *inTimeStamp, 
     638                                  UInt32                      inBusNumber, 
     639                                  UInt32                      inNumberFrames, 
     640                                  AudioBufferList            *ioData) 
    516641{ 
    517642    struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon; 
     
    521646    AudioBufferList *buf = strm->audio_buf; 
    522647    pj_int16_t *input; 
    523  
    524    if (strm->quit_flag) 
    525         goto on_break; 
     648    UInt32 resampleSize; 
     649    UInt32 size; 
     650 
     651    pj_assert(!strm->quit_flag); 
    526652 
    527653    /* Known cases of callback's thread: 
     
    537663                                    &strm->rec_thread); 
    538664        strm->rec_thread_initialized = 1; 
    539         PJ_LOG(5,(THIS_FILE, "Recorder thread started")); 
     665        PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)",  
     666                  inNumberFrames)); 
    540667    } 
    541668 
     
    557684    input = (pj_int16_t *)buf->mBuffers[0].mData; 
    558685 
     686    /* Calculate how many frames we need to fill an entire packet */ 
     687    resampleSize = strm->param.samples_per_frame * 
     688                   strm->param.bits_per_sample >> 3; 
     689    size = sizeof(resampleSize); 
     690    ostatus = AudioConverterGetProperty( 
     691                  strm->resample, 
     692                  kAudioConverterPropertyCalculateInputBufferSize, 
     693                  &size, 
     694                  &resampleSize); 
     695    if (ostatus != noErr) { 
     696        PJ_LOG(5, (THIS_FILE, "Core audio converter measure error %i", 
     697                   ostatus)); 
     698        goto on_break; 
     699    } 
     700    resampleSize /= strm->param.bits_per_sample >> 3; 
     701 
     702    nsamples = inNumberFrames * strm->param.channel_count + 
     703               strm->resample_buf_count; 
     704    pj_assert(nsamples < strm->resample_buf_size); 
     705 
     706    if (nsamples >= resampleSize) { 
     707        UInt32 resampleOutput = strm->param.samples_per_frame; 
     708        unsigned chunk_count = 0; 
     709        pjmedia_frame frame; 
     710        AudioBufferList ab; 
     711 
     712        frame.type = PJMEDIA_FRAME_TYPE_AUDIO; 
     713        frame.size = strm->param.samples_per_frame * 
     714                     strm->param.bits_per_sample >> 3; 
     715        frame.bit_info = 0; 
     716 
     717        /* If buffer is not empty, combine the buffer with the just incoming 
     718         * samples, then call put_frame. 
     719         */ 
     720 
     721        chunk_count = resampleSize - strm->resample_buf_count; 
     722        pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count, 
     723                             input, chunk_count); 
     724        strm->resample_buf_count += chunk_count; 
     725 
     726        frame.buf = (void*) strm->rec_buf; 
     727        frame.timestamp.u64 = strm->rec_timestamp.u64; 
     728 
     729        ab.mNumberBuffers = 1; 
     730        ab.mBuffers[0].mNumberChannels = 1; 
     731        ab.mBuffers[0].mDataByteSize = frame.size; 
     732        ab.mBuffers[0].mData = frame.buf; 
     733 
     734        /* Do the resample */ 
     735        ostatus = AudioConverterFillComplexBuffer(strm->resample, 
     736                                                  resampleProc, 
     737                                                  strm, 
     738                                                  &resampleOutput, 
     739                                                  &ab, 
     740                                                  NULL); 
     741        if (ostatus != noErr) { 
     742            goto on_break; 
     743        } 
     744 
     745        status = (*strm->rec_cb)(strm->user_data, &frame); 
     746 
     747        input = input + chunk_count; 
     748        nsamples -= resampleSize; 
     749        strm->resample_buf_count = 0; 
     750        strm->rec_timestamp.u64 += strm->param.samples_per_frame / 
     751                                   strm->param.channel_count; 
     752 
     753        pj_assert(nsamples < resampleSize); 
     754 
     755        /* Store the remaining samples into the buffer */ 
     756        if (nsamples && status == 0) { 
     757            strm->resample_buf_count = nsamples; 
     758            pjmedia_copy_samples(strm->resample_buf, input, 
     759                                 nsamples); 
     760        } 
     761 
     762    } else { 
     763        /* Not enough samples, let's just store them in the buffer */ 
     764        pjmedia_copy_samples(strm->resample_buf + strm->resample_buf_count, 
     765                             input, 
     766                             inNumberFrames * strm->param.channel_count); 
     767        strm->resample_buf_count += inNumberFrames * 
     768                                    strm->param.channel_count; 
     769    } 
     770 
     771    return noErr; 
     772 
     773on_break: 
     774    return -1; 
     775} 
     776 
     777static OSStatus input_callback(void                       *inRefCon, 
     778                               AudioUnitRenderActionFlags *ioActionFlags, 
     779                               const AudioTimeStamp       *inTimeStamp, 
     780                               UInt32                      inBusNumber, 
     781                               UInt32                      inNumberFrames, 
     782                               AudioBufferList            *ioData) 
     783{ 
     784    struct coreaudio_stream *strm = (struct coreaudio_stream*)inRefCon; 
     785    OSStatus ostatus; 
     786    pj_status_t status = 0; 
     787    unsigned nsamples; 
     788    AudioBufferList *buf = strm->audio_buf; 
     789    pj_int16_t *input; 
     790 
     791    pj_assert(!strm->quit_flag); 
     792 
     793    /* Known cases of callback's thread: 
     794     * - The thread may be changed in the middle of a session 
     795     *   it happens when plugging/unplugging headphone. 
     796     * - The same thread may be reused in consecutive sessions. The first 
     797     *   session will leave TLS set, but release the TLS data address, 
     798     *   so the second session must re-register the callback's thread. 
     799     */ 
     800    if (strm->rec_thread_initialized == 0 || !pj_thread_is_registered()) 
     801    { 
     802        status = pj_thread_register("ca_rec", strm->rec_thread_desc, 
     803                                    &strm->rec_thread); 
     804        strm->rec_thread_initialized = 1; 
     805        PJ_LOG(5,(THIS_FILE, "Recorder thread started, (%i frames)", 
     806                  inNumberFrames)); 
     807    } 
     808 
     809    buf->mBuffers[0].mData = NULL; 
     810    buf->mBuffers[0].mDataByteSize = inNumberFrames * 
     811                                     strm->streamFormat.mChannelsPerFrame; 
     812    /* Render the unit to get input data */ 
     813    ostatus = AudioUnitRender(strm->io_units[0], 
     814                              ioActionFlags, 
     815                              inTimeStamp, 
     816                              inBusNumber, 
     817                              inNumberFrames, 
     818                              buf); 
     819 
     820    if (ostatus != noErr) { 
     821        PJ_LOG(5, (THIS_FILE, "Core audio unit render error %i", ostatus)); 
     822        goto on_break; 
     823    } 
     824    input = (pj_int16_t *)buf->mBuffers[0].mData; 
     825 
    559826    /* Calculate number of samples we've got */ 
    560     nsamples = inNumberFrames * strm->param.channel_count + strm->rec_buf_count; 
     827    nsamples = inNumberFrames * strm->param.channel_count + 
     828               strm->rec_buf_count; 
    561829    if (nsamples >= strm->param.samples_per_frame) 
    562830     { 
     
    620888    return noErr; 
    621889 
    622     on_break: 
    623         strm->rec_thread_exited = 1; 
    624         return -1; 
     890on_break: 
     891    return -1; 
    625892} 
    626893 
     
    637904    pj_int16_t *output = ioData->mBuffers[0].mData; 
    638905 
    639     if (stream->quit_flag) 
    640         goto on_break; 
     906    pj_assert(!stream->quit_flag); 
    641907 
    642908    /* Known cases of callback's thread: 
     
    652918                                    &stream->play_thread); 
    653919        stream->play_thread_initialized = 1; 
    654         PJ_LOG(5,(THIS_FILE, "Player thread started")); 
     920        PJ_LOG(5,(THIS_FILE, "Player thread started, (%i frames)", inNumberFrames)); 
    655921    } 
    656922 
     
    725991    return noErr; 
    726992 
    727     on_break: 
    728         stream->play_thread_exited = 1; 
    729         return -1; 
     993on_break: 
     994    return -1; 
    730995} 
    731996 
     
    7361001                         const void *           inData) 
    7371002{ 
    738     struct coreaudio_stream *strm = (struct coreaudio_stream*)inClientData; 
    739  
    740     if (inID == kAudioSessionProperty_AudioRouteChange) { 
    741  
    742         PJ_LOG(3, (THIS_FILE, "audio route changed")); 
    743         if (strm->interrupted) 
    744             return; 
    745  
    746         ca_stream_stop((pjmedia_aud_stream *)strm); 
    747         AudioUnitUninitialize(strm->io_units[0]); 
    748         AudioComponentInstanceDispose(strm->io_units[0]); 
    749  
    750         if (create_audio_unit(strm->cf->io_comp, 0, 
    751                               strm->param.dir, strm, 
    752                               &strm->io_units[0]) != PJ_SUCCESS) 
    753         { 
     1003    struct coreaudio_factory *cf = (struct coreaudio_factory*)inClientData; 
     1004    struct stream_list *it, *itBegin; 
     1005    pj_status_t status; 
     1006    OSStatus ostatus; 
     1007    CFDictionaryRef routeDictionary; 
     1008    CFNumberRef reason; 
     1009    SInt32 reasonVal; 
     1010    pj_assert(cf); 
     1011 
     1012    if (inID != kAudioSessionProperty_AudioRouteChange) 
     1013        return; 
     1014 
     1015    routeDictionary = (CFDictionaryRef)inData; 
     1016    reason = (CFNumberRef) 
     1017             CFDictionaryGetValue( 
     1018                 routeDictionary,  
     1019                 CFSTR(kAudioSession_AudioRouteChangeKey_Reason)); 
     1020    CFNumberGetValue(reason, kCFNumberSInt32Type, &reasonVal); 
     1021 
     1022    if (reasonVal == kAudioSessionRouteChangeReason_CategoryChange) { 
     1023        PJ_LOG(3, (THIS_FILE, "audio route changed due to category change, " 
     1024                   "ignoring...")); 
     1025        return; 
     1026    } 
     1027    if (reasonVal == kAudioSessionRouteChangeReason_Override) { 
     1028        PJ_LOG(3, (THIS_FILE, "audio route changed due to user override, " 
     1029                   "ignoring...")); 
     1030        return; 
     1031    } 
     1032 
     1033    PJ_LOG(3, (THIS_FILE, "audio route changed")); 
     1034 
     1035    pj_mutex_lock(cf->mutex); 
     1036    itBegin = &cf->streams; 
     1037    for (it = itBegin->next; it != itBegin; it = it->next) { 
     1038        pj_bool_t running = it->stream->running; 
     1039 
     1040        if (it->stream->interrupted) 
     1041            continue; 
     1042 
     1043        status = ca_stream_stop((pjmedia_aud_stream *)it->stream); 
     1044 
     1045        ostatus = AudioUnitUninitialize(it->stream->io_units[0]); 
     1046        ostatus = AudioComponentInstanceDispose(it->stream->io_units[0]); 
     1047 
     1048        status = create_audio_unit(it->stream->cf->io_comp, 0, 
     1049                                   it->stream->param.dir, 
     1050                                   it->stream, &it->stream->io_units[0]); 
     1051        if (status != PJ_SUCCESS) { 
    7541052            PJ_LOG(3, (THIS_FILE, 
    755                    "Error: failed to create a new instance of audio unit")); 
    756             return; 
    757         } 
    758         if (ca_stream_start((pjmedia_aud_stream *)strm) != PJ_SUCCESS) { 
     1053                       "Error: failed to create a replacement audio unit (%i)", 
     1054                       status)); 
     1055            continue; 
     1056        } 
     1057 
     1058        if (running) { 
     1059            status = ca_stream_start((pjmedia_aud_stream *)it->stream); 
     1060        } 
     1061        if (status != PJ_SUCCESS) { 
    7591062            PJ_LOG(3, (THIS_FILE, 
    760                    "Error: failed to restart audio unit")); 
     1063                       "Error: failed to restart the audio unit (%i)", 
     1064                       status)); 
     1065            continue; 
    7611066        } 
    7621067        PJ_LOG(3, (THIS_FILE, "core audio unit successfully reinstantiated")); 
    7631068    } 
     1069    pj_mutex_unlock(cf->mutex); 
    7641070} 
    7651071 
    7661072static void interruptionListener(void *inClientData, UInt32 inInterruption) 
    7671073{ 
    768     struct coreaudio_stream *strm = ((struct coreaudio_factory*)inClientData)-> 
    769                                     stream; 
    770     if (!strm) 
    771         return; 
     1074    struct coreaudio_factory *cf = (struct coreaudio_factory*)inClientData; 
     1075    struct stream_list *it, *itBegin; 
     1076    pj_status_t status; 
     1077    OSStatus ostatus; 
     1078    pj_assert(cf); 
    7721079 
    7731080    PJ_LOG(3, (THIS_FILE, "Session interrupted! --- %s ---", 
     
    7751082           "Begin Interruption" : "End Interruption")); 
    7761083 
    777     if (inInterruption == kAudioSessionEndInterruption) { 
    778         strm->interrupted = PJ_FALSE; 
    779         /* There may be an audio route change during the interruption 
    780          * (such as when the alarm rings), so we have to notify the 
    781          * listener as well. 
    782          */ 
    783         propListener(strm, kAudioSessionProperty_AudioRouteChange, 
    784                      0, NULL); 
    785     } else if (inInterruption == kAudioSessionBeginInterruption) { 
    786         strm->interrupted = PJ_TRUE; 
    787         AudioOutputUnitStop(strm->io_units[0]); 
    788     } 
    789 } 
    790  
     1084    pj_mutex_lock(cf->mutex); 
     1085    itBegin = &cf->streams; 
     1086    for (it = itBegin->next; it != itBegin; it = it->next) { 
     1087        if (!it->stream->running) 
     1088            continue; 
     1089 
     1090        if (inInterruption == kAudioSessionEndInterruption && 
     1091            it->stream->interrupted == PJ_TRUE) 
     1092        { 
     1093            ostatus = AudioUnitUninitialize(it->stream->io_units[0]); 
     1094            ostatus = AudioComponentInstanceDispose(it->stream->io_units[0]); 
     1095 
     1096            status = create_audio_unit(it->stream->cf->io_comp, 0, 
     1097                                       it->stream->param.dir, 
     1098                                       it->stream, &it->stream->io_units[0]); 
     1099            if (status != PJ_SUCCESS) { 
     1100                PJ_LOG(3, (THIS_FILE, 
     1101                           "Error: failed to create a replacement " 
     1102                           "audio unit (%i)", ostatus)); 
     1103                continue; 
     1104            } 
     1105 
     1106            status = ca_stream_start((pjmedia_aud_stream*)it->stream); 
     1107            if (status != PJ_SUCCESS) { 
     1108                PJ_LOG(3, (THIS_FILE, 
     1109                           "Error: failed to restart the audio unit (%i)", 
     1110                           ostatus)); 
     1111                       continue; 
     1112            } 
     1113            PJ_LOG(3, (THIS_FILE, "core audio unit successfully " 
     1114                       "reinstantiated")); 
     1115        } else if (inInterruption == kAudioSessionBeginInterruption && 
     1116                   it->stream->running == PJ_TRUE) 
     1117        { 
     1118            status = ca_stream_stop((pjmedia_aud_stream*)it->stream); 
     1119            it->stream->interrupted = PJ_TRUE; 
     1120        } 
     1121    } 
     1122    pj_mutex_unlock(cf->mutex); 
     1123} 
     1124 
     1125#endif 
     1126 
     1127#if COREAUDIO_MAC 
     1128/* Internal: create audio converter for resampling the recorder device */ 
     1129static pj_status_t create_audio_resample(struct coreaudio_stream     *strm, 
     1130                                         AudioStreamBasicDescription *desc) 
     1131{ 
     1132    OSStatus ostatus; 
     1133 
     1134    pj_assert(strm->streamFormat.mSampleRate != desc->mSampleRate); 
     1135    pj_assert(NULL == strm->resample); 
     1136    pj_assert(NULL == strm->resample_buf); 
     1137 
     1138    /* Create the audio converter */ 
     1139    ostatus = AudioConverterNew(desc, &strm->streamFormat, &strm->resample); 
     1140    if (ostatus != noErr) { 
     1141        return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 
     1142    } 
     1143 
     1144    /* 
     1145     * Allocate the buffer required to hold the enough input data 
     1146     * for two complete frames of audio. 
     1147     */ 
     1148    strm->resample_buf_size = (unsigned)(desc->mSampleRate * 
     1149                              strm->param.samples_per_frame / 
     1150                              strm->param.clock_rate * 2); 
     1151    strm->resample_buf = (pj_int16_t*) 
     1152                         pj_pool_alloc(strm->pool, 
     1153                                       strm->resample_buf_size * 
     1154                                       strm->param.bits_per_sample >> 3); 
     1155    if (!strm->resample_buf) 
     1156        return PJ_ENOMEM; 
     1157    strm->resample_buf_count = 0; 
     1158 
     1159    return PJ_SUCCESS; 
     1160} 
    7911161#endif 
    7921162 
     
    7991169{ 
    8001170    OSStatus ostatus; 
    801 #if !COREAUDIO_MAC 
    802     UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord; 
    803     if (!(dir & PJMEDIA_DIR_CAPTURE)) { 
    804         audioCategory = kAudioSessionCategory_MediaPlayback; 
    805     } else if (!(dir & PJMEDIA_DIR_PLAYBACK)) { 
    806         audioCategory = kAudioSessionCategory_RecordAudio; 
    807     } 
    808     AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, 
    809                             sizeof(audioCategory), &audioCategory); 
    810 #endif 
    8111171 
    8121172    /* Create an audio unit to interface with the device */ 
     
    8861246        AudioStreamBasicDescription deviceFormat; 
    8871247        UInt32 size; 
     1248 
     1249        /* 
     1250         * Keep the sample rate from the device, otherwise we will confuse 
     1251         * AUHAL 
     1252         */ 
     1253        size = sizeof(AudioStreamBasicDescription); 
     1254        ostatus = AudioUnitGetProperty(*io_unit, 
     1255                                       kAudioUnitProperty_StreamFormat, 
     1256                                       kAudioUnitScope_Input, 
     1257                                       1, 
     1258                                       &deviceFormat, 
     1259                                       &size); 
     1260        if (ostatus != noErr) { 
     1261            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 
     1262        } 
     1263        strm->streamFormat.mSampleRate = deviceFormat.mSampleRate; 
    8881264#endif 
    8891265 
     
    9031279 
    9041280#if COREAUDIO_MAC 
     1281        strm->streamFormat.mSampleRate = strm->param.clock_rate; 
    9051282        size = sizeof(AudioStreamBasicDescription); 
    9061283        ostatus = AudioUnitGetProperty (*io_unit, 
    9071284                                        kAudioUnitProperty_StreamFormat, 
    908                                         kAudioUnitScope_Input, 
     1285                                        kAudioUnitScope_Output, 
    9091286                                        1, 
    9101287                                        &deviceFormat, 
     
    9121289        if (ostatus == noErr) { 
    9131290            if (strm->streamFormat.mSampleRate != deviceFormat.mSampleRate) { 
    914                 return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 
     1291                pj_status_t rc = create_audio_resample(strm, &deviceFormat); 
     1292                if (PJ_SUCCESS != rc) 
     1293                    return rc; 
    9151294            } 
    9161295        } else { 
     
    9661345 
    9671346        /* Set input callback */ 
    968         input_cb.inputProc = input_callback; 
     1347        input_cb.inputProc = strm->resample ? resample_callback : 
     1348                             input_callback; 
    9691349        input_cb.inputProcRefCon = strm; 
    970         ostatus = AudioUnitSetProperty(*io_unit, 
    971                                        kAudioOutputUnitProperty_SetInputCallback, 
    972                                        kAudioUnitScope_Global, 
    973                                        0, 
    974                                        &input_cb, 
    975                                        sizeof(input_cb)); 
     1350        ostatus = AudioUnitSetProperty( 
     1351                      *io_unit, 
     1352                      kAudioOutputUnitProperty_SetInputCallback, 
     1353                      kAudioUnitScope_Global, 
     1354                      0, 
     1355                      &input_cb, 
     1356                      sizeof(input_cb)); 
    9761357        if (ostatus != noErr) { 
    9771358            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 
     
    10581439 
    10591440    strm = PJ_POOL_ZALLOC_T(pool, struct coreaudio_stream); 
    1060     cf->stream = strm; 
     1441    pj_list_init(&strm->list_entry); 
     1442    strm->list_entry.stream = strm; 
    10611443    strm->cf = cf; 
    10621444    pj_memcpy(&strm->param, param, sizeof(*param)); 
     
    11461528                          &param->output_vol); 
    11471529    } 
     1530 
     1531    pj_mutex_lock(strm->cf->mutex); 
     1532    pj_assert(pj_list_empty(&strm->list_entry)); 
     1533    pj_list_insert_after(&strm->cf->streams, &strm->list_entry); 
     1534    pj_mutex_unlock(strm->cf->mutex); 
    11481535 
    11491536    /* Done */ 
     
    12521639            &size, &latency2) == kAudioSessionNoError)) 
    12531640        { 
    1254             strm->param.input_latency_ms = (unsigned)((latency + latency2) * 1000); 
     1641            strm->param.input_latency_ms = (unsigned) 
     1642                                           ((latency + latency2) * 1000); 
    12551643            strm->param.input_latency_ms++; 
    12561644        } 
     
    12991687            &size, &latency2) == kAudioSessionNoError)) 
    13001688        { 
    1301             strm->param.output_latency_ms = (unsigned)((latency + latency2) * 1000); 
     1689            strm->param.output_latency_ms = (unsigned) 
     1690                                            ((latency + latency2) * 1000); 
    13021691            strm->param.output_latency_ms++; 
    13031692        } 
     
    14011790    struct coreaudio_stream *strm = (struct coreaudio_stream*)s; 
    14021791 
    1403     PJ_UNUSED_ARG(strm); 
    1404  
    14051792    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL); 
    14061793 
     
    14271814        return PJ_SUCCESS; 
    14281815    } 
    1429 #endif 
    1430  
    1431 #if !COREAUDIO_MAC 
    1432     if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE && 
     1816 
     1817#else 
     1818 
     1819    if ((cap==PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY && 
     1820         (strm->param.dir & PJMEDIA_DIR_CAPTURE)) || 
     1821        (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && 
     1822         (strm->param.dir & PJMEDIA_DIR_PLAYBACK))) 
     1823    { 
     1824        Float32 bufferDuration = *(unsigned *)pval; 
     1825        OSStatus ostatus; 
     1826        unsigned latency; 
     1827         
     1828        /* For low-latency audio streaming, you can set this value to 
     1829         * as low as 5 ms (the default is 23ms). However, lowering the 
     1830         * latency may cause a decrease in audio quality. 
     1831         */ 
     1832        bufferDuration /= 1000; 
     1833        ostatus = AudioSessionSetProperty( 
     1834                      kAudioSessionProperty_PreferredHardwareIOBufferDuration, 
     1835                      sizeof(bufferDuration), &bufferDuration); 
     1836        if (ostatus != kAudioSessionNoError) { 
     1837            PJ_LOG(4, (THIS_FILE, 
     1838                       "Error: cannot set the preferred buffer duration (%i)", 
     1839                       ostatus)); 
     1840            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 
     1841        } 
     1842         
     1843        ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY, &latency); 
     1844        ca_stream_get_cap(s, PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY, &latency); 
     1845         
     1846        return PJ_SUCCESS; 
     1847    } else if (cap==PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE && 
    14331848               (strm->param.dir & PJMEDIA_DIR_CAPTURE)) 
    14341849    { 
     
    14931908    OSStatus ostatus; 
    14941909    UInt32 i; 
     1910    pj_bool_t should_activate; 
     1911    struct stream_list *it, *itBegin; 
     1912 
     1913    if (stream->running) 
     1914        return PJ_SUCCESS; 
    14951915 
    14961916    stream->quit_flag = 0; 
    1497     stream->rec_thread_exited = 0; 
    1498     stream->play_thread_exited = 0; 
    14991917    stream->interrupted = PJ_FALSE; 
     1918    stream->rec_buf_count = 0; 
     1919    stream->play_buf_count = 0; 
     1920    stream->resample_buf_count = 0; 
     1921 
     1922    if (stream->resample) { 
     1923        ostatus = AudioConverterReset(stream->resample); 
     1924        if (ostatus != noErr) 
     1925            return PJMEDIA_AUDIODEV_ERRNO_FROM_COREAUDIO(ostatus); 
     1926    } 
    15001927 
    15011928    for (i = 0; i < 2; i++) { 
     
    15091936    } 
    15101937 
     1938    /* 
     1939     * Make sure this stream is not in the list of running streams. 
     1940     * If this is the 1st stream that is running we need to activate 
     1941     * the audio session. 
     1942     */ 
     1943    pj_mutex_lock(stream->cf->mutex); 
     1944    pj_assert(!pj_list_empty(&stream->cf->streams)); 
     1945    pj_assert(!pj_list_empty(&stream->list_entry)); 
     1946    should_activate = PJ_TRUE; 
     1947    itBegin = &stream->cf->streams; 
     1948    for (it = itBegin->next; it != itBegin; it = it->next) { 
     1949        if (it->stream->running) { 
     1950            should_activate = PJ_FALSE; 
     1951            break; 
     1952        } 
     1953    } 
     1954    stream->running = PJ_TRUE; 
     1955    pj_mutex_unlock(stream->cf->mutex); 
     1956 
    15111957#if !COREAUDIO_MAC 
     1958    if (should_activate) 
    15121959    AudioSessionSetActive(true); 
    1513  
    1514     AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, 
    1515                                     propListener, strm); 
    15161960#endif 
    15171961 
     
    15271971    OSStatus ostatus; 
    15281972    unsigned i; 
    1529  
    1530     stream->quit_flag = 1; 
    1531     for (i=0; !stream->rec_thread_exited && i<100; ++i) 
    1532         pj_thread_sleep(10); 
    1533     for (i=0; !stream->play_thread_exited && i<100; ++i) 
    1534         pj_thread_sleep(10); 
    1535  
    1536     pj_thread_sleep(1); 
    1537     pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc)); 
    1538     pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc)); 
    1539  
    1540 #if !COREAUDIO_MAC 
    1541     AudioSessionSetActive(false); 
    1542 #endif 
     1973    int should_deactivate; 
     1974    struct stream_list *it, *itBegin; 
     1975 
     1976    if (!stream->running) 
     1977        return PJ_SUCCESS; 
    15431978 
    15441979    for (i = 0; i < 2; i++) { 
     
    15511986        } 
    15521987    } 
     1988 
     1989    /* 
     1990     * Make sure this stream is not in the list of running streams. 
     1991     * If this is the 1st stream that is running we need to activate 
     1992     * the audio session. 
     1993     */ 
     1994    pj_mutex_lock(stream->cf->mutex); 
     1995    pj_assert(!pj_list_empty(&stream->cf->streams)); 
     1996    pj_assert(!pj_list_empty(&stream->list_entry)); 
     1997    stream->running = PJ_FALSE; 
     1998    should_deactivate = PJ_TRUE; 
     1999    itBegin = &stream->cf->streams; 
     2000    for (it = itBegin->next; it != itBegin; it = it->next) { 
     2001        if (it->stream->running) { 
     2002            should_deactivate = PJ_FALSE; 
     2003            break; 
     2004        } 
     2005    } 
     2006    pj_mutex_unlock(stream->cf->mutex); 
     2007 
     2008#if !COREAUDIO_MAC 
     2009    if (should_deactivate) 
     2010        AudioSessionSetActive(false); 
     2011#endif 
     2012 
     2013    stream->quit_flag = 1; 
    15532014    stream->play_thread_initialized = 0; 
    15542015    stream->rec_thread_initialized = 0; 
     2016    pj_bzero(stream->rec_thread_desc, sizeof(pj_thread_desc)); 
     2017    pj_bzero(stream->play_thread_desc, sizeof(pj_thread_desc)); 
    15552018 
    15562019    PJ_LOG(4, (THIS_FILE, "core audio stream stopped")); 
     
    15672030 
    15682031    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); 
    1569  
    1570 #if !COREAUDIO_MAC 
    1571     AudioSessionRemovePropertyListenerWithUserData( 
    1572         kAudioSessionProperty_AudioRouteChange, propListener, strm); 
    1573 #endif 
    15742032 
    15752033    ca_stream_stop(strm); 
     
    15832041    } 
    15842042 
    1585     stream->cf->stream = NULL; 
     2043    if (stream->resample) 
     2044        AudioConverterDispose(stream->resample); 
     2045 
     2046    pj_mutex_lock(stream->cf->mutex); 
     2047    if (!pj_list_empty(&stream->list_entry)) 
     2048        pj_list_erase(&stream->list_entry); 
     2049    pj_mutex_unlock(stream->cf->mutex); 
     2050 
    15862051    pj_pool_release(stream->pool); 
    15872052 
Note: See TracChangeset for help on using the changeset viewer.