Changeset 3982 for pjproject/trunk
- Timestamp:
- Mar 22, 2012 9:56:52 AM (13 years ago)
- Location:
- pjproject/trunk
- Files:
-
- 3 added
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/Makefile
r3669 r3982 4 4 include version.mak 5 5 6 DIRS = pjlib/build pjlib-util/build pjnath/build third_party/build pjmedia/build pjsip/build pjsip-apps/build $(EXTRA_DIRS) 6 LIB_DIRS = pjlib/build pjlib-util/build pjnath/build third_party/build pjmedia/build pjsip/build 7 DIRS = $(LIB_DIRS) pjsip-apps/build $(EXTRA_DIRS) 7 8 8 9 ifdef MINSIZE 9 10 MAKE_FLAGS := MINSIZE=1 10 11 endif 12 13 lib: 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 11 23 12 24 all clean dep depend distclean print realclean: … … 18 30 fi; \ 19 31 done 32 33 .PHONY: lib 20 34 21 35 doc: -
pjproject/trunk/pjmedia/build/Makefile
r3911 r3982 65 65 resample_port.o rtcp.o rtcp_xr.o rtp.o \ 66 66 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 \ 69 69 transport_ice.o transport_loop.o transport_srtp.o transport_udp.o \ 70 70 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 \ 72 72 wav_player.o wav_playlist.o wav_writer.o wave.o \ 73 73 wsola.o -
pjproject/trunk/pjmedia/src/pjmedia/stream.c
r3917 r3982 2794 2794 } 2795 2795 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 is2837 * 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, because2889 * this info is used for calculationg buffer size, etc in stream2890 */2891 if (si->fmt.pt == PJMEDIA_RTP_PT_G722)2892 si->fmt.clock_rate = 16000;2893 #endif2894 2895 /* For audio codecs, rtpmap parameters denotes the number of2896 * 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 of2939 * 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 the2948 * payload type will be resetted to its default (it might have2949 * been rewritten by the SDP negotiator to match to the remote2950 * offer), this is intentional as currently some components may2951 * 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 finding2965 * 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't3042 * need to return a failure here, as returning failure will cause3043 * the whole SDP to be rejected. See ticket #:3044 * http://3045 *3046 * Thanks Alain Totouom3047 */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_XR3140 /* Set default RTCP XR enabled/disabled */3141 si->rtcp_xr_enabled = PJ_TRUE;3142 #endif3143 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 address3259 * 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 would3293 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 3321 2796 /* 3322 2797 * Send RTCP SDES. -
pjproject/trunk/pjmedia/src/pjmedia/vid_stream.c
r3975 r3982 23 23 #include <pjmedia/rtcp.h> 24 24 #include <pjmedia/jbuf.h> 25 #include <pjmedia/sdp_neg.h>26 25 #include <pjmedia/stream_common.h> 27 26 #include <pj/array.h> 28 27 #include <pj/assert.h> 29 #include <pj/ctype.h>30 28 #include <pj/compat/socket.h> 31 29 #include <pj/errno.h> … … 1791 1789 } 1792 1790 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 finding1848 * 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 remote1900 * 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't1935 * need to return a failure here, as returning failure will cause1936 * the whole SDP to be rejected. See ticket #:1937 * http://1938 *1939 * Thanks Alain Totouom1940 */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 address2112 * 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 2154 1791 /* 2155 1792 * Force stream to send video keyframe. -
pjproject/trunk/pjmedia/src/test/mips_test.c
r3841 r3982 794 794 } 795 795 796 #if PJMEDIA_HAS_G711_CODEC 796 797 /* G.711 benchmark */ 797 798 static pjmedia_port* g711_encode_decode( pj_pool_t *pool, … … 807 808 samples_per_frame, flags, te); 808 809 } 810 #endif 809 811 810 812 /* GSM benchmark */ 813 #if PJMEDIA_HAS_GSM_CODEC 811 814 static pjmedia_port* gsm_encode_decode( pj_pool_t *pool, 812 815 unsigned clock_rate, … … 821 824 samples_per_frame, flags, te); 822 825 } 823 826 #endif 827 828 #if PJMEDIA_HAS_ILBC_CODEC 824 829 static pj_status_t ilbc_init(pjmedia_endpt *endpt) 825 830 { … … 840 845 channel_count, samples_per_frame, flags, te); 841 846 } 842 847 #endif 848 849 #if PJMEDIA_HAS_SPEEX_CODEC 843 850 /* Speex narrowband benchmark */ 844 851 static pjmedia_port* speex8_encode_decode(pj_pool_t *pool, … … 870 877 samples_per_frame, flags, te); 871 878 } 872 879 #endif 880 881 #if PJMEDIA_HAS_G722_CODEC 873 882 /* G.722 benchmark benchmark */ 874 883 static pjmedia_port* g722_encode_decode(pj_pool_t *pool, … … 884 893 samples_per_frame, flags, te); 885 894 } 895 #endif 886 896 887 897 #if PJMEDIA_HAS_G7221_CODEC … … 1797 1807 } 1798 1808 1809 #if PJMEDIA_HAS_G711_CODEC 1799 1810 /* G.711 stream, no SRTP */ 1800 1811 static pjmedia_port* create_stream_pcmu( pj_pool_t *pool, … … 1871 1882 samples_per_frame, flags, te); 1872 1883 } 1873 1884 #endif 1885 1886 #if PJMEDIA_HAS_GSM_CODEC 1874 1887 /* GSM stream */ 1875 1888 static pjmedia_port* create_stream_gsm( pj_pool_t *pool, … … 1946 1959 samples_per_frame, flags, te); 1947 1960 } 1948 1961 #endif 1962 1963 #if PJMEDIA_HAS_G722_CODEC 1949 1964 /* G722 stream */ 1950 1965 static pjmedia_port* create_stream_g722( pj_pool_t *pool, … … 1961 1976 samples_per_frame, flags, te); 1962 1977 } 1963 1978 #endif 1979 1980 #if PJMEDIA_HAS_G7221_CODEC 1964 1981 /* G722.1 stream */ 1965 #if PJMEDIA_HAS_G7221_CODEC1966 1982 static pjmedia_port* create_stream_g7221( pj_pool_t *pool, 1967 1983 unsigned clock_rate, -
pjproject/trunk/pjsip/build/Makefile
r3669 r3982 78 78 pjsua_acc.o pjsua_call.o pjsua_core.o \ 79 79 pjsua_im.o pjsua_media.o pjsua_pres.o \ 80 pjsua_dump.o pjsua_ vid.o80 pjsua_dump.o pjsua_aud.o pjsua_vid.o 81 81 export PJSUA_LIB_CFLAGS += $(_CFLAGS) $(PJ_VIDEO_CFLAGS) 82 82 -
pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
r3938 r3982 4915 4915 4916 4916 /** 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 /** 4917 4926 * Max ports in the conference bridge. This setting is the default value 4918 4927 * for pjsua_media_config.max_media_ports. -
pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
r3938 r3982 602 602 pj_bool_t async, 603 603 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); 604 void pjsua_set_media_tp_state(pjsua_call_media *call_med, pjsua_med_tp_st tp_st); 610 605 611 606 /* Callback to receive media events */ … … 754 749 755 750 /* 751 * Audio 752 */ 753 pj_status_t pjsua_aud_subsys_init(void); 754 pj_status_t pjsua_aud_subsys_start(void); 755 pj_status_t pjsua_aud_subsys_destroy(void); 756 void pjsua_aud_stop_stream(pjsua_call_media *call_med); 757 pj_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 /* 756 764 * Video 757 765 */ … … 759 767 pj_status_t pjsua_vid_subsys_start(void); 760 768 pj_status_t pjsua_vid_subsys_destroy(void); 769 void pjsua_vid_stop_stream(pjsua_call_media *call_med); 770 pj_status_t pjsua_vid_channel_init(pjsua_call_media *call_med); 771 pj_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); 761 776 762 777 #if PJSUA_HAS_VIDEO -
pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
r3966 r3982 1390 1390 1391 1391 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 1404 1392 /* Acquire lock to the specified call_id */ 1405 1393 pj_status_t acquire_call(const char *title, … … 1476 1464 return PJ_SUCCESS; 1477 1465 } 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/13711493 */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 1508 1466 1509 1467 … … 1736 1694 *p_type = pjsua_var.calls[call_id].rem_nat_type; 1737 1695 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 #endif1778 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 #endif1830 default:1831 status = PJMEDIA_EINVALIMEDIATYPE;1832 break;1833 }1834 1835 PJSUA_UNLOCK();1836 return status;1837 1696 } 1838 1697 … … 2526 2385 on_error: 2527 2386 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);2565 2387 pj_log_pop_indent(); 2566 2388 return status; -
pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
r3981 r3982 26 26 #define DEFAULT_RTP_PORT 4000 27 27 28 #define NULL_SND_DEV_ID -9929 30 28 #ifndef PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT 31 29 # define PJSUA_REQUIRE_CONSECUTIVE_RTCP_PORT 0 32 30 #endif 33 31 34 35 32 /* Next RTP port to be used */ 36 33 static 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 51 34 52 35 static void pjsua_media_config_dup(pj_pool_t *pool, … … 65 48 pj_status_t pjsua_media_subsys_init(const pjsua_media_config *cfg) 66 49 { 67 pj_str_t codec_id = {NULL, 0};68 unsigned opt;69 pjmedia_audio_codec_config codec_cfg;70 50 pj_status_t status; 71 72 /* To suppress warning about unused var when all codecs are disabled */73 PJ_UNUSED_ARG(codec_id);74 51 75 52 pj_log_push_indent(); … … 116 93 } 117 94 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) 180 97 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 future203 * 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);249 98 250 99 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) … … 273 122 } 274 123 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 is290 * any calls active.291 */292 call_cnt = pjsua_call_get_count();293 294 /* When this function is called from pjsua_media_channel_deinit() upon295 * disconnecting call, actually the call count hasn't been updated/296 * decreased. So we put additional check here, if there is only one297 * call and it's in DISCONNECTED state, there is actually no active298 * 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 and314 * 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 351 124 /* 352 125 * Start pjsua media subsystem. … … 375 148 #endif 376 149 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 } 379 156 380 157 /* Video */ … … 382 159 status = pjsua_vid_subsys_start(); 383 160 if (status != PJ_SUCCESS) { 161 pjsua_aud_subsys_destroy(); 384 162 pj_log_pop_indent(); 385 163 return status; … … 408 186 pj_log_push_indent(); 409 187 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(); 436 190 } 437 191 … … 1328 1082 1329 1083 /* 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)1084 void pjsua_set_media_tp_state(pjsua_call_media *call_med, 1085 pjsua_med_tp_st tp_st) 1332 1086 { 1333 1087 if (pjsua_var.ua_cfg.cb.on_call_media_transport_state && … … 1363 1117 1364 1118 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); 1366 1120 1367 1121 if (!call_med->tp_orig && … … 1467 1221 if (call_med->tp == NULL) { 1468 1222 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) 1469 pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];1470 1471 1223 /* While in initial call, set default video devices */ 1472 1224 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; 1485 1228 } 1486 1229 #endif 1487 1230 1488 set_media_tp_state(call_med, PJSUA_MED_TP_CREATING);1231 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_CREATING); 1489 1232 1490 1233 if (pjsua_var.media_cfg.enable_ice) { … … 1515 1258 } else if (call_med->tp_st == PJSUA_MED_TP_DISABLED) { 1516 1259 /* 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); 1518 1261 } 1519 1262 … … 1616 1359 } 1617 1360 1618 set_media_tp_state(call_med, PJSUA_MED_TP_INIT);1361 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT); 1619 1362 } 1620 1363 } … … 1881 1624 pj_assert(call_med->tp_st == PJSUA_MED_TP_INIT || 1882 1625 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); 1884 1627 } 1885 1628 … … 2243 1986 2244 1987 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); 2277 1989 } 2278 1990 2279 1991 #if PJMEDIA_HAS_VIDEO 2280 1992 else if (call_med->type == PJMEDIA_TYPE_VIDEO) { 2281 stop_video_stream(call_med);1993 pjsua_vid_stop_stream(call_med); 2282 1994 } 2283 1995 #endif … … 2315 2027 pj_log_push_indent(); 2316 2028 2029 stop_media_session(call_id); 2030 2317 2031 for (mi=0; mi<call->med_cnt; ++mi) { 2318 2032 pjsua_call_media *call_med = &call->media[mi]; 2319 2033 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 2329 2034 if (call_med->tp_st > PJSUA_MED_TP_IDLE) { 2330 2035 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); 2332 2037 } 2333 2038 … … 2345 2050 } 2346 2051 2347 check_snd_dev_idle();2348 2052 pj_log_pop_indent(); 2349 2053 … … 2351 2055 } 2352 2056 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 this2365 * callback, please see ticket #460:2366 * http://trac.pjsip.org/repos/ticket/460#comment:42367 */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 here2452 * (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 intialized2463 * automatically when stream session created, but for some cases (e.g:2464 * call reinvite, call update) timestamp and sequence need to be kept2465 * 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!=02472 /* Enable/disable stream keep-alive and NAT hole punch. */2473 si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;2474 #endif2475 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 our2494 * 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 different2509 * media port2510 */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 else2551 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 }2592 2057 2593 2058 pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, … … 2690 2155 } 2691 2156 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 2696 2268 if (call->audio_idx==-1 && status==PJ_SUCCESS && 2697 call_med->strm.a.stream)2269 si->dir != PJMEDIA_DIR_NONE) 2698 2270 { 2699 2271 call->audio_idx = mi; 2700 2272 } 2701 break; 2273 2702 2274 #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 2707 2386 #endif 2708 default:2387 } else { 2709 2388 status = PJMEDIA_EINVALIMEDIATYPE; 2710 break;2711 2389 } 2712 2390 … … 2718 2396 pjmedia_transport_close(call_med->tp); 2719 2397 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); 2721 2399 } 2722 2400 … … 2758 2436 } 2759 2437 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 port2798 */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 attributes2886 * (format, clock rate, channel count) must match to peer's.2887 * Note that sound device can be reopened only if it doesn't have2888 * 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 modify2909 * settings to match its peer. Sound device must be idle in this case2910 * 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(¶m.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(¶m);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 the3026 * 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 (to3037 * 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 to3077 * 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 port3165 * 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 free3292 * 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 to3327 * 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 Format3337 {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_param3615 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 app3635 * startup), retrieve the audio settings such as volume level3636 * 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, ¶m);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_param3666 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's3727 * 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 is3764 * 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 some3835 * settings from the device (such as volume settings) so that the3836 * 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, ¶m);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 at3905 * 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 conference3932 * bridge (meaning audio format is always PCM), otherwise lock on3933 * 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(¶m.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(¶m);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 created3982 * (for example when pjsua_start() is not called), it is possible that3983 * 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 to4030 * 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 to4147 * retrieve the initial setting from the device (e.g. audio4148 * 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 4170 2438 /***************************************************************************** 4171 2439 * Codecs. -
pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c
r3956 r3982 20 20 #include <pjsua-lib/pjsua_internal.h> 21 21 22 #if defined(PJSUA_MEDIA_HAS_PJMEDIA) && PJSUA_MEDIA_HAS_PJMEDIA != 0 23 22 24 #define THIS_FILE "pjsua_vid.c" 23 25 … … 668 670 } 669 671 672 /* Initialize video call media */ 673 pj_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 } 670 692 671 693 /* 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) 694 pj_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) 676 699 { 677 700 pjsua_call *call = call_med->call; 678 701 pjsua_acc *acc = &pjsua_var.acc[call->acc_id]; 679 pjmedia_vid_stream_info the_si, *si = &the_si;680 702 pjmedia_port *media_port; 681 unsigned strm_idx = call_med->idx;682 703 pj_status_t status; 683 704 … … 685 706 pj_log_push_indent(); 686 707 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 692 708 /* 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) { 729 710 /* Optionally, application may modify other stream settings here 730 711 * (such as jitter buffer parameters, codec ptime, etc.) … … 926 907 } 927 908 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 else937 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));972 909 } 973 910 … … 989 926 990 927 /* Internal function to stop video stream */ 991 void stop_video_stream(pjsua_call_media *call_med)928 void pjsua_vid_stop_stream(pjsua_call_media *call_med) 992 929 { 993 930 pjmedia_vid_stream *strm = call_med->strm.v.stream; … … 1590 1527 goto on_error; 1591 1528 1592 set_media_tp_state(call_med, PJSUA_MED_TP_INIT);1529 pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_INIT); 1593 1530 1594 1531 /* Get transport address info */ … … 1785 1722 // Don't close this here, as SDP negotiation has not been 1786 1723 // 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); 1788 1725 1789 1726 /* Deactivate the stream */ … … 2186 2123 #endif /* PJSUA_HAS_VIDEO */ 2187 2124 2125 #endif /* PJSUA_MEDIA_HAS_PJMEDIA */
Note: See TracChangeset
for help on using the changeset viewer.