Changeset 3982 for pjproject


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
Location:
pjproject/trunk
Files:
3 added
11 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/Makefile

    r3669 r3982  
    44include version.mak 
    55 
    6 DIRS = pjlib/build pjlib-util/build pjnath/build third_party/build pjmedia/build pjsip/build pjsip-apps/build $(EXTRA_DIRS) 
     6LIB_DIRS = pjlib/build pjlib-util/build pjnath/build third_party/build pjmedia/build pjsip/build 
     7DIRS = $(LIB_DIRS) pjsip-apps/build $(EXTRA_DIRS) 
    78 
    89ifdef MINSIZE 
    910MAKE_FLAGS := MINSIZE=1 
    1011endif 
     12 
     13lib: 
     14        for dir in $(LIB_DIRS); do \ 
     15                if $(MAKE) $(MAKE_FLAGS) -C $$dir all; then \ 
     16                    true; \ 
     17                else \ 
     18                    exit 1; \ 
     19                fi; \ 
     20        done; \ 
     21        make -C pjsip-apps/src/third_party_media 
     22 
    1123 
    1224all clean dep depend distclean print realclean: 
     
    1830                fi; \ 
    1931        done 
     32 
     33.PHONY: lib  
    2034 
    2135doc: 
  • pjproject/trunk/pjmedia/build/Makefile

    r3911 r3982  
    6565                        resample_port.o rtcp.o rtcp_xr.o rtp.o \ 
    6666                        sdp.o sdp_cmp.o sdp_neg.o session.o silencedet.o \ 
    67                         sound_legacy.o sound_port.o stereo_port.o \ 
    68                         stream_common.o stream.o tonegen.o transport_adapter_sample.o \ 
     67                        sound_legacy.o sound_port.o stereo_port.o stream_common.o \ 
     68                        stream.o stream_info.o tonegen.o transport_adapter_sample.o \ 
    6969                        transport_ice.o transport_loop.o transport_srtp.o transport_udp.o \ 
    7070                        types.o vid_codec.o vid_codec_util.o \ 
    71                         vid_port.o vid_stream.o vid_tee.o \ 
     71                        vid_port.o vid_stream.o vid_stream_info.o vid_tee.o \ 
    7272                        wav_player.o wav_playlist.o wav_writer.o wave.o \ 
    7373                        wsola.o 
  • pjproject/trunk/pjmedia/src/pjmedia/stream.c

    r3917 r3982  
    27942794} 
    27952795 
    2796  
    2797 static const pj_str_t ID_AUDIO = { "audio", 5}; 
    2798 static const pj_str_t ID_IN = { "IN", 2 }; 
    2799 static const pj_str_t ID_IP4 = { "IP4", 3}; 
    2800 static const pj_str_t ID_IP6 = { "IP6", 3}; 
    2801 static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; 
    2802 static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 }; 
    2803 //static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; 
    2804 static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; 
    2805 static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 }; 
    2806  
    2807 static const pj_str_t STR_INACTIVE = { "inactive", 8 }; 
    2808 static const pj_str_t STR_SENDRECV = { "sendrecv", 8 }; 
    2809 static const pj_str_t STR_SENDONLY = { "sendonly", 8 }; 
    2810 static const pj_str_t STR_RECVONLY = { "recvonly", 8 }; 
    2811  
    2812  
    2813 /* 
    2814  * Internal function for collecting codec info and param from the SDP media. 
    2815  */ 
    2816 static pj_status_t get_audio_codec_info_param(pjmedia_stream_info *si, 
    2817                                               pj_pool_t *pool, 
    2818                                               pjmedia_codec_mgr *mgr, 
    2819                                               const pjmedia_sdp_media *local_m, 
    2820                                               const pjmedia_sdp_media *rem_m) 
    2821 { 
    2822     const pjmedia_sdp_attr *attr; 
    2823     pjmedia_sdp_rtpmap *rtpmap; 
    2824     unsigned i, fmti, pt = 0; 
    2825     pj_status_t status; 
    2826  
    2827     /* Find the first codec which is not telephone-event */ 
    2828     for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) { 
    2829         pjmedia_sdp_rtpmap r; 
    2830  
    2831         if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) ) 
    2832             return PJMEDIA_EINVALIDPT; 
    2833         pt = pj_strtoul(&local_m->desc.fmt[fmti]); 
    2834  
    2835         if (pt < 96) { 
    2836             /* This is known static PT. Skip rtpmap checking because it is 
    2837              * optional. */ 
    2838             break; 
    2839         } 
    2840  
    2841         attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 
    2842                                            &local_m->desc.fmt[fmti]); 
    2843         if (attr == NULL) 
    2844             continue; 
    2845  
    2846         status = pjmedia_sdp_attr_get_rtpmap(attr, &r); 
    2847         if (status != PJ_SUCCESS) 
    2848             continue; 
    2849  
    2850         if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) != 0) 
    2851             break; 
    2852     } 
    2853     if ( fmti >= local_m->desc.fmt_count ) 
    2854         return PJMEDIA_EINVALIDPT; 
    2855  
    2856     /* Get payload type for receiving direction */ 
    2857     si->rx_pt = pt; 
    2858  
    2859     /* Get codec info. 
    2860      * For static payload types, get the info from codec manager. 
    2861      * For dynamic payload types, MUST get the rtpmap. 
    2862      */ 
    2863     if (pt < 96) { 
    2864         pj_bool_t has_rtpmap; 
    2865  
    2866         rtpmap = NULL; 
    2867         has_rtpmap = PJ_TRUE; 
    2868  
    2869         attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,  
    2870                                            &local_m->desc.fmt[fmti]); 
    2871         if (attr == NULL) { 
    2872             has_rtpmap = PJ_FALSE; 
    2873         } 
    2874         if (attr != NULL) { 
    2875             status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); 
    2876             if (status != PJ_SUCCESS) 
    2877                 has_rtpmap = PJ_FALSE; 
    2878         } 
    2879  
    2880         /* Build codec format info: */ 
    2881         if (has_rtpmap) { 
    2882             si->fmt.type = si->type; 
    2883             si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); 
    2884             pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); 
    2885             si->fmt.clock_rate = rtpmap->clock_rate; 
    2886              
    2887 #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0) 
    2888             /* The session info should have the actual clock rate, because  
    2889              * this info is used for calculationg buffer size, etc in stream  
    2890              */ 
    2891             if (si->fmt.pt == PJMEDIA_RTP_PT_G722) 
    2892                 si->fmt.clock_rate = 16000; 
    2893 #endif 
    2894  
    2895             /* For audio codecs, rtpmap parameters denotes the number of 
    2896              * channels. 
    2897              */ 
    2898             if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { 
    2899                 si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); 
    2900             } else { 
    2901                 si->fmt.channel_cnt = 1; 
    2902             } 
    2903  
    2904         } else {             
    2905             const pjmedia_codec_info *p_info; 
    2906  
    2907             status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info); 
    2908             if (status != PJ_SUCCESS) 
    2909                 return status; 
    2910  
    2911             pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); 
    2912         } 
    2913  
    2914         /* For static payload type, pt's are symetric */ 
    2915         si->tx_pt = pt; 
    2916  
    2917     } else { 
    2918         pjmedia_codec_id codec_id; 
    2919         pj_str_t codec_id_st; 
    2920         const pjmedia_codec_info *p_info; 
    2921  
    2922         attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,  
    2923                                            &local_m->desc.fmt[fmti]); 
    2924         if (attr == NULL) 
    2925             return PJMEDIA_EMISSINGRTPMAP; 
    2926  
    2927         status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); 
    2928         if (status != PJ_SUCCESS) 
    2929             return status; 
    2930  
    2931         /* Build codec format info: */ 
    2932  
    2933         si->fmt.type = si->type; 
    2934         si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); 
    2935         si->fmt.encoding_name = rtpmap->enc_name; 
    2936         si->fmt.clock_rate = rtpmap->clock_rate; 
    2937  
    2938         /* For audio codecs, rtpmap parameters denotes the number of 
    2939          * channels. 
    2940          */ 
    2941         if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { 
    2942             si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); 
    2943         } else { 
    2944             si->fmt.channel_cnt = 1; 
    2945         } 
    2946  
    2947         /* Normalize the codec info from codec manager. Note that the 
    2948          * payload type will be resetted to its default (it might have 
    2949          * been rewritten by the SDP negotiator to match to the remote 
    2950          * offer), this is intentional as currently some components may 
    2951          * prefer (or even require) the default PT in codec info. 
    2952          */ 
    2953         pjmedia_codec_info_to_id(&si->fmt, codec_id, sizeof(codec_id)); 
    2954  
    2955         i = 1; 
    2956         codec_id_st = pj_str(codec_id); 
    2957         status = pjmedia_codec_mgr_find_codecs_by_id(mgr, &codec_id_st, 
    2958                                                      &i, &p_info, NULL); 
    2959         if (status != PJ_SUCCESS) 
    2960             return status; 
    2961  
    2962         pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); 
    2963  
    2964         /* Determine payload type for outgoing channel, by finding 
    2965          * dynamic payload type in remote SDP that matches the answer. 
    2966          */ 
    2967         si->tx_pt = 0xFFFF; 
    2968         for (i=0; i<rem_m->desc.fmt_count; ++i) { 
    2969             unsigned rpt; 
    2970             pjmedia_sdp_attr *r_attr; 
    2971             pjmedia_sdp_rtpmap r_rtpmap; 
    2972  
    2973             rpt = pj_strtoul(&rem_m->desc.fmt[i]); 
    2974             if (rpt < 96) 
    2975                 continue; 
    2976  
    2977             r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP, 
    2978                                                  &rem_m->desc.fmt[i]); 
    2979             if (!r_attr) 
    2980                 continue; 
    2981  
    2982             if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS) 
    2983                 continue; 
    2984  
    2985             if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) && 
    2986                 rtpmap->clock_rate == r_rtpmap.clock_rate) 
    2987             { 
    2988                 /* Found matched codec. */ 
    2989                 si->tx_pt = rpt; 
    2990  
    2991                 break; 
    2992             } 
    2993         } 
    2994  
    2995         if (si->tx_pt == 0xFFFF) 
    2996             return PJMEDIA_EMISSINGRTPMAP; 
    2997     } 
    2998  
    2999    
    3000     /* Now that we have codec info, get the codec param. */ 
    3001     si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param); 
    3002     status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, 
    3003                                                  si->param); 
    3004  
    3005     /* Get remote fmtp for our encoder. */ 
    3006     pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt, 
    3007                                    &si->param->setting.enc_fmtp); 
    3008  
    3009     /* Get local fmtp for our decoder. */ 
    3010     pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt, 
    3011                                    &si->param->setting.dec_fmtp); 
    3012  
    3013     /* Get the remote ptime for our encoder. */ 
    3014     attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, 
    3015                                   "ptime", NULL); 
    3016     if (attr) { 
    3017         pj_str_t tmp_val = attr->value; 
    3018         unsigned frm_per_pkt; 
    3019   
    3020         pj_strltrim(&tmp_val); 
    3021  
    3022         /* Round up ptime when the specified is not multiple of frm_ptime */ 
    3023         frm_per_pkt = (pj_strtoul(&tmp_val) +  
    3024                       si->param->info.frm_ptime/2) / 
    3025                       si->param->info.frm_ptime; 
    3026         if (frm_per_pkt != 0) { 
    3027             si->param->setting.frm_per_pkt = (pj_uint8_t)frm_per_pkt; 
    3028         } 
    3029     } 
    3030  
    3031     /* Get remote maxptime for our encoder. */ 
    3032     attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, 
    3033                                   "maxptime", NULL); 
    3034     if (attr) { 
    3035         pj_str_t tmp_val = attr->value; 
    3036  
    3037         pj_strltrim(&tmp_val); 
    3038         si->tx_maxptime = pj_strtoul(&tmp_val); 
    3039     } 
    3040  
    3041     /* When direction is NONE (it means SDP negotiation has failed) we don't 
    3042      * need to return a failure here, as returning failure will cause 
    3043      * the whole SDP to be rejected. See ticket #: 
    3044      *  http:// 
    3045      * 
    3046      * Thanks Alain Totouom  
    3047      */ 
    3048     if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) 
    3049         return status; 
    3050  
    3051  
    3052     /* Get incomming payload type for telephone-events */ 
    3053     si->rx_event_pt = -1; 
    3054     for (i=0; i<local_m->attr_count; ++i) { 
    3055         pjmedia_sdp_rtpmap r; 
    3056  
    3057         attr = local_m->attr[i]; 
    3058         if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) 
    3059             continue; 
    3060         if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) 
    3061             continue; 
    3062         if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { 
    3063             si->rx_event_pt = pj_strtoul(&r.pt); 
    3064             break; 
    3065         } 
    3066     } 
    3067  
    3068     /* Get outgoing payload type for telephone-events */ 
    3069     si->tx_event_pt = -1; 
    3070     for (i=0; i<rem_m->attr_count; ++i) { 
    3071         pjmedia_sdp_rtpmap r; 
    3072  
    3073         attr = rem_m->attr[i]; 
    3074         if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) 
    3075             continue; 
    3076         if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) 
    3077             continue; 
    3078         if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { 
    3079             si->tx_event_pt = pj_strtoul(&r.pt); 
    3080             break; 
    3081         } 
    3082     } 
    3083  
    3084     return PJ_SUCCESS; 
    3085 } 
    3086  
    3087  
    3088  
    3089 /* 
    3090  * Create stream info from SDP media line. 
    3091  */ 
    3092 PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( 
    3093                                            pjmedia_stream_info *si, 
    3094                                            pj_pool_t *pool, 
    3095                                            pjmedia_endpt *endpt, 
    3096                                            const pjmedia_sdp_session *local, 
    3097                                            const pjmedia_sdp_session *remote, 
    3098                                            unsigned stream_idx) 
    3099 { 
    3100     pjmedia_codec_mgr *mgr; 
    3101     const pjmedia_sdp_attr *attr; 
    3102     const pjmedia_sdp_media *local_m; 
    3103     const pjmedia_sdp_media *rem_m; 
    3104     const pjmedia_sdp_conn *local_conn; 
    3105     const pjmedia_sdp_conn *rem_conn; 
    3106     int rem_af, local_af; 
    3107     pj_sockaddr local_addr; 
    3108     pj_status_t status; 
    3109  
    3110      
    3111     /* Validate arguments: */ 
    3112     PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); 
    3113     PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); 
    3114     PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); 
    3115  
    3116     /* Keep SDP shortcuts */ 
    3117     local_m = local->media[stream_idx]; 
    3118     rem_m = remote->media[stream_idx]; 
    3119  
    3120     local_conn = local_m->conn ? local_m->conn : local->conn; 
    3121     if (local_conn == NULL) 
    3122         return PJMEDIA_SDP_EMISSINGCONN; 
    3123  
    3124     rem_conn = rem_m->conn ? rem_m->conn : remote->conn; 
    3125     if (rem_conn == NULL) 
    3126         return PJMEDIA_SDP_EMISSINGCONN; 
    3127  
    3128     /* Media type must be audio */ 
    3129     if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) != 0) 
    3130         return PJMEDIA_EINVALIMEDIATYPE; 
    3131  
    3132     /* Get codec manager. */ 
    3133     mgr = pjmedia_endpt_get_codec_mgr(endpt); 
    3134  
    3135     /* Reset: */ 
    3136  
    3137     pj_bzero(si, sizeof(*si)); 
    3138  
    3139 #if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR 
    3140     /* Set default RTCP XR enabled/disabled */ 
    3141     si->rtcp_xr_enabled = PJ_TRUE; 
    3142 #endif 
    3143  
    3144     /* Media type: */ 
    3145     si->type = PJMEDIA_TYPE_AUDIO; 
    3146  
    3147     /* Transport protocol */ 
    3148  
    3149     /* At this point, transport type must be compatible,  
    3150      * the transport instance will do more validation later. 
    3151      */ 
    3152     status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport,  
    3153                                        &local_m->desc.transport); 
    3154     if (status != PJ_SUCCESS) 
    3155         return PJMEDIA_SDPNEG_EINVANSTP; 
    3156  
    3157     if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) { 
    3158  
    3159         si->proto = PJMEDIA_TP_PROTO_RTP_AVP; 
    3160  
    3161     } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) { 
    3162  
    3163         si->proto = PJMEDIA_TP_PROTO_RTP_SAVP; 
    3164  
    3165     } else { 
    3166  
    3167         si->proto = PJMEDIA_TP_PROTO_UNKNOWN; 
    3168         return PJ_SUCCESS; 
    3169     } 
    3170  
    3171  
    3172     /* Check address family in remote SDP */ 
    3173     rem_af = pj_AF_UNSPEC(); 
    3174     if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { 
    3175         if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { 
    3176             rem_af = pj_AF_INET(); 
    3177         } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { 
    3178             rem_af = pj_AF_INET6(); 
    3179         } 
    3180     } 
    3181  
    3182     if (rem_af==pj_AF_UNSPEC()) { 
    3183         /* Unsupported address family */ 
    3184         return PJ_EAFNOTSUP; 
    3185     } 
    3186  
    3187     /* Set remote address: */ 
    3188     status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr,  
    3189                               rem_m->desc.port); 
    3190     if (status != PJ_SUCCESS) { 
    3191         /* Invalid IP address. */ 
    3192         return PJMEDIA_EINVALIDIP; 
    3193     } 
    3194  
    3195     /* Check address family of local info */ 
    3196     local_af = pj_AF_UNSPEC(); 
    3197     if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { 
    3198         if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { 
    3199             local_af = pj_AF_INET(); 
    3200         } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { 
    3201             local_af = pj_AF_INET6(); 
    3202         } 
    3203     } 
    3204  
    3205     if (local_af==pj_AF_UNSPEC()) { 
    3206         /* Unsupported address family */ 
    3207         return PJ_SUCCESS; 
    3208     } 
    3209  
    3210     /* Set remote address: */ 
    3211     status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr,  
    3212                               local_m->desc.port); 
    3213     if (status != PJ_SUCCESS) { 
    3214         /* Invalid IP address. */ 
    3215         return PJMEDIA_EINVALIDIP; 
    3216     } 
    3217  
    3218     /* Local and remote address family must match */ 
    3219     if (local_af != rem_af) 
    3220         return PJ_EAFNOTSUP; 
    3221  
    3222     /* Media direction: */ 
    3223  
    3224     if (local_m->desc.port == 0 ||  
    3225         pj_sockaddr_has_addr(&local_addr)==PJ_FALSE || 
    3226         pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || 
    3227         pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) 
    3228     { 
    3229         /* Inactive stream. */ 
    3230  
    3231         si->dir = PJMEDIA_DIR_NONE; 
    3232  
    3233     } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { 
    3234  
    3235         /* Send only stream. */ 
    3236  
    3237         si->dir = PJMEDIA_DIR_ENCODING; 
    3238  
    3239     } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { 
    3240  
    3241         /* Recv only stream. */ 
    3242  
    3243         si->dir = PJMEDIA_DIR_DECODING; 
    3244  
    3245     } else { 
    3246  
    3247         /* Send and receive stream. */ 
    3248  
    3249         si->dir = PJMEDIA_DIR_ENCODING_DECODING; 
    3250  
    3251     } 
    3252  
    3253     /* No need to do anything else if stream is rejected */ 
    3254     if (local_m->desc.port == 0) { 
    3255         return PJ_SUCCESS; 
    3256     } 
    3257  
    3258     /* If "rtcp" attribute is present in the SDP, set the RTCP address 
    3259      * from that attribute. Otherwise, calculate from RTP address. 
    3260      */ 
    3261     attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, 
    3262                                   "rtcp", NULL); 
    3263     if (attr) { 
    3264         pjmedia_sdp_rtcp_attr rtcp; 
    3265         status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); 
    3266         if (status == PJ_SUCCESS) { 
    3267             if (rtcp.addr.slen) { 
    3268                 status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, 
    3269                                           (pj_uint16_t)rtcp.port); 
    3270             } else { 
    3271                 pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL,  
    3272                                  (pj_uint16_t)rtcp.port); 
    3273                 pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), 
    3274                           pj_sockaddr_get_addr(&si->rem_addr), 
    3275                           pj_sockaddr_get_addr_len(&si->rem_addr)); 
    3276             } 
    3277         } 
    3278     } 
    3279      
    3280     if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { 
    3281         int rtcp_port; 
    3282  
    3283         pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); 
    3284         rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; 
    3285         pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); 
    3286     } 
    3287  
    3288  
    3289     /* Get the payload number for receive channel. */ 
    3290     /* 
    3291        Previously we used to rely on fmt[0] being the selected codec, 
    3292        but some UA sends telephone-event as fmt[0] and this would 
    3293        cause assert failure below. 
    3294  
    3295        Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch. 
    3296  
    3297     // And codec must be numeric! 
    3298     if (!pj_isdigit(*local_m->desc.fmt[0].ptr) ||  
    3299         !pj_isdigit(*rem_m->desc.fmt[0].ptr)) 
    3300     { 
    3301         return PJMEDIA_EINVALIDPT; 
    3302     } 
    3303  
    3304     pt = pj_strtoul(&local_m->desc.fmt[0]); 
    3305     pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 || 
    3306               pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS); 
    3307     */ 
    3308  
    3309     /* Get codec info and param */ 
    3310     status = get_audio_codec_info_param(si, pool, mgr, local_m, rem_m); 
    3311  
    3312     /* Leave SSRC to random. */ 
    3313     si->ssrc = pj_rand(); 
    3314  
    3315     /* Set default jitter buffer parameter. */ 
    3316     si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; 
    3317  
    3318     return status; 
    3319 } 
    3320  
    33212796/* 
    33222797 * Send RTCP SDES. 
  • pjproject/trunk/pjmedia/src/pjmedia/vid_stream.c

    r3975 r3982  
    2323#include <pjmedia/rtcp.h> 
    2424#include <pjmedia/jbuf.h> 
    25 #include <pjmedia/sdp_neg.h> 
    2625#include <pjmedia/stream_common.h> 
    2726#include <pj/array.h> 
    2827#include <pj/assert.h> 
    29 #include <pj/ctype.h> 
    3028#include <pj/compat/socket.h> 
    3129#include <pj/errno.h> 
     
    17911789} 
    17921790 
    1793  
    1794 static const pj_str_t ID_VIDEO = { "video", 5}; 
    1795 static const pj_str_t ID_IN = { "IN", 2 }; 
    1796 static const pj_str_t ID_IP4 = { "IP4", 3}; 
    1797 static const pj_str_t ID_IP6 = { "IP6", 3}; 
    1798 static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; 
    1799 static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 }; 
    1800 //static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; 
    1801 static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; 
    1802  
    1803 static const pj_str_t STR_INACTIVE = { "inactive", 8 }; 
    1804 static const pj_str_t STR_SENDRECV = { "sendrecv", 8 }; 
    1805 static const pj_str_t STR_SENDONLY = { "sendonly", 8 }; 
    1806 static const pj_str_t STR_RECVONLY = { "recvonly", 8 }; 
    1807  
    1808  
    1809 /* 
    1810  * Internal function for collecting codec info and param from the SDP media. 
    1811  */ 
    1812 static pj_status_t get_video_codec_info_param(pjmedia_vid_stream_info *si, 
    1813                                               pj_pool_t *pool, 
    1814                                               pjmedia_vid_codec_mgr *mgr, 
    1815                                               const pjmedia_sdp_media *local_m, 
    1816                                               const pjmedia_sdp_media *rem_m) 
    1817 { 
    1818     unsigned pt = 0; 
    1819     const pjmedia_vid_codec_info *p_info; 
    1820     pj_status_t status; 
    1821  
    1822     pt = pj_strtoul(&local_m->desc.fmt[0]); 
    1823  
    1824     /* Get payload type for receiving direction */ 
    1825     si->rx_pt = pt; 
    1826  
    1827     /* Get codec info and payload type for transmitting direction. */ 
    1828     if (pt < 96) { 
    1829         /* For static payload types, get the codec info from codec manager. */ 
    1830         status = pjmedia_vid_codec_mgr_get_codec_info(mgr, pt, &p_info); 
    1831         if (status != PJ_SUCCESS) 
    1832             return status; 
    1833  
    1834         si->codec_info = *p_info; 
    1835  
    1836         /* Get payload type for transmitting direction. 
    1837          * For static payload type, pt's are symetric. 
    1838          */ 
    1839         si->tx_pt = pt; 
    1840     } else { 
    1841         const pjmedia_sdp_attr *attr; 
    1842         pjmedia_sdp_rtpmap *rtpmap; 
    1843         pjmedia_codec_id codec_id; 
    1844         pj_str_t codec_id_st; 
    1845         unsigned i; 
    1846  
    1847         /* Determine payload type for outgoing channel, by finding 
    1848          * dynamic payload type in remote SDP that matches the answer. 
    1849          */ 
    1850         si->tx_pt = 0xFFFF; 
    1851         for (i=0; i<rem_m->desc.fmt_count; ++i) { 
    1852             if (pjmedia_sdp_neg_fmt_match(NULL, 
    1853                                           (pjmedia_sdp_media*)local_m, 0, 
    1854                                           (pjmedia_sdp_media*)rem_m, i, 0) == 
    1855                 PJ_SUCCESS) 
    1856             { 
    1857                 /* Found matched codec. */ 
    1858                 si->tx_pt = pj_strtoul(&rem_m->desc.fmt[i]); 
    1859                 break; 
    1860             } 
    1861         } 
    1862  
    1863         if (si->tx_pt == 0xFFFF) 
    1864             return PJMEDIA_EMISSINGRTPMAP; 
    1865  
    1866         /* For dynamic payload types, get codec name from the rtpmap */ 
    1867         attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,  
    1868                                            &local_m->desc.fmt[0]); 
    1869         if (attr == NULL) 
    1870             return PJMEDIA_EMISSINGRTPMAP; 
    1871  
    1872         status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); 
    1873         if (status != PJ_SUCCESS) 
    1874             return status; 
    1875  
    1876         /* Then get the codec info from the codec manager */ 
    1877         pj_ansi_snprintf(codec_id, sizeof(codec_id), "%.*s/",  
    1878                          (int)rtpmap->enc_name.slen, rtpmap->enc_name.ptr); 
    1879         codec_id_st = pj_str(codec_id); 
    1880         i = 1; 
    1881         status = pjmedia_vid_codec_mgr_find_codecs_by_id(mgr, &codec_id_st, 
    1882                                                          &i, &p_info, NULL); 
    1883         if (status != PJ_SUCCESS) 
    1884             return status; 
    1885  
    1886         si->codec_info = *p_info; 
    1887     } 
    1888  
    1889  
    1890     /* Request for codec with the correct packing for streaming */ 
    1891     si->codec_info.packings = PJMEDIA_VID_PACKING_PACKETS; 
    1892  
    1893     /* Now that we have codec info, get the codec param. */ 
    1894     si->codec_param = PJ_POOL_ALLOC_T(pool, pjmedia_vid_codec_param); 
    1895     status = pjmedia_vid_codec_mgr_get_default_param(mgr,  
    1896                                                      &si->codec_info, 
    1897                                                      si->codec_param); 
    1898  
    1899     /* Adjust encoding bitrate, if higher than remote preference. The remote 
    1900      * bitrate preference is read from SDP "b=TIAS" line in media level. 
    1901      */ 
    1902     if ((si->dir & PJMEDIA_DIR_ENCODING) && rem_m->bandw_count) { 
    1903         unsigned i, bandw = 0; 
    1904  
    1905         for (i = 0; i < rem_m->bandw_count; ++i) { 
    1906             const pj_str_t STR_BANDW_MODIFIER_TIAS = { "TIAS", 4 }; 
    1907             if (!pj_stricmp(&rem_m->bandw[i]->modifier,  
    1908                 &STR_BANDW_MODIFIER_TIAS)) 
    1909             { 
    1910                 bandw = rem_m->bandw[i]->value; 
    1911                 break; 
    1912             } 
    1913         } 
    1914  
    1915         if (bandw) { 
    1916             pjmedia_video_format_detail *enc_vfd; 
    1917             enc_vfd = pjmedia_format_get_video_format_detail( 
    1918                                         &si->codec_param->enc_fmt, PJ_TRUE); 
    1919             if (!enc_vfd->avg_bps || enc_vfd->avg_bps > bandw) 
    1920                 enc_vfd->avg_bps = bandw * 3 / 4; 
    1921             if (!enc_vfd->max_bps || enc_vfd->max_bps > bandw) 
    1922                 enc_vfd->max_bps = bandw; 
    1923         } 
    1924     } 
    1925  
    1926     /* Get remote fmtp for our encoder. */ 
    1927     pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt, 
    1928                                    &si->codec_param->enc_fmtp); 
    1929  
    1930     /* Get local fmtp for our decoder. */ 
    1931     pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt, 
    1932                                    &si->codec_param->dec_fmtp); 
    1933  
    1934     /* When direction is NONE (it means SDP negotiation has failed) we don't 
    1935      * need to return a failure here, as returning failure will cause 
    1936      * the whole SDP to be rejected. See ticket #: 
    1937      *  http:// 
    1938      * 
    1939      * Thanks Alain Totouom  
    1940      */ 
    1941     if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) 
    1942         return status; 
    1943  
    1944     return PJ_SUCCESS; 
    1945 } 
    1946  
    1947  
    1948  
    1949 /* 
    1950  * Create stream info from SDP media line. 
    1951  */ 
    1952 PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp( 
    1953                                            pjmedia_vid_stream_info *si, 
    1954                                            pj_pool_t *pool, 
    1955                                            pjmedia_endpt *endpt, 
    1956                                            const pjmedia_sdp_session *local, 
    1957                                            const pjmedia_sdp_session *remote, 
    1958                                            unsigned stream_idx) 
    1959 { 
    1960     const pjmedia_sdp_attr *attr; 
    1961     const pjmedia_sdp_media *local_m; 
    1962     const pjmedia_sdp_media *rem_m; 
    1963     const pjmedia_sdp_conn *local_conn; 
    1964     const pjmedia_sdp_conn *rem_conn; 
    1965     int rem_af, local_af; 
    1966     pj_sockaddr local_addr; 
    1967     pj_status_t status; 
    1968  
    1969     PJ_UNUSED_ARG(endpt); 
    1970  
    1971     /* Validate arguments: */ 
    1972     PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); 
    1973     PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); 
    1974     PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); 
    1975  
    1976     /* Keep SDP shortcuts */ 
    1977     local_m = local->media[stream_idx]; 
    1978     rem_m = remote->media[stream_idx]; 
    1979  
    1980     local_conn = local_m->conn ? local_m->conn : local->conn; 
    1981     if (local_conn == NULL) 
    1982         return PJMEDIA_SDP_EMISSINGCONN; 
    1983  
    1984     rem_conn = rem_m->conn ? rem_m->conn : remote->conn; 
    1985     if (rem_conn == NULL) 
    1986         return PJMEDIA_SDP_EMISSINGCONN; 
    1987  
    1988     /* Media type must be video */ 
    1989     if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) != 0) 
    1990         return PJMEDIA_EINVALIMEDIATYPE; 
    1991  
    1992  
    1993     /* Reset: */ 
    1994  
    1995     pj_bzero(si, sizeof(*si)); 
    1996  
    1997     /* Media type: */ 
    1998     si->type = PJMEDIA_TYPE_VIDEO; 
    1999  
    2000     /* Transport protocol */ 
    2001  
    2002     /* At this point, transport type must be compatible,  
    2003      * the transport instance will do more validation later. 
    2004      */ 
    2005     status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport,  
    2006                                        &local_m->desc.transport); 
    2007     if (status != PJ_SUCCESS) 
    2008         return PJMEDIA_SDPNEG_EINVANSTP; 
    2009  
    2010     if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) { 
    2011  
    2012         si->proto = PJMEDIA_TP_PROTO_RTP_AVP; 
    2013  
    2014     } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) { 
    2015  
    2016         si->proto = PJMEDIA_TP_PROTO_RTP_SAVP; 
    2017  
    2018     } else { 
    2019  
    2020         si->proto = PJMEDIA_TP_PROTO_UNKNOWN; 
    2021         return PJ_SUCCESS; 
    2022     } 
    2023  
    2024  
    2025     /* Check address family in remote SDP */ 
    2026     rem_af = pj_AF_UNSPEC(); 
    2027     if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { 
    2028         if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { 
    2029             rem_af = pj_AF_INET(); 
    2030         } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { 
    2031             rem_af = pj_AF_INET6(); 
    2032         } 
    2033     } 
    2034  
    2035     if (rem_af==pj_AF_UNSPEC()) { 
    2036         /* Unsupported address family */ 
    2037         return PJ_EAFNOTSUP; 
    2038     } 
    2039  
    2040     /* Set remote address: */ 
    2041     status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr,  
    2042                               rem_m->desc.port); 
    2043     if (status != PJ_SUCCESS) { 
    2044         /* Invalid IP address. */ 
    2045         return PJMEDIA_EINVALIDIP; 
    2046     } 
    2047  
    2048     /* Check address family of local info */ 
    2049     local_af = pj_AF_UNSPEC(); 
    2050     if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { 
    2051         if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { 
    2052             local_af = pj_AF_INET(); 
    2053         } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { 
    2054             local_af = pj_AF_INET6(); 
    2055         } 
    2056     } 
    2057  
    2058     if (local_af==pj_AF_UNSPEC()) { 
    2059         /* Unsupported address family */ 
    2060         return PJ_SUCCESS; 
    2061     } 
    2062  
    2063     /* Set remote address: */ 
    2064     status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr,  
    2065                               local_m->desc.port); 
    2066     if (status != PJ_SUCCESS) { 
    2067         /* Invalid IP address. */ 
    2068         return PJMEDIA_EINVALIDIP; 
    2069     } 
    2070  
    2071     /* Local and remote address family must match */ 
    2072     if (local_af != rem_af) 
    2073         return PJ_EAFNOTSUP; 
    2074  
    2075     /* Media direction: */ 
    2076  
    2077     if (local_m->desc.port == 0 ||  
    2078         pj_sockaddr_has_addr(&local_addr)==PJ_FALSE || 
    2079         pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || 
    2080         pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) 
    2081     { 
    2082         /* Inactive stream. */ 
    2083  
    2084         si->dir = PJMEDIA_DIR_NONE; 
    2085  
    2086     } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { 
    2087  
    2088         /* Send only stream. */ 
    2089  
    2090         si->dir = PJMEDIA_DIR_ENCODING; 
    2091  
    2092     } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { 
    2093  
    2094         /* Recv only stream. */ 
    2095  
    2096         si->dir = PJMEDIA_DIR_DECODING; 
    2097  
    2098     } else { 
    2099  
    2100         /* Send and receive stream. */ 
    2101  
    2102         si->dir = PJMEDIA_DIR_ENCODING_DECODING; 
    2103  
    2104     } 
    2105  
    2106     /* No need to do anything else if stream is rejected */ 
    2107     if (local_m->desc.port == 0) { 
    2108         return PJ_SUCCESS; 
    2109     } 
    2110  
    2111     /* If "rtcp" attribute is present in the SDP, set the RTCP address 
    2112      * from that attribute. Otherwise, calculate from RTP address. 
    2113      */ 
    2114     attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, 
    2115                                   "rtcp", NULL); 
    2116     if (attr) { 
    2117         pjmedia_sdp_rtcp_attr rtcp; 
    2118         status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); 
    2119         if (status == PJ_SUCCESS) { 
    2120             if (rtcp.addr.slen) { 
    2121                 status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, 
    2122                                           (pj_uint16_t)rtcp.port); 
    2123             } else { 
    2124                 pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL,  
    2125                                  (pj_uint16_t)rtcp.port); 
    2126                 pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), 
    2127                           pj_sockaddr_get_addr(&si->rem_addr), 
    2128                           pj_sockaddr_get_addr_len(&si->rem_addr)); 
    2129             } 
    2130         } 
    2131     } 
    2132      
    2133     if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { 
    2134         int rtcp_port; 
    2135  
    2136         pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); 
    2137         rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; 
    2138         pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); 
    2139     } 
    2140  
    2141     /* Get codec info and param */ 
    2142     status = get_video_codec_info_param(si, pool, NULL, local_m, rem_m); 
    2143  
    2144     /* Leave SSRC to random. */ 
    2145     si->ssrc = pj_rand(); 
    2146  
    2147     /* Set default jitter buffer parameter. */ 
    2148     si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; 
    2149  
    2150     return status; 
    2151 } 
    2152  
    2153  
    21541791/* 
    21551792 * Force stream to send video keyframe. 
  • pjproject/trunk/pjmedia/src/test/mips_test.c

    r3841 r3982  
    794794} 
    795795 
     796#if PJMEDIA_HAS_G711_CODEC 
    796797/* G.711 benchmark */ 
    797798static pjmedia_port* g711_encode_decode(  pj_pool_t *pool, 
     
    807808                               samples_per_frame, flags, te); 
    808809} 
     810#endif 
    809811 
    810812/* GSM benchmark */ 
     813#if PJMEDIA_HAS_GSM_CODEC 
    811814static pjmedia_port* gsm_encode_decode(  pj_pool_t *pool, 
    812815                                         unsigned clock_rate, 
     
    821824                               samples_per_frame, flags, te); 
    822825} 
    823  
     826#endif 
     827 
     828#if PJMEDIA_HAS_ILBC_CODEC 
    824829static pj_status_t ilbc_init(pjmedia_endpt *endpt) 
    825830{ 
     
    840845                               channel_count, samples_per_frame, flags, te); 
    841846} 
    842  
     847#endif 
     848 
     849#if PJMEDIA_HAS_SPEEX_CODEC 
    843850/* Speex narrowband benchmark */ 
    844851static pjmedia_port* speex8_encode_decode(pj_pool_t *pool, 
     
    870877                               samples_per_frame, flags, te); 
    871878} 
    872  
     879#endif 
     880 
     881#if PJMEDIA_HAS_G722_CODEC 
    873882/* G.722 benchmark benchmark */ 
    874883static pjmedia_port* g722_encode_decode(pj_pool_t *pool, 
     
    884893                               samples_per_frame, flags, te); 
    885894} 
     895#endif 
    886896 
    887897#if PJMEDIA_HAS_G7221_CODEC 
     
    17971807} 
    17981808 
     1809#if PJMEDIA_HAS_G711_CODEC 
    17991810/* G.711 stream, no SRTP */ 
    18001811static pjmedia_port* create_stream_pcmu( pj_pool_t *pool, 
     
    18711882                         samples_per_frame, flags, te); 
    18721883} 
    1873  
     1884#endif 
     1885 
     1886#if PJMEDIA_HAS_GSM_CODEC 
    18741887/* GSM stream */ 
    18751888static pjmedia_port* create_stream_gsm(  pj_pool_t *pool, 
     
    19461959                         samples_per_frame, flags, te); 
    19471960} 
    1948  
     1961#endif 
     1962 
     1963#if PJMEDIA_HAS_G722_CODEC 
    19491964/* G722 stream */ 
    19501965static pjmedia_port* create_stream_g722( pj_pool_t *pool, 
     
    19611976                         samples_per_frame, flags, te); 
    19621977} 
    1963  
     1978#endif 
     1979 
     1980#if PJMEDIA_HAS_G7221_CODEC 
    19641981/* G722.1 stream */ 
    1965 #if PJMEDIA_HAS_G7221_CODEC 
    19661982static pjmedia_port* create_stream_g7221( pj_pool_t *pool, 
    19671983                                          unsigned clock_rate, 
  • pjproject/trunk/pjsip/build/Makefile

    r3669 r3982  
    7878                        pjsua_acc.o pjsua_call.o pjsua_core.o \ 
    7979                        pjsua_im.o pjsua_media.o pjsua_pres.o \ 
    80                         pjsua_dump.o pjsua_vid.o 
     80                        pjsua_dump.o pjsua_aud.o pjsua_vid.o 
    8181export PJSUA_LIB_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS) 
    8282 
  • pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h

    r3938 r3982  
    49154915 
    49164916/** 
     4917 * Use PJMEDIA for media? Set this to zero when using third party media 
     4918 * stack. 
     4919 */ 
     4920#ifndef PJSUA_MEDIA_HAS_PJMEDIA 
     4921#  define PJSUA_MEDIA_HAS_PJMEDIA       1 
     4922#endif  /* PJSUA_MEDIA_HAS_PJMEDIA */ 
     4923 
     4924 
     4925/** 
    49174926 * Max ports in the conference bridge. This setting is the default value 
    49184927 * for pjsua_media_config.max_media_ports. 
  • pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h

    r3938 r3982  
    602602                                  pj_bool_t async, 
    603603                                  pjsua_med_tp_state_cb cb); 
    604 pj_status_t video_channel_update(pjsua_call_media *call_med, 
    605                                  pj_pool_t *tmp_pool, 
    606                                  const pjmedia_sdp_session *local_sdp, 
    607                                  const pjmedia_sdp_session *remote_sdp); 
    608 void stop_video_stream(pjsua_call_media *call_med); 
    609 void set_media_tp_state(pjsua_call_media *call_med, pjsua_med_tp_st tp_st); 
     604void pjsua_set_media_tp_state(pjsua_call_media *call_med, pjsua_med_tp_st tp_st); 
    610605 
    611606/* Callback to receive media events */ 
     
    754749 
    755750/* 
     751 * Audio 
     752 */ 
     753pj_status_t pjsua_aud_subsys_init(void); 
     754pj_status_t pjsua_aud_subsys_start(void); 
     755pj_status_t pjsua_aud_subsys_destroy(void); 
     756void pjsua_aud_stop_stream(pjsua_call_media *call_med); 
     757pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med, 
     758                                     pj_pool_t *tmp_pool, 
     759                                     pjmedia_stream_info *si, 
     760                                     const pjmedia_sdp_session *local_sdp, 
     761                                     const pjmedia_sdp_session *remote_sdp); 
     762 
     763/* 
    756764 * Video 
    757765 */ 
     
    759767pj_status_t pjsua_vid_subsys_start(void); 
    760768pj_status_t pjsua_vid_subsys_destroy(void); 
     769void pjsua_vid_stop_stream(pjsua_call_media *call_med); 
     770pj_status_t pjsua_vid_channel_init(pjsua_call_media *call_med); 
     771pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med, 
     772                                     pj_pool_t *tmp_pool, 
     773                                     pjmedia_vid_stream_info *si, 
     774                                     const pjmedia_sdp_session *local_sdp, 
     775                                     const pjmedia_sdp_session *remote_sdp); 
    761776 
    762777#if PJSUA_HAS_VIDEO 
  • pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c

    r3966 r3982  
    13901390 
    13911391 
    1392 /* 
    1393  * Check if call has an active media session. 
    1394  */ 
    1395 PJ_DEF(pj_bool_t) pjsua_call_has_media(pjsua_call_id call_id) 
    1396 { 
    1397     pjsua_call *call = &pjsua_var.calls[call_id]; 
    1398     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,  
    1399                      PJ_EINVAL); 
    1400     return call->audio_idx >= 0 && call->media[call->audio_idx].strm.a.stream; 
    1401 } 
    1402  
    1403  
    14041392/* Acquire lock to the specified call_id */ 
    14051393pj_status_t acquire_call(const char *title, 
     
    14761464    return PJ_SUCCESS; 
    14771465} 
    1478  
    1479  
    1480 /* 
    1481  * Get the conference port identification associated with the call. 
    1482  */ 
    1483 PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id) 
    1484 { 
    1485     pjsua_call *call; 
    1486     pjsua_conf_port_id port_id = PJSUA_INVALID_ID; 
    1487  
    1488     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,  
    1489                      PJ_EINVAL); 
    1490  
    1491     /* Use PJSUA_LOCK() instead of acquire_call(): 
    1492      *  https://trac.pjsip.org/repos/ticket/1371 
    1493      */ 
    1494     PJSUA_LOCK(); 
    1495  
    1496     if (!pjsua_call_is_active(call_id)) 
    1497         goto on_return; 
    1498  
    1499     call = &pjsua_var.calls[call_id]; 
    1500     port_id = call->media[call->audio_idx].strm.a.conf_slot; 
    1501  
    1502 on_return: 
    1503     PJSUA_UNLOCK(); 
    1504  
    1505     return port_id; 
    1506 } 
    1507  
    15081466 
    15091467 
     
    17361694    *p_type = pjsua_var.calls[call_id].rem_nat_type; 
    17371695    return PJ_SUCCESS; 
    1738 } 
    1739  
    1740  
    1741 /* 
    1742  * Get media stream info for the specified media index. 
    1743  */ 
    1744 PJ_DEF(pj_status_t) pjsua_call_get_stream_info( pjsua_call_id call_id, 
    1745                                                 unsigned med_idx, 
    1746                                                 pjsua_stream_info *psi) 
    1747 { 
    1748     pjsua_call *call; 
    1749     pjsua_call_media *call_med; 
    1750     pj_status_t status; 
    1751  
    1752     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
    1753                      PJ_EINVAL); 
    1754     PJ_ASSERT_RETURN(psi, PJ_EINVAL); 
    1755  
    1756     PJSUA_LOCK(); 
    1757  
    1758     call = &pjsua_var.calls[call_id]; 
    1759      
    1760     if (med_idx >= call->med_cnt) { 
    1761         PJSUA_UNLOCK(); 
    1762         return PJ_EINVAL; 
    1763     } 
    1764  
    1765     call_med = &call->media[med_idx]; 
    1766     psi->type = call_med->type; 
    1767     switch (call_med->type) { 
    1768     case PJMEDIA_TYPE_AUDIO: 
    1769         status = pjmedia_stream_get_info(call_med->strm.a.stream, 
    1770                                          &psi->info.aud); 
    1771         break; 
    1772 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) 
    1773     case PJMEDIA_TYPE_VIDEO: 
    1774         status = pjmedia_vid_stream_get_info(call_med->strm.v.stream, 
    1775                                              &psi->info.vid); 
    1776         break; 
    1777 #endif 
    1778     default: 
    1779         status = PJMEDIA_EINVALIMEDIATYPE; 
    1780         break; 
    1781     } 
    1782      
    1783     PJSUA_UNLOCK(); 
    1784     return status; 
    1785 } 
    1786  
    1787  
    1788 /* 
    1789  *  Get media stream statistic for the specified media index. 
    1790  */ 
    1791 PJ_DEF(pj_status_t) pjsua_call_get_stream_stat( pjsua_call_id call_id, 
    1792                                                 unsigned med_idx, 
    1793                                                 pjsua_stream_stat *stat) 
    1794 { 
    1795     pjsua_call *call; 
    1796     pjsua_call_media *call_med; 
    1797     pj_status_t status; 
    1798  
    1799     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
    1800                      PJ_EINVAL); 
    1801     PJ_ASSERT_RETURN(stat, PJ_EINVAL); 
    1802  
    1803     PJSUA_LOCK(); 
    1804  
    1805     call = &pjsua_var.calls[call_id]; 
    1806      
    1807     if (med_idx >= call->med_cnt) { 
    1808         PJSUA_UNLOCK(); 
    1809         return PJ_EINVAL; 
    1810     } 
    1811  
    1812     call_med = &call->media[med_idx]; 
    1813     switch (call_med->type) { 
    1814     case PJMEDIA_TYPE_AUDIO: 
    1815         status = pjmedia_stream_get_stat(call_med->strm.a.stream, 
    1816                                          &stat->rtcp); 
    1817         if (status == PJ_SUCCESS) 
    1818             status = pjmedia_stream_get_stat_jbuf(call_med->strm.a.stream, 
    1819                                                   &stat->jbuf); 
    1820         break; 
    1821 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) 
    1822     case PJMEDIA_TYPE_VIDEO: 
    1823         status = pjmedia_vid_stream_get_stat(call_med->strm.v.stream, 
    1824                                              &stat->rtcp); 
    1825         if (status == PJ_SUCCESS) 
    1826             status = pjmedia_vid_stream_get_stat_jbuf(call_med->strm.v.stream, 
    1827                                                   &stat->jbuf); 
    1828         break; 
    1829 #endif 
    1830     default: 
    1831         status = PJMEDIA_EINVALIMEDIATYPE; 
    1832         break; 
    1833     } 
    1834      
    1835     PJSUA_UNLOCK(); 
    1836     return status; 
    18371696} 
    18381697 
     
    25262385on_error: 
    25272386    if (dest_dlg) pjsip_dlg_dec_lock(dest_dlg); 
    2528     pj_log_pop_indent(); 
    2529     return status; 
    2530 } 
    2531  
    2532  
    2533 /* 
    2534  * Send DTMF digits to remote using RFC 2833 payload formats. 
    2535  */ 
    2536 PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( pjsua_call_id call_id,  
    2537                                           const pj_str_t *digits) 
    2538 { 
    2539     pjsua_call *call; 
    2540     pjsip_dialog *dlg = NULL; 
    2541     pj_status_t status; 
    2542  
    2543     PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, 
    2544                      PJ_EINVAL); 
    2545      
    2546     PJ_LOG(4,(THIS_FILE, "Call %d dialing DTMF %.*s", 
    2547                          call_id, (int)digits->slen, digits->ptr)); 
    2548     pj_log_push_indent(); 
    2549  
    2550     status = acquire_call("pjsua_call_dial_dtmf()", call_id, &call, &dlg); 
    2551     if (status != PJ_SUCCESS) 
    2552         goto on_return; 
    2553  
    2554     if (!pjsua_call_has_media(call_id)) { 
    2555         PJ_LOG(3,(THIS_FILE, "Media is not established yet!")); 
    2556         status = PJ_EINVALIDOP; 
    2557         goto on_return; 
    2558     } 
    2559  
    2560     status = pjmedia_stream_dial_dtmf( 
    2561                 call->media[call->audio_idx].strm.a.stream, digits); 
    2562  
    2563 on_return: 
    2564     if (dlg) pjsip_dlg_dec_lock(dlg); 
    25652387    pj_log_pop_indent(); 
    25662388    return status; 
  • 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. 
  • pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c

    r3956 r3982  
    2020#include <pjsua-lib/pjsua_internal.h> 
    2121 
     22#if defined(PJSUA_MEDIA_HAS_PJMEDIA) && PJSUA_MEDIA_HAS_PJMEDIA != 0 
     23 
    2224#define THIS_FILE       "pjsua_vid.c" 
    2325 
     
    668670} 
    669671 
     672/* Initialize video call media */ 
     673pj_status_t pjsua_vid_channel_init(pjsua_call_media *call_med) 
     674{ 
     675    pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id]; 
     676 
     677    call_med->strm.v.rdr_dev = acc->cfg.vid_rend_dev; 
     678    call_med->strm.v.cap_dev = acc->cfg.vid_cap_dev; 
     679    if (call_med->strm.v.rdr_dev == PJMEDIA_VID_DEFAULT_RENDER_DEV) { 
     680        pjmedia_vid_dev_info info; 
     681        pjmedia_vid_dev_get_info(call_med->strm.v.rdr_dev, &info); 
     682        call_med->strm.v.rdr_dev = info.id; 
     683    } 
     684    if (call_med->strm.v.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) { 
     685        pjmedia_vid_dev_info info; 
     686        pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev, &info); 
     687        call_med->strm.v.cap_dev = info.id; 
     688    } 
     689 
     690    return PJ_SUCCESS; 
     691} 
    670692 
    671693/* Internal function: update video channel after SDP negotiation */ 
    672 pj_status_t video_channel_update(pjsua_call_media *call_med, 
    673                                  pj_pool_t *tmp_pool, 
    674                                  const pjmedia_sdp_session *local_sdp, 
    675                                  const pjmedia_sdp_session *remote_sdp) 
     694pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med, 
     695                                     pj_pool_t *tmp_pool, 
     696                                     pjmedia_vid_stream_info *si, 
     697                                     const pjmedia_sdp_session *local_sdp, 
     698                                     const pjmedia_sdp_session *remote_sdp) 
    676699{ 
    677700    pjsua_call *call = call_med->call; 
    678701    pjsua_acc  *acc  = &pjsua_var.acc[call->acc_id]; 
    679     pjmedia_vid_stream_info the_si, *si = &the_si; 
    680702    pjmedia_port *media_port; 
    681     unsigned strm_idx = call_med->idx; 
    682703    pj_status_t status; 
    683704     
     
    685706    pj_log_push_indent(); 
    686707 
    687     status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt, 
    688                                               local_sdp, remote_sdp, strm_idx); 
    689     if (status != PJ_SUCCESS) 
    690         goto on_error; 
    691  
    692708    /* Check if no media is active */ 
    693     if (si->dir == PJMEDIA_DIR_NONE) { 
    694         /* Call media state */ 
    695         call_med->state = PJSUA_CALL_MEDIA_NONE; 
    696  
    697         /* Call media direction */ 
    698         call_med->dir = PJMEDIA_DIR_NONE; 
    699  
    700     } else { 
    701         pjmedia_transport_info tp_info; 
    702  
    703         /* Start/restart media transport */ 
    704         status = pjmedia_transport_media_start(call_med->tp, 
    705                                                tmp_pool, local_sdp, 
    706                                                remote_sdp, strm_idx); 
    707         if (status != PJ_SUCCESS) 
    708             goto on_error; 
    709  
    710         set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING); 
    711  
    712         /* Get remote SRTP usage policy */ 
    713         pjmedia_transport_info_init(&tp_info); 
    714         pjmedia_transport_get_info(call_med->tp, &tp_info); 
    715         if (tp_info.specific_info_cnt > 0) { 
    716             unsigned i; 
    717             for (i = 0; i < tp_info.specific_info_cnt; ++i) { 
    718                 if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP)  
    719                 { 
    720                     pjmedia_srtp_info *srtp_info =  
    721                                 (pjmedia_srtp_info*) tp_info.spc_info[i].buffer; 
    722  
    723                     call_med->rem_srtp_use = srtp_info->peer_use; 
    724                     break; 
    725                 } 
    726             } 
    727         } 
    728  
     709    if (si->dir != PJMEDIA_DIR_NONE) { 
    729710        /* Optionally, application may modify other stream settings here 
    730711         * (such as jitter buffer parameters, codec ptime, etc.) 
     
    926907        } 
    927908 
    928         /* Call media direction */ 
    929         call_med->dir = si->dir; 
    930  
    931         /* Call media state */ 
    932         if (call->local_hold) 
    933             call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD; 
    934         else if (call_med->dir == PJMEDIA_DIR_DECODING) 
    935             call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD; 
    936         else 
    937             call_med->state = PJSUA_CALL_MEDIA_ACTIVE; 
    938     } 
    939  
    940     /* Print info. */ 
    941     { 
    942         char info[80]; 
    943         int info_len = 0; 
    944         int len; 
    945         const char *dir; 
    946  
    947         switch (si->dir) { 
    948         case PJMEDIA_DIR_NONE: 
    949             dir = "inactive"; 
    950             break; 
    951         case PJMEDIA_DIR_ENCODING: 
    952             dir = "sendonly"; 
    953             break; 
    954         case PJMEDIA_DIR_DECODING: 
    955             dir = "recvonly"; 
    956             break; 
    957         case PJMEDIA_DIR_ENCODING_DECODING: 
    958             dir = "sendrecv"; 
    959             break; 
    960         default: 
    961             dir = "unknown"; 
    962             break; 
    963         } 
    964         len = pj_ansi_sprintf( info+info_len, 
    965                                ", stream #%d: %.*s (%s)", strm_idx, 
    966                                (int)si->codec_info.encoding_name.slen, 
    967                                si->codec_info.encoding_name.ptr, 
    968                                dir); 
    969         if (len > 0) 
    970             info_len += len; 
    971         PJ_LOG(4,(THIS_FILE,"Video updated%s", info)); 
    972909    } 
    973910 
     
    989926 
    990927/* Internal function to stop video stream */ 
    991 void stop_video_stream(pjsua_call_media *call_med) 
     928void pjsua_vid_stop_stream(pjsua_call_media *call_med) 
    992929{ 
    993930    pjmedia_vid_stream *strm = call_med->strm.v.stream; 
     
    15901527        goto on_error; 
    15911528 
    1592     set_media_tp_state(call_med, PJSUA_MED_TP_INIT); 
     1529    pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT); 
    15931530 
    15941531    /* Get transport address info */ 
     
    17851722        // Don't close this here, as SDP negotiation has not been 
    17861723        // done and stream may be still active. 
    1787         set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED); 
     1724        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED); 
    17881725 
    17891726        /* Deactivate the stream */ 
     
    21862123#endif /* PJSUA_HAS_VIDEO */ 
    21872124 
     2125#endif /* PJSUA_MEDIA_HAS_PJMEDIA */ 
Note: See TracChangeset for help on using the changeset viewer.