Ignore:
Timestamp:
Feb 5, 2013 5:15:01 AM (7 years ago)
Author:
bennylp
Message:

Re #1570: Update to work with audio routing (speaker vs handset). Thanks Bob Cripps for the patch!

File:
1 edited

Legend:

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

    r4316 r4340  
    123123 
    124124    /* Playback */ 
     125    unsigned int pb_ctrl_audio_manager_handle; 
    125126    snd_pcm_t           *pb_pcm; 
    126127    unsigned int pb_audio_manager_handle; 
     
    169170    pjmedia_aud_dev_info *adi; 
    170171    int pb_result, ca_result; 
    171     int card = -1; 
    172     int dev = 0; 
    173172    unsigned int handle; 
    174173    snd_pcm_t *pcm_handle; 
     
    184183                                                     &pcm_handle, 
    185184                                                     &handle, 
    186                                                      "/dev/snd/voicep", 
     185                                                     (char*)"voice", 
    187186                                                     SND_PCM_OPEN_PLAYBACK)) 
    188187                                                     >= 0) 
    189188    { 
    190         if ((pb_result = snd_pcm_plugin_set_disable (pcm_handle, PLUGIN_DISABLE_MMAP)) < 0) { 
    191             TRACE_((THIS_FILE, "snd_pcm_plugin_set_disable ret = %d", pb_result)); 
    192         }else{ 
    193             TRACE_((THIS_FILE, "Try to open the device for playback - success")); 
    194         } 
    195             snd_pcm_close (pcm_handle); 
    196             audio_manager_free_handle(handle); 
     189        snd_pcm_close (pcm_handle); 
     190        audio_manager_free_handle(handle); 
    197191    } else { 
    198192        TRACE_((THIS_FILE, "Try to open the device for playback - failure")); 
     
    202196                                                     &pcm_handle, 
    203197                                                     &handle, 
    204                                                      "/dev/snd/voicec", 
     198                                                     (char*)"voice", 
    205199                                                     SND_PCM_OPEN_CAPTURE)) 
    206200                                                     >= 0) 
    207201    { 
    208         if ((ca_result = snd_pcm_plugin_set_disable (pcm_handle, PLUGIN_DISABLE_MMAP)) < 0) { 
    209             TRACE_((THIS_FILE, "snd_pcm_plugin_set_disable ret = %d", ca_result)); 
    210         }else{ 
    211             TRACE_((THIS_FILE, "Try to open the device for capture - success"));         
    212         } 
    213         snd_pcm_close (pcm_handle); 
    214             audio_manager_free_handle(handle); 
     202        snd_pcm_close (pcm_handle); 
     203        audio_manager_free_handle(handle); 
    215204 
    216205    } else { 
     
    397386        snd_pcm_close(stream->pb_pcm); 
    398387        stream->pb_pcm = NULL; 
    399         if(stream->pb_audio_manager_handle != 0){ 
     388 
     389        if (stream->pb_audio_manager_handle != 0) { 
    400390            audio_manager_free_handle(stream->pb_audio_manager_handle); 
    401391            stream->pb_audio_manager_handle = 0; 
    402392        } 
     393 
     394        if (stream->pb_ctrl_audio_manager_handle != 0) { 
     395            audio_manager_free_handle(stream->pb_ctrl_audio_manager_handle); 
     396            stream->pb_ctrl_audio_manager_handle = 0; 
     397        } 
    403398    } 
    404399} 
     
    416411        snd_pcm_close(stream->ca_pcm); 
    417412        stream->ca_pcm = NULL; 
    418         if(stream->ca_audio_manager_handle != 0){ 
     413 
     414        if (stream->ca_audio_manager_handle != 0) { 
    419415            audio_manager_free_handle(stream->ca_audio_manager_handle); 
    420416            stream->ca_audio_manager_handle = 0; 
     
    458454        close_play_pcm(stream); 
    459455        TRACE_((THIS_FILE, "pb_thread_func failed prepare = %d", result)); 
    460         return PJ_SUCCESS; 
     456        return PJ_SUCCESS; 
    461457    } 
    462458 
     
    471467        frame.bit_info = 0; 
    472468 
     469        /* Read the audio from pjmedia */ 
    473470        result = stream->pb_cb (user_data, &frame); 
    474471        if (result != PJ_SUCCESS || stream->quit) 
     
    481478        result = snd_pcm_plugin_write(stream->pb_pcm,buf,size); 
    482479        if (result != size || result < 0) { 
    483             snd_pcm_channel_status_t status; 
     480            /* either the write to output device has failed or not the 
     481             * full amount of bytes have been written. This usually happens 
     482             * when audio routing is being changed by another thread 
     483             * Use a status variable for reading the error 
     484             */ 
     485            snd_pcm_channel_status_t status; 
     486            status.channel = SND_PCM_CHANNEL_PLAYBACK; 
    484487            if (snd_pcm_plugin_status (stream->pb_pcm, &status) < 0) { 
    485                 PJ_LOG(4,(THIS_FILE, 
     488                /* Call has failed nothing we can do except log and 
     489                 * continue */ 
     490                PJ_LOG(4,(THIS_FILE, 
    486491                          "underrun: playback channel status error")); 
    487                 continue; 
    488             } 
    489  
    490             if (status.status == SND_PCM_STATUS_READY || 
    491                 status.status == SND_PCM_STATUS_UNDERRUN) 
    492             { 
    493                 if (snd_pcm_plugin_prepare (stream->pb_pcm, 
    494                                             SND_PCM_CHANNEL_PLAYBACK) < 0) 
    495                 { 
    496                     PJ_LOG(4,(THIS_FILE, 
    497                               "underrun: playback channel prepare error")); 
    498                     continue; 
    499                 } 
    500             } 
    501  
    502             TRACE_((THIS_FILE, "pb_thread_func failed write = %d", result)); 
     492            } else { 
     493                /* The status of the error has been read 
     494                 * RIM say these are expected so we can "re-prepare" the stream 
     495                 */ 
     496                PJ_LOG(4,(THIS_FILE,"PLAY thread ERROR status = %d", 
     497                          status.status)); 
     498                if (status.status == SND_PCM_STATUS_READY || 
     499                    status.status == SND_PCM_STATUS_UNDERRUN || 
     500                    status.status == SND_PCM_STATUS_ERROR )  
     501                { 
     502                    if (snd_pcm_plugin_prepare (stream->pb_pcm, 
     503                                                SND_PCM_CHANNEL_PLAYBACK) < 0) 
     504                    { 
     505                        PJ_LOG(4,(THIS_FILE, 
     506                                  "underrun: playback channel prepare error")); 
     507                    } 
     508                } 
     509            } 
    503510        } 
    504  
    505511        tstamp.u64 += nframes; 
    506512    } 
     
    553559        close_capture_pcm(stream); 
    554560        TRACE_((THIS_FILE, "ca_thread_func failed prepare = %d", result)); 
    555         return PJ_SUCCESS; 
     561        return PJ_SUCCESS; 
    556562    } 
    557563 
     
    561567        pj_bzero (buf, size); 
    562568 
     569        /* read the input device */ 
    563570        result = snd_pcm_plugin_read(stream->ca_pcm, buf,size); 
    564571        if(result <0 || result != size) { 
     572            /* We expect result to be size (640) 
     573             * It's not so we have to read the status error and "prepare" 
     574             * the channel. This usually happens when output audio routing 
     575             * has been changed by another thread. 
     576             * We won't "continue", instead just do what we can and leave 
     577             * the end of the loop to write what's in the buffer. Not entirely 
     578             * correct but saves a potential underrun in PJMEDIA 
     579             */ 
     580            PJ_LOG (4,(THIS_FILE, 
     581                       "snd_pcm_plugin_read ERROR read = %d required = %d", 
     582                       result,size)); 
    565583            snd_pcm_channel_status_t status; 
    566             if (snd_pcm_plugin_status (stream->ca_pcm, &status) < 0) { 
    567                 PJ_LOG (4,(THIS_FILE, "overrun: capture channel status " 
    568                                       "error")); 
    569                 continue; 
    570             } 
    571  
    572             if (status.status == SND_PCM_STATUS_READY || 
    573                 status.status == SND_PCM_STATUS_OVERRUN) { 
    574                 if (snd_pcm_plugin_prepare (stream->ca_pcm, 
    575                                             SND_PCM_CHANNEL_CAPTURE) < 0) 
    576                 { 
    577                     PJ_LOG (4,(THIS_FILE, "overrun: capture channel prepare  " 
    578                                           "error")); 
    579                     continue; 
    580                 } 
     584            status.channel = SND_PCM_CHANNEL_CAPTURE; 
     585            if ((result = snd_pcm_plugin_status (stream->ca_pcm, &status)) < 0) 
     586            { 
     587                /* Should not fail but all we can do is continue */ 
     588                PJ_LOG(4,(THIS_FILE, "capture: snd_pcm_plugin_status ret = %d", 
     589                          result)); 
     590            } else { 
     591                /* RIM say these are the errors that we should "prepare" 
     592                 * after */ 
     593                if (status.status == SND_PCM_STATUS_READY || 
     594                        status.status == SND_PCM_STATUS_OVERRUN || 
     595                        status.status == SND_PCM_STATUS_ERROR) 
     596                { 
     597                    if (snd_pcm_plugin_prepare (stream->ca_pcm, 
     598                                                SND_PCM_CHANNEL_CAPTURE) < 0) 
     599                    { 
     600                        PJ_LOG (4,(THIS_FILE, 
     601                                   "overrun: capture channel prepare  error")); 
     602                    } 
     603                } 
    581604            } 
    582605        } 
     
    585608            break; 
    586609 
     610        /* Write the capture audio data to PJMEDIA */ 
    587611        frame.type = PJMEDIA_FRAME_TYPE_AUDIO; 
    588612        frame.buf = (void *) buf; 
     
    605629} 
    606630 
     631/* Audio routing, speaker/headset */ 
     632static pj_status_t bb10_initialize_playback_ctrl(struct bb10_stream *stream, 
     633                                                 bool speaker) 
     634{ 
     635    /* Although the play and capture have audio manager handles, audio routing 
     636     * requires a separate handle 
     637     */ 
     638    int ret = PJ_SUCCESS; 
     639 
     640    if (stream->pb_ctrl_audio_manager_handle == 0) { 
     641        /* lazy init an audio manager handle */ 
     642        ret = audio_manager_get_handle(AUDIO_TYPE_VIDEO_CHAT, 0, false, 
     643                                       &stream->pb_ctrl_audio_manager_handle); 
     644        if (ret != 0) { 
     645            TRACE_((THIS_FILE, "audio_manager_get_handle ret = %d",ret)); 
     646            return PJMEDIA_EAUD_SYSERR; 
     647        } 
     648    } 
     649 
     650    /* Set for either speaker or earpiece */ 
     651    if (speaker) { 
     652        ret = audio_manager_set_handle_type( 
     653                stream->pb_ctrl_audio_manager_handle, 
     654                AUDIO_TYPE_VIDEO_CHAT, 
     655                AUDIO_DEVICE_SPEAKER, 
     656                AUDIO_DEVICE_DEFAULT); 
     657    } else { 
     658        ret = audio_manager_set_handle_type( 
     659                stream->pb_ctrl_audio_manager_handle, 
     660                AUDIO_TYPE_VIDEO_CHAT, 
     661                AUDIO_DEVICE_HANDSET, 
     662                AUDIO_DEVICE_DEFAULT); 
     663    } 
     664 
     665    if (ret == 0) { 
     666        /* RIM recommend this call */ 
     667        ret = audio_manager_set_handle_routing_conditions( 
     668                stream->pb_ctrl_audio_manager_handle, 
     669                SETTINGS_RESET_ON_DEVICE_CONNECTION); 
     670        if (ret != 0) { 
     671            TRACE_((THIS_FILE, 
     672                    "audio_manager_set_handle_routing_conditions ret = %d", 
     673                    ret)); 
     674            return PJMEDIA_EAUD_SYSERR; 
     675        } 
     676    } else { 
     677        TRACE_((THIS_FILE, "audio_manager_set_handle_type ret = %d", ret)); 
     678        return PJMEDIA_EAUD_SYSERR; 
     679    } 
     680 
     681    return PJ_SUCCESS; 
     682} 
    607683 
    608684static pj_status_t bb10_open_playback (struct bb10_stream *stream, 
    609685                                       const pjmedia_aud_param *param) 
    610686{ 
    611     int card = -1; 
    612     int dev = 0; 
    613687    int ret = 0; 
    614688    snd_pcm_channel_info_t pi; 
     
    623697    } 
    624698 
    625     if ((ret = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT, 
    626                                                &stream->pb_pcm, &stream->pb_audio_manager_handle, 
    627                                                "/dev/snd/voicep", 
    628                                                SND_PCM_OPEN_PLAYBACK)) < 0) 
     699    /* Use the bb10 audio manager API to open as opposed to QNX core audio 
     700     * Echo cancellation built in 
     701     */ 
     702    if ((ret = audio_manager_snd_pcm_open_name( 
     703            AUDIO_TYPE_VIDEO_CHAT, 
     704            &stream->pb_pcm, &stream->pb_audio_manager_handle, 
     705            (char*)"voice", 
     706            SND_PCM_OPEN_PLAYBACK)) < 0) 
    629707    { 
    630708        TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret)); 
    631709        return PJMEDIA_EAUD_SYSERR; 
    632710    } 
    633     ret = audio_manager_set_handle_type(stream->pb_audio_manager_handle, AUDIO_TYPE_VIDEO_CHAT, AUDIO_DEVICE_HANDSET , AUDIO_DEVICE_HANDSET); 
    634     if (ret==0) { 
    635           ret = audio_manager_set_handle_routing_conditions(stream->pb_audio_manager_handle, SETTINGS_RESET_ON_DEVICE_CONNECTION); 
    636           if(ret != 0){ 
    637               TRACE_((THIS_FILE, "audio_manager_set_handle_routing_conditions ret = %d", ret)); 
    638               return PJMEDIA_EAUD_SYSERR; 
    639           } 
    640     }else{ 
    641         TRACE_((THIS_FILE, "audio_manager_set_handle_type ret = %d", ret)); 
    642         return PJMEDIA_EAUD_SYSERR; 
    643     } 
    644  
    645     if ((ret = snd_pcm_plugin_set_disable (stream->pb_pcm, PLUGIN_DISABLE_MMAP)) < 0) { 
    646         TRACE_((THIS_FILE, "snd_pcm_plugin_set_disable ret = %d", ret)); 
    647         return PJMEDIA_EAUD_SYSERR; 
    648     } 
     711 
     712    /* Required call from January 2013 gold OS release */ 
     713    if ((ret = snd_pcm_plugin_set_disable(stream->pb_pcm, 
     714                                          PLUGIN_DISABLE_MMAP)) < 0) 
     715    { 
     716        TRACE_((THIS_FILE, "snd_pcm_plugin_set_disable ret = %d", ret)); 
     717        return PJMEDIA_EAUD_SYSERR; 
     718    } 
     719 
     720    /* Required call from January 2013 gold OS release */ 
     721    if ((ret = snd_pcm_plugin_set_enable(stream->pb_pcm, 
     722                                         PLUGIN_ROUTING)) < 0) 
     723    { 
     724        TRACE_((THIS_FILE, "snd_pcm_plugin_set_enable ret = %d", ret)); 
     725        return PJMEDIA_EAUD_SYSERR; 
     726    } 
     727 
    649728    /* TODO PJ_ZERO */ 
    650729    memset (&pi, 0, sizeof (pi)); 
    651730    pi.channel = SND_PCM_CHANNEL_PLAYBACK; 
    652731    if ((ret = snd_pcm_plugin_info (stream->pb_pcm, &pi)) < 0) { 
    653         TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret)); 
    654         return PJMEDIA_EAUD_SYSERR; 
     732        TRACE_((THIS_FILE, "snd_pcm_plugin_info ret = %d", ret)); 
     733        return PJMEDIA_EAUD_SYSERR; 
    655734    } 
    656735 
    657736    memset (&pp, 0, sizeof (pp)); 
    658737 
    659     /* Request VoIP compatible capabilities 
    660      * On simulator frag_size is always negotiated to 170 
    661      */ 
     738    /* Request VoIP compatible capabilities */ 
    662739    pp.mode = SND_PCM_MODE_BLOCK; 
    663740    pp.channel = SND_PCM_CHANNEL_PLAYBACK; 
     
    666743    /* HARD CODE for the time being PJMEDIA expects 640 for 16khz */ 
    667744    pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2; 
    668     /* Increasing this internal buffer count delays write failure in the loop */ 
    669     pp.buf.block.frags_max = 4; 
     745    /* RIM recommends maximum of 3 */ 
     746    pp.buf.block.frags_max = 3; 
    670747    pp.buf.block.frags_min = 1; 
    671748    pp.format.interleave = 1; 
     
    715792    TRACE_((THIS_FILE, "bb10_open_playback: pb_frames = %d clock = %d", 
    716793                       stream->pb_frames, param->clock_rate)); 
    717  
     794     
    718795    return PJ_SUCCESS; 
    719796} 
     
    725802    unsigned int rate; 
    726803    unsigned long tmp_buf_size; 
    727     int card = -1; 
    728     int dev = 0; 
    729804    int frame_size; 
    730805    snd_pcm_channel_info_t pi; 
     
    736811        return PJMEDIA_EAUD_INVDEV; 
    737812 
    738     if ((ret = audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT, 
    739                                                &stream->ca_pcm, 
    740                                                &stream->ca_audio_manager_handle, 
    741                                                "/dev/snd/voicec", 
    742                                                SND_PCM_OPEN_CAPTURE)) < 0) 
     813    if ((ret=audio_manager_snd_pcm_open_name(AUDIO_TYPE_VIDEO_CHAT, 
     814                                             &stream->ca_pcm, 
     815                                             &stream->ca_audio_manager_handle, 
     816                                             (char*)"voice", 
     817                                             SND_PCM_OPEN_CAPTURE)) < 0) 
    743818    { 
    744819        TRACE_((THIS_FILE, "audio_manager_snd_pcm_open_name ret = %d", ret)); 
    745820        return PJMEDIA_EAUD_SYSERR; 
    746821    } 
    747     if ((ret = snd_pcm_plugin_set_disable (stream->ca_pcm, PLUGIN_DISABLE_MMAP)) < 0) { 
     822    /* Required call from January 2013 gold OS release */ 
     823    if ((ret = snd_pcm_plugin_set_disable (stream->ca_pcm, 
     824                                           PLUGIN_DISABLE_MMAP)) < 0) 
     825    { 
    748826        TRACE_(("snd_pcm_plugin_set_disable failed: %d",ret)); 
     827        return PJMEDIA_EAUD_SYSERR; 
     828    } 
     829    /* Required call from January 2013 gold OS release */ 
     830    if ((ret = snd_pcm_plugin_set_enable(stream->ca_pcm, 
     831                                         PLUGIN_ROUTING)) < 0) 
     832    { 
     833        TRACE_(("snd_pcm_plugin_set_enable failed: %d",ret)); 
    749834        return PJMEDIA_EAUD_SYSERR; 
    750835    } 
     
    770855    /* HARD CODE for the time being PJMEDIA expects 640 for 16khz */ 
    771856    pp.buf.block.frag_size = PREFERRED_FRAME_SIZE*2; 
    772     /* Not applicable for capture hence -1 */ 
    773     pp.buf.block.frags_max = -1; 
     857    /* From January 2013 gold OS release. RIM recommend these for capture */ 
     858    pp.buf.block.frags_max = 1; 
    774859    pp.buf.block.frags_min = 1; 
    775860    pp.format.interleave = 1; 
     
    803888    } 
    804889 
    805     /* frag_size should be 160 */ 
    806890    frame_size = setup.buf.block.frag_size; 
    807  
    808     /* END BB10 init */ 
    809891 
    810892    /* Set clock rate */ 
     
    883965    } 
    884966 
     967    /* Part of the play functionality but the RIM/Truphone loopback sample 
     968     * initialializes after the play and capture 
     969     * "false" is default/earpiece for output 
     970     */ 
     971    status = bb10_initialize_playback_ctrl(stream,false); 
     972    if (status != PJ_SUCCESS) { 
     973        return PJMEDIA_EAUD_SYSERR; 
     974    } 
     975 
    885976    *p_strm = &stream->base; 
    886977    return PJ_SUCCESS; 
     
    888979 
    889980 
    890 /* API: get running parameter */ 
     981/*  
     982 * API: get running parameter 
     983 * based on ALSA template 
     984 */ 
    891985static pj_status_t bb10_stream_get_param(pjmedia_aud_stream *s, 
    892986                                         pjmedia_aud_param *pi) 
     
    902996 
    903997 
    904 /* API: get capability */ 
     998/* 
     999 * API: get capability 
     1000 * based on ALSA template  
     1001*/ 
    9051002static pj_status_t bb10_stream_get_cap(pjmedia_aud_stream *s, 
    9061003                                       pjmedia_aud_dev_cap cap, 
     
    9171014        *(unsigned*)pval = stream->param.input_latency_ms; 
    9181015        return PJ_SUCCESS; 
     1016 
    9191017    } else if (cap==PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY && 
    9201018               (stream->param.dir & PJMEDIA_DIR_PLAYBACK)) 
     
    9231021        *(unsigned*)pval = stream->param.output_latency_ms; 
    9241022        return PJ_SUCCESS; 
     1023 
    9251024    } else { 
    9261025        return PJMEDIA_EAUD_INVCAP; 
     
    9291028 
    9301029 
    931 /* API: set capability */ 
     1030/* 
     1031 * API: set capability 
     1032 * Currently just supporting toggle between speaker and earpiece 
     1033 */ 
    9321034static pj_status_t bb10_stream_set_cap(pjmedia_aud_stream *strm, 
    9331035                                       pjmedia_aud_dev_cap cap, 
    9341036                                       const void *value) 
    9351037{ 
    936     PJ_UNUSED_ARG(strm); 
    937     PJ_UNUSED_ARG(cap); 
    938     PJ_UNUSED_ARG(value); 
    939  
    940     return PJMEDIA_EAUD_INVCAP; 
     1038    pj_status_t ret = PJ_SUCCESS; 
     1039    struct bb10_stream *stream = (struct bb10_stream*)strm; 
     1040 
     1041    if (cap != PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE || value == NULL) { 
     1042        TRACE_((THIS_FILE,"bb10_stream_set_cap() = PJMEDIA_EAUD_INVCAP")); 
     1043        return PJMEDIA_EAUD_INVCAP;  
     1044 
     1045    } else { 
     1046        pjmedia_aud_dev_route route = *((pjmedia_aud_dev_route*)value); 
     1047        /* Use the initialization function which lazy-inits the 
     1048         * handle for routing 
     1049         */ 
     1050        if (route == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER) { 
     1051            ret = bb10_initialize_playback_ctrl(stream,true); 
     1052        } else { 
     1053            ret = bb10_initialize_playback_ctrl(stream,false);           
     1054        } 
     1055    } 
     1056 
     1057    if (ret != PJ_SUCCESS) { 
     1058        TRACE_((THIS_FILE,"bb10_stream_set_cap() = %d",ret)); 
     1059    } 
     1060    return ret; 
    9411061} 
    9421062 
Note: See TracChangeset for help on using the changeset viewer.