- Timestamp:
- Oct 20, 2010 5:31:08 AM (14 years ago)
- Location:
- pjproject/trunk/pjsip
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
r3330 r3349 94 94 struct { 95 95 pj_timer_entry reinv_timer;/**< Reinvite retry timer. */ 96 pjmedia_sdp_session *new_sdp;/**< The new SDP offer. */ 96 pj_uint32_t sdp_ver; /**< SDP version of the bad answer */ 97 int retry_cnt; /**< Retry count. */ 97 98 } lock_codec; /**< Data for codec locking when answer 98 99 contains multiple codecs. */ -
pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
r3334 r3349 30 30 #define LOCK_CODEC_RETRY_INTERVAL 200 31 31 32 /* 33 * Max UPDATE/re-INVITE retry to lock codec 34 */ 35 #define LOCK_CODEC_MAX_RETRY 5 32 36 33 37 /* This callback receives notification from invite session when the … … 2989 2993 } 2990 2994 2991 2992 /* Timer callback to close sound device */ 2995 /* Proto */ 2996 static pj_status_t perform_lock_codec(pjsua_call *call); 2997 2998 /* Timer callback to send re-INVITE or UPDATE to lock codec */ 2993 2999 static void reinv_timer_cb(pj_timer_heap_t *th, 2994 3000 pj_timer_entry *entry) … … 2997 3003 pjsip_dialog *dlg; 2998 3004 pjsua_call *call; 2999 pjsip_tx_data *tdata;3000 3005 pj_status_t status; 3001 3006 … … 3007 3012 if (status != PJ_SUCCESS) 3008 3013 return; 3014 3015 status = perform_lock_codec(call); 3016 3017 pjsip_dlg_dec_lock(dlg); 3018 } 3019 3020 3021 /* Check if the specified format can be skipped in counting codecs */ 3022 static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m, 3023 const pj_str_t *fmt) 3024 { 3025 const pj_str_t STR_TEL = {"telephone-event", 15}; 3026 unsigned pt; 3027 3028 pt = pj_strtoul(fmt); 3029 3030 /* Check for comfort noise */ 3031 if (pt == PJMEDIA_RTP_PT_CN) 3032 return PJ_TRUE; 3033 3034 /* Dynamic PT, check the format name */ 3035 if (pt >= 96) { 3036 pjmedia_sdp_attr *a; 3037 pjmedia_sdp_rtpmap rtpmap; 3038 3039 /* Get the format name */ 3040 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt); 3041 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) { 3042 /* Check for telephone-event */ 3043 if (pj_stricmp(&rtpmap.enc_name, &STR_TEL)==0) 3044 return PJ_TRUE; 3045 } else { 3046 /* Invalid SDP, should not reach here */ 3047 pj_assert(!"SDP should have been validated!"); 3048 return PJ_TRUE; 3049 } 3050 } 3051 3052 return PJ_FALSE; 3053 } 3054 3055 3056 /* Send re-INVITE or UPDATE with new SDP offer to select only one codec 3057 * out of several codecs presented by callee in his answer. 3058 */ 3059 static pj_status_t perform_lock_codec(pjsua_call *call) 3060 { 3061 const pj_str_t STR_UPDATE = {"UPDATE", 6}; 3062 const pjmedia_sdp_session *local_sdp = NULL, *new_sdp; 3063 const pjmedia_sdp_media *rem_m; 3064 pjmedia_sdp_media *m; 3065 unsigned i, codec_cnt = 0; 3066 pj_bool_t rem_can_update; 3067 pjsip_tx_data *tdata; 3068 pj_status_t status; 3069 3070 PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE, 3071 PJ_EINVALIDOP); 3009 3072 3010 3073 /* Verify if another SDP negotiation is in progress, e.g: session timer … … 3014 3077 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) 3015 3078 { 3016 goto on_return;3079 return PJ_SUCCESS; 3017 3080 } 3018 3081 … … 3020 3083 * the SDP version. 3021 3084 */ 3085 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp); 3086 if (status != PJ_SUCCESS) 3087 return status; 3088 if (local_sdp->origin.version > call->lock_codec.sdp_ver) 3089 return PJMEDIA_SDP_EINVER; 3090 3091 PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec..")); 3092 3093 /* Update the new offer so it contains only a codec. Note that formats 3094 * order in the offer should have been matched to the answer, so we can 3095 * just directly update the offer without looking-up the answer. 3096 */ 3097 new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp); 3098 rem_m = local_sdp->media[call->audio_idx]; 3099 m = new_sdp->media[call->audio_idx]; 3100 codec_cnt = 0; 3101 i = 0; 3102 while (i < m->desc.fmt_count) { 3103 pjmedia_sdp_attr *a; 3104 pj_str_t *fmt = &m->desc.fmt[i]; 3105 3106 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) { 3107 ++i; 3108 continue; 3109 } 3110 3111 /* Remove format */ 3112 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt); 3113 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); 3114 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt); 3115 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); 3116 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]), 3117 m->desc.fmt_count, i); 3118 --m->desc.fmt_count; 3119 } 3120 3121 /* Last check if SDP trully needs to be udpated. It is possible that OA 3122 * negotiations have completed and SDP has changed but remote didn't 3123 * increase it's SDP version. 3124 */ 3125 if (rem_m->desc.fmt_count == m->desc.fmt_count || 3126 rem_m->desc.port == 0 || m->desc.port == 0) 3022 3127 { 3023 const pjmedia_sdp_session *sdp; 3024 3025 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp); 3026 if (status == PJ_SUCCESS && 3027 sdp->origin.version > call->lock_codec.new_sdp->origin.version) 3028 { 3029 goto on_return; 3030 } 3031 } 3032 3033 /* Create re-INVITE with the new offer */ 3034 status = pjsip_inv_reinvite(call->inv, NULL, call->lock_codec.new_sdp, 3035 &tdata); 3036 if (status == PJ_EINVALIDOP) { 3128 return PJ_SUCCESS; 3129 } 3130 3131 /* Send UPDATE or re-INVITE */ 3132 rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg, 3133 PJSIP_H_ALLOW, 3134 NULL, &STR_UPDATE) == 3135 PJSIP_DIALOG_CAP_SUPPORTED; 3136 if (rem_can_update) { 3137 status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata); 3138 } else { 3139 status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata); 3140 } 3141 3142 if (status==PJ_EINVALIDOP && 3143 ++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY) 3144 { 3037 3145 /* Ups, let's reschedule again */ 3038 3146 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL}; 3147 pj_time_val_normalize(&delay); 3039 3148 call->lock_codec.reinv_timer.id = PJ_TRUE; 3040 pjsip_endpt_schedule_timer(pjsua_var.endpt, 3149 pjsip_endpt_schedule_timer(pjsua_var.endpt, 3041 3150 &call->lock_codec.reinv_timer, &delay); 3151 return status; 3042 3152 } else if (status != PJ_SUCCESS) { 3043 pjsua_perror(THIS_FILE, " Failed creating re-INVITE inlock codec",3153 pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec", 3044 3154 status); 3155 return status; 3045 3156 } 3046 3157 … … 3048 3159 status = pjsip_inv_send_msg(call->inv, tdata); 3049 3160 if (status != PJ_SUCCESS) { 3050 pjsua_perror(THIS_FILE, " Failed sendingre-INVITE in lock codec",3161 pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec", 3051 3162 status); 3052 } 3053 3054 on_return: 3055 pjsip_dlg_dec_lock(dlg); 3056 } 3057 3058 3059 /* Check if the specified format can be skipped in counting codecs */ 3060 static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m, 3061 const pj_str_t *fmt) 3062 { 3063 unsigned pt; 3064 3065 pt = pj_strtoul(fmt); 3066 3067 /* Check for comfort noise */ 3068 if (pt == PJMEDIA_RTP_PT_CN) 3069 return PJ_TRUE; 3070 3071 /* Dynamic PT, check the format name */ 3072 if (pt >= 96) { 3073 pjmedia_sdp_attr *a; 3074 pjmedia_sdp_rtpmap rtpmap; 3075 3076 /* Get the format name */ 3077 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt); 3078 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) { 3079 /* Check for telephone-event */ 3080 if (pj_stricmp2(&rtpmap.enc_name, "telephone-event")==0) 3081 return PJ_TRUE; 3082 } else { 3083 /* Invalid SDP, should not reach here */ 3084 pj_assert(!"SDP should have been validated!"); 3085 return PJ_TRUE; 3086 } 3087 } 3088 3089 return PJ_FALSE; 3090 } 3091 3163 return status; 3164 } 3165 3166 return status; 3167 } 3092 3168 3093 3169 /* Check if remote answerer has given us more than one codecs. If so, … … 3096 3172 static pj_status_t lock_codec(pjsua_call *call) 3097 3173 { 3098 const pj_str_t st_update = {"UPDATE", 6};3099 3174 pjsip_inv_session *inv = call->inv; 3100 3175 const pjmedia_sdp_session *local_sdp; 3101 3176 const pjmedia_sdp_session *remote_sdp; 3102 3177 const pjmedia_sdp_media *rem_m; 3103 pjmedia_sdp_session *new_sdp; 3104 pjmedia_sdp_media *m; 3105 pjsip_tx_data *tdata; 3106 unsigned i, codec_cnt = 0; 3178 unsigned codec_cnt=0, i; 3179 pj_time_val delay = {0, 0}; 3107 3180 pj_status_t status; 3108 3181 … … 3138 3211 } 3139 3212 3140 PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, s tart"3213 PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, scheduling " 3141 3214 "updating media session to use only one codec..")); 3142 3215 3143 /* Clone the offer */ 3144 new_sdp = pjmedia_sdp_session_clone(inv->pool_prov, local_sdp); 3145 /* Note that the usage of pool_prov above is risky when locking codec 3146 * delays the re-INVITE (using timer) and there are two SDP negotiations 3147 * done before the re-INVITE. 3216 call->lock_codec.sdp_ver = local_sdp->origin.version; 3217 call->lock_codec.retry_cnt = 0; 3218 3219 /* Stop lock codec timer, if it is active */ 3220 if (call->lock_codec.reinv_timer.id) { 3221 pjsip_endpt_cancel_timer(pjsua_var.endpt, 3222 &call->lock_codec.reinv_timer); 3223 call->lock_codec.reinv_timer.id = PJ_FALSE; 3224 } 3225 3226 /* Can't send UPDATE or re-INVITE now, so just schedule it immediately. 3227 * See: https://trac.pjsip.org/repos/ticket/1149 3148 3228 */ 3149 3150 /* Update the new offer so it contains only a codec. Note that formats 3151 * order in the offer should have been matched to the answer, so we can 3152 * just directly update the offer without looking-up the answer. 3153 */ 3154 m = new_sdp->media[call->audio_idx]; 3155 codec_cnt = 0; 3156 i = 0; 3157 while (i < m->desc.fmt_count) { 3158 pjmedia_sdp_attr *a; 3159 pj_str_t *fmt = &m->desc.fmt[i]; 3160 3161 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) { 3162 ++i; 3163 continue; 3164 } 3165 3166 /* Remove format */ 3167 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt); 3168 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); 3169 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt); 3170 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); 3171 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]), 3172 m->desc.fmt_count, i); 3173 --m->desc.fmt_count; 3174 } 3175 3176 /* Send new SDP offer via UPDATE or re-INVITE */ 3177 if (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)== 3178 PJSIP_DIALOG_CAP_SUPPORTED) 3179 { 3180 /* Create UPDATE with the new offer */ 3181 status = pjsip_inv_update(inv, NULL, new_sdp, &tdata); 3182 if (status != PJ_SUCCESS) 3183 return status; 3184 3185 } else { 3186 /* Create re-INVITE with the new offer */ 3187 status = pjsip_inv_reinvite(inv, NULL, new_sdp, &tdata); 3188 if (status == PJ_EINVALIDOP) { 3189 /* Current INVITE transaction is pending, reschedule re-INVITE. */ 3190 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL}; 3191 3192 call->lock_codec.new_sdp = new_sdp; 3193 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE, 3194 (void*)(pj_size_t)call->index, 3195 &reinv_timer_cb); 3196 pjsip_endpt_schedule_timer(pjsua_var.endpt, 3197 &call->lock_codec.reinv_timer, &delay); 3198 return PJ_SUCCESS; 3199 3200 } else if (status != PJ_SUCCESS) 3201 return status; 3202 } 3203 3204 /* Send the UPDATE/re-INVITE request */ 3205 status = pjsip_inv_send_msg(inv, tdata); 3206 if (status != PJ_SUCCESS) 3207 return status; 3229 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE, 3230 (void*)(pj_size_t)call->index, 3231 &reinv_timer_cb); 3232 pjsip_endpt_schedule_timer(pjsua_var.endpt, 3233 &call->lock_codec.reinv_timer, &delay); 3208 3234 3209 3235 return PJ_SUCCESS; … … 3565 3591 pjsua_perror(THIS_FILE, "Unable to lock codec", status); 3566 3592 } 3593 } else if (inv->state == PJSIP_INV_STATE_CONFIRMED && 3594 call->media_st == PJSUA_CALL_MEDIA_ACTIVE) 3595 { 3596 /* https://trac.pjsip.org/repos/ticket/1149 */ 3597 status = lock_codec(call); 3598 if (status != PJ_SUCCESS) { 3599 pjsua_perror(THIS_FILE, "Unable to lock codec", status); 3600 } 3567 3601 } 3568 3602
Note: See TracChangeset
for help on using the changeset viewer.