Changeset 3206
- Timestamp:
- Jun 15, 2010 9:56:39 AM (14 years ago)
- Location:
- pjproject/trunk
- Files:
-
- 4 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
r3144 r3206 91 91 char last_text_buf_[128]; /**< Buffer for last_text. */ 92 92 93 struct { 94 pj_timer_entry reinv_timer;/**< Reinvite retry timer. */ 95 pjmedia_sdp_session *new_sdp;/**< The new SDP offer. */ 96 } lock_codec; /**< Data for codec locking when answer 97 contains multiple codecs. */ 98 93 99 } pjsua_call; 94 100 -
pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
r3196 r3206 23 23 24 24 #define THIS_FILE "pjsua_call.c" 25 26 27 /* Retry interval of sending re-INVITE for locking a codec when remote 28 * SDP answer contains multiple codec, in milliseconds. 29 */ 30 #define LOCK_CODEC_RETRY_INTERVAL 200 25 31 26 32 … … 1512 1518 pjsip_dlg_dec_lock(dlg); 1513 1519 return status; 1520 } 1521 1522 /* Stop lock codec timer, if it is active */ 1523 if (call->lock_codec.reinv_timer.id) { 1524 pjsip_endpt_cancel_timer(pjsua_var.endpt, 1525 &call->lock_codec.reinv_timer); 1526 call->lock_codec.reinv_timer.id = PJ_FALSE; 1514 1527 } 1515 1528 … … 2914 2927 2915 2928 2929 /* Timer callback to close sound device */ 2930 static void reinv_timer_cb(pj_timer_heap_t *th, 2931 pj_timer_entry *entry) 2932 { 2933 pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data; 2934 pjsip_dialog *dlg; 2935 pjsua_call *call; 2936 pjsip_tx_data *tdata; 2937 pj_status_t status; 2938 2939 PJ_UNUSED_ARG(th); 2940 2941 pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE; 2942 2943 status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg); 2944 if (status != PJ_SUCCESS) 2945 return; 2946 2947 /* Verify if another SDP negotiation is in progress, e.g: session timer 2948 * or another re-INVITE. 2949 */ 2950 if (call->inv==NULL || call->inv->neg==NULL || 2951 pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) 2952 { 2953 goto on_return; 2954 } 2955 2956 /* Verify if another SDP negotiation has been completed by comparing 2957 * the SDP version. 2958 */ 2959 { 2960 const pjmedia_sdp_session *sdp; 2961 2962 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp); 2963 if (status == PJ_SUCCESS && 2964 sdp->origin.version > call->lock_codec.new_sdp->origin.version) 2965 { 2966 goto on_return; 2967 } 2968 } 2969 2970 /* Create re-INVITE with the new offer */ 2971 status = pjsip_inv_reinvite(call->inv, NULL, call->lock_codec.new_sdp, 2972 &tdata); 2973 if (status == PJ_EINVALIDOP) { 2974 /* Ups, let's reschedule again */ 2975 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL}; 2976 call->lock_codec.reinv_timer.id = PJ_TRUE; 2977 pjsip_endpt_schedule_timer(pjsua_var.endpt, 2978 &call->lock_codec.reinv_timer, &delay); 2979 } else if (status != PJ_SUCCESS) { 2980 pjsua_perror(THIS_FILE, "Failed creating re-INVITE in lock codec", 2981 status); 2982 } 2983 2984 /* Send the UPDATE/re-INVITE request */ 2985 status = pjsip_inv_send_msg(call->inv, tdata); 2986 if (status != PJ_SUCCESS) { 2987 pjsua_perror(THIS_FILE, "Failed sending re-INVITE in lock codec", 2988 status); 2989 } 2990 2991 on_return: 2992 pjsip_dlg_dec_lock(dlg); 2993 } 2994 2995 2996 /* Check if the specified format can be skipped in counting codecs */ 2997 static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m, 2998 const pj_str_t *fmt) 2999 { 3000 unsigned pt; 3001 3002 pt = pj_strtoul(fmt); 3003 3004 /* Check for comfort noise */ 3005 if (pt == PJMEDIA_RTP_PT_CN) 3006 return PJ_TRUE; 3007 3008 /* Dynamic PT, check the format name */ 3009 if (pt >= 96) { 3010 pjmedia_sdp_attr *a; 3011 pjmedia_sdp_rtpmap rtpmap; 3012 3013 /* Get the format name */ 3014 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt); 3015 if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) { 3016 /* Check for telephone-event */ 3017 if (pj_stricmp2(&rtpmap.enc_name, "telephone-event")==0) 3018 return PJ_TRUE; 3019 } else { 3020 /* Invalid SDP, should not reach here */ 3021 pj_assert(!"SDP should have been validated!"); 3022 return PJ_TRUE; 3023 } 3024 } 3025 3026 return PJ_FALSE; 3027 } 3028 3029 3030 /* Check if remote answerer has given us more than one codecs. If so, 3031 * create another offer with one codec only to lock down the codec. 3032 */ 3033 static pj_status_t lock_codec(pjsua_call *call) 3034 { 3035 const pj_str_t st_update = {"UPDATE", 6}; 3036 pjsip_inv_session *inv = call->inv; 3037 const pjmedia_sdp_session *local_sdp; 3038 const pjmedia_sdp_session *remote_sdp; 3039 const pjmedia_sdp_media *rem_m; 3040 pjmedia_sdp_session *new_sdp; 3041 pjmedia_sdp_media *m; 3042 pjsip_tx_data *tdata; 3043 unsigned i, codec_cnt = 0; 3044 pj_status_t status; 3045 3046 if (!pjmedia_sdp_neg_was_answer_remote(inv->neg)) 3047 return PJ_SUCCESS; 3048 3049 status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp); 3050 if (status != PJ_SUCCESS) 3051 return status; 3052 status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp); 3053 if (status != PJ_SUCCESS) 3054 return status; 3055 3056 PJ_ASSERT_RETURN(call->audio_idx>=0 && 3057 call->audio_idx < (int)remote_sdp->media_count, 3058 PJ_EINVALIDOP); 3059 3060 rem_m = remote_sdp->media[call->audio_idx]; 3061 3062 /* Check if media is disabled or only one format in the answer. */ 3063 if (rem_m->desc.port==0 || rem_m->desc.fmt_count==1) 3064 return PJ_SUCCESS; 3065 3066 /* Count the formats in the answer. */ 3067 for (i=0; i<rem_m->desc.fmt_count && codec_cnt <= 1; ++i) { 3068 if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[i])) 3069 ++codec_cnt; 3070 } 3071 3072 if (codec_cnt <= 1) { 3073 /* Answer contains single codec. */ 3074 return PJ_SUCCESS; 3075 } 3076 3077 PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, start " 3078 "updating media session to use only one codec..")); 3079 3080 /* Clone the offer */ 3081 new_sdp = pjmedia_sdp_session_clone(inv->pool_prov, local_sdp); 3082 /* Note that the usage of pool_prov above is risky when locking codec 3083 * delays the re-INVITE (using timer) and there are two SDP negotiations 3084 * done before the re-INVITE. 3085 */ 3086 3087 /* Update the new offer so it contains only a codec. Note that formats 3088 * order in the offer should have been matched to the answer, so we can 3089 * just directly update the offer without looking-up the answer. 3090 */ 3091 m = new_sdp->media[call->audio_idx]; 3092 codec_cnt = 0; 3093 i = 0; 3094 while (i < m->desc.fmt_count) { 3095 pjmedia_sdp_attr *a; 3096 pj_str_t *fmt = &m->desc.fmt[i]; 3097 3098 if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) { 3099 ++i; 3100 continue; 3101 } 3102 3103 /* Remove format */ 3104 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt); 3105 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); 3106 a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt); 3107 if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a); 3108 pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]), 3109 m->desc.fmt_count, i); 3110 --m->desc.fmt_count; 3111 } 3112 3113 /* Send new SDP offer via UPDATE or re-INVITE */ 3114 if (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)== 3115 PJSIP_DIALOG_CAP_SUPPORTED) 3116 { 3117 /* Create UPDATE with the new offer */ 3118 status = pjsip_inv_update(inv, NULL, new_sdp, &tdata); 3119 if (status != PJ_SUCCESS) 3120 return status; 3121 3122 } else { 3123 /* Create re-INVITE with the new offer */ 3124 status = pjsip_inv_reinvite(inv, NULL, new_sdp, &tdata); 3125 if (status == PJ_EINVALIDOP) { 3126 /* Current INVITE transaction is pending, reschedule re-INVITE. */ 3127 pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL}; 3128 3129 call->lock_codec.new_sdp = new_sdp; 3130 pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE, 3131 (void*)(pj_size_t)call->index, 3132 &reinv_timer_cb); 3133 pjsip_endpt_schedule_timer(pjsua_var.endpt, 3134 &call->lock_codec.reinv_timer, &delay); 3135 return PJ_SUCCESS; 3136 3137 } else if (status != PJ_SUCCESS) 3138 return status; 3139 } 3140 3141 /* Send the UPDATE/re-INVITE request */ 3142 status = pjsip_inv_send_msg(inv, tdata); 3143 if (status != PJ_SUCCESS) 3144 return status; 3145 3146 return PJ_SUCCESS; 3147 } 3148 2916 3149 /* 2917 3150 * This callback receives notification from invite session when the … … 2947 3180 case PJSIP_INV_STATE_CONFIRMED: 2948 3181 pj_gettimeofday(&call->conn_time); 3182 3183 /* Ticket #476, locking a codec in the media session. */ 3184 { 3185 pj_status_t status; 3186 status = lock_codec(call); 3187 if (status != PJ_SUCCESS) { 3188 pjsua_perror(THIS_FILE, "Unable to lock codec", status); 3189 } 3190 } 3191 2949 3192 break; 2950 3193 case PJSIP_INV_STATE_DISCONNECTED: … … 2966 3209 sizeof(call->last_text_buf_)); 2967 3210 } 3211 3212 /* Stop lock codec timer, if it is active */ 3213 if (call->lock_codec.reinv_timer.id) { 3214 pjsip_endpt_cancel_timer(pjsua_var.endpt, 3215 &call->lock_codec.reinv_timer); 3216 call->lock_codec.reinv_timer.id = PJ_FALSE; 3217 } 2968 3218 break; 2969 3219 default: … … 3170 3420 const pjmedia_sdp_session *local_sdp; 3171 3421 const pjmedia_sdp_session *remote_sdp; 3422 const pj_str_t st_update = {"UPDATE", 6}; 3172 3423 3173 3424 PJSUA_LOCK(); … … 3241 3492 } 3242 3493 3494 /* Ticket #476, handle the case of early media and remote support UPDATE */ 3495 if (inv->state == PJSIP_INV_STATE_EARLY && 3496 pjmedia_sdp_neg_was_answer_remote(inv->neg) && 3497 pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)== 3498 PJSIP_DIALOG_CAP_SUPPORTED) 3499 { 3500 status = lock_codec(call); 3501 if (status != PJ_SUCCESS) { 3502 pjsua_perror(THIS_FILE, "Unable to lock codec", status); 3503 } 3504 } 3243 3505 3244 3506 /* Call application callback, if any */
Note: See TracChangeset
for help on using the changeset viewer.