- Timestamp:
- Feb 24, 2011 7:47:55 AM (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/branches/projects/2.0-dev/pjmedia/src/pjmedia/stream.c
r3418 r3420 23 23 #include <pjmedia/rtcp.h> 24 24 #include <pjmedia/jbuf.h> 25 #include <pjmedia/stream_common.h> 25 26 #include <pj/array.h> 26 27 #include <pj/assert.h> … … 2739 2740 } 2740 2741 2742 2743 static const pj_str_t ID_AUDIO = { "audio", 5}; 2744 static const pj_str_t ID_VIDEO = { "video", 5}; 2745 static const pj_str_t ID_APPLICATION = { "application", 11}; 2746 static const pj_str_t ID_IN = { "IN", 2 }; 2747 static const pj_str_t ID_IP4 = { "IP4", 3}; 2748 static const pj_str_t ID_IP6 = { "IP6", 3}; 2749 static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 }; 2750 static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 }; 2751 //static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 }; 2752 static const pj_str_t ID_RTPMAP = { "rtpmap", 6 }; 2753 static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 }; 2754 2755 static const pj_str_t STR_INACTIVE = { "inactive", 8 }; 2756 static const pj_str_t STR_SENDRECV = { "sendrecv", 8 }; 2757 static const pj_str_t STR_SENDONLY = { "sendonly", 8 }; 2758 static const pj_str_t STR_RECVONLY = { "recvonly", 8 }; 2759 2760 2761 /* 2762 * Internal function for collecting codec info and param from the SDP media. 2763 */ 2764 static pj_status_t get_audio_codec_info_param(pjmedia_stream_info *si, 2765 pj_pool_t *pool, 2766 pjmedia_codec_mgr *mgr, 2767 const pjmedia_sdp_media *local_m, 2768 const pjmedia_sdp_media *rem_m) 2769 { 2770 const pjmedia_sdp_attr *attr; 2771 pjmedia_sdp_rtpmap *rtpmap; 2772 unsigned i, fmti, pt = 0; 2773 pj_status_t status; 2774 2775 /* Find the first codec which is not telephone-event */ 2776 for ( fmti = 0; fmti < local_m->desc.fmt_count; ++fmti ) { 2777 if ( !pj_isdigit(*local_m->desc.fmt[fmti].ptr) ) 2778 return PJMEDIA_EINVALIDPT; 2779 pt = pj_strtoul(&local_m->desc.fmt[fmti]); 2780 if ( PJMEDIA_RTP_PT_TELEPHONE_EVENTS == 0 || 2781 pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS ) 2782 break; 2783 } 2784 if ( fmti >= local_m->desc.fmt_count ) 2785 return PJMEDIA_EINVALIDPT; 2786 2787 /* Get codec info. 2788 * For static payload types, get the info from codec manager. 2789 * For dynamic payload types, MUST get the rtpmap. 2790 */ 2791 if (pt < 96) { 2792 pj_bool_t has_rtpmap; 2793 2794 rtpmap = NULL; 2795 has_rtpmap = PJ_TRUE; 2796 2797 attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 2798 &local_m->desc.fmt[fmti]); 2799 if (attr == NULL) { 2800 has_rtpmap = PJ_FALSE; 2801 } 2802 if (attr != NULL) { 2803 status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); 2804 if (status != PJ_SUCCESS) 2805 has_rtpmap = PJ_FALSE; 2806 } 2807 2808 /* Build codec format info: */ 2809 if (has_rtpmap) { 2810 si->fmt.type = si->type; 2811 si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); 2812 pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); 2813 si->fmt.clock_rate = rtpmap->clock_rate; 2814 2815 #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0) 2816 /* The session info should have the actual clock rate, because 2817 * this info is used for calculationg buffer size, etc in stream 2818 */ 2819 if (si->fmt.pt == PJMEDIA_RTP_PT_G722) 2820 si->fmt.clock_rate = 16000; 2821 #endif 2822 2823 /* For audio codecs, rtpmap parameters denotes the number of 2824 * channels. 2825 */ 2826 if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { 2827 si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); 2828 } else { 2829 si->fmt.channel_cnt = 1; 2830 } 2831 2832 } else { 2833 const pjmedia_codec_info *p_info; 2834 2835 status = pjmedia_codec_mgr_get_codec_info( mgr, pt, &p_info); 2836 if (status != PJ_SUCCESS) 2837 return status; 2838 2839 pj_memcpy(&si->fmt, p_info, sizeof(pjmedia_codec_info)); 2840 } 2841 2842 /* For static payload type, pt's are symetric */ 2843 si->tx_pt = pt; 2844 2845 } else { 2846 2847 attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP, 2848 &local_m->desc.fmt[fmti]); 2849 if (attr == NULL) 2850 return PJMEDIA_EMISSINGRTPMAP; 2851 2852 status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap); 2853 if (status != PJ_SUCCESS) 2854 return status; 2855 2856 /* Build codec format info: */ 2857 2858 si->fmt.type = si->type; 2859 si->fmt.pt = pj_strtoul(&local_m->desc.fmt[fmti]); 2860 pj_strdup(pool, &si->fmt.encoding_name, &rtpmap->enc_name); 2861 si->fmt.clock_rate = rtpmap->clock_rate; 2862 2863 /* For audio codecs, rtpmap parameters denotes the number of 2864 * channels. 2865 */ 2866 if (si->type == PJMEDIA_TYPE_AUDIO && rtpmap->param.slen) { 2867 si->fmt.channel_cnt = (unsigned) pj_strtoul(&rtpmap->param); 2868 } else { 2869 si->fmt.channel_cnt = 1; 2870 } 2871 2872 /* Determine payload type for outgoing channel, by finding 2873 * dynamic payload type in remote SDP that matches the answer. 2874 */ 2875 si->tx_pt = 0xFFFF; 2876 for (i=0; i<rem_m->desc.fmt_count; ++i) { 2877 unsigned rpt; 2878 pjmedia_sdp_attr *r_attr; 2879 pjmedia_sdp_rtpmap r_rtpmap; 2880 2881 rpt = pj_strtoul(&rem_m->desc.fmt[i]); 2882 if (rpt < 96) 2883 continue; 2884 2885 r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP, 2886 &rem_m->desc.fmt[i]); 2887 if (!r_attr) 2888 continue; 2889 2890 if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS) 2891 continue; 2892 2893 if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) && 2894 rtpmap->clock_rate == r_rtpmap.clock_rate) 2895 { 2896 /* Found matched codec. */ 2897 si->tx_pt = rpt; 2898 2899 break; 2900 } 2901 } 2902 2903 if (si->tx_pt == 0xFFFF) 2904 return PJMEDIA_EMISSINGRTPMAP; 2905 } 2906 2907 2908 /* Now that we have codec info, get the codec param. */ 2909 si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param); 2910 status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, 2911 si->param); 2912 2913 /* Get remote fmtp for our encoder. */ 2914 pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt, 2915 &si->param->setting.enc_fmtp); 2916 2917 /* Get local fmtp for our decoder. */ 2918 pjmedia_stream_info_parse_fmtp(pool, local_m, si->fmt.pt, 2919 &si->param->setting.dec_fmtp); 2920 2921 /* Get the remote ptime for our encoder. */ 2922 attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, 2923 "ptime", NULL); 2924 if (attr) { 2925 pj_str_t tmp_val = attr->value; 2926 unsigned frm_per_pkt; 2927 2928 pj_strltrim(&tmp_val); 2929 2930 /* Round up ptime when the specified is not multiple of frm_ptime */ 2931 frm_per_pkt = (pj_strtoul(&tmp_val) + 2932 si->param->info.frm_ptime/2) / 2933 si->param->info.frm_ptime; 2934 if (frm_per_pkt != 0) { 2935 si->param->setting.frm_per_pkt = (pj_uint8_t)frm_per_pkt; 2936 } 2937 } 2938 2939 /* Get remote maxptime for our encoder. */ 2940 attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, 2941 "maxptime", NULL); 2942 if (attr) { 2943 pj_str_t tmp_val = attr->value; 2944 2945 pj_strltrim(&tmp_val); 2946 si->tx_maxptime = pj_strtoul(&tmp_val); 2947 } 2948 2949 /* When direction is NONE (it means SDP negotiation has failed) we don't 2950 * need to return a failure here, as returning failure will cause 2951 * the whole SDP to be rejected. See ticket #: 2952 * http:// 2953 * 2954 * Thanks Alain Totouom 2955 */ 2956 if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) 2957 return status; 2958 2959 2960 /* Get incomming payload type for telephone-events */ 2961 si->rx_event_pt = -1; 2962 for (i=0; i<local_m->attr_count; ++i) { 2963 pjmedia_sdp_rtpmap r; 2964 2965 attr = local_m->attr[i]; 2966 if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) 2967 continue; 2968 if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) 2969 continue; 2970 if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { 2971 si->rx_event_pt = pj_strtoul(&r.pt); 2972 break; 2973 } 2974 } 2975 2976 /* Get outgoing payload type for telephone-events */ 2977 si->tx_event_pt = -1; 2978 for (i=0; i<rem_m->attr_count; ++i) { 2979 pjmedia_sdp_rtpmap r; 2980 2981 attr = rem_m->attr[i]; 2982 if (pj_strcmp(&attr->name, &ID_RTPMAP) != 0) 2983 continue; 2984 if (pjmedia_sdp_attr_get_rtpmap(attr, &r) != PJ_SUCCESS) 2985 continue; 2986 if (pj_strcmp(&r.enc_name, &ID_TELEPHONE_EVENT) == 0) { 2987 si->tx_event_pt = pj_strtoul(&r.pt); 2988 break; 2989 } 2990 } 2991 2992 return PJ_SUCCESS; 2993 } 2994 2995 2996 2997 /* 2998 * Create stream info from SDP media line. 2999 */ 3000 PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( 3001 pjmedia_stream_info *si, 3002 pj_pool_t *pool, 3003 pjmedia_endpt *endpt, 3004 const pjmedia_sdp_session *local, 3005 const pjmedia_sdp_session *remote, 3006 unsigned stream_idx) 3007 { 3008 pjmedia_codec_mgr *mgr; 3009 const pjmedia_sdp_attr *attr; 3010 const pjmedia_sdp_media *local_m; 3011 const pjmedia_sdp_media *rem_m; 3012 const pjmedia_sdp_conn *local_conn; 3013 const pjmedia_sdp_conn *rem_conn; 3014 int rem_af, local_af; 3015 pj_sockaddr local_addr; 3016 pj_status_t status; 3017 3018 3019 /* Validate arguments: */ 3020 PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL); 3021 PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL); 3022 PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL); 3023 3024 /* Keep SDP shortcuts */ 3025 local_m = local->media[stream_idx]; 3026 rem_m = remote->media[stream_idx]; 3027 3028 local_conn = local_m->conn ? local_m->conn : local->conn; 3029 if (local_conn == NULL) 3030 return PJMEDIA_SDP_EMISSINGCONN; 3031 3032 rem_conn = rem_m->conn ? rem_m->conn : remote->conn; 3033 if (rem_conn == NULL) 3034 return PJMEDIA_SDP_EMISSINGCONN; 3035 3036 /* Media type must be audio */ 3037 if (pj_stricmp(&local_m->desc.media, &ID_AUDIO) == 0) 3038 return PJMEDIA_EINVALIMEDIATYPE; 3039 3040 /* Get codec manager. */ 3041 mgr = pjmedia_endpt_get_codec_mgr(endpt); 3042 3043 /* Reset: */ 3044 3045 pj_bzero(si, sizeof(*si)); 3046 3047 #if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR 3048 /* Set default RTCP XR enabled/disabled */ 3049 si->rtcp_xr_enabled = PJ_TRUE; 3050 #endif 3051 3052 /* Media type: */ 3053 si->type = PJMEDIA_TYPE_AUDIO; 3054 3055 /* Transport protocol */ 3056 3057 /* At this point, transport type must be compatible, 3058 * the transport instance will do more validation later. 3059 */ 3060 status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport, 3061 &local_m->desc.transport); 3062 if (status != PJ_SUCCESS) 3063 return PJMEDIA_SDPNEG_EINVANSTP; 3064 3065 if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) { 3066 3067 si->proto = PJMEDIA_TP_PROTO_RTP_AVP; 3068 3069 } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) { 3070 3071 si->proto = PJMEDIA_TP_PROTO_RTP_SAVP; 3072 3073 } else { 3074 3075 si->proto = PJMEDIA_TP_PROTO_UNKNOWN; 3076 return PJ_SUCCESS; 3077 } 3078 3079 3080 /* Check address family in remote SDP */ 3081 rem_af = pj_AF_UNSPEC(); 3082 if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) { 3083 if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) { 3084 rem_af = pj_AF_INET(); 3085 } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) { 3086 rem_af = pj_AF_INET6(); 3087 } 3088 } 3089 3090 if (rem_af==pj_AF_UNSPEC()) { 3091 /* Unsupported address family */ 3092 return PJ_EAFNOTSUP; 3093 } 3094 3095 /* Set remote address: */ 3096 status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr, 3097 rem_m->desc.port); 3098 if (status != PJ_SUCCESS) { 3099 /* Invalid IP address. */ 3100 return PJMEDIA_EINVALIDIP; 3101 } 3102 3103 /* Check address family of local info */ 3104 local_af = pj_AF_UNSPEC(); 3105 if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) { 3106 if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) { 3107 local_af = pj_AF_INET(); 3108 } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) { 3109 local_af = pj_AF_INET6(); 3110 } 3111 } 3112 3113 if (local_af==pj_AF_UNSPEC()) { 3114 /* Unsupported address family */ 3115 return PJ_SUCCESS; 3116 } 3117 3118 /* Set remote address: */ 3119 status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr, 3120 local_m->desc.port); 3121 if (status != PJ_SUCCESS) { 3122 /* Invalid IP address. */ 3123 return PJMEDIA_EINVALIDIP; 3124 } 3125 3126 /* Local and remote address family must match */ 3127 if (local_af != rem_af) 3128 return PJ_EAFNOTSUP; 3129 3130 /* Media direction: */ 3131 3132 if (local_m->desc.port == 0 || 3133 pj_sockaddr_has_addr(&local_addr)==PJ_FALSE || 3134 pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE || 3135 pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL) 3136 { 3137 /* Inactive stream. */ 3138 3139 si->dir = PJMEDIA_DIR_NONE; 3140 3141 } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) { 3142 3143 /* Send only stream. */ 3144 3145 si->dir = PJMEDIA_DIR_ENCODING; 3146 3147 } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) { 3148 3149 /* Recv only stream. */ 3150 3151 si->dir = PJMEDIA_DIR_DECODING; 3152 3153 } else { 3154 3155 /* Send and receive stream. */ 3156 3157 si->dir = PJMEDIA_DIR_ENCODING_DECODING; 3158 3159 } 3160 3161 /* No need to do anything else if stream is rejected */ 3162 if (local_m->desc.port == 0) { 3163 return PJ_SUCCESS; 3164 } 3165 3166 /* If "rtcp" attribute is present in the SDP, set the RTCP address 3167 * from that attribute. Otherwise, calculate from RTP address. 3168 */ 3169 attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, 3170 "rtcp", NULL); 3171 if (attr) { 3172 pjmedia_sdp_rtcp_attr rtcp; 3173 status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp); 3174 if (status == PJ_SUCCESS) { 3175 if (rtcp.addr.slen) { 3176 status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr, 3177 (pj_uint16_t)rtcp.port); 3178 } else { 3179 pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL, 3180 (pj_uint16_t)rtcp.port); 3181 pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp), 3182 pj_sockaddr_get_addr(&si->rem_addr), 3183 pj_sockaddr_get_addr_len(&si->rem_addr)); 3184 } 3185 } 3186 } 3187 3188 if (!pj_sockaddr_has_addr(&si->rem_rtcp)) { 3189 int rtcp_port; 3190 3191 pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr)); 3192 rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1; 3193 pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port); 3194 } 3195 3196 3197 /* Get the payload number for receive channel. */ 3198 /* 3199 Previously we used to rely on fmt[0] being the selected codec, 3200 but some UA sends telephone-event as fmt[0] and this would 3201 cause assert failure below. 3202 3203 Thanks Chris Hamilton <chamilton .at. cs.dal.ca> for this patch. 3204 3205 // And codec must be numeric! 3206 if (!pj_isdigit(*local_m->desc.fmt[0].ptr) || 3207 !pj_isdigit(*rem_m->desc.fmt[0].ptr)) 3208 { 3209 return PJMEDIA_EINVALIDPT; 3210 } 3211 3212 pt = pj_strtoul(&local_m->desc.fmt[0]); 3213 pj_assert(PJMEDIA_RTP_PT_TELEPHONE_EVENTS==0 || 3214 pt != PJMEDIA_RTP_PT_TELEPHONE_EVENTS); 3215 */ 3216 3217 /* Get codec info and param */ 3218 status = get_audio_codec_info_param(si, pool, mgr, local_m, rem_m); 3219 3220 /* Leave SSRC to random. */ 3221 si->ssrc = pj_rand(); 3222 3223 /* Set default jitter buffer parameter. */ 3224 si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1; 3225 3226 return status; 3227 }
Note: See TracChangeset
for help on using the changeset viewer.