Ignore:
Timestamp:
Mar 12, 2009 6:11:37 PM (15 years ago)
Author:
bennylp
Message:

(Major) Task #737 and #738: integration of APS-Direct and Audiodev from aps-direct branch to trunk.

Location:
pjproject/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk

  • pjproject/trunk/pjmedia/src/pjmedia/sound_port.c

    r2394 r2506  
    1919 */ 
    2020#include <pjmedia/sound_port.h> 
     21#include <pjmedia/alaw_ulaw.h> 
    2122#include <pjmedia/delaybuf.h> 
    2223#include <pjmedia/echo.h> 
    2324#include <pjmedia/errno.h> 
    24 #include <pjmedia/plc.h> 
    2525#include <pj/assert.h> 
    2626#include <pj/log.h> 
     
    2828#include <pj/string.h>      /* pj_memset() */ 
    2929 
    30 //#define SIMULATE_LOST_PCT   20 
    3130#define AEC_TAIL            128     /* default AEC length in ms */ 
    3231#define AEC_SUSPEND_LIMIT   5       /* seconds of no activity   */ 
     
    3635//#define TEST_OVERFLOW_UNDERFLOW 
    3736 
    38 enum 
    39 { 
    40     PJMEDIA_PLC_ENABLED     = 1, 
    41 }; 
    42  
    43 //#define DEFAULT_OPTIONS       PJMEDIA_PLC_ENABLED 
    44 #define DEFAULT_OPTIONS     0 
    45  
    46  
    4737struct pjmedia_snd_port 
    4838{ 
    4939    int                  rec_id; 
    5040    int                  play_id; 
    51     pjmedia_snd_stream  *snd_stream; 
     41    pj_uint32_t          aud_caps; 
     42    pjmedia_aud_param    aud_param; 
     43    pjmedia_aud_stream  *aud_stream; 
    5244    pjmedia_dir          dir; 
    5345    pjmedia_port        *port; 
    54     unsigned             options; 
    55  
    56     pjmedia_echo_state  *ec_state; 
    57     unsigned             aec_tail_len; 
    58  
    59     pj_bool_t            ec_suspended; 
    60     unsigned             ec_suspend_count; 
    61     unsigned             ec_suspend_limit; 
    62  
    63     pjmedia_plc         *plc; 
    6446 
    6547    unsigned             clock_rate; 
     
    6850    unsigned             bits_per_sample; 
    6951 
    70 #if PJMEDIA_SOUND_USE_DELAYBUF 
    71     pjmedia_delay_buf   *delay_buf; 
    72 #endif 
     52    /* software ec */ 
     53    pjmedia_echo_state  *ec_state; 
     54    unsigned             ec_options; 
     55    unsigned             ec_tail_len; 
     56    pj_bool_t            ec_suspended; 
     57    unsigned             ec_suspend_count; 
     58    unsigned             ec_suspend_limit; 
    7359}; 
    7460 
     
    7763 * played. 
    7864 */ 
    79 static pj_status_t play_cb(/* in */   void *user_data, 
    80                            /* in */   pj_uint32_t timestamp, 
    81                            /* out */  void *output, 
    82                            /* out */  unsigned size) 
     65static pj_status_t play_cb(void *user_data, pjmedia_frame *frame) 
    8366{ 
    8467    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; 
    8568    pjmedia_port *port; 
    86     pjmedia_frame frame; 
     69    unsigned required_size = frame->size; 
    8770    pj_status_t status; 
    8871 
    89     /* We're risking accessing the port without holding any mutex. 
    90      * It's possible that port is disconnected then destroyed while 
    91      * we're trying to access it. 
    92      * But in the name of performance, we'll try this approach until 
    93      * someone complains when it crashes. 
    94      */ 
    9572    port = snd_port->port; 
    9673    if (port == NULL) 
    9774        goto no_frame; 
    9875 
    99     frame.buf = output; 
    100     frame.size = size; 
    101     frame.timestamp.u32.hi = 0; 
    102     frame.timestamp.u32.lo = timestamp; 
    103  
    104 #if PJMEDIA_SOUND_USE_DELAYBUF 
    105     if (snd_port->delay_buf) { 
    106         status = pjmedia_delay_buf_get(snd_port->delay_buf, (pj_int16_t*)output); 
    107         if (status != PJ_SUCCESS) 
    108             pj_bzero(output, size); 
    109  
    110         frame.type = PJMEDIA_FRAME_TYPE_AUDIO; 
    111         pjmedia_port_put_frame(port, &frame); 
    112  
    113 #ifdef TEST_OVERFLOW_UNDERFLOW 
    114         { 
    115             static int count = 1; 
    116             if (++count % 10 == 0) { 
    117                 status = pjmedia_delay_buf_get(snd_port->delay_buf,  
    118                                                (pj_int16_t*)output); 
    119                 if (status != PJ_SUCCESS) 
    120                     pj_bzero(output, size); 
    121  
    122                 frame.type = PJMEDIA_FRAME_TYPE_AUDIO; 
    123                 pjmedia_port_put_frame(port, &frame); 
    124             } 
    125         } 
    126 #endif 
    127  
    128     } 
    129 #endif 
    130  
    131     status = pjmedia_port_get_frame(port, &frame); 
     76    status = pjmedia_port_get_frame(port, frame); 
    13277    if (status != PJ_SUCCESS) 
    13378        goto no_frame; 
    13479 
    135     if (frame.type != PJMEDIA_FRAME_TYPE_AUDIO) 
     80    if (frame->type != PJMEDIA_FRAME_TYPE_AUDIO) 
    13681        goto no_frame; 
    13782 
    13883    /* Must supply the required samples */ 
    139     pj_assert(frame.size == size); 
    140  
    141 #ifdef SIMULATE_LOST_PCT 
    142     /* Simulate packet lost */ 
    143     if (pj_rand() % 100 < SIMULATE_LOST_PCT) { 
    144         PJ_LOG(4,(THIS_FILE, "Frame dropped")); 
    145         goto no_frame; 
    146     } 
    147 #endif 
    148  
    149     if (snd_port->plc) 
    150         pjmedia_plc_save(snd_port->plc, (pj_int16_t*) output); 
     84    PJ_UNUSED_ARG(required_size); 
     85    pj_assert(frame->size == required_size); 
    15186 
    15287    if (snd_port->ec_state) { 
     
    15792        } 
    15893        snd_port->ec_suspend_count = 0; 
    159         pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)output); 
     94        pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf); 
    16095    } 
    16196 
     
    173108        if (snd_port->ec_state) { 
    174109            /* To maintain correct delay in EC */ 
    175             pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)output); 
    176         } 
    177     } 
    178  
    179     /* Apply PLC */ 
    180     if (snd_port->plc) { 
    181  
    182         pjmedia_plc_generate(snd_port->plc, (pj_int16_t*) output); 
    183 #ifdef SIMULATE_LOST_PCT 
    184         PJ_LOG(4,(THIS_FILE, "Lost frame generated")); 
    185 #endif 
    186     } else { 
    187         pj_bzero(output, size); 
    188     } 
    189  
     110            pjmedia_echo_playback(snd_port->ec_state, (pj_int16_t*)frame->buf); 
     111        } 
     112    } 
    190113 
    191114    return PJ_SUCCESS; 
     
    197120 * frame. 
    198121 */ 
    199 static pj_status_t rec_cb(/* in */   void *user_data, 
    200                           /* in */   pj_uint32_t timestamp, 
    201                           /* in */   void *input, 
    202                           /* in*/    unsigned size) 
     122static pj_status_t rec_cb(void *user_data, pjmedia_frame *frame) 
    203123{ 
    204124    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; 
    205125    pjmedia_port *port; 
    206     pjmedia_frame frame; 
    207  
    208     /* We're risking accessing the port without holding any mutex. 
    209      * It's possible that port is disconnected then destroyed while 
    210      * we're trying to access it. 
    211      * But in the name of performance, we'll try this approach until 
    212      * someone complains when it crashes. 
    213      */ 
     126 
    214127    port = snd_port->port; 
    215128    if (port == NULL) 
     
    218131    /* Cancel echo */ 
    219132    if (snd_port->ec_state && !snd_port->ec_suspended) { 
    220         pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) input, 0); 
    221     } 
    222  
    223 #if PJMEDIA_SOUND_USE_DELAYBUF 
    224     if (snd_port->delay_buf) { 
    225         pjmedia_delay_buf_put(snd_port->delay_buf, (pj_int16_t*)input); 
    226     } else { 
    227         frame.buf = (void*)input; 
    228         frame.size = size; 
    229         frame.type = PJMEDIA_FRAME_TYPE_AUDIO; 
    230         frame.timestamp.u32.lo = timestamp; 
    231  
    232         pjmedia_port_put_frame(port, &frame); 
    233     } 
    234 #else 
    235     frame.buf = (void*)input; 
    236     frame.size = size; 
    237     frame.type = PJMEDIA_FRAME_TYPE_AUDIO; 
    238     frame.timestamp.u32.lo = timestamp; 
    239  
    240     pjmedia_port_put_frame(port, &frame); 
    241 #endif 
     133        pjmedia_echo_capture(snd_port->ec_state, (pj_int16_t*) frame->buf, 0); 
     134    } 
     135 
     136    pjmedia_port_put_frame(port, frame); 
     137 
     138    return PJ_SUCCESS; 
     139} 
     140 
     141/* 
     142 * The callback called by sound player when it needs more samples to be 
     143 * played. This version is for non-PCM data. 
     144 */ 
     145static pj_status_t play_cb_ext(void *user_data, pjmedia_frame *frame) 
     146{ 
     147    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; 
     148    pjmedia_port *port = snd_port->port; 
     149 
     150    if (port == NULL) { 
     151        frame->type = PJMEDIA_FRAME_TYPE_NONE; 
     152        return PJ_SUCCESS; 
     153    } 
     154 
     155    pjmedia_port_get_frame(port, frame); 
     156 
     157    return PJ_SUCCESS; 
     158} 
     159 
     160 
     161/* 
     162 * The callback called by sound recorder when it has finished capturing a 
     163 * frame. This version is for non-PCM data. 
     164 */ 
     165static pj_status_t rec_cb_ext(void *user_data, pjmedia_frame *frame) 
     166{ 
     167    pjmedia_snd_port *snd_port = (pjmedia_snd_port*) user_data; 
     168    pjmedia_port *port; 
     169 
     170    port = snd_port->port; 
     171    if (port == NULL) 
     172        return PJ_SUCCESS; 
     173 
     174    pjmedia_port_put_frame(port, frame); 
    242175 
    243176    return PJ_SUCCESS; 
     
    251184                                       pjmedia_snd_port *snd_port ) 
    252185{ 
     186    pjmedia_aud_rec_cb snd_rec_cb; 
     187    pjmedia_aud_play_cb snd_play_cb; 
     188    pjmedia_aud_param param_copy; 
    253189    pj_status_t status; 
    254190 
    255191    /* Check if sound has been started. */ 
    256     if (snd_port->snd_stream != NULL) 
     192    if (snd_port->aud_stream != NULL) 
    257193        return PJ_SUCCESS; 
    258194 
    259     /* Open sound stream. */ 
    260     if (snd_port->dir == PJMEDIA_DIR_CAPTURE) { 
    261         status = pjmedia_snd_open_rec( snd_port->rec_id,  
    262                                        snd_port->clock_rate, 
    263                                        snd_port->channel_count, 
    264                                        snd_port->samples_per_frame, 
    265                                        snd_port->bits_per_sample, 
    266                                        &rec_cb, 
     195    PJ_ASSERT_RETURN(snd_port->dir == PJMEDIA_DIR_CAPTURE || 
     196                     snd_port->dir == PJMEDIA_DIR_PLAYBACK || 
     197                     snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK, 
     198                     PJ_EBUG); 
     199 
     200    /* Get device caps */ 
     201    if (snd_port->aud_param.dir & PJMEDIA_DIR_CAPTURE) { 
     202        pjmedia_aud_dev_info dev_info; 
     203 
     204        status = pjmedia_aud_dev_get_info(snd_port->aud_param.rec_id,  
     205                                          &dev_info); 
     206        if (status != PJ_SUCCESS) 
     207            return status; 
     208 
     209        snd_port->aud_caps = dev_info.caps; 
     210    } else { 
     211        snd_port->aud_caps = 0; 
     212    } 
     213 
     214    /* Process EC settings */ 
     215    pj_memcpy(&param_copy, &snd_port->aud_param, sizeof(param_copy)); 
     216    if (param_copy.flags & PJMEDIA_AUD_DEV_CAP_EC) { 
     217        /* EC is wanted */ 
     218        if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) { 
     219            /* Device supports EC */ 
     220            /* Nothing to do */ 
     221        } else { 
     222            /* Device doesn't support EC, remove EC settings from 
     223             * device parameters 
     224             */ 
     225            param_copy.flags &= ~(PJMEDIA_AUD_DEV_CAP_EC | 
     226                                  PJMEDIA_AUD_DEV_CAP_EC_TAIL); 
     227        } 
     228    } 
     229 
     230    /* Use different callback if format is not PCM */ 
     231    if (snd_port->aud_param.ext_fmt.id == PJMEDIA_FORMAT_L16) { 
     232        snd_rec_cb = &rec_cb; 
     233        snd_play_cb = &play_cb; 
     234    } else { 
     235        snd_rec_cb = &rec_cb_ext; 
     236        snd_play_cb = &play_cb_ext; 
     237    } 
     238 
     239    /* Open the device */ 
     240    status = pjmedia_aud_stream_create(&param_copy, 
     241                                       snd_rec_cb, 
     242                                       snd_play_cb, 
    267243                                       snd_port, 
    268                                        &snd_port->snd_stream); 
    269  
    270     } else if (snd_port->dir == PJMEDIA_DIR_PLAYBACK) { 
    271         status = pjmedia_snd_open_player( snd_port->play_id,  
    272                                           snd_port->clock_rate, 
    273                                           snd_port->channel_count, 
    274                                           snd_port->samples_per_frame, 
    275                                           snd_port->bits_per_sample, 
    276                                           &play_cb, 
    277                                           snd_port, 
    278                                           &snd_port->snd_stream); 
    279  
    280     } else if (snd_port->dir == PJMEDIA_DIR_CAPTURE_PLAYBACK) { 
    281         status = pjmedia_snd_open( snd_port->rec_id,  
    282                                    snd_port->play_id, 
    283                                    snd_port->clock_rate, 
    284                                    snd_port->channel_count, 
    285                                    snd_port->samples_per_frame, 
    286                                    snd_port->bits_per_sample, 
    287                                    &rec_cb, 
    288                                    &play_cb, 
    289                                    snd_port, 
    290                                    &snd_port->snd_stream); 
    291     } else { 
    292         pj_assert(!"Invalid dir"); 
    293         status = PJ_EBUG; 
    294     } 
     244                                       &snd_port->aud_stream); 
    295245 
    296246    if (status != PJ_SUCCESS) 
    297247        return status; 
    298  
    299  
    300 #ifdef SIMULATE_LOST_PCT 
    301     snd_port->options |= PJMEDIA_PLC_ENABLED; 
    302 #endif 
    303  
    304     /* If we have player components, allocate buffer to save the last 
    305      * frame played to the speaker. The last frame is used for packet 
    306      * lost concealment (PLC) algorithm. 
    307      */ 
    308     if ((snd_port->dir & PJMEDIA_DIR_PLAYBACK) && 
    309         (snd_port->options & PJMEDIA_PLC_ENABLED))  
    310     { 
    311         status = pjmedia_plc_create(pool, snd_port->clock_rate,  
    312                                     snd_port->samples_per_frame *  
    313                                         snd_port->channel_count, 
    314                                     0, &snd_port->plc); 
    315         if (status != PJ_SUCCESS) { 
    316             PJ_LOG(4,(THIS_FILE, "Unable to create PLC")); 
    317             snd_port->plc = NULL; 
    318         } 
    319     } 
    320248 
    321249    /* Inactivity limit before EC is suspended. */ 
     
    324252                                  snd_port->samples_per_frame); 
    325253 
     254    /* Create software EC if parameter specifies EC but device  
     255     * doesn't support EC. Only do this if the format is PCM! 
     256     */ 
     257    if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC) && 
     258        (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC)==0 && 
     259        param_copy.ext_fmt.id == PJMEDIA_FORMAT_PCM) 
     260    { 
     261        if ((snd_port->aud_param.flags & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) { 
     262            snd_port->aud_param.flags |= PJMEDIA_AUD_DEV_CAP_EC_TAIL; 
     263            snd_port->aud_param.ec_tail_ms = AEC_TAIL; 
     264            PJ_LOG(4,(THIS_FILE, "AEC tail is set to default %u ms", 
     265                                 snd_port->aud_param.ec_tail_ms)); 
     266        } 
     267             
     268        status = pjmedia_snd_port_set_ec(snd_port, pool,  
     269                                         snd_port->aud_param.ec_tail_ms, 0); 
     270        if (status != PJ_SUCCESS) { 
     271            pjmedia_aud_stream_destroy(snd_port->aud_stream); 
     272            snd_port->aud_stream = NULL; 
     273            return status; 
     274        } 
     275    } 
     276 
    326277    /* Start sound stream. */ 
    327     status = pjmedia_snd_stream_start(snd_port->snd_stream); 
     278    status = pjmedia_aud_stream_start(snd_port->aud_stream); 
    328279    if (status != PJ_SUCCESS) { 
    329         pjmedia_snd_stream_close(snd_port->snd_stream); 
    330         snd_port->snd_stream = NULL; 
     280        pjmedia_aud_stream_destroy(snd_port->aud_stream); 
     281        snd_port->aud_stream = NULL; 
    331282        return status; 
    332283    } 
     
    343294{ 
    344295    /* Check if we have sound stream device. */ 
    345     if (snd_port->snd_stream) { 
    346         pjmedia_snd_stream_stop(snd_port->snd_stream); 
    347         pjmedia_snd_stream_close(snd_port->snd_stream); 
    348         snd_port->snd_stream = NULL; 
     296    if (snd_port->aud_stream) { 
     297        pjmedia_aud_stream_stop(snd_port->aud_stream); 
     298        pjmedia_aud_stream_destroy(snd_port->aud_stream); 
     299        snd_port->aud_stream = NULL; 
    349300    } 
    350301 
     
    372323                                             pjmedia_snd_port **p_port) 
    373324{ 
    374     pjmedia_snd_port *snd_port; 
    375  
    376     PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL); 
    377  
    378     snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port); 
    379     PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM); 
    380  
    381     snd_port->rec_id = rec_id; 
    382     snd_port->play_id = play_id; 
    383     snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; 
    384     snd_port->options = options | DEFAULT_OPTIONS; 
    385     snd_port->clock_rate = clock_rate; 
    386     snd_port->channel_count = channel_count; 
    387     snd_port->samples_per_frame = samples_per_frame; 
    388     snd_port->bits_per_sample = bits_per_sample; 
    389      
    390 #if PJMEDIA_SOUND_USE_DELAYBUF 
    391     do { 
    392         pj_status_t status; 
    393         unsigned ptime; 
    394      
    395         ptime = samples_per_frame * 1000 / (clock_rate * channel_count); 
    396      
    397         status = pjmedia_delay_buf_create(pool, "snd_buff",  
    398                                           clock_rate, samples_per_frame, 
    399                                           channel_count, 
    400                                           PJMEDIA_SOUND_BUFFER_COUNT * ptime, 
    401                                           0, &snd_port->delay_buf); 
    402         PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 
    403     } while (0); 
    404 #endif 
    405  
    406     *p_port = snd_port; 
    407  
    408  
    409     /* Start sound device immediately. 
    410      * If there's no port connected, the sound callback will return 
    411      * empty signal. 
    412      */ 
    413     return start_sound_device( pool, snd_port ); 
    414  
     325    pjmedia_aud_param param; 
     326    pj_status_t status; 
     327 
     328    PJ_UNUSED_ARG(options); 
     329 
     330    status = pjmedia_aud_dev_default_param(rec_id, &param); 
     331    if (status != PJ_SUCCESS) 
     332        return status; 
     333 
     334    param.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; 
     335    param.rec_id = rec_id; 
     336    param.play_id = play_id; 
     337    param.clock_rate = clock_rate; 
     338    param.channel_count = channel_count; 
     339    param.samples_per_frame = samples_per_frame; 
     340    param.bits_per_sample = bits_per_sample; 
     341 
     342    return pjmedia_snd_port_create2(pool, &param, p_port); 
    415343} 
    416344 
     
    427355                                                 pjmedia_snd_port **p_port) 
    428356{ 
    429     pjmedia_snd_port *snd_port; 
    430  
    431     PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL); 
    432  
    433     snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port); 
    434     PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM); 
    435  
    436     snd_port->rec_id = dev_id; 
    437     snd_port->dir = PJMEDIA_DIR_CAPTURE; 
    438     snd_port->options = options | DEFAULT_OPTIONS; 
    439     snd_port->clock_rate = clock_rate; 
    440     snd_port->channel_count = channel_count; 
    441     snd_port->samples_per_frame = samples_per_frame; 
    442     snd_port->bits_per_sample = bits_per_sample; 
    443  
    444     *p_port = snd_port; 
    445  
    446     /* Start sound device immediately. 
    447      * If there's no port connected, the sound callback will return 
    448      * empty signal. 
    449      */ 
    450     return start_sound_device( pool, snd_port ); 
     357    pjmedia_aud_param param; 
     358    pj_status_t status; 
     359 
     360    PJ_UNUSED_ARG(options); 
     361 
     362    status = pjmedia_aud_dev_default_param(dev_id, &param); 
     363    if (status != PJ_SUCCESS) 
     364        return status; 
     365 
     366    param.dir = PJMEDIA_DIR_CAPTURE; 
     367    param.rec_id = dev_id; 
     368    param.clock_rate = clock_rate; 
     369    param.channel_count = channel_count; 
     370    param.samples_per_frame = samples_per_frame; 
     371    param.bits_per_sample = bits_per_sample; 
     372 
     373    return pjmedia_snd_port_create2(pool, &param, p_port); 
    451374} 
    452375 
     
    464387                                                    pjmedia_snd_port **p_port) 
    465388{ 
     389    pjmedia_aud_param param; 
     390    pj_status_t status; 
     391 
     392    PJ_UNUSED_ARG(options); 
     393 
     394    status = pjmedia_aud_dev_default_param(dev_id, &param); 
     395    if (status != PJ_SUCCESS) 
     396        return status; 
     397 
     398    param.dir = PJMEDIA_DIR_PLAYBACK; 
     399    param.play_id = dev_id; 
     400    param.clock_rate = clock_rate; 
     401    param.channel_count = channel_count; 
     402    param.samples_per_frame = samples_per_frame; 
     403    param.bits_per_sample = bits_per_sample; 
     404 
     405    return pjmedia_snd_port_create2(pool, &param, p_port); 
     406} 
     407 
     408 
     409/* 
     410 * Create sound port. 
     411 */ 
     412PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool, 
     413                                             const pjmedia_aud_param *prm, 
     414                                             pjmedia_snd_port **p_port) 
     415{ 
    466416    pjmedia_snd_port *snd_port; 
    467  
    468     PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL); 
     417    pj_status_t status; 
     418 
     419    PJ_ASSERT_RETURN(pool && prm && p_port, PJ_EINVAL); 
    469420 
    470421    snd_port = PJ_POOL_ZALLOC_T(pool, pjmedia_snd_port); 
    471422    PJ_ASSERT_RETURN(snd_port, PJ_ENOMEM); 
    472423 
    473     snd_port->play_id = dev_id; 
    474     snd_port->dir = PJMEDIA_DIR_PLAYBACK; 
    475     snd_port->options = options | DEFAULT_OPTIONS; 
    476     snd_port->clock_rate = clock_rate; 
    477     snd_port->channel_count = channel_count; 
    478     snd_port->samples_per_frame = samples_per_frame; 
    479     snd_port->bits_per_sample = bits_per_sample; 
    480  
    481     *p_port = snd_port; 
    482  
     424    snd_port->dir = prm->dir; 
     425    snd_port->rec_id = prm->rec_id; 
     426    snd_port->play_id = prm->play_id; 
     427    snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; 
     428    snd_port->clock_rate = prm->clock_rate; 
     429    snd_port->channel_count = prm->channel_count; 
     430    snd_port->samples_per_frame = prm->samples_per_frame; 
     431    snd_port->bits_per_sample = prm->bits_per_sample; 
     432    pj_memcpy(&snd_port->aud_param, prm, sizeof(*prm)); 
     433     
    483434    /* Start sound device immediately. 
    484435     * If there's no port connected, the sound callback will return 
    485436     * empty signal. 
    486437     */ 
    487     return start_sound_device( pool, snd_port ); 
     438    status = start_sound_device( pool, snd_port ); 
     439    if (status != PJ_SUCCESS) { 
     440        pjmedia_snd_port_destroy(snd_port); 
     441        return status; 
     442    } 
     443 
     444    *p_port = snd_port; 
     445    return PJ_SUCCESS; 
    488446} 
    489447 
     
    503461 * Retrieve the sound stream associated by this sound device port. 
    504462 */ 
    505 PJ_DEF(pjmedia_snd_stream*) pjmedia_snd_port_get_snd_stream( 
     463PJ_DEF(pjmedia_aud_stream*) pjmedia_snd_port_get_snd_stream( 
    506464                                                pjmedia_snd_port *snd_port) 
    507465{ 
    508466    PJ_ASSERT_RETURN(snd_port, NULL); 
    509     return snd_port->snd_stream; 
    510 } 
    511  
    512  
    513 /* 
    514  * Enable AEC 
     467    return snd_port->aud_stream; 
     468} 
     469 
     470 
     471/* 
     472 * Change EC settings. 
    515473 */ 
    516474PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port, 
     
    519477                                             unsigned options) 
    520478{ 
    521     pjmedia_snd_stream_info si; 
     479    pjmedia_aud_param prm; 
    522480    pj_status_t status; 
    523481 
     
    527485                     PJ_EINVALIDOP); 
    528486 
    529     /* Sound port must have 16bits per sample */ 
    530     PJ_ASSERT_RETURN(snd_port->bits_per_sample == 16, 
    531                      PJ_EINVALIDOP); 
    532  
    533     /* Destroy AEC */ 
    534     if (snd_port->ec_state) { 
    535         pjmedia_echo_destroy(snd_port->ec_state); 
    536         snd_port->ec_state = NULL; 
    537     } 
    538  
    539     snd_port->aec_tail_len = tail_ms; 
    540  
    541     if (tail_ms != 0) { 
    542         unsigned delay_ms; 
    543  
    544         status = pjmedia_snd_stream_get_info(snd_port->snd_stream, &si); 
     487    /* Determine whether we use device or software EC */ 
     488    if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) { 
     489        /* We use device EC */ 
     490        pj_bool_t ec_enabled; 
     491 
     492        /* Query EC status */ 
     493        status = pjmedia_aud_stream_get_cap(snd_port->aud_stream, 
     494                                            PJMEDIA_AUD_DEV_CAP_EC, 
     495                                            &ec_enabled); 
    545496        if (status != PJ_SUCCESS) 
    546             si.rec_latency = si.play_latency = 0; 
    547  
    548         //No need to add input latency in the latency calculation, 
    549         //since actual input latency should be zero. 
    550         //delay_ms = (si.rec_latency + si.play_latency) * 1000 / 
    551         //         snd_port->clock_rate; 
    552         delay_ms = si.play_latency * 1000 / snd_port->clock_rate; 
    553         status = pjmedia_echo_create2(pool, snd_port->clock_rate,  
    554                                       snd_port->channel_count, 
    555                                       snd_port->samples_per_frame,  
    556                                       tail_ms, delay_ms, 
    557                                       options, &snd_port->ec_state); 
     497            return status; 
     498 
     499        if (tail_ms != 0) { 
     500            /* Change EC setting */ 
     501 
     502            if (!ec_enabled) { 
     503                /* Enable EC first */ 
     504                pj_bool_t value = PJ_TRUE; 
     505                status = pjmedia_aud_stream_set_cap(snd_port->aud_stream,  
     506                                                    PJMEDIA_AUD_DEV_CAP_EC, 
     507                                                    &value); 
     508                if (status != PJ_SUCCESS) 
     509                    return status; 
     510            } 
     511 
     512            if ((snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL)==0) { 
     513                /* Device does not support setting EC tail */ 
     514                return PJMEDIA_EAUD_INVCAP; 
     515            } 
     516 
     517            return pjmedia_aud_stream_set_cap(snd_port->aud_stream, 
     518                                              PJMEDIA_AUD_DEV_CAP_EC_TAIL, 
     519                                              &tail_ms); 
     520 
     521        } else if (ec_enabled) { 
     522            /* Disable EC */ 
     523            pj_bool_t value = PJ_FALSE; 
     524            return pjmedia_aud_stream_set_cap(snd_port->aud_stream,  
     525                                              PJMEDIA_AUD_DEV_CAP_EC, 
     526                                              &value); 
     527        } else { 
     528            /* Request to disable EC but EC has been disabled */ 
     529            /* Do nothing */ 
     530            return PJ_SUCCESS; 
     531        } 
     532 
     533    } else { 
     534        /* We use software EC */ 
     535 
     536        /* Check if there is change in parameters */ 
     537        if (tail_ms==snd_port->ec_tail_len && options==snd_port->ec_options) { 
     538            PJ_LOG(5,(THIS_FILE, "pjmedia_snd_port_set_ec() ignored, no " 
     539                                 "change in settings")); 
     540            return PJ_SUCCESS; 
     541        } 
     542 
     543        status = pjmedia_aud_stream_get_param(snd_port->aud_stream, &prm); 
    558544        if (status != PJ_SUCCESS) 
     545            return status; 
     546 
     547        /* Audio stream must be in PCM format */ 
     548        PJ_ASSERT_RETURN(prm.ext_fmt.id == PJMEDIA_FORMAT_PCM, 
     549                         PJ_EINVALIDOP); 
     550 
     551        /* Destroy AEC */ 
     552        if (snd_port->ec_state) { 
     553            pjmedia_echo_destroy(snd_port->ec_state); 
    559554            snd_port->ec_state = NULL; 
    560         else 
    561             snd_port->ec_suspended = PJ_FALSE; 
    562     } else { 
    563         PJ_LOG(4,(THIS_FILE, "Echo canceller is now disabled in the " 
    564                              "sound port")); 
    565         status = PJ_SUCCESS; 
     555        } 
     556 
     557        if (tail_ms != 0) { 
     558            unsigned delay_ms; 
     559 
     560            //No need to add input latency in the latency calculation, 
     561            //since actual input latency should be zero. 
     562            //delay_ms = (si.rec_latency + si.play_latency) * 1000 / 
     563            //     snd_port->clock_rate; 
     564            delay_ms = prm.output_latency_ms; 
     565            status = pjmedia_echo_create2(pool, snd_port->clock_rate,  
     566                                          snd_port->channel_count, 
     567                                          snd_port->samples_per_frame,  
     568                                          tail_ms, delay_ms, 
     569                                          options, &snd_port->ec_state); 
     570            if (status != PJ_SUCCESS) 
     571                snd_port->ec_state = NULL; 
     572            else 
     573                snd_port->ec_suspended = PJ_FALSE; 
     574        } else { 
     575            PJ_LOG(4,(THIS_FILE, "Echo canceller is now disabled in the " 
     576                                 "sound port")); 
     577            status = PJ_SUCCESS; 
     578        } 
     579 
     580        snd_port->ec_options = options; 
     581        snd_port->ec_tail_len = tail_ms; 
    566582    } 
    567583 
     
    575591{ 
    576592    PJ_ASSERT_RETURN(snd_port && p_length, PJ_EINVAL); 
    577     *p_length =  snd_port->ec_state ? snd_port->aec_tail_len : 0; 
    578     return PJ_SUCCESS; 
    579 } 
    580  
     593 
     594    /* Determine whether we use device or software EC */ 
     595    if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC) { 
     596        /* We use device EC */ 
     597        pj_bool_t ec_enabled; 
     598        pj_status_t status; 
     599 
     600        /* Query EC status */ 
     601        status = pjmedia_aud_stream_get_cap(snd_port->aud_stream, 
     602                                            PJMEDIA_AUD_DEV_CAP_EC, 
     603                                            &ec_enabled); 
     604        if (status != PJ_SUCCESS) 
     605            return status; 
     606 
     607        if (!ec_enabled) { 
     608            *p_length = 0; 
     609        } else if (snd_port->aud_caps & PJMEDIA_AUD_DEV_CAP_EC_TAIL) { 
     610            /* Get device EC tail */ 
     611            status = pjmedia_aud_stream_get_cap(snd_port->aud_stream, 
     612                                                PJMEDIA_AUD_DEV_CAP_EC_TAIL, 
     613                                                p_length); 
     614            if (status != PJ_SUCCESS) 
     615                return status; 
     616        } else { 
     617            /* Just use default */ 
     618            *p_length = AEC_TAIL; 
     619        } 
     620 
     621    } else { 
     622        /* We use software EC */ 
     623        *p_length =  snd_port->ec_state ? snd_port->ec_tail_len : 0; 
     624    } 
     625    return PJ_SUCCESS; 
     626} 
    581627 
    582628 
Note: See TracChangeset for help on using the changeset viewer.