Ignore:
Timestamp:
Mar 22, 2012 9:56:52 AM (12 years ago)
Author:
bennylp
Message:

Re: #1463 (Third party media support). Tnitial work and it works, tested on Linux. Details:

  • add PJSUA_MEDIA_HAS_PJMEDIA macro
  • move pjmedia specific implementation in pjsua_media.c and pjsua_call.c into pjsua_aud.c
  • add pjsip-apps/src/third_party_media sample containing:
    • alt_pjsua_aud.c
    • alt_pjsua_vid.c
  • moved pjmedia_vid_stream_info_from_sdp() into pjmedia/vid_stream_info.c
  • moved pjmedia_stream_info_from_sdp() into pjmedia/stream_info.c
  • misc: fixed mips_test.c if codecs are disabled
File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c

    r3981 r3982  
    2626#define DEFAULT_RTP_PORT        4000 
    2727 
    28 #define NULL_SND_DEV_ID         -99 
    29  
    3028#ifndef PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT 
    3129#   define PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT  0 
    3230#endif 
    3331 
    34  
    3532/* Next RTP port to be used */ 
    3633static pj_uint16_t next_rtp_port; 
    37  
    38 /* Open sound dev */ 
    39 static pj_status_t open_snd_dev(pjmedia_snd_port_param *param); 
    40 /* Close existing sound device */ 
    41 static void close_snd_dev(void); 
    42 /* Create audio device param */ 
    43 static pj_status_t create_aud_param(pjmedia_aud_param *param, 
    44                                     pjmedia_aud_dev_index capture_dev, 
    45                                     pjmedia_aud_dev_index playback_dev, 
    46                                     unsigned clock_rate, 
    47                                     unsigned channel_count, 
    48                                     unsigned samples_per_frame, 
    49                                     unsigned bits_per_sample); 
    50  
    5134 
    5235static void pjsua_media_config_dup(pj_pool_t *pool, 
     
    6548pj_status_t pjsua_media_subsys_init(const pjsua_media_config *cfg) 
    6649{ 
    67     pj_str_t codec_id = {NULL, 0}; 
    68     unsigned opt; 
    69     pjmedia_audio_codec_config codec_cfg; 
    7050    pj_status_t status; 
    71  
    72     /* To suppress warning about unused var when all codecs are disabled */ 
    73     PJ_UNUSED_ARG(codec_id); 
    7451 
    7552    pj_log_push_indent(); 
     
    11693    } 
    11794 
    118     /* 
    119      * Register all codecs 
    120      */ 
    121     pjmedia_audio_codec_config_default(&codec_cfg); 
    122     codec_cfg.speex.quality = pjsua_var.media_cfg.quality; 
    123     codec_cfg.speex.complexity = -1; 
    124     codec_cfg.ilbc.mode = pjsua_var.media_cfg.ilbc_mode; 
    125  
    126 #if PJMEDIA_HAS_PASSTHROUGH_CODECS 
    127     /* Register passthrough codecs */ 
    128     { 
    129         unsigned aud_idx; 
    130         unsigned ext_fmt_cnt = 0; 
    131         pjmedia_format ext_fmts[32]; 
    132  
    133         /* List extended formats supported by audio devices */ 
    134         for (aud_idx = 0; aud_idx < pjmedia_aud_dev_count(); ++aud_idx) { 
    135             pjmedia_aud_dev_info aud_info; 
    136             unsigned i; 
    137              
    138             status = pjmedia_aud_dev_get_info(aud_idx, &aud_info); 
    139             if (status != PJ_SUCCESS) { 
    140                 pjsua_perror(THIS_FILE, "Error querying audio device info", 
    141                              status); 
    142                 goto on_error; 
    143             } 
    144              
    145             /* Collect extended formats supported by this audio device */ 
    146             for (i = 0; i < aud_info.ext_fmt_cnt; ++i) { 
    147                 unsigned j; 
    148                 pj_bool_t is_listed = PJ_FALSE; 
    149  
    150                 /* See if this extended format is already in the list */ 
    151                 for (j = 0; j < ext_fmt_cnt && !is_listed; ++j) { 
    152                     if (ext_fmts[j].id == aud_info.ext_fmt[i].id && 
    153                         ext_fmts[j].det.aud.avg_bps == 
    154                         aud_info.ext_fmt[i].det.aud.avg_bps) 
    155                     { 
    156                         is_listed = PJ_TRUE; 
    157                     } 
    158                 } 
    159                  
    160                 /* Put this format into the list, if it is not in the list */ 
    161                 if (!is_listed) 
    162                     ext_fmts[ext_fmt_cnt++] = aud_info.ext_fmt[i]; 
    163  
    164                 pj_assert(ext_fmt_cnt <= PJ_ARRAY_SIZE(ext_fmts)); 
    165             } 
    166         } 
    167  
    168         /* Init the passthrough codec with supported formats only */ 
    169         codec_cfg.passthrough.setting.fmt_cnt = ext_fmt_cnt; 
    170         codec_cfg.passthrough.setting.fmts = ext_fmts; 
    171         codec_cfg.passthrough.setting.ilbc_mode = cfg->ilbc_mode; 
    172     } 
    173 #endif /* PJMEDIA_HAS_PASSTHROUGH_CODECS */ 
    174  
    175     /* Register all codecs */ 
    176     status = pjmedia_codec_register_audio_codecs(pjsua_var.med_endpt, 
    177                                                  &codec_cfg); 
    178     if (status != PJ_SUCCESS) { 
    179         PJ_PERROR(1,(THIS_FILE, status, "Error registering codecs")); 
     95    status = pjsua_aud_subsys_init(); 
     96    if (status != PJ_SUCCESS) 
    18097        goto on_error; 
    181     } 
    182  
    183     /* Set speex/16000 to higher priority*/ 
    184     codec_id = pj_str("speex/16000"); 
    185     pjmedia_codec_mgr_set_codec_priority( 
    186         pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt), 
    187         &codec_id, PJMEDIA_CODEC_PRIO_NORMAL+2); 
    188  
    189     /* Set speex/8000 to next higher priority*/ 
    190     codec_id = pj_str("speex/8000"); 
    191     pjmedia_codec_mgr_set_codec_priority( 
    192         pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt), 
    193         &codec_id, PJMEDIA_CODEC_PRIO_NORMAL+1); 
    194  
    195     /* Disable ALL L16 codecs */ 
    196     codec_id = pj_str("L16"); 
    197     pjmedia_codec_mgr_set_codec_priority(  
    198         pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt), 
    199         &codec_id, PJMEDIA_CODEC_PRIO_DISABLED); 
    200  
    201  
    202     /* Save additional conference bridge parameters for future 
    203      * reference. 
    204      */ 
    205     pjsua_var.mconf_cfg.channel_count = pjsua_var.media_cfg.channel_count; 
    206     pjsua_var.mconf_cfg.bits_per_sample = 16; 
    207     pjsua_var.mconf_cfg.samples_per_frame = pjsua_var.media_cfg.clock_rate *  
    208                                             pjsua_var.mconf_cfg.channel_count * 
    209                                             pjsua_var.media_cfg.audio_frame_ptime /  
    210                                             1000; 
    211  
    212     /* Init options for conference bridge. */ 
    213     opt = PJMEDIA_CONF_NO_DEVICE; 
    214     if (pjsua_var.media_cfg.quality >= 3 && 
    215         pjsua_var.media_cfg.quality <= 4) 
    216     { 
    217         opt |= PJMEDIA_CONF_SMALL_FILTER; 
    218     } 
    219     else if (pjsua_var.media_cfg.quality < 3) { 
    220         opt |= PJMEDIA_CONF_USE_LINEAR; 
    221     } 
    222          
    223     /* Init conference bridge. */ 
    224     status = pjmedia_conf_create(pjsua_var.pool,  
    225                                  pjsua_var.media_cfg.max_media_ports, 
    226                                  pjsua_var.media_cfg.clock_rate,  
    227                                  pjsua_var.mconf_cfg.channel_count, 
    228                                  pjsua_var.mconf_cfg.samples_per_frame,  
    229                                  pjsua_var.mconf_cfg.bits_per_sample,  
    230                                  opt, &pjsua_var.mconf); 
    231     if (status != PJ_SUCCESS) { 
    232         pjsua_perror(THIS_FILE, "Error creating conference bridge",  
    233                      status); 
    234         goto on_error; 
    235     } 
    236  
    237     /* Are we using the audio switchboard (a.k.a APS-Direct)? */ 
    238     pjsua_var.is_mswitch = pjmedia_conf_get_master_port(pjsua_var.mconf) 
    239                             ->info.signature == PJMEDIA_CONF_SWITCH_SIGNATURE; 
    240  
    241     /* Create null port just in case user wants to use null sound. */ 
    242     status = pjmedia_null_port_create(pjsua_var.pool,  
    243                                       pjsua_var.media_cfg.clock_rate, 
    244                                       pjsua_var.mconf_cfg.channel_count, 
    245                                       pjsua_var.mconf_cfg.samples_per_frame, 
    246                                       pjsua_var.mconf_cfg.bits_per_sample, 
    247                                       &pjsua_var.null_port); 
    248     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 
    24998 
    25099#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) 
     
    273122} 
    274123 
    275  
    276 /* Check if sound device is idle. */ 
    277 static void check_snd_dev_idle() 
    278 { 
    279     unsigned call_cnt; 
    280  
    281     /* Check if the sound device auto-close feature is disabled. */ 
    282     if (pjsua_var.media_cfg.snd_auto_close_time < 0) 
    283         return; 
    284  
    285     /* Check if the sound device is currently closed. */ 
    286     if (!pjsua_var.snd_is_on) 
    287         return; 
    288  
    289     /* Get the call count, we shouldn't close the sound device when there is 
    290      * any calls active. 
    291      */ 
    292     call_cnt = pjsua_call_get_count(); 
    293  
    294     /* When this function is called from pjsua_media_channel_deinit() upon 
    295      * disconnecting call, actually the call count hasn't been updated/ 
    296      * decreased. So we put additional check here, if there is only one 
    297      * call and it's in DISCONNECTED state, there is actually no active 
    298      * call. 
    299      */ 
    300     if (call_cnt == 1) { 
    301         pjsua_call_id call_id; 
    302         pj_status_t status; 
    303  
    304         status = pjsua_enum_calls(&call_id, &call_cnt); 
    305         if (status == PJ_SUCCESS && call_cnt > 0 && 
    306             !pjsua_call_is_active(call_id)) 
    307         { 
    308             call_cnt = 0; 
    309         } 
    310     } 
    311  
    312     /* Activate sound device auto-close timer if sound device is idle. 
    313      * It is idle when there is no port connection in the bridge and 
    314      * there is no active call. 
    315      */ 
    316     if (pjsua_var.snd_idle_timer.id == PJ_FALSE && 
    317         call_cnt == 0 && 
    318         pjmedia_conf_get_connect_count(pjsua_var.mconf) == 0) 
    319     { 
    320         pj_time_val delay; 
    321  
    322         delay.msec = 0; 
    323         delay.sec = pjsua_var.media_cfg.snd_auto_close_time; 
    324  
    325         pjsua_var.snd_idle_timer.id = PJ_TRUE; 
    326         pjsip_endpt_schedule_timer(pjsua_var.endpt, &pjsua_var.snd_idle_timer, 
    327                                    &delay); 
    328     } 
    329 } 
    330  
    331  
    332 /* Timer callback to close sound device */ 
    333 static void close_snd_timer_cb( pj_timer_heap_t *th, 
    334                                 pj_timer_entry *entry) 
    335 { 
    336     PJ_UNUSED_ARG(th); 
    337  
    338     PJSUA_LOCK(); 
    339     if (entry->id) { 
    340         PJ_LOG(4,(THIS_FILE,"Closing sound device after idle for %d seconds", 
    341                   pjsua_var.media_cfg.snd_auto_close_time)); 
    342  
    343         entry->id = PJ_FALSE; 
    344  
    345         close_snd_dev(); 
    346     } 
    347     PJSUA_UNLOCK(); 
    348 } 
    349  
    350  
    351124/* 
    352125 * Start pjsua media subsystem. 
     
    375148#endif 
    376149 
    377     pj_timer_entry_init(&pjsua_var.snd_idle_timer, PJ_FALSE, NULL, 
    378                         &close_snd_timer_cb); 
     150    /* Audio */ 
     151    status = pjsua_aud_subsys_start(); 
     152    if (status != PJ_SUCCESS) { 
     153        pj_log_pop_indent(); 
     154        return status; 
     155    } 
    379156 
    380157    /* Video */ 
     
    382159    status = pjsua_vid_subsys_start(); 
    383160    if (status != PJ_SUCCESS) { 
     161        pjsua_aud_subsys_destroy(); 
    384162        pj_log_pop_indent(); 
    385163        return status; 
     
    408186    pj_log_push_indent(); 
    409187 
    410     close_snd_dev(); 
    411  
    412     if (pjsua_var.mconf) { 
    413         pjmedia_conf_destroy(pjsua_var.mconf); 
    414         pjsua_var.mconf = NULL; 
    415     } 
    416  
    417     if (pjsua_var.null_port) { 
    418         pjmedia_port_destroy(pjsua_var.null_port); 
    419         pjsua_var.null_port = NULL; 
    420     } 
    421  
    422     /* Destroy file players */ 
    423     for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.player); ++i) { 
    424         if (pjsua_var.player[i].port) { 
    425             pjmedia_port_destroy(pjsua_var.player[i].port); 
    426             pjsua_var.player[i].port = NULL; 
    427         } 
    428     } 
    429  
    430     /* Destroy file recorders */ 
    431     for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.recorder); ++i) { 
    432         if (pjsua_var.recorder[i].port) { 
    433             pjmedia_port_destroy(pjsua_var.recorder[i].port); 
    434             pjsua_var.recorder[i].port = NULL; 
    435         } 
     188    if (pjsua_var.med_endpt) { 
     189        pjsua_aud_subsys_destroy(); 
    436190    } 
    437191 
     
    13281082 
    13291083/* Set media transport state and notify the application via the callback. */ 
    1330 void set_media_tp_state(pjsua_call_media *call_med, 
    1331                         pjsua_med_tp_st tp_st) 
     1084void pjsua_set_media_tp_state(pjsua_call_media *call_med, 
     1085                              pjsua_med_tp_st tp_st) 
    13321086{ 
    13331087    if (pjsua_var.ua_cfg.cb.on_call_media_transport_state && 
     
    13631117 
    13641118    if (call_med->tp_st == PJSUA_MED_TP_CREATING) 
    1365         set_media_tp_state(call_med, PJSUA_MED_TP_IDLE); 
     1119        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE); 
    13661120 
    13671121    if (!call_med->tp_orig && 
     
    14671221    if (call_med->tp == NULL) { 
    14681222#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) 
    1469         pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id]; 
    1470  
    14711223        /* While in initial call, set default video devices */ 
    14721224        if (type == PJMEDIA_TYPE_VIDEO) { 
    1473             call_med->strm.v.rdr_dev = acc->cfg.vid_rend_dev; 
    1474             call_med->strm.v.cap_dev = acc->cfg.vid_cap_dev; 
    1475             if (call_med->strm.v.rdr_dev == PJMEDIA_VID_DEFAULT_RENDER_DEV) { 
    1476                 pjmedia_vid_dev_info info; 
    1477                 pjmedia_vid_dev_get_info(call_med->strm.v.rdr_dev, &info); 
    1478                 call_med->strm.v.rdr_dev = info.id; 
    1479             } 
    1480             if (call_med->strm.v.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) { 
    1481                 pjmedia_vid_dev_info info; 
    1482                 pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev, &info); 
    1483                 call_med->strm.v.cap_dev = info.id; 
    1484             } 
     1225            status = pjsua_vid_channel_init(call_med); 
     1226            if (status != PJ_SUCCESS) 
     1227                return status; 
    14851228        } 
    14861229#endif 
    14871230 
    1488         set_media_tp_state(call_med, PJSUA_MED_TP_CREATING); 
     1231        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_CREATING); 
    14891232 
    14901233        if (pjsua_var.media_cfg.enable_ice) { 
     
    15151258    } else if (call_med->tp_st == PJSUA_MED_TP_DISABLED) { 
    15161259        /* Media is being reenabled. */ 
    1517         set_media_tp_state(call_med, PJSUA_MED_TP_INIT); 
     1260        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT); 
    15181261    } 
    15191262 
     
    16161359            } 
    16171360 
    1618             set_media_tp_state(call_med, PJSUA_MED_TP_INIT); 
     1361            pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT); 
    16191362        } 
    16201363    } 
     
    18811624                pj_assert(call_med->tp_st == PJSUA_MED_TP_INIT ||  
    18821625                          call_med->tp_st == PJSUA_MED_TP_RUNNING); 
    1883                 set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED); 
     1626                pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED); 
    18841627            } 
    18851628 
     
    22431986 
    22441987        if (call_med->type == PJMEDIA_TYPE_AUDIO) { 
    2245             pjmedia_stream *strm = call_med->strm.a.stream; 
    2246             pjmedia_rtcp_stat stat; 
    2247  
    2248             if (strm) { 
    2249                 if (call_med->strm.a.conf_slot != PJSUA_INVALID_ID) { 
    2250                     if (pjsua_var.mconf) { 
    2251                         pjsua_conf_remove_port(call_med->strm.a.conf_slot); 
    2252                     } 
    2253                     call_med->strm.a.conf_slot = PJSUA_INVALID_ID; 
    2254                 } 
    2255  
    2256                 if ((call_med->dir & PJMEDIA_DIR_ENCODING) && 
    2257                     (pjmedia_stream_get_stat(strm, &stat) == PJ_SUCCESS)) 
    2258                 { 
    2259                     /* Save RTP timestamp & sequence, so when media session is 
    2260                      * restarted, those values will be restored as the initial 
    2261                      * RTP timestamp & sequence of the new media session. So in 
    2262                      * the same call session, RTP timestamp and sequence are 
    2263                      * guaranteed to be contigue. 
    2264                      */ 
    2265                     call_med->rtp_tx_seq_ts_set = 1 | (1 << 1); 
    2266                     call_med->rtp_tx_seq = stat.rtp_tx_last_seq; 
    2267                     call_med->rtp_tx_ts = stat.rtp_tx_last_ts; 
    2268                 } 
    2269  
    2270                 if (pjsua_var.ua_cfg.cb.on_stream_destroyed) { 
    2271                     pjsua_var.ua_cfg.cb.on_stream_destroyed(call_id, strm, mi); 
    2272                 } 
    2273  
    2274                 pjmedia_stream_destroy(strm); 
    2275                 call_med->strm.a.stream = NULL; 
    2276             } 
     1988            pjsua_aud_stop_stream(call_med); 
    22771989        } 
    22781990 
    22791991#if PJMEDIA_HAS_VIDEO 
    22801992        else if (call_med->type == PJMEDIA_TYPE_VIDEO) { 
    2281             stop_video_stream(call_med); 
     1993            pjsua_vid_stop_stream(call_med); 
    22821994        } 
    22831995#endif 
     
    23152027    pj_log_push_indent(); 
    23162028 
     2029    stop_media_session(call_id); 
     2030 
    23172031    for (mi=0; mi<call->med_cnt; ++mi) { 
    23182032        pjsua_call_media *call_med = &call->media[mi]; 
    23192033 
    2320         if (call_med->type == PJMEDIA_TYPE_AUDIO && call_med->strm.a.stream) 
    2321             pjmedia_stream_send_rtcp_bye(call_med->strm.a.stream); 
    2322     } 
    2323  
    2324     stop_media_session(call_id); 
    2325  
    2326     for (mi=0; mi<call->med_cnt; ++mi) { 
    2327         pjsua_call_media *call_med = &call->media[mi]; 
    2328  
    23292034        if (call_med->tp_st > PJSUA_MED_TP_IDLE) { 
    23302035            pjmedia_transport_media_stop(call_med->tp); 
    2331             set_media_tp_state(call_med, PJSUA_MED_TP_IDLE); 
     2036            pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE); 
    23322037        } 
    23332038 
     
    23452050    } 
    23462051 
    2347     check_snd_dev_idle(); 
    23482052    pj_log_pop_indent(); 
    23492053 
     
    23512055} 
    23522056 
    2353  
    2354 /* 
    2355  * DTMF callback from the stream. 
    2356  */ 
    2357 static void dtmf_callback(pjmedia_stream *strm, void *user_data, 
    2358                           int digit) 
    2359 { 
    2360     PJ_UNUSED_ARG(strm); 
    2361  
    2362     pj_log_push_indent(); 
    2363  
    2364     /* For discussions about call mutex protection related to this  
    2365      * callback, please see ticket #460: 
    2366      *  http://trac.pjsip.org/repos/ticket/460#comment:4 
    2367      */ 
    2368     if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { 
    2369         pjsua_call_id call_id; 
    2370  
    2371         call_id = (pjsua_call_id)(long)user_data; 
    2372         pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit); 
    2373     } 
    2374  
    2375     pj_log_pop_indent(); 
    2376 } 
    2377  
    2378  
    2379 static pj_status_t audio_channel_update(pjsua_call_media *call_med, 
    2380                                         pj_pool_t *tmp_pool, 
    2381                                         const pjmedia_sdp_session *local_sdp, 
    2382                                         const pjmedia_sdp_session *remote_sdp) 
    2383 { 
    2384     pjsua_call *call = call_med->call; 
    2385     pjmedia_stream_info the_si, *si = &the_si; 
    2386     pjmedia_port *media_port; 
    2387     unsigned strm_idx = call_med->idx; 
    2388     pj_status_t status; 
    2389  
    2390     PJ_LOG(4,(THIS_FILE,"Audio channel update..")); 
    2391     pj_log_push_indent(); 
    2392      
    2393     status = pjmedia_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt, 
    2394                                           local_sdp, remote_sdp, strm_idx); 
    2395     if (status != PJ_SUCCESS) 
    2396         goto on_return; 
    2397  
    2398     si->rtcp_sdes_bye_disabled = PJ_TRUE; 
    2399  
    2400     /* Check if no media is active */ 
    2401     if (si->dir == PJMEDIA_DIR_NONE) { 
    2402         /* Call media state */ 
    2403         call_med->state = PJSUA_CALL_MEDIA_NONE; 
    2404  
    2405         /* Call media direction */ 
    2406         call_med->dir = PJMEDIA_DIR_NONE; 
    2407  
    2408     } else { 
    2409         pjmedia_transport_info tp_info; 
    2410  
    2411         /* Start/restart media transport */ 
    2412         status = pjmedia_transport_media_start(call_med->tp, 
    2413                                                tmp_pool, local_sdp, 
    2414                                                remote_sdp, strm_idx); 
    2415         if (status != PJ_SUCCESS) 
    2416             goto on_return; 
    2417  
    2418         set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING); 
    2419  
    2420         /* Get remote SRTP usage policy */ 
    2421         pjmedia_transport_info_init(&tp_info); 
    2422         pjmedia_transport_get_info(call_med->tp, &tp_info); 
    2423         if (tp_info.specific_info_cnt > 0) { 
    2424             unsigned i; 
    2425             for (i = 0; i < tp_info.specific_info_cnt; ++i) { 
    2426                 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)  
    2427                 { 
    2428                     pjmedia_srtp_info *srtp_info =  
    2429                                 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer; 
    2430  
    2431                     call_med->rem_srtp_use = srtp_info->peer_use; 
    2432                     break; 
    2433                 } 
    2434             } 
    2435         } 
    2436  
    2437         /* Override ptime, if this option is specified. */ 
    2438         if (pjsua_var.media_cfg.ptime != 0) { 
    2439             si->param->setting.frm_per_pkt = (pj_uint8_t) 
    2440                 (pjsua_var.media_cfg.ptime / si->param->info.frm_ptime); 
    2441             if (si->param->setting.frm_per_pkt == 0) 
    2442                 si->param->setting.frm_per_pkt = 1; 
    2443         } 
    2444  
    2445         /* Disable VAD, if this option is specified. */ 
    2446         if (pjsua_var.media_cfg.no_vad) { 
    2447             si->param->setting.vad = 0; 
    2448         } 
    2449  
    2450  
    2451         /* Optionally, application may modify other stream settings here 
    2452          * (such as jitter buffer parameters, codec ptime, etc.) 
    2453          */ 
    2454         si->jb_init = pjsua_var.media_cfg.jb_init; 
    2455         si->jb_min_pre = pjsua_var.media_cfg.jb_min_pre; 
    2456         si->jb_max_pre = pjsua_var.media_cfg.jb_max_pre; 
    2457         si->jb_max = pjsua_var.media_cfg.jb_max; 
    2458  
    2459         /* Set SSRC */ 
    2460         si->ssrc = call_med->ssrc; 
    2461  
    2462         /* Set RTP timestamp & sequence, normally these value are intialized 
    2463          * automatically when stream session created, but for some cases (e.g: 
    2464          * call reinvite, call update) timestamp and sequence need to be kept 
    2465          * contigue. 
    2466          */ 
    2467         si->rtp_ts = call_med->rtp_tx_ts; 
    2468         si->rtp_seq = call_med->rtp_tx_seq; 
    2469         si->rtp_seq_ts_set = call_med->rtp_tx_seq_ts_set; 
    2470  
    2471 #if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0 
    2472         /* Enable/disable stream keep-alive and NAT hole punch. */ 
    2473         si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka; 
    2474 #endif 
    2475  
    2476         /* Create session based on session info. */ 
    2477         status = pjmedia_stream_create(pjsua_var.med_endpt, NULL, si, 
    2478                                        call_med->tp, NULL, 
    2479                                        &call_med->strm.a.stream); 
    2480         if (status != PJ_SUCCESS) { 
    2481             goto on_return; 
    2482         } 
    2483  
    2484         /* Start stream */ 
    2485         status = pjmedia_stream_start(call_med->strm.a.stream); 
    2486         if (status != PJ_SUCCESS) { 
    2487             goto on_return; 
    2488         } 
    2489  
    2490         if (call_med->prev_state == PJSUA_CALL_MEDIA_NONE) 
    2491             pjmedia_stream_send_rtcp_sdes(call_med->strm.a.stream); 
    2492  
    2493         /* If DTMF callback is installed by application, install our 
    2494          * callback to the session. 
    2495          */ 
    2496         if (pjsua_var.ua_cfg.cb.on_dtmf_digit) { 
    2497             pjmedia_stream_set_dtmf_callback(call_med->strm.a.stream, 
    2498                                              &dtmf_callback, 
    2499                                              (void*)(long)(call->index)); 
    2500         } 
    2501  
    2502         /* Get the port interface of the first stream in the session. 
    2503          * We need the port interface to add to the conference bridge. 
    2504          */ 
    2505         pjmedia_stream_get_port(call_med->strm.a.stream, &media_port); 
    2506  
    2507         /* Notify application about stream creation. 
    2508          * Note: application may modify media_port to point to different 
    2509          * media port 
    2510          */ 
    2511         if (pjsua_var.ua_cfg.cb.on_stream_created) { 
    2512             pjsua_var.ua_cfg.cb.on_stream_created(call->index, 
    2513                                                   call_med->strm.a.stream, 
    2514                                                   strm_idx, &media_port); 
    2515         } 
    2516  
    2517         /* 
    2518          * Add the call to conference bridge. 
    2519          */ 
    2520         { 
    2521             char tmp[PJSIP_MAX_URL_SIZE]; 
    2522             pj_str_t port_name; 
    2523  
    2524             port_name.ptr = tmp; 
    2525             port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, 
    2526                                              call->inv->dlg->remote.info->uri, 
    2527                                              tmp, sizeof(tmp)); 
    2528             if (port_name.slen < 1) { 
    2529                 port_name = pj_str("call"); 
    2530             } 
    2531             status = pjmedia_conf_add_port( pjsua_var.mconf,  
    2532                                             call->inv->pool_prov, 
    2533                                             media_port,  
    2534                                             &port_name, 
    2535                                             (unsigned*) 
    2536                                             &call_med->strm.a.conf_slot); 
    2537             if (status != PJ_SUCCESS) { 
    2538                 goto on_return; 
    2539             } 
    2540         } 
    2541  
    2542         /* Call media direction */ 
    2543         call_med->dir = si->dir; 
    2544  
    2545         /* Call media state */ 
    2546         if (call->local_hold) 
    2547             call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD; 
    2548         else if (call_med->dir == PJMEDIA_DIR_DECODING) 
    2549             call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD; 
    2550         else 
    2551             call_med->state = PJSUA_CALL_MEDIA_ACTIVE; 
    2552     } 
    2553  
    2554     /* Print info. */ 
    2555     { 
    2556         char info[80]; 
    2557         int info_len = 0; 
    2558         int len; 
    2559         const char *dir; 
    2560  
    2561         switch (si->dir) { 
    2562         case PJMEDIA_DIR_NONE: 
    2563             dir = "inactive"; 
    2564             break; 
    2565         case PJMEDIA_DIR_ENCODING: 
    2566             dir = "sendonly"; 
    2567             break; 
    2568         case PJMEDIA_DIR_DECODING: 
    2569             dir = "recvonly"; 
    2570             break; 
    2571         case PJMEDIA_DIR_ENCODING_DECODING: 
    2572             dir = "sendrecv"; 
    2573             break; 
    2574         default: 
    2575             dir = "unknown"; 
    2576             break; 
    2577         } 
    2578         len = pj_ansi_sprintf( info+info_len, 
    2579                                ", stream #%d: %.*s (%s)", strm_idx, 
    2580                                (int)si->fmt.encoding_name.slen, 
    2581                                si->fmt.encoding_name.ptr, 
    2582                                dir); 
    2583         if (len > 0) 
    2584             info_len += len; 
    2585         PJ_LOG(4,(THIS_FILE,"Audio updated%s", info)); 
    2586     } 
    2587  
    2588 on_return: 
    2589     pj_log_pop_indent(); 
    2590     return status; 
    2591 } 
    25922057 
    25932058pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, 
     
    26902155        } 
    26912156 
    2692         switch (call_med->type) { 
    2693         case PJMEDIA_TYPE_AUDIO: 
    2694             status = audio_channel_update(call_med, tmp_pool, 
    2695                                           local_sdp, remote_sdp); 
     2157        if (call_med->type==PJMEDIA_TYPE_AUDIO) { 
     2158            pjmedia_stream_info the_si, *si = &the_si; 
     2159 
     2160            status = pjmedia_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt, 
     2161                                                  local_sdp, remote_sdp, mi); 
     2162            if (status != PJ_SUCCESS) { 
     2163                PJ_PERROR(1,(THIS_FILE, status, 
     2164                             "pjmedia_stream_info_from_sdp() failed " 
     2165                                 "for call_id %d media %d", 
     2166                             call_id, mi)); 
     2167                continue; 
     2168            } 
     2169 
     2170            /* Check if no media is active */ 
     2171            if (si->dir == PJMEDIA_DIR_NONE) { 
     2172                /* Update call media state and direction */ 
     2173                call_med->state = PJSUA_CALL_MEDIA_NONE; 
     2174                call_med->dir = PJMEDIA_DIR_NONE; 
     2175 
     2176            } else { 
     2177                pjmedia_transport_info tp_info; 
     2178 
     2179                /* Start/restart media transport based on info in SDP */ 
     2180                status = pjmedia_transport_media_start(call_med->tp, 
     2181                                                       tmp_pool, local_sdp, 
     2182                                                       remote_sdp, mi); 
     2183                if (status != PJ_SUCCESS) { 
     2184                    PJ_PERROR(1,(THIS_FILE, status, 
     2185                                 "pjmedia_transport_media_start() failed " 
     2186                                     "for call_id %d media %d", 
     2187                                 call_id, mi)); 
     2188                    continue; 
     2189                } 
     2190 
     2191                pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING); 
     2192 
     2193                /* Get remote SRTP usage policy */ 
     2194                pjmedia_transport_info_init(&tp_info); 
     2195                pjmedia_transport_get_info(call_med->tp, &tp_info); 
     2196                if (tp_info.specific_info_cnt > 0) { 
     2197                    unsigned i; 
     2198                    for (i = 0; i < tp_info.specific_info_cnt; ++i) { 
     2199                        if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP) 
     2200                        { 
     2201                            pjmedia_srtp_info *srtp_info = 
     2202                                        (pjmedia_srtp_info*) tp_info.spc_info[i].buffer; 
     2203 
     2204                            call_med->rem_srtp_use = srtp_info->peer_use; 
     2205                            break; 
     2206                        } 
     2207                    } 
     2208                } 
     2209 
     2210                /* Call media direction */ 
     2211                call_med->dir = si->dir; 
     2212 
     2213                /* Call media state */ 
     2214                if (call->local_hold) 
     2215                    call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD; 
     2216                else if (call_med->dir == PJMEDIA_DIR_DECODING) 
     2217                    call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD; 
     2218                else 
     2219                    call_med->state = PJSUA_CALL_MEDIA_ACTIVE; 
     2220            } 
     2221 
     2222            /* Call implementation */ 
     2223            status = pjsua_aud_channel_update(call_med, tmp_pool, si, 
     2224                                              local_sdp, remote_sdp); 
     2225            if (status != PJ_SUCCESS) { 
     2226                PJ_PERROR(1,(THIS_FILE, status, 
     2227                             "pjsua_aud_channel_update() failed " 
     2228                                 "for call_id %d media %d", 
     2229                             call_id, mi)); 
     2230                continue; 
     2231            } 
     2232 
     2233            /* Print info. */ 
     2234            if (status == PJ_SUCCESS) { 
     2235                char info[80]; 
     2236                int info_len = 0; 
     2237                int len; 
     2238                const char *dir; 
     2239 
     2240                switch (si->dir) { 
     2241                case PJMEDIA_DIR_NONE: 
     2242                    dir = "inactive"; 
     2243                    break; 
     2244                case PJMEDIA_DIR_ENCODING: 
     2245                    dir = "sendonly"; 
     2246                    break; 
     2247                case PJMEDIA_DIR_DECODING: 
     2248                    dir = "recvonly"; 
     2249                    break; 
     2250                case PJMEDIA_DIR_ENCODING_DECODING: 
     2251                    dir = "sendrecv"; 
     2252                    break; 
     2253                default: 
     2254                    dir = "unknown"; 
     2255                    break; 
     2256                } 
     2257                len = pj_ansi_sprintf( info+info_len, 
     2258                                       ", stream #%d: %.*s (%s)", mi, 
     2259                                       (int)si->fmt.encoding_name.slen, 
     2260                                       si->fmt.encoding_name.ptr, 
     2261                                       dir); 
     2262                if (len > 0) 
     2263                    info_len += len; 
     2264                PJ_LOG(4,(THIS_FILE,"Audio updated%s", info)); 
     2265            } 
     2266 
     2267 
    26962268            if (call->audio_idx==-1 && status==PJ_SUCCESS && 
    2697                 call_med->strm.a.stream) 
     2269                si->dir != PJMEDIA_DIR_NONE) 
    26982270            { 
    26992271                call->audio_idx = mi; 
    27002272            } 
    2701             break; 
     2273 
    27022274#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) 
    2703         case PJMEDIA_TYPE_VIDEO: 
    2704             status = video_channel_update(call_med, tmp_pool, 
    2705                                           local_sdp, remote_sdp); 
    2706             break; 
     2275        } else if (call_med->type==PJMEDIA_TYPE_VIDEO) { 
     2276            pjmedia_vid_stream_info the_si, *si = &the_si; 
     2277 
     2278            status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt, 
     2279                                                      local_sdp, remote_sdp, mi); 
     2280            if (status != PJ_SUCCESS) { 
     2281                PJ_PERROR(1,(THIS_FILE, status, 
     2282                             "pjmedia_vid_stream_info_from_sdp() failed " 
     2283                                 "for call_id %d media %d", 
     2284                             call_id, mi)); 
     2285                continue; 
     2286            } 
     2287 
     2288            /* Check if no media is active */ 
     2289            if (si->dir == PJMEDIA_DIR_NONE) { 
     2290                /* Call media state */ 
     2291                call_med->state = PJSUA_CALL_MEDIA_NONE; 
     2292 
     2293                /* Call media direction */ 
     2294                call_med->dir = PJMEDIA_DIR_NONE; 
     2295 
     2296            } else { 
     2297                pjmedia_transport_info tp_info; 
     2298 
     2299                /* Start/restart media transport */ 
     2300                status = pjmedia_transport_media_start(call_med->tp, 
     2301                                                       tmp_pool, local_sdp, 
     2302                                                       remote_sdp, mi); 
     2303                if (status != PJ_SUCCESS) { 
     2304                    PJ_PERROR(1,(THIS_FILE, status, 
     2305                                 "pjmedia_transport_media_start() failed " 
     2306                                     "for call_id %d media %d", 
     2307                                 call_id, mi)); 
     2308                    continue; 
     2309                } 
     2310 
     2311                pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING); 
     2312 
     2313                /* Get remote SRTP usage policy */ 
     2314                pjmedia_transport_info_init(&tp_info); 
     2315                pjmedia_transport_get_info(call_med->tp, &tp_info); 
     2316                if (tp_info.specific_info_cnt > 0) { 
     2317                    unsigned i; 
     2318                    for (i = 0; i < tp_info.specific_info_cnt; ++i) { 
     2319                        if (tp_info.spc_info[i].type == 
     2320                                PJMEDIA_TRANSPORT_TYPE_SRTP) 
     2321                        { 
     2322                            pjmedia_srtp_info *sri; 
     2323                            sri=(pjmedia_srtp_info*)tp_info.spc_info[i].buffer; 
     2324                            call_med->rem_srtp_use = sri->peer_use; 
     2325                            break; 
     2326                        } 
     2327                    } 
     2328                } 
     2329 
     2330                /* Call media direction */ 
     2331                call_med->dir = si->dir; 
     2332 
     2333                /* Call media state */ 
     2334                if (call->local_hold) 
     2335                    call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD; 
     2336                else if (call_med->dir == PJMEDIA_DIR_DECODING) 
     2337                    call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD; 
     2338                else 
     2339                    call_med->state = PJSUA_CALL_MEDIA_ACTIVE; 
     2340            } 
     2341 
     2342            status = pjsua_vid_channel_update(call_med, tmp_pool, si, 
     2343                                              local_sdp, remote_sdp); 
     2344            if (status != PJ_SUCCESS) { 
     2345                PJ_PERROR(1,(THIS_FILE, status, 
     2346                             "pjmedia_transport_media_start() failed " 
     2347                                 "for call_id %d media %d", 
     2348                             call_id, mi)); 
     2349                continue; 
     2350            } 
     2351 
     2352            /* Print info. */ 
     2353            { 
     2354                char info[80]; 
     2355                int info_len = 0; 
     2356                int len; 
     2357                const char *dir; 
     2358 
     2359                switch (si->dir) { 
     2360                case PJMEDIA_DIR_NONE: 
     2361                    dir = "inactive"; 
     2362                    break; 
     2363                case PJMEDIA_DIR_ENCODING: 
     2364                    dir = "sendonly"; 
     2365                    break; 
     2366                case PJMEDIA_DIR_DECODING: 
     2367                    dir = "recvonly"; 
     2368                    break; 
     2369                case PJMEDIA_DIR_ENCODING_DECODING: 
     2370                    dir = "sendrecv"; 
     2371                    break; 
     2372                default: 
     2373                    dir = "unknown"; 
     2374                    break; 
     2375                } 
     2376                len = pj_ansi_sprintf( info+info_len, 
     2377                                       ", stream #%d: %.*s (%s)", mi, 
     2378                                       (int)si->codec_info.encoding_name.slen, 
     2379                                       si->codec_info.encoding_name.ptr, 
     2380                                       dir); 
     2381                if (len > 0) 
     2382                    info_len += len; 
     2383                PJ_LOG(4,(THIS_FILE,"Video updated%s", info)); 
     2384            } 
     2385 
    27072386#endif 
    2708         default: 
     2387        } else { 
    27092388            status = PJMEDIA_EINVALIMEDIATYPE; 
    2710             break; 
    27112389        } 
    27122390 
     
    27182396            pjmedia_transport_close(call_med->tp); 
    27192397            call_med->tp = call_med->tp_orig = NULL; 
    2720             set_media_tp_state(call_med, PJSUA_MED_TP_IDLE); 
     2398            pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_IDLE); 
    27212399        } 
    27222400 
     
    27582436} 
    27592437 
    2760 /* 
    2761  * Get maxinum number of conference ports. 
    2762  */ 
    2763 PJ_DEF(unsigned) pjsua_conf_get_max_ports(void) 
    2764 { 
    2765     return pjsua_var.media_cfg.max_media_ports; 
    2766 } 
    2767  
    2768  
    2769 /* 
    2770  * Get current number of active ports in the bridge. 
    2771  */ 
    2772 PJ_DEF(unsigned) pjsua_conf_get_active_ports(void) 
    2773 { 
    2774     unsigned ports[PJSUA_MAX_CONF_PORTS]; 
    2775     unsigned count = PJ_ARRAY_SIZE(ports); 
    2776     pj_status_t status; 
    2777  
    2778     status = pjmedia_conf_enum_ports(pjsua_var.mconf, ports, &count); 
    2779     if (status != PJ_SUCCESS) 
    2780         count = 0; 
    2781  
    2782     return count; 
    2783 } 
    2784  
    2785  
    2786 /* 
    2787  * Enumerate all conference ports. 
    2788  */ 
    2789 PJ_DEF(pj_status_t) pjsua_enum_conf_ports(pjsua_conf_port_id id[], 
    2790                                           unsigned *count) 
    2791 { 
    2792     return pjmedia_conf_enum_ports(pjsua_var.mconf, (unsigned*)id, count); 
    2793 } 
    2794  
    2795  
    2796 /* 
    2797  * Get information about the specified conference port 
    2798  */ 
    2799 PJ_DEF(pj_status_t) pjsua_conf_get_port_info( pjsua_conf_port_id id, 
    2800                                               pjsua_conf_port_info *info) 
    2801 { 
    2802     pjmedia_conf_port_info cinfo; 
    2803     unsigned i; 
    2804     pj_status_t status; 
    2805  
    2806     status = pjmedia_conf_get_port_info( pjsua_var.mconf, id, &cinfo); 
    2807     if (status != PJ_SUCCESS) 
    2808         return status; 
    2809  
    2810     pj_bzero(info, sizeof(*info)); 
    2811     info->slot_id = id; 
    2812     info->name = cinfo.name; 
    2813     info->clock_rate = cinfo.clock_rate; 
    2814     info->channel_count = cinfo.channel_count; 
    2815     info->samples_per_frame = cinfo.samples_per_frame; 
    2816     info->bits_per_sample = cinfo.bits_per_sample; 
    2817  
    2818     /* Build array of listeners */ 
    2819     info->listener_cnt = cinfo.listener_cnt; 
    2820     for (i=0; i<cinfo.listener_cnt; ++i) { 
    2821         info->listeners[i] = cinfo.listener_slots[i]; 
    2822     } 
    2823  
    2824     return PJ_SUCCESS; 
    2825 } 
    2826  
    2827  
    2828 /* 
    2829  * Add arbitrary media port to PJSUA's conference bridge. 
    2830  */ 
    2831 PJ_DEF(pj_status_t) pjsua_conf_add_port( pj_pool_t *pool, 
    2832                                          pjmedia_port *port, 
    2833                                          pjsua_conf_port_id *p_id) 
    2834 { 
    2835     pj_status_t status; 
    2836  
    2837     status = pjmedia_conf_add_port(pjsua_var.mconf, pool, 
    2838                                    port, NULL, (unsigned*)p_id); 
    2839     if (status != PJ_SUCCESS) { 
    2840         if (p_id) 
    2841             *p_id = PJSUA_INVALID_ID; 
    2842     } 
    2843  
    2844     return status; 
    2845 } 
    2846  
    2847  
    2848 /* 
    2849  * Remove arbitrary slot from the conference bridge. 
    2850  */ 
    2851 PJ_DEF(pj_status_t) pjsua_conf_remove_port(pjsua_conf_port_id id) 
    2852 { 
    2853     pj_status_t status; 
    2854  
    2855     status = pjmedia_conf_remove_port(pjsua_var.mconf, (unsigned)id); 
    2856     check_snd_dev_idle(); 
    2857  
    2858     return status; 
    2859 } 
    2860  
    2861  
    2862 /* 
    2863  * Establish unidirectional media flow from souce to sink.  
    2864  */ 
    2865 PJ_DEF(pj_status_t) pjsua_conf_connect( pjsua_conf_port_id source, 
    2866                                         pjsua_conf_port_id sink) 
    2867 { 
    2868     pj_status_t status = PJ_SUCCESS; 
    2869  
    2870     PJ_LOG(4,(THIS_FILE, "%s connect: %d --> %d", 
    2871               (pjsua_var.is_mswitch ? "Switch" : "Conf"), 
    2872               source, sink)); 
    2873     pj_log_push_indent(); 
    2874  
    2875     /* If sound device idle timer is active, cancel it first. */ 
    2876     PJSUA_LOCK(); 
    2877     if (pjsua_var.snd_idle_timer.id) { 
    2878         pjsip_endpt_cancel_timer(pjsua_var.endpt, &pjsua_var.snd_idle_timer); 
    2879         pjsua_var.snd_idle_timer.id = PJ_FALSE; 
    2880     } 
    2881     PJSUA_UNLOCK(); 
    2882  
    2883  
    2884     /* For audio switchboard (i.e. APS-Direct): 
    2885      * Check if sound device need to be reopened, i.e: its attributes 
    2886      * (format, clock rate, channel count) must match to peer's.  
    2887      * Note that sound device can be reopened only if it doesn't have 
    2888      * any connection. 
    2889      */ 
    2890     if (pjsua_var.is_mswitch) { 
    2891         pjmedia_conf_port_info port0_info; 
    2892         pjmedia_conf_port_info peer_info; 
    2893         unsigned peer_id; 
    2894         pj_bool_t need_reopen = PJ_FALSE; 
    2895  
    2896         peer_id = (source!=0)? source : sink; 
    2897         status = pjmedia_conf_get_port_info(pjsua_var.mconf, peer_id,  
    2898                                             &peer_info); 
    2899         pj_assert(status == PJ_SUCCESS); 
    2900  
    2901         status = pjmedia_conf_get_port_info(pjsua_var.mconf, 0, &port0_info); 
    2902         pj_assert(status == PJ_SUCCESS); 
    2903  
    2904         /* Check if sound device is instantiated. */ 
    2905         need_reopen = (pjsua_var.snd_port==NULL && pjsua_var.null_snd==NULL &&  
    2906                       !pjsua_var.no_snd); 
    2907  
    2908         /* Check if sound device need to reopen because it needs to modify  
    2909          * settings to match its peer. Sound device must be idle in this case  
    2910          * though. 
    2911          */ 
    2912         if (!need_reopen &&  
    2913             port0_info.listener_cnt==0 && port0_info.transmitter_cnt==0)  
    2914         { 
    2915             need_reopen = (peer_info.format.id != port0_info.format.id || 
    2916                            peer_info.format.det.aud.avg_bps != 
    2917                                    port0_info.format.det.aud.avg_bps || 
    2918                            peer_info.clock_rate != port0_info.clock_rate || 
    2919                            peer_info.channel_count!=port0_info.channel_count); 
    2920         } 
    2921  
    2922         if (need_reopen) { 
    2923             if (pjsua_var.cap_dev != NULL_SND_DEV_ID) { 
    2924                 pjmedia_snd_port_param param; 
    2925  
    2926                 /* Create parameter based on peer info */ 
    2927                 status = create_aud_param(&param.base, pjsua_var.cap_dev,  
    2928                                           pjsua_var.play_dev, 
    2929                                           peer_info.clock_rate, 
    2930                                           peer_info.channel_count, 
    2931                                           peer_info.samples_per_frame, 
    2932                                           peer_info.bits_per_sample); 
    2933                 if (status != PJ_SUCCESS) { 
    2934                     pjsua_perror(THIS_FILE, "Error opening sound device", 
    2935                                  status); 
    2936                     goto on_return; 
    2937                 } 
    2938  
    2939                 /* And peer format */ 
    2940                 if (peer_info.format.id != PJMEDIA_FORMAT_PCM) { 
    2941                     param.base.flags |= PJMEDIA_AUD_DEV_CAP_EXT_FORMAT; 
    2942                     param.base.ext_fmt = peer_info.format; 
    2943                 } 
    2944  
    2945                 param.options = 0; 
    2946                 status = open_snd_dev(&param); 
    2947                 if (status != PJ_SUCCESS) { 
    2948                     pjsua_perror(THIS_FILE, "Error opening sound device", 
    2949                                  status); 
    2950                     goto on_return; 
    2951                 } 
    2952             } else { 
    2953                 /* Null-audio */ 
    2954                 status = pjsua_set_snd_dev(pjsua_var.cap_dev, 
    2955                                            pjsua_var.play_dev); 
    2956                 if (status != PJ_SUCCESS) { 
    2957                     pjsua_perror(THIS_FILE, "Error opening sound device", 
    2958                                  status); 
    2959                     goto on_return; 
    2960                 } 
    2961             } 
    2962         } else if (pjsua_var.no_snd) { 
    2963             if (!pjsua_var.snd_is_on) { 
    2964                 pjsua_var.snd_is_on = PJ_TRUE; 
    2965                 /* Notify app */ 
    2966                 if (pjsua_var.ua_cfg.cb.on_snd_dev_operation) { 
    2967                     (*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(1); 
    2968                 } 
    2969             } 
    2970         } 
    2971  
    2972     } else { 
    2973         /* The bridge version */ 
    2974  
    2975         /* Create sound port if none is instantiated */ 
    2976         if (pjsua_var.snd_port==NULL && pjsua_var.null_snd==NULL &&  
    2977             !pjsua_var.no_snd)  
    2978         { 
    2979             pj_status_t status; 
    2980  
    2981             status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev); 
    2982             if (status != PJ_SUCCESS) { 
    2983                 pjsua_perror(THIS_FILE, "Error opening sound device", status); 
    2984                 goto on_return; 
    2985             } 
    2986         } else if (pjsua_var.no_snd && !pjsua_var.snd_is_on) { 
    2987             pjsua_var.snd_is_on = PJ_TRUE; 
    2988             /* Notify app */ 
    2989             if (pjsua_var.ua_cfg.cb.on_snd_dev_operation) { 
    2990                 (*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(1); 
    2991             } 
    2992         } 
    2993     } 
    2994  
    2995     status = pjmedia_conf_connect_port(pjsua_var.mconf, source, sink, 0); 
    2996  
    2997 on_return: 
    2998     pj_log_pop_indent(); 
    2999     return status; 
    3000 } 
    3001  
    3002  
    3003 /* 
    3004  * Disconnect media flow from the source to destination port. 
    3005  */ 
    3006 PJ_DEF(pj_status_t) pjsua_conf_disconnect( pjsua_conf_port_id source, 
    3007                                            pjsua_conf_port_id sink) 
    3008 { 
    3009     pj_status_t status; 
    3010  
    3011     PJ_LOG(4,(THIS_FILE, "%s disconnect: %d -x- %d", 
    3012               (pjsua_var.is_mswitch ? "Switch" : "Conf"), 
    3013               source, sink)); 
    3014     pj_log_push_indent(); 
    3015  
    3016     status = pjmedia_conf_disconnect_port(pjsua_var.mconf, source, sink); 
    3017     check_snd_dev_idle(); 
    3018  
    3019     pj_log_pop_indent(); 
    3020     return status; 
    3021 } 
    3022  
    3023  
    3024 /* 
    3025  * Adjust the signal level to be transmitted from the bridge to the  
    3026  * specified port by making it louder or quieter. 
    3027  */ 
    3028 PJ_DEF(pj_status_t) pjsua_conf_adjust_tx_level(pjsua_conf_port_id slot, 
    3029                                                float level) 
    3030 { 
    3031     return pjmedia_conf_adjust_tx_level(pjsua_var.mconf, slot, 
    3032                                         (int)((level-1) * 128)); 
    3033 } 
    3034  
    3035 /* 
    3036  * Adjust the signal level to be received from the specified port (to 
    3037  * the bridge) by making it louder or quieter. 
    3038  */ 
    3039 PJ_DEF(pj_status_t) pjsua_conf_adjust_rx_level(pjsua_conf_port_id slot, 
    3040                                                float level) 
    3041 { 
    3042     return pjmedia_conf_adjust_rx_level(pjsua_var.mconf, slot, 
    3043                                         (int)((level-1) * 128)); 
    3044 } 
    3045  
    3046  
    3047 /* 
    3048  * Get last signal level transmitted to or received from the specified port. 
    3049  */ 
    3050 PJ_DEF(pj_status_t) pjsua_conf_get_signal_level(pjsua_conf_port_id slot, 
    3051                                                 unsigned *tx_level, 
    3052                                                 unsigned *rx_level) 
    3053 { 
    3054     return pjmedia_conf_get_signal_level(pjsua_var.mconf, slot,  
    3055                                          tx_level, rx_level); 
    3056 } 
    3057  
    3058 /***************************************************************************** 
    3059  * File player. 
    3060  */ 
    3061  
    3062 static char* get_basename(const char *path, unsigned len) 
    3063 { 
    3064     char *p = ((char*)path) + len; 
    3065  
    3066     if (len==0) 
    3067         return p; 
    3068  
    3069     for (--p; p!=path && *p!='/' && *p!='\\'; ) --p; 
    3070  
    3071     return (p==path) ? p : p+1; 
    3072 } 
    3073  
    3074  
    3075 /* 
    3076  * Create a file player, and automatically connect this player to 
    3077  * the conference bridge. 
    3078  */ 
    3079 PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename, 
    3080                                          unsigned options, 
    3081                                          pjsua_player_id *p_id) 
    3082 { 
    3083     unsigned slot, file_id; 
    3084     char path[PJ_MAXPATH]; 
    3085     pj_pool_t *pool = NULL; 
    3086     pjmedia_port *port; 
    3087     pj_status_t status = PJ_SUCCESS; 
    3088  
    3089     if (pjsua_var.player_cnt >= PJ_ARRAY_SIZE(pjsua_var.player)) 
    3090         return PJ_ETOOMANY; 
    3091  
    3092     PJ_LOG(4,(THIS_FILE, "Creating file player: %.*s..", 
    3093               (int)filename->slen, filename->ptr)); 
    3094     pj_log_push_indent(); 
    3095  
    3096     PJSUA_LOCK(); 
    3097  
    3098     for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.player); ++file_id) { 
    3099         if (pjsua_var.player[file_id].port == NULL) 
    3100             break; 
    3101     } 
    3102  
    3103     if (file_id == PJ_ARRAY_SIZE(pjsua_var.player)) { 
    3104         /* This is unexpected */ 
    3105         pj_assert(0); 
    3106         status = PJ_EBUG; 
    3107         goto on_error; 
    3108     } 
    3109  
    3110     pj_memcpy(path, filename->ptr, filename->slen); 
    3111     path[filename->slen] = '\0'; 
    3112  
    3113     pool = pjsua_pool_create(get_basename(path, filename->slen), 1000, 1000); 
    3114     if (!pool) { 
    3115         status = PJ_ENOMEM; 
    3116         goto on_error; 
    3117     } 
    3118  
    3119     status = pjmedia_wav_player_port_create( 
    3120                                     pool, path, 
    3121                                     pjsua_var.mconf_cfg.samples_per_frame * 
    3122                                     1000 / pjsua_var.media_cfg.channel_count / 
    3123                                     pjsua_var.media_cfg.clock_rate,  
    3124                                     options, 0, &port); 
    3125     if (status != PJ_SUCCESS) { 
    3126         pjsua_perror(THIS_FILE, "Unable to open file for playback", status); 
    3127         goto on_error; 
    3128     } 
    3129  
    3130     status = pjmedia_conf_add_port(pjsua_var.mconf, pool,  
    3131                                    port, filename, &slot); 
    3132     if (status != PJ_SUCCESS) { 
    3133         pjmedia_port_destroy(port); 
    3134         pjsua_perror(THIS_FILE, "Unable to add file to conference bridge",  
    3135                      status); 
    3136         goto on_error; 
    3137     } 
    3138  
    3139     pjsua_var.player[file_id].type = 0; 
    3140     pjsua_var.player[file_id].pool = pool; 
    3141     pjsua_var.player[file_id].port = port; 
    3142     pjsua_var.player[file_id].slot = slot; 
    3143  
    3144     if (p_id) *p_id = file_id; 
    3145  
    3146     ++pjsua_var.player_cnt; 
    3147  
    3148     PJSUA_UNLOCK(); 
    3149  
    3150     PJ_LOG(4,(THIS_FILE, "Player created, id=%d, slot=%d", file_id, slot)); 
    3151  
    3152     pj_log_pop_indent(); 
    3153     return PJ_SUCCESS; 
    3154  
    3155 on_error: 
    3156     PJSUA_UNLOCK(); 
    3157     if (pool) pj_pool_release(pool); 
    3158     pj_log_pop_indent(); 
    3159     return status; 
    3160 } 
    3161  
    3162  
    3163 /* 
    3164  * Create a file playlist media port, and automatically add the port 
    3165  * to the conference bridge. 
    3166  */ 
    3167 PJ_DEF(pj_status_t) pjsua_playlist_create( const pj_str_t file_names[], 
    3168                                            unsigned file_count, 
    3169                                            const pj_str_t *label, 
    3170                                            unsigned options, 
    3171                                            pjsua_player_id *p_id) 
    3172 { 
    3173     unsigned slot, file_id, ptime; 
    3174     pj_pool_t *pool = NULL; 
    3175     pjmedia_port *port; 
    3176     pj_status_t status = PJ_SUCCESS; 
    3177  
    3178     if (pjsua_var.player_cnt >= PJ_ARRAY_SIZE(pjsua_var.player)) 
    3179         return PJ_ETOOMANY; 
    3180  
    3181     PJ_LOG(4,(THIS_FILE, "Creating playlist with %d file(s)..", file_count)); 
    3182     pj_log_push_indent(); 
    3183  
    3184     PJSUA_LOCK(); 
    3185  
    3186     for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.player); ++file_id) { 
    3187         if (pjsua_var.player[file_id].port == NULL) 
    3188             break; 
    3189     } 
    3190  
    3191     if (file_id == PJ_ARRAY_SIZE(pjsua_var.player)) { 
    3192         /* This is unexpected */ 
    3193         pj_assert(0); 
    3194         status = PJ_EBUG; 
    3195         goto on_error; 
    3196     } 
    3197  
    3198  
    3199     ptime = pjsua_var.mconf_cfg.samples_per_frame * 1000 /  
    3200             pjsua_var.media_cfg.clock_rate; 
    3201  
    3202     pool = pjsua_pool_create("playlist", 1000, 1000); 
    3203     if (!pool) { 
    3204         status = PJ_ENOMEM; 
    3205         goto on_error; 
    3206     } 
    3207  
    3208     status = pjmedia_wav_playlist_create(pool, label,  
    3209                                          file_names, file_count, 
    3210                                          ptime, options, 0, &port); 
    3211     if (status != PJ_SUCCESS) { 
    3212         pjsua_perror(THIS_FILE, "Unable to create playlist", status); 
    3213         goto on_error; 
    3214     } 
    3215  
    3216     status = pjmedia_conf_add_port(pjsua_var.mconf, pool,  
    3217                                    port, &port->info.name, &slot); 
    3218     if (status != PJ_SUCCESS) { 
    3219         pjmedia_port_destroy(port); 
    3220         pjsua_perror(THIS_FILE, "Unable to add port", status); 
    3221         goto on_error; 
    3222     } 
    3223  
    3224     pjsua_var.player[file_id].type = 1; 
    3225     pjsua_var.player[file_id].pool = pool; 
    3226     pjsua_var.player[file_id].port = port; 
    3227     pjsua_var.player[file_id].slot = slot; 
    3228  
    3229     if (p_id) *p_id = file_id; 
    3230  
    3231     ++pjsua_var.player_cnt; 
    3232  
    3233     PJSUA_UNLOCK(); 
    3234  
    3235     PJ_LOG(4,(THIS_FILE, "Playlist created, id=%d, slot=%d", file_id, slot)); 
    3236  
    3237     pj_log_pop_indent(); 
    3238  
    3239     return PJ_SUCCESS; 
    3240  
    3241 on_error: 
    3242     PJSUA_UNLOCK(); 
    3243     if (pool) pj_pool_release(pool); 
    3244     pj_log_pop_indent(); 
    3245  
    3246     return status; 
    3247 } 
    3248  
    3249  
    3250 /* 
    3251  * Get conference port ID associated with player. 
    3252  */ 
    3253 PJ_DEF(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id) 
    3254 { 
    3255     PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL); 
    3256     PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL); 
    3257  
    3258     return pjsua_var.player[id].slot; 
    3259 } 
    3260  
    3261 /* 
    3262  * Get the media port for the player. 
    3263  */ 
    3264 PJ_DEF(pj_status_t) pjsua_player_get_port( pjsua_player_id id, 
    3265                                            pjmedia_port **p_port) 
    3266 { 
    3267     PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL); 
    3268     PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL); 
    3269     PJ_ASSERT_RETURN(p_port != NULL, PJ_EINVAL); 
    3270      
    3271     *p_port = pjsua_var.player[id].port; 
    3272  
    3273     return PJ_SUCCESS; 
    3274 } 
    3275  
    3276 /* 
    3277  * Set playback position. 
    3278  */ 
    3279 PJ_DEF(pj_status_t) pjsua_player_set_pos( pjsua_player_id id, 
    3280                                           pj_uint32_t samples) 
    3281 { 
    3282     PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL); 
    3283     PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL); 
    3284     PJ_ASSERT_RETURN(pjsua_var.player[id].type == 0, PJ_EINVAL); 
    3285  
    3286     return pjmedia_wav_player_port_set_pos(pjsua_var.player[id].port, samples); 
    3287 } 
    3288  
    3289  
    3290 /* 
    3291  * Close the file, remove the player from the bridge, and free 
    3292  * resources associated with the file player. 
    3293  */ 
    3294 PJ_DEF(pj_status_t) pjsua_player_destroy(pjsua_player_id id) 
    3295 { 
    3296     PJ_ASSERT_RETURN(id>=0&&id<(int)PJ_ARRAY_SIZE(pjsua_var.player), PJ_EINVAL); 
    3297     PJ_ASSERT_RETURN(pjsua_var.player[id].port != NULL, PJ_EINVAL); 
    3298  
    3299     PJ_LOG(4,(THIS_FILE, "Destroying player %d..", id)); 
    3300     pj_log_push_indent(); 
    3301  
    3302     PJSUA_LOCK(); 
    3303  
    3304     if (pjsua_var.player[id].port) { 
    3305         pjsua_conf_remove_port(pjsua_var.player[id].slot); 
    3306         pjmedia_port_destroy(pjsua_var.player[id].port); 
    3307         pjsua_var.player[id].port = NULL; 
    3308         pjsua_var.player[id].slot = 0xFFFF; 
    3309         pj_pool_release(pjsua_var.player[id].pool); 
    3310         pjsua_var.player[id].pool = NULL; 
    3311         pjsua_var.player_cnt--; 
    3312     } 
    3313  
    3314     PJSUA_UNLOCK(); 
    3315     pj_log_pop_indent(); 
    3316  
    3317     return PJ_SUCCESS; 
    3318 } 
    3319  
    3320  
    3321 /***************************************************************************** 
    3322  * File recorder. 
    3323  */ 
    3324  
    3325 /* 
    3326  * Create a file recorder, and automatically connect this recorder to 
    3327  * the conference bridge. 
    3328  */ 
    3329 PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename, 
    3330                                            unsigned enc_type, 
    3331                                            void *enc_param, 
    3332                                            pj_ssize_t max_size, 
    3333                                            unsigned options, 
    3334                                            pjsua_recorder_id *p_id) 
    3335 { 
    3336     enum Format 
    3337     { 
    3338         FMT_UNKNOWN, 
    3339         FMT_WAV, 
    3340         FMT_MP3, 
    3341     }; 
    3342     unsigned slot, file_id; 
    3343     char path[PJ_MAXPATH]; 
    3344     pj_str_t ext; 
    3345     int file_format; 
    3346     pj_pool_t *pool = NULL; 
    3347     pjmedia_port *port; 
    3348     pj_status_t status = PJ_SUCCESS; 
    3349  
    3350     /* Filename must present */ 
    3351     PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL); 
    3352  
    3353     /* Don't support max_size at present */ 
    3354     PJ_ASSERT_RETURN(max_size == 0 || max_size == -1, PJ_EINVAL); 
    3355  
    3356     /* Don't support encoding type at present */ 
    3357     PJ_ASSERT_RETURN(enc_type == 0, PJ_EINVAL); 
    3358  
    3359     PJ_LOG(4,(THIS_FILE, "Creating recorder %.*s..", 
    3360               (int)filename->slen, filename->ptr)); 
    3361     pj_log_push_indent(); 
    3362  
    3363     if (pjsua_var.rec_cnt >= PJ_ARRAY_SIZE(pjsua_var.recorder)) { 
    3364         pj_log_pop_indent(); 
    3365         return PJ_ETOOMANY; 
    3366     } 
    3367  
    3368     /* Determine the file format */ 
    3369     ext.ptr = filename->ptr + filename->slen - 4; 
    3370     ext.slen = 4; 
    3371  
    3372     if (pj_stricmp2(&ext, ".wav") == 0) 
    3373         file_format = FMT_WAV; 
    3374     else if (pj_stricmp2(&ext, ".mp3") == 0) 
    3375         file_format = FMT_MP3; 
    3376     else { 
    3377         PJ_LOG(1,(THIS_FILE, "pjsua_recorder_create() error: unable to " 
    3378                              "determine file format for %.*s", 
    3379                              (int)filename->slen, filename->ptr)); 
    3380         pj_log_pop_indent(); 
    3381         return PJ_ENOTSUP; 
    3382     } 
    3383  
    3384     PJSUA_LOCK(); 
    3385  
    3386     for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.recorder); ++file_id) { 
    3387         if (pjsua_var.recorder[file_id].port == NULL) 
    3388             break; 
    3389     } 
    3390  
    3391     if (file_id == PJ_ARRAY_SIZE(pjsua_var.recorder)) { 
    3392         /* This is unexpected */ 
    3393         pj_assert(0); 
    3394         status = PJ_EBUG; 
    3395         goto on_return; 
    3396     } 
    3397  
    3398     pj_memcpy(path, filename->ptr, filename->slen); 
    3399     path[filename->slen] = '\0'; 
    3400  
    3401     pool = pjsua_pool_create(get_basename(path, filename->slen), 1000, 1000); 
    3402     if (!pool) { 
    3403         status = PJ_ENOMEM; 
    3404         goto on_return; 
    3405     } 
    3406  
    3407     if (file_format == FMT_WAV) { 
    3408         status = pjmedia_wav_writer_port_create(pool, path,  
    3409                                                 pjsua_var.media_cfg.clock_rate,  
    3410                                                 pjsua_var.mconf_cfg.channel_count, 
    3411                                                 pjsua_var.mconf_cfg.samples_per_frame, 
    3412                                                 pjsua_var.mconf_cfg.bits_per_sample,  
    3413                                                 options, 0, &port); 
    3414     } else { 
    3415         PJ_UNUSED_ARG(enc_param); 
    3416         port = NULL; 
    3417         status = PJ_ENOTSUP; 
    3418     } 
    3419  
    3420     if (status != PJ_SUCCESS) { 
    3421         pjsua_perror(THIS_FILE, "Unable to open file for recording", status); 
    3422         goto on_return; 
    3423     } 
    3424  
    3425     status = pjmedia_conf_add_port(pjsua_var.mconf, pool,  
    3426                                    port, filename, &slot); 
    3427     if (status != PJ_SUCCESS) { 
    3428         pjmedia_port_destroy(port); 
    3429         goto on_return; 
    3430     } 
    3431  
    3432     pjsua_var.recorder[file_id].port = port; 
    3433     pjsua_var.recorder[file_id].slot = slot; 
    3434     pjsua_var.recorder[file_id].pool = pool; 
    3435  
    3436     if (p_id) *p_id = file_id; 
    3437  
    3438     ++pjsua_var.rec_cnt; 
    3439  
    3440     PJSUA_UNLOCK(); 
    3441  
    3442     PJ_LOG(4,(THIS_FILE, "Recorder created, id=%d, slot=%d", file_id, slot)); 
    3443  
    3444     pj_log_pop_indent(); 
    3445     return PJ_SUCCESS; 
    3446  
    3447 on_return: 
    3448     PJSUA_UNLOCK(); 
    3449     if (pool) pj_pool_release(pool); 
    3450     pj_log_pop_indent(); 
    3451     return status; 
    3452 } 
    3453  
    3454  
    3455 /* 
    3456  * Get conference port associated with recorder. 
    3457  */ 
    3458 PJ_DEF(pjsua_conf_port_id) pjsua_recorder_get_conf_port(pjsua_recorder_id id) 
    3459 { 
    3460     PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.recorder),  
    3461                      PJ_EINVAL); 
    3462     PJ_ASSERT_RETURN(pjsua_var.recorder[id].port != NULL, PJ_EINVAL); 
    3463  
    3464     return pjsua_var.recorder[id].slot; 
    3465 } 
    3466  
    3467 /* 
    3468  * Get the media port for the recorder. 
    3469  */ 
    3470 PJ_DEF(pj_status_t) pjsua_recorder_get_port( pjsua_recorder_id id, 
    3471                                              pjmedia_port **p_port) 
    3472 { 
    3473     PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.recorder),  
    3474                      PJ_EINVAL); 
    3475     PJ_ASSERT_RETURN(pjsua_var.recorder[id].port != NULL, PJ_EINVAL); 
    3476     PJ_ASSERT_RETURN(p_port != NULL, PJ_EINVAL); 
    3477  
    3478     *p_port = pjsua_var.recorder[id].port; 
    3479     return PJ_SUCCESS; 
    3480 } 
    3481  
    3482 /* 
    3483  * Destroy recorder (this will complete recording). 
    3484  */ 
    3485 PJ_DEF(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id) 
    3486 { 
    3487     PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.recorder),  
    3488                      PJ_EINVAL); 
    3489     PJ_ASSERT_RETURN(pjsua_var.recorder[id].port != NULL, PJ_EINVAL); 
    3490  
    3491     PJ_LOG(4,(THIS_FILE, "Destroying recorder %d..", id)); 
    3492     pj_log_push_indent(); 
    3493  
    3494     PJSUA_LOCK(); 
    3495  
    3496     if (pjsua_var.recorder[id].port) { 
    3497         pjsua_conf_remove_port(pjsua_var.recorder[id].slot); 
    3498         pjmedia_port_destroy(pjsua_var.recorder[id].port); 
    3499         pjsua_var.recorder[id].port = NULL; 
    3500         pjsua_var.recorder[id].slot = 0xFFFF; 
    3501         pj_pool_release(pjsua_var.recorder[id].pool); 
    3502         pjsua_var.recorder[id].pool = NULL; 
    3503         pjsua_var.rec_cnt--; 
    3504     } 
    3505  
    3506     PJSUA_UNLOCK(); 
    3507     pj_log_pop_indent(); 
    3508  
    3509     return PJ_SUCCESS; 
    3510 } 
    3511  
    3512  
    3513 /***************************************************************************** 
    3514  * Sound devices. 
    3515  */ 
    3516  
    3517 /* 
    3518  * Enum sound devices. 
    3519  */ 
    3520  
    3521 PJ_DEF(pj_status_t) pjsua_enum_aud_devs( pjmedia_aud_dev_info info[], 
    3522                                          unsigned *count) 
    3523 { 
    3524     unsigned i, dev_count; 
    3525  
    3526     dev_count = pjmedia_aud_dev_count(); 
    3527      
    3528     if (dev_count > *count) dev_count = *count; 
    3529  
    3530     for (i=0; i<dev_count; ++i) { 
    3531         pj_status_t status; 
    3532  
    3533         status = pjmedia_aud_dev_get_info(i, &info[i]); 
    3534         if (status != PJ_SUCCESS) 
    3535             return status; 
    3536     } 
    3537  
    3538     *count = dev_count; 
    3539  
    3540     return PJ_SUCCESS; 
    3541 } 
    3542  
    3543  
    3544 PJ_DEF(pj_status_t) pjsua_enum_snd_devs( pjmedia_snd_dev_info info[], 
    3545                                          unsigned *count) 
    3546 { 
    3547     unsigned i, dev_count; 
    3548  
    3549     dev_count = pjmedia_aud_dev_count(); 
    3550      
    3551     if (dev_count > *count) dev_count = *count; 
    3552     pj_bzero(info, dev_count * sizeof(pjmedia_snd_dev_info)); 
    3553  
    3554     for (i=0; i<dev_count; ++i) { 
    3555         pjmedia_aud_dev_info ai; 
    3556         pj_status_t status; 
    3557  
    3558         status = pjmedia_aud_dev_get_info(i, &ai); 
    3559         if (status != PJ_SUCCESS) 
    3560             return status; 
    3561  
    3562         strncpy(info[i].name, ai.name, sizeof(info[i].name)); 
    3563         info[i].name[sizeof(info[i].name)-1] = '\0'; 
    3564         info[i].input_count = ai.input_count; 
    3565         info[i].output_count = ai.output_count; 
    3566         info[i].default_samples_per_sec = ai.default_samples_per_sec; 
    3567     } 
    3568  
    3569     *count = dev_count; 
    3570  
    3571     return PJ_SUCCESS; 
    3572 } 
    3573  
    3574 /* Create audio device parameter to open the device */ 
    3575 static pj_status_t create_aud_param(pjmedia_aud_param *param, 
    3576                                     pjmedia_aud_dev_index capture_dev, 
    3577                                     pjmedia_aud_dev_index playback_dev, 
    3578                                     unsigned clock_rate, 
    3579                                     unsigned channel_count, 
    3580                                     unsigned samples_per_frame, 
    3581                                     unsigned bits_per_sample) 
    3582 { 
    3583     pj_status_t status; 
    3584  
    3585     /* Normalize device ID with new convention about default device ID */ 
    3586     if (playback_dev == PJMEDIA_AUD_DEFAULT_CAPTURE_DEV) 
    3587         playback_dev = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV; 
    3588  
    3589     /* Create default parameters for the device */ 
    3590     status = pjmedia_aud_dev_default_param(capture_dev, param); 
    3591     if (status != PJ_SUCCESS) { 
    3592         pjsua_perror(THIS_FILE, "Error retrieving default audio " 
    3593                                 "device parameters", status); 
    3594         return status; 
    3595     } 
    3596     param->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK; 
    3597     param->rec_id = capture_dev; 
    3598     param->play_id = playback_dev; 
    3599     param->clock_rate = clock_rate; 
    3600     param->channel_count = channel_count; 
    3601     param->samples_per_frame = samples_per_frame; 
    3602     param->bits_per_sample = bits_per_sample; 
    3603  
    3604     /* Update the setting with user preference */ 
    3605 #define update_param(cap, field)    \ 
    3606         if (pjsua_var.aud_param.flags & cap) { \ 
    3607             param->flags |= cap; \ 
    3608             param->field = pjsua_var.aud_param.field; \ 
    3609         } 
    3610     update_param( PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING, input_vol); 
    3611     update_param( PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, output_vol); 
    3612     update_param( PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE, input_route); 
    3613     update_param( PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, output_route); 
    3614 #undef update_param 
    3615  
    3616     /* Latency settings */ 
    3617     param->flags |= (PJMEDIA_AUD_DEV_CAP_INPUT_LATENCY |  
    3618                      PJMEDIA_AUD_DEV_CAP_OUTPUT_LATENCY); 
    3619     param->input_latency_ms = pjsua_var.media_cfg.snd_rec_latency; 
    3620     param->output_latency_ms = pjsua_var.media_cfg.snd_play_latency; 
    3621  
    3622     /* EC settings */ 
    3623     if (pjsua_var.media_cfg.ec_tail_len) { 
    3624         param->flags |= (PJMEDIA_AUD_DEV_CAP_EC | PJMEDIA_AUD_DEV_CAP_EC_TAIL); 
    3625         param->ec_enabled = PJ_TRUE; 
    3626         param->ec_tail_ms = pjsua_var.media_cfg.ec_tail_len; 
    3627     } else { 
    3628         param->flags &= ~(PJMEDIA_AUD_DEV_CAP_EC|PJMEDIA_AUD_DEV_CAP_EC_TAIL); 
    3629     } 
    3630  
    3631     return PJ_SUCCESS; 
    3632 } 
    3633  
    3634 /* Internal: the first time the audio device is opened (during app 
    3635  *   startup), retrieve the audio settings such as volume level 
    3636  *   so that aud_get_settings() will work. 
    3637  */ 
    3638 static pj_status_t update_initial_aud_param() 
    3639 { 
    3640     pjmedia_aud_stream *strm; 
    3641     pjmedia_aud_param param; 
    3642     pj_status_t status; 
    3643  
    3644     PJ_ASSERT_RETURN(pjsua_var.snd_port != NULL, PJ_EBUG); 
    3645  
    3646     strm = pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port); 
    3647  
    3648     status = pjmedia_aud_stream_get_param(strm, &param); 
    3649     if (status != PJ_SUCCESS) { 
    3650         pjsua_perror(THIS_FILE, "Error audio stream " 
    3651                                 "device parameters", status); 
    3652         return status; 
    3653     } 
    3654  
    3655 #define update_saved_param(cap, field)  \ 
    3656         if (param.flags & cap) { \ 
    3657             pjsua_var.aud_param.flags |= cap; \ 
    3658             pjsua_var.aud_param.field = param.field; \ 
    3659         } 
    3660  
    3661     update_saved_param(PJMEDIA_AUD_DEV_CAP_INPUT_VOLUME_SETTING, input_vol); 
    3662     update_saved_param(PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, output_vol); 
    3663     update_saved_param(PJMEDIA_AUD_DEV_CAP_INPUT_ROUTE, input_route); 
    3664     update_saved_param(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, output_route); 
    3665 #undef update_saved_param 
    3666  
    3667     return PJ_SUCCESS; 
    3668 } 
    3669  
    3670 /* Get format name */ 
    3671 static const char *get_fmt_name(pj_uint32_t id) 
    3672 { 
    3673     static char name[8]; 
    3674  
    3675     if (id == PJMEDIA_FORMAT_L16) 
    3676         return "PCM"; 
    3677     pj_memcpy(name, &id, 4); 
    3678     name[4] = '\0'; 
    3679     return name; 
    3680 } 
    3681  
    3682 /* Open sound device with the setting. */ 
    3683 static pj_status_t open_snd_dev(pjmedia_snd_port_param *param) 
    3684 { 
    3685     pjmedia_port *conf_port; 
    3686     pj_status_t status; 
    3687  
    3688     PJ_ASSERT_RETURN(param, PJ_EINVAL); 
    3689  
    3690     /* Check if NULL sound device is used */ 
    3691     if (NULL_SND_DEV_ID==param->base.rec_id || 
    3692         NULL_SND_DEV_ID==param->base.play_id) 
    3693     { 
    3694         return pjsua_set_null_snd_dev(); 
    3695     } 
    3696  
    3697     /* Close existing sound port */ 
    3698     close_snd_dev(); 
    3699  
    3700     /* Notify app */ 
    3701     if (pjsua_var.ua_cfg.cb.on_snd_dev_operation) { 
    3702         (*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(1); 
    3703     } 
    3704  
    3705     /* Create memory pool for sound device. */ 
    3706     pjsua_var.snd_pool = pjsua_pool_create("pjsua_snd", 4000, 4000); 
    3707     PJ_ASSERT_RETURN(pjsua_var.snd_pool, PJ_ENOMEM); 
    3708  
    3709  
    3710     PJ_LOG(4,(THIS_FILE, "Opening sound device %s@%d/%d/%dms", 
    3711               get_fmt_name(param->base.ext_fmt.id), 
    3712               param->base.clock_rate, param->base.channel_count, 
    3713               param->base.samples_per_frame / param->base.channel_count * 
    3714               1000 / param->base.clock_rate)); 
    3715     pj_log_push_indent(); 
    3716  
    3717     status = pjmedia_snd_port_create2( pjsua_var.snd_pool,  
    3718                                        param, &pjsua_var.snd_port); 
    3719     if (status != PJ_SUCCESS) 
    3720         goto on_error; 
    3721  
    3722     /* Get the port0 of the conference bridge. */ 
    3723     conf_port = pjmedia_conf_get_master_port(pjsua_var.mconf); 
    3724     pj_assert(conf_port != NULL); 
    3725  
    3726     /* For conference bridge, resample if necessary if the bridge's 
    3727      * clock rate is different than the sound device's clock rate. 
    3728      */ 
    3729     if (!pjsua_var.is_mswitch && 
    3730         param->base.ext_fmt.id == PJMEDIA_FORMAT_PCM && 
    3731         PJMEDIA_PIA_SRATE(&conf_port->info) != param->base.clock_rate) 
    3732     { 
    3733         pjmedia_port *resample_port; 
    3734         unsigned resample_opt = 0; 
    3735  
    3736         if (pjsua_var.media_cfg.quality >= 3 && 
    3737             pjsua_var.media_cfg.quality <= 4) 
    3738         { 
    3739             resample_opt |= PJMEDIA_RESAMPLE_USE_SMALL_FILTER; 
    3740         } 
    3741         else if (pjsua_var.media_cfg.quality < 3) { 
    3742             resample_opt |= PJMEDIA_RESAMPLE_USE_LINEAR; 
    3743         } 
    3744          
    3745         status = pjmedia_resample_port_create(pjsua_var.snd_pool,  
    3746                                               conf_port, 
    3747                                               param->base.clock_rate, 
    3748                                               resample_opt,  
    3749                                               &resample_port); 
    3750         if (status != PJ_SUCCESS) { 
    3751             char errmsg[PJ_ERR_MSG_SIZE]; 
    3752             pj_strerror(status, errmsg, sizeof(errmsg)); 
    3753             PJ_LOG(4, (THIS_FILE,  
    3754                        "Error creating resample port: %s",  
    3755                        errmsg)); 
    3756             close_snd_dev(); 
    3757             goto on_error; 
    3758         }  
    3759              
    3760         conf_port = resample_port; 
    3761     } 
    3762  
    3763     /* Otherwise for audio switchboard, the switch's port0 setting is 
    3764      * derived from the sound device setting, so update the setting. 
    3765      */ 
    3766     if (pjsua_var.is_mswitch) { 
    3767         if (param->base.flags & PJMEDIA_AUD_DEV_CAP_EXT_FORMAT) { 
    3768             conf_port->info.fmt = param->base.ext_fmt; 
    3769         } else { 
    3770             unsigned bps, ptime_usec; 
    3771             bps = param->base.clock_rate * param->base.bits_per_sample; 
    3772             ptime_usec = param->base.samples_per_frame / 
    3773                          param->base.channel_count * 1000000 / 
    3774                          param->base.clock_rate; 
    3775             pjmedia_format_init_audio(&conf_port->info.fmt, 
    3776                                       PJMEDIA_FORMAT_PCM, 
    3777                                       param->base.clock_rate, 
    3778                                       param->base.channel_count, 
    3779                                       param->base.bits_per_sample, 
    3780                                       ptime_usec, 
    3781                                       bps, bps); 
    3782         } 
    3783     } 
    3784  
    3785  
    3786     /* Connect sound port to the bridge */ 
    3787     status = pjmedia_snd_port_connect(pjsua_var.snd_port,         
    3788                                       conf_port );        
    3789     if (status != PJ_SUCCESS) {           
    3790         pjsua_perror(THIS_FILE, "Unable to connect conference port to " 
    3791                                 "sound device", status);          
    3792         pjmedia_snd_port_destroy(pjsua_var.snd_port);     
    3793         pjsua_var.snd_port = NULL;        
    3794         goto on_error; 
    3795     } 
    3796  
    3797     /* Save the device IDs */ 
    3798     pjsua_var.cap_dev = param->base.rec_id; 
    3799     pjsua_var.play_dev = param->base.play_id; 
    3800  
    3801     /* Update sound device name. */ 
    3802     { 
    3803         pjmedia_aud_dev_info rec_info; 
    3804         pjmedia_aud_stream *strm; 
    3805         pjmedia_aud_param si; 
    3806         pj_str_t tmp; 
    3807  
    3808         strm = pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port); 
    3809         status = pjmedia_aud_stream_get_param(strm, &si); 
    3810         if (status == PJ_SUCCESS) 
    3811             status = pjmedia_aud_dev_get_info(si.rec_id, &rec_info); 
    3812  
    3813         if (status==PJ_SUCCESS) { 
    3814             if (param->base.clock_rate != pjsua_var.media_cfg.clock_rate) { 
    3815                 char tmp_buf[128]; 
    3816                 int tmp_buf_len = sizeof(tmp_buf); 
    3817  
    3818                 tmp_buf_len = pj_ansi_snprintf(tmp_buf, sizeof(tmp_buf)-1,  
    3819                                                "%s (%dKHz)", 
    3820                                                rec_info.name,  
    3821                                                param->base.clock_rate/1000); 
    3822                 pj_strset(&tmp, tmp_buf, tmp_buf_len); 
    3823                 pjmedia_conf_set_port0_name(pjsua_var.mconf, &tmp);  
    3824             } else { 
    3825                 pjmedia_conf_set_port0_name(pjsua_var.mconf,  
    3826                                             pj_cstr(&tmp, rec_info.name)); 
    3827             } 
    3828         } 
    3829  
    3830         /* Any error is not major, let it through */ 
    3831         status = PJ_SUCCESS; 
    3832     } 
    3833  
    3834     /* If this is the first time the audio device is open, retrieve some 
    3835      * settings from the device (such as volume settings) so that the 
    3836      * pjsua_snd_get_setting() work. 
    3837      */ 
    3838     if (pjsua_var.aud_open_cnt == 0) { 
    3839         update_initial_aud_param(); 
    3840         ++pjsua_var.aud_open_cnt; 
    3841     } 
    3842  
    3843     pjsua_var.snd_is_on = PJ_TRUE; 
    3844  
    3845     pj_log_pop_indent(); 
    3846     return PJ_SUCCESS; 
    3847  
    3848 on_error: 
    3849     pj_log_pop_indent(); 
    3850     return status; 
    3851 } 
    3852  
    3853  
    3854 /* Close existing sound device */ 
    3855 static void close_snd_dev(void) 
    3856 { 
    3857     pj_log_push_indent(); 
    3858  
    3859     /* Notify app */ 
    3860     if (pjsua_var.snd_is_on && pjsua_var.ua_cfg.cb.on_snd_dev_operation) { 
    3861         (*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(0); 
    3862     } 
    3863  
    3864     /* Close sound device */ 
    3865     if (pjsua_var.snd_port) { 
    3866         pjmedia_aud_dev_info cap_info, play_info; 
    3867         pjmedia_aud_stream *strm; 
    3868         pjmedia_aud_param param; 
    3869  
    3870         strm = pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port); 
    3871         pjmedia_aud_stream_get_param(strm, &param); 
    3872  
    3873         if (pjmedia_aud_dev_get_info(param.rec_id, &cap_info) != PJ_SUCCESS) 
    3874             cap_info.name[0] = '\0'; 
    3875         if (pjmedia_aud_dev_get_info(param.play_id, &play_info) != PJ_SUCCESS) 
    3876             play_info.name[0] = '\0'; 
    3877  
    3878         PJ_LOG(4,(THIS_FILE, "Closing %s sound playback device and " 
    3879                              "%s sound capture device", 
    3880                              play_info.name, cap_info.name)); 
    3881  
    3882         pjmedia_snd_port_disconnect(pjsua_var.snd_port); 
    3883         pjmedia_snd_port_destroy(pjsua_var.snd_port); 
    3884         pjsua_var.snd_port = NULL; 
    3885     } 
    3886  
    3887     /* Close null sound device */ 
    3888     if (pjsua_var.null_snd) { 
    3889         PJ_LOG(4,(THIS_FILE, "Closing null sound device..")); 
    3890         pjmedia_master_port_destroy(pjsua_var.null_snd, PJ_FALSE); 
    3891         pjsua_var.null_snd = NULL; 
    3892     } 
    3893  
    3894     if (pjsua_var.snd_pool)  
    3895         pj_pool_release(pjsua_var.snd_pool); 
    3896     pjsua_var.snd_pool = NULL; 
    3897     pjsua_var.snd_is_on = PJ_FALSE; 
    3898  
    3899     pj_log_pop_indent(); 
    3900 } 
    3901  
    3902  
    3903 /* 
    3904  * Select or change sound device. Application may call this function at 
    3905  * any time to replace current sound device. 
    3906  */ 
    3907 PJ_DEF(pj_status_t) pjsua_set_snd_dev( int capture_dev, 
    3908                                        int playback_dev) 
    3909 { 
    3910     unsigned alt_cr_cnt = 1; 
    3911     unsigned alt_cr[] = {0, 44100, 48000, 32000, 16000, 8000}; 
    3912     unsigned i; 
    3913     pj_status_t status = -1; 
    3914  
    3915     PJ_LOG(4,(THIS_FILE, "Set sound device: capture=%d, playback=%d", 
    3916               capture_dev, playback_dev)); 
    3917     pj_log_push_indent(); 
    3918  
    3919     /* Null-sound */ 
    3920     if (capture_dev==NULL_SND_DEV_ID && playback_dev==NULL_SND_DEV_ID) { 
    3921         status = pjsua_set_null_snd_dev(); 
    3922         pj_log_pop_indent(); 
    3923         return status; 
    3924     } 
    3925  
    3926     /* Set default clock rate */ 
    3927     alt_cr[0] = pjsua_var.media_cfg.snd_clock_rate; 
    3928     if (alt_cr[0] == 0) 
    3929         alt_cr[0] = pjsua_var.media_cfg.clock_rate; 
    3930  
    3931     /* Allow retrying of different clock rate if we're using conference  
    3932      * bridge (meaning audio format is always PCM), otherwise lock on 
    3933      * to one clock rate. 
    3934      */ 
    3935     if (pjsua_var.is_mswitch) { 
    3936         alt_cr_cnt = 1; 
    3937     } else { 
    3938         alt_cr_cnt = PJ_ARRAY_SIZE(alt_cr); 
    3939     } 
    3940  
    3941     /* Attempts to open the sound device with different clock rates */ 
    3942     for (i=0; i<alt_cr_cnt; ++i) { 
    3943         pjmedia_snd_port_param param; 
    3944         unsigned samples_per_frame; 
    3945  
    3946         /* Create the default audio param */ 
    3947         samples_per_frame = alt_cr[i] * 
    3948                             pjsua_var.media_cfg.audio_frame_ptime * 
    3949                             pjsua_var.media_cfg.channel_count / 1000; 
    3950         status = create_aud_param(&param.base, capture_dev, playback_dev,  
    3951                                   alt_cr[i], pjsua_var.media_cfg.channel_count, 
    3952                                   samples_per_frame, 16); 
    3953         if (status != PJ_SUCCESS) 
    3954             goto on_error; 
    3955  
    3956         /* Open! */ 
    3957         param.options = 0; 
    3958         status = open_snd_dev(&param); 
    3959         if (status == PJ_SUCCESS) 
    3960             break; 
    3961     } 
    3962  
    3963     if (status != PJ_SUCCESS) { 
    3964         pjsua_perror(THIS_FILE, "Unable to open sound device", status); 
    3965         goto on_error; 
    3966     } 
    3967  
    3968     pjsua_var.no_snd = PJ_FALSE; 
    3969     pjsua_var.snd_is_on = PJ_TRUE; 
    3970  
    3971     pj_log_pop_indent(); 
    3972     return PJ_SUCCESS; 
    3973  
    3974 on_error: 
    3975     pj_log_pop_indent(); 
    3976     return status; 
    3977 } 
    3978  
    3979  
    3980 /* 
    3981  * Get currently active sound devices. If sound devices has not been created 
    3982  * (for example when pjsua_start() is not called), it is possible that 
    3983  * the function returns PJ_SUCCESS with -1 as device IDs. 
    3984  */ 
    3985 PJ_DEF(pj_status_t) pjsua_get_snd_dev(int *capture_dev, 
    3986                                       int *playback_dev) 
    3987 { 
    3988     if (capture_dev) { 
    3989         *capture_dev = pjsua_var.cap_dev; 
    3990     } 
    3991     if (playback_dev) { 
    3992         *playback_dev = pjsua_var.play_dev; 
    3993     } 
    3994  
    3995     return PJ_SUCCESS; 
    3996 } 
    3997  
    3998  
    3999 /* 
    4000  * Use null sound device. 
    4001  */ 
    4002 PJ_DEF(pj_status_t) pjsua_set_null_snd_dev(void) 
    4003 { 
    4004     pjmedia_port *conf_port; 
    4005     pj_status_t status; 
    4006  
    4007     PJ_LOG(4,(THIS_FILE, "Setting null sound device..")); 
    4008     pj_log_push_indent(); 
    4009  
    4010  
    4011     /* Close existing sound device */ 
    4012     close_snd_dev(); 
    4013  
    4014     /* Notify app */ 
    4015     if (pjsua_var.ua_cfg.cb.on_snd_dev_operation) { 
    4016         (*pjsua_var.ua_cfg.cb.on_snd_dev_operation)(1); 
    4017     } 
    4018  
    4019     /* Create memory pool for sound device. */ 
    4020     pjsua_var.snd_pool = pjsua_pool_create("pjsua_snd", 4000, 4000); 
    4021     PJ_ASSERT_RETURN(pjsua_var.snd_pool, PJ_ENOMEM); 
    4022  
    4023     PJ_LOG(4,(THIS_FILE, "Opening null sound device..")); 
    4024  
    4025     /* Get the port0 of the conference bridge. */ 
    4026     conf_port = pjmedia_conf_get_master_port(pjsua_var.mconf); 
    4027     pj_assert(conf_port != NULL); 
    4028  
    4029     /* Create master port, connecting port0 of the conference bridge to 
    4030      * a null port. 
    4031      */ 
    4032     status = pjmedia_master_port_create(pjsua_var.snd_pool, pjsua_var.null_port, 
    4033                                         conf_port, 0, &pjsua_var.null_snd); 
    4034     if (status != PJ_SUCCESS) { 
    4035         pjsua_perror(THIS_FILE, "Unable to create null sound device", 
    4036                      status); 
    4037         pj_log_pop_indent(); 
    4038         return status; 
    4039     } 
    4040  
    4041     /* Start the master port */ 
    4042     status = pjmedia_master_port_start(pjsua_var.null_snd); 
    4043     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); 
    4044  
    4045     pjsua_var.cap_dev = NULL_SND_DEV_ID; 
    4046     pjsua_var.play_dev = NULL_SND_DEV_ID; 
    4047  
    4048     pjsua_var.no_snd = PJ_FALSE; 
    4049     pjsua_var.snd_is_on = PJ_TRUE; 
    4050  
    4051     pj_log_pop_indent(); 
    4052     return PJ_SUCCESS; 
    4053 } 
    4054  
    4055  
    4056  
    4057 /* 
    4058  * Use no device! 
    4059  */ 
    4060 PJ_DEF(pjmedia_port*) pjsua_set_no_snd_dev(void) 
    4061 { 
    4062     /* Close existing sound device */ 
    4063     close_snd_dev(); 
    4064  
    4065     pjsua_var.no_snd = PJ_TRUE; 
    4066     return pjmedia_conf_get_master_port(pjsua_var.mconf); 
    4067 } 
    4068  
    4069  
    4070 /* 
    4071  * Configure the AEC settings of the sound port. 
    4072  */ 
    4073 PJ_DEF(pj_status_t) pjsua_set_ec(unsigned tail_ms, unsigned options) 
    4074 { 
    4075     pjsua_var.media_cfg.ec_tail_len = tail_ms; 
    4076  
    4077     if (pjsua_var.snd_port) 
    4078         return pjmedia_snd_port_set_ec( pjsua_var.snd_port, pjsua_var.pool, 
    4079                                         tail_ms, options); 
    4080      
    4081     return PJ_SUCCESS; 
    4082 } 
    4083  
    4084  
    4085 /* 
    4086  * Get current AEC tail length. 
    4087  */ 
    4088 PJ_DEF(pj_status_t) pjsua_get_ec_tail(unsigned *p_tail_ms) 
    4089 { 
    4090     *p_tail_ms = pjsua_var.media_cfg.ec_tail_len; 
    4091     return PJ_SUCCESS; 
    4092 } 
    4093  
    4094  
    4095 /* 
    4096  * Check whether the sound device is currently active. 
    4097  */ 
    4098 PJ_DEF(pj_bool_t) pjsua_snd_is_active(void) 
    4099 { 
    4100     return pjsua_var.snd_port != NULL; 
    4101 } 
    4102  
    4103  
    4104 /* 
    4105  * Configure sound device setting to the sound device being used.  
    4106  */ 
    4107 PJ_DEF(pj_status_t) pjsua_snd_set_setting( pjmedia_aud_dev_cap cap, 
    4108                                            const void *pval, 
    4109                                            pj_bool_t keep) 
    4110 { 
    4111     pj_status_t status; 
    4112  
    4113     /* Check if we are allowed to set the cap */ 
    4114     if ((cap & pjsua_var.aud_svmask) == 0) { 
    4115         return PJMEDIA_EAUD_INVCAP; 
    4116     } 
    4117  
    4118     /* If sound is active, set it immediately */ 
    4119     if (pjsua_snd_is_active()) { 
    4120         pjmedia_aud_stream *strm; 
    4121          
    4122         strm = pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port); 
    4123         status = pjmedia_aud_stream_set_cap(strm, cap, pval); 
    4124     } else { 
    4125         status = PJ_SUCCESS; 
    4126     } 
    4127  
    4128     if (status != PJ_SUCCESS) 
    4129         return status; 
    4130  
    4131     /* Save in internal param for later device open */ 
    4132     if (keep) { 
    4133         status = pjmedia_aud_param_set_cap(&pjsua_var.aud_param, 
    4134                                            cap, pval); 
    4135     } 
    4136  
    4137     return status; 
    4138 } 
    4139  
    4140 /* 
    4141  * Retrieve a sound device setting. 
    4142  */ 
    4143 PJ_DEF(pj_status_t) pjsua_snd_get_setting( pjmedia_aud_dev_cap cap, 
    4144                                            void *pval) 
    4145 { 
    4146     /* If sound device has never been opened before, open it to  
    4147      * retrieve the initial setting from the device (e.g. audio 
    4148      * volume) 
    4149      */ 
    4150     if (pjsua_var.aud_open_cnt==0) { 
    4151         PJ_LOG(4,(THIS_FILE, "Opening sound device to get initial settings")); 
    4152         pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev); 
    4153         close_snd_dev(); 
    4154     } 
    4155  
    4156     if (pjsua_snd_is_active()) { 
    4157         /* Sound is active, retrieve from device directly */ 
    4158         pjmedia_aud_stream *strm; 
    4159          
    4160         strm = pjmedia_snd_port_get_snd_stream(pjsua_var.snd_port); 
    4161         return pjmedia_aud_stream_get_cap(strm, cap, pval); 
    4162     } else { 
    4163         /* Otherwise retrieve from internal param */ 
    4164         return pjmedia_aud_param_get_cap(&pjsua_var.aud_param, 
    4165                                          cap, pval); 
    4166     } 
    4167 } 
    4168  
    4169  
    41702438/***************************************************************************** 
    41712439 * Codecs. 
Note: See TracChangeset for help on using the changeset viewer.