Changeset 2364 for pjproject/trunk


Ignore:
Timestamp:
Nov 11, 2008 11:51:18 AM (15 years ago)
Author:
nanang
Message:

Ticket #674:

  • Updated SDP negotiation module to allow special treatment for AMR negotiation.
  • Fixed SDP negotiation when it prefers local codec order it gets wrong in identifying the remote offer.
  • Added some test scripts to test AMR negotiation.
Location:
pjproject/trunk
Files:
3 added
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c

    r2236 r2364  
    5252}; 
    5353 
     54#define GET_FMTP_IVAL(ival, fmtp, param, default_val) \ 
     55    do { \ 
     56        pj_str_t s; \ 
     57        char *p; \ 
     58        p = pj_stristr(&fmtp.fmt_param, &param); \ 
     59        if (!p) { \ 
     60            ival = default_val; \ 
     61            break; \ 
     62        } \ 
     63        pj_strset(&s, p + param.slen, fmtp.fmt_param.slen - \ 
     64                  (p - fmtp.fmt_param.ptr) - param.slen); \ 
     65        ival = pj_strtoul(&s); \ 
     66    } while (0) 
     67 
    5468/* 
    5569 * Get string representation of negotiator state. 
     
    541555                              unsigned a_fmt_idx) 
    542556{ 
    543     const pjmedia_sdp_attr *a_ans; 
    544     const pjmedia_sdp_attr *a_off; 
     557    const pjmedia_sdp_attr *attr_ans; 
     558    const pjmedia_sdp_attr *attr_ofr; 
    545559    pjmedia_sdp_fmtp fmtp; 
    546     unsigned a_bitrate = 0, o_bitrate = 0; 
     560    unsigned a_bitrate, o_bitrate; 
    547561    const pj_str_t bitrate = {"bitrate=", 8}; 
    548     const char *p; 
    549  
    550     a_ans = pjmedia_sdp_media_find_attr2(answer, "fmtp",  
    551                                          &answer->desc.fmt[a_fmt_idx]); 
    552     if (!a_ans) 
     562 
     563    /* Parse offer */ 
     564    attr_ofr = pjmedia_sdp_media_find_attr2(offer, "fmtp",  
     565                                            &offer->desc.fmt[o_fmt_idx]); 
     566    if (!attr_ofr) 
    553567        return PJ_FALSE; 
    554568 
    555     if (pjmedia_sdp_attr_get_fmtp(a_ans, &fmtp) != PJ_SUCCESS) 
     569    if (pjmedia_sdp_attr_get_fmtp(attr_ofr, &fmtp) != PJ_SUCCESS) 
    556570        return PJ_FALSE; 
    557571 
    558     p = pj_stristr(&fmtp.fmt_param, &bitrate); 
    559     if (p == NULL) 
     572    GET_FMTP_IVAL(o_bitrate, fmtp, bitrate, 0); 
     573 
     574    /* Parse answer */ 
     575    attr_ans = pjmedia_sdp_media_find_attr2(answer, "fmtp",  
     576                                            &answer->desc.fmt[a_fmt_idx]); 
     577    if (!attr_ans) 
    560578        return PJ_FALSE; 
    561579 
    562     a_bitrate = atoi(p + bitrate.slen); 
    563  
    564     a_off = pjmedia_sdp_media_find_attr2(offer, "fmtp",  
    565                                          &offer->desc.fmt[o_fmt_idx]); 
    566     if (!a_off) 
     580    if (pjmedia_sdp_attr_get_fmtp(attr_ans, &fmtp) != PJ_SUCCESS) 
    567581        return PJ_FALSE; 
    568582 
    569     if (pjmedia_sdp_attr_get_fmtp(a_off, &fmtp) != PJ_SUCCESS) 
    570         return PJ_FALSE; 
    571  
    572     p = pj_stristr(&fmtp.fmt_param, &bitrate); 
    573     if (p == NULL) 
    574         return PJ_FALSE; 
    575  
    576     o_bitrate = atoi(p + bitrate.slen); 
    577  
     583    GET_FMTP_IVAL(a_bitrate, fmtp, bitrate, 0); 
     584 
     585    /* Compare bitrate in answer and offer. */ 
    578586    return (a_bitrate == o_bitrate); 
    579587} 
     588 
     589/* Negotiate AMR format params between offer and answer. Format params 
     590 * to be matched are: octet-align, crc, robust-sorting, interleaving,  
     591 * and channels (channels is negotiated by rtpmap line negotiation).  
     592 * Note: For answerer, octet-align mode setting is adaptable to offerer  
     593 *       setting. In the case that octet-align mode need to be adjusted, 
     594 *       pt_need_adapt will be set to the format ID. 
     595 *        
     596 */ 
     597static pj_bool_t match_amr( const pjmedia_sdp_media *offer, 
     598                            unsigned o_fmt_idx, 
     599                            const pjmedia_sdp_media *answer, 
     600                            unsigned a_fmt_idx, 
     601                            pj_bool_t answerer, 
     602                            pj_str_t *pt_need_adapt) 
     603{ 
     604    /* Negotiated format-param field-names constants. */ 
     605    const pj_str_t STR_OCTET_ALIGN      = {"octet-align=", 12}; 
     606    const pj_str_t STR_CRC              = {"crc=", 4}; 
     607    const pj_str_t STR_ROBUST_SORTING   = {"robust-sorting=", 15}; 
     608    const pj_str_t STR_INTERLEAVING     = {"interleaving=", 13}; 
     609 
     610    /* Negotiated params and their default values. */ 
     611    unsigned a_octet_align = 0, o_octet_align = 0; 
     612    unsigned a_crc = 0, o_crc = 0; 
     613    unsigned a_robust_sorting = 0, o_robust_sorting = 0; 
     614    unsigned a_interleaving = 0, o_interleaving = 0; 
     615 
     616    const pjmedia_sdp_attr *attr_ans; 
     617    const pjmedia_sdp_attr *attr_ofr; 
     618    pjmedia_sdp_fmtp fmtp; 
     619    pj_bool_t match; 
     620 
     621    /* Parse offerer FMTP */ 
     622    attr_ofr = pjmedia_sdp_media_find_attr2(offer, "fmtp",  
     623                                            &offer->desc.fmt[o_fmt_idx]); 
     624    if (attr_ofr) { 
     625        if (pjmedia_sdp_attr_get_fmtp(attr_ofr, &fmtp) != PJ_SUCCESS) 
     626            /* Invalid fmtp format. */ 
     627            return PJ_FALSE; 
     628 
     629        GET_FMTP_IVAL(o_octet_align, fmtp, STR_OCTET_ALIGN, 0); 
     630        GET_FMTP_IVAL(o_crc, fmtp, STR_CRC, 0); 
     631        GET_FMTP_IVAL(o_robust_sorting, fmtp, STR_ROBUST_SORTING, 0); 
     632        GET_FMTP_IVAL(o_interleaving, fmtp, STR_INTERLEAVING, 0); 
     633    } 
     634 
     635    /* Parse answerer FMTP */ 
     636    attr_ans = pjmedia_sdp_media_find_attr2(answer, "fmtp",  
     637                                            &answer->desc.fmt[a_fmt_idx]); 
     638    if (attr_ans) { 
     639        if (pjmedia_sdp_attr_get_fmtp(attr_ans, &fmtp) != PJ_SUCCESS) 
     640            /* Invalid fmtp format. */ 
     641            return PJ_FALSE; 
     642 
     643        GET_FMTP_IVAL(a_octet_align, fmtp, STR_OCTET_ALIGN, 0); 
     644        GET_FMTP_IVAL(a_crc, fmtp, STR_CRC, 0); 
     645        GET_FMTP_IVAL(a_robust_sorting, fmtp, STR_ROBUST_SORTING, 0); 
     646        GET_FMTP_IVAL(a_interleaving, fmtp, STR_INTERLEAVING, 0); 
     647    } 
     648 
     649    if (answerer) { 
     650        match = a_crc == o_crc && 
     651                a_robust_sorting == o_robust_sorting && 
     652                a_interleaving == o_interleaving; 
     653 
     654        /* If answerer octet-align setting doesn't match to the offerer's,  
     655         * set pt_need_adapt to this media format ID to signal the caller 
     656         * that this format ID needs to be adjusted. 
     657         */ 
     658        if (a_octet_align != o_octet_align && match) { 
     659            pj_assert(pt_need_adapt != NULL); 
     660            *pt_need_adapt = answer->desc.fmt[a_fmt_idx]; 
     661        } 
     662    } else { 
     663        match = (a_octet_align == o_octet_align && 
     664                 a_crc == o_crc && 
     665                 a_robust_sorting == o_robust_sorting && 
     666                 a_interleaving == o_interleaving); 
     667    } 
     668 
     669    return match; 
     670} 
     671 
     672 
     673/* Toggle AMR octet-align setting in the fmtp. 
     674 */ 
     675static pj_status_t amr_toggle_octet_align(pj_pool_t *pool, 
     676                                          pjmedia_sdp_media *media, 
     677                                          unsigned fmt_idx) 
     678{ 
     679    pjmedia_sdp_attr *attr; 
     680    pjmedia_sdp_fmtp fmtp; 
     681    const pj_str_t STR_OCTET_ALIGN = {"octet-align=", 12}; 
     682     
     683    enum { MAX_FMTP_STR_LEN = 160 }; 
     684 
     685    attr = pjmedia_sdp_media_find_attr2(media, "fmtp",  
     686                                        &media->desc.fmt[fmt_idx]); 
     687    /* Check if the AMR media format has FMTP attribute */ 
     688    if (attr) { 
     689        char *p; 
     690        pj_status_t status; 
     691 
     692        status = pjmedia_sdp_attr_get_fmtp(attr, &fmtp); 
     693        if (status != PJ_SUCCESS) 
     694            return status; 
     695 
     696        /* Check if the fmtp has octet-align field. */ 
     697        p = pj_stristr(&fmtp.fmt_param, &STR_OCTET_ALIGN); 
     698        if (p) { 
     699            /* It has, just toggle the value */ 
     700            unsigned octet_align; 
     701            pj_str_t s; 
     702 
     703            pj_strset(&s, p + STR_OCTET_ALIGN.slen, fmtp.fmt_param.slen - 
     704                      (p - fmtp.fmt_param.ptr) - STR_OCTET_ALIGN.slen); 
     705            octet_align = pj_strtoul(&s); 
     706            *(p + STR_OCTET_ALIGN.slen) = (char)(octet_align? '0' : '1'); 
     707        } else { 
     708            /* It doesn't, append octet-align field */ 
     709            char buf[MAX_FMTP_STR_LEN]; 
     710 
     711            pj_ansi_snprintf(buf, MAX_FMTP_STR_LEN, "%.*s;octet-align=1", 
     712                             (int)fmtp.fmt_param.slen, fmtp.fmt_param.ptr); 
     713            attr->value = pj_strdup3(pool, buf); 
     714        } 
     715    } else { 
     716        /* Add new attribute for the AMR media format with octet-align  
     717         * field set. 
     718         */ 
     719        char buf[MAX_FMTP_STR_LEN]; 
     720 
     721        attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); 
     722        attr->name = pj_str("fmtp"); 
     723        pj_ansi_snprintf(buf, MAX_FMTP_STR_LEN, "%.*s octet-align=1", 
     724                         (int)media->desc.fmt[fmt_idx].slen, 
     725                         media->desc.fmt[fmt_idx].ptr); 
     726        attr->value = pj_strdup3(pool, buf); 
     727        media->attr[media->attr_count++] = attr; 
     728    } 
     729 
     730    return PJ_SUCCESS; 
     731} 
     732 
    580733 
    581734/* Update single local media description to after receiving answer 
     
    707860                                if (match_g7221(offer, i, answer, j)) 
    708861                                    break; 
     862                            } else 
     863                            /* Further check for AMR, negotiate fmtp. */ 
     864                            if (pj_strcmp2(&or_.enc_name, "AMR") == 0) { 
     865                                if (match_amr(offer, i, answer, j, PJ_FALSE,  
     866                                              NULL)) 
     867                                    break; 
    709868                            } else { 
    710869                                /* Match! */ 
     
    819978/* Try to match offer with answer. */ 
    820979static pj_status_t match_offer(pj_pool_t *pool, 
     980                               pj_bool_t prefer_remote_codec_order, 
    821981                               const pjmedia_sdp_media *offer, 
    822982                               const pjmedia_sdp_media *preanswer, 
    823                                const pjmedia_sdp_media *orig_local, 
    824983                               pjmedia_sdp_media **p_answer) 
    825984{ 
    826985    unsigned i; 
    827     pj_bool_t offer_has_codec = 0, 
    828               offer_has_telephone_event = 0, 
    829               offer_has_other = 0, 
     986    pj_bool_t master_has_codec = 0, 
     987              master_has_telephone_event = 0, 
     988              master_has_other = 0, 
    830989              found_matching_codec = 0, 
    831990              found_matching_telephone_event = 0, 
     
    834993    pj_str_t pt_answer[PJMEDIA_MAX_SDP_FMT]; 
    835994    pjmedia_sdp_media *answer; 
    836  
     995    const pjmedia_sdp_media *master, *slave; 
     996    pj_str_t pt_amr_need_adapt = {NULL, 0}; 
     997 
     998    /* Set master/slave negotiator based on prefer_remote_codec_order. */ 
     999    if (prefer_remote_codec_order) { 
     1000        master = offer; 
     1001        slave  = preanswer; 
     1002    } else { 
     1003        master = preanswer; 
     1004        slave  = offer; 
     1005    } 
     1006     
    8371007    /* With the addition of telephone-event and dodgy MS RTC SDP,  
    8381008     * the answer generation algorithm looks really shitty... 
    8391009     */ 
    840     for (i=0; i<offer->desc.fmt_count; ++i) { 
     1010    for (i=0; i<master->desc.fmt_count; ++i) { 
    8411011        unsigned j; 
    8421012         
    843         if (pj_isdigit(*offer->desc.fmt[i].ptr)) { 
     1013        if (pj_isdigit(*master->desc.fmt[i].ptr)) { 
    8441014            /* This is normal/standard payload type, where it's identified 
    8451015             * by payload number. 
     
    8471017            unsigned pt; 
    8481018 
    849             pt = pj_strtoul(&offer->desc.fmt[i]); 
     1019            pt = pj_strtoul(&master->desc.fmt[i]); 
    8501020             
    8511021            if (pt < 96) { 
     
    8541024                 */ 
    8551025 
    856                 offer_has_codec = 1; 
     1026                master_has_codec = 1; 
    8571027 
    8581028                /* We just need to select one codec.  
     
    8641034 
    8651035                /* Find matching codec in local descriptor. */ 
    866                 for (j=0; j<preanswer->desc.fmt_count; ++j) { 
     1036                for (j=0; j<slave->desc.fmt_count; ++j) { 
    8671037                    unsigned p; 
    868                     p = pj_strtoul(&preanswer->desc.fmt[j]); 
    869                     if (p == pt && pj_isdigit(*preanswer->desc.fmt[j].ptr)) { 
     1038                    p = pj_strtoul(&slave->desc.fmt[j]); 
     1039                    if (p == pt && pj_isdigit(*slave->desc.fmt[j].ptr)) { 
    8701040                        found_matching_codec = 1; 
    871                         pt_answer[pt_answer_count++] = preanswer->desc.fmt[j]; 
     1041                        pt_answer[pt_answer_count++] = slave->desc.fmt[j]; 
    8721042                        break; 
    8731043                    } 
     
    8831053                pj_bool_t is_codec; 
    8841054 
    885                 /* Get the rtpmap for the payload type in the offer. */ 
    886                 a = pjmedia_sdp_media_find_attr2(offer, "rtpmap",  
    887                                                  &offer->desc.fmt[i]); 
     1055                /* Get the rtpmap for the payload type in the master. */ 
     1056                a = pjmedia_sdp_media_find_attr2(master, "rtpmap",  
     1057                                                 &master->desc.fmt[i]); 
    8881058                if (!a) { 
    8891059                    pj_assert(!"Bug! Offer should have been validated"); 
     
    8931063 
    8941064                if (!pj_strcmp2(&or_.enc_name, "telephone-event")) { 
    895                     offer_has_telephone_event = 1; 
     1065                    master_has_telephone_event = 1; 
    8961066                    if (found_matching_telephone_event) 
    8971067                        continue; 
    8981068                    is_codec = 0; 
    8991069                } else { 
    900                     offer_has_codec = 1; 
     1070                    master_has_codec = 1; 
    9011071                    if (found_matching_codec) 
    9021072                        continue; 
     
    9071077                 * encoding name and clock rate. 
    9081078                 */ 
    909                 for (j=0; j<preanswer->desc.fmt_count; ++j) { 
    910                     a = pjmedia_sdp_media_find_attr2(preanswer, "rtpmap",  
    911                                                      &preanswer->desc.fmt[j]); 
     1079                for (j=0; j<slave->desc.fmt_count; ++j) { 
     1080                    a = pjmedia_sdp_media_find_attr2(slave, "rtpmap",  
     1081                                                     &slave->desc.fmt[j]); 
    9121082                    if (a) { 
    9131083                        pjmedia_sdp_rtpmap lr; 
     
    9241094                            /* Match! */ 
    9251095                            if (is_codec) { 
    926                                 /* Further check for G7221, negotiate bitrate. */ 
    927                                 if (pj_strcmp2(&or_.enc_name, "G7221")  == 0 && 
    928                                     match_g7221(offer, i, preanswer, j) == 0) 
     1096                                /* Further check for G7221, negotiate bitrate */ 
     1097                                if (pj_strcmp2(&or_.enc_name, "G7221") == 0 && 
     1098                                    !match_g7221(master, i, slave, j)) 
    9291099                                { 
    9301100                                    continue; 
     1101                                } else  
     1102                                /* Further check for AMR, negotiate fmtp */ 
     1103                                if (pj_strcmp2(&or_.enc_name, "AMR")==0) { 
     1104                                    unsigned o_med_idx, a_med_idx; 
     1105 
     1106                                    o_med_idx = prefer_remote_codec_order? i:j; 
     1107                                    a_med_idx = prefer_remote_codec_order? j:i; 
     1108                                    if (!match_amr(offer, o_med_idx,  
     1109                                                   preanswer, a_med_idx, 
     1110                                                   PJ_TRUE, &pt_amr_need_adapt)) 
     1111                                        continue; 
    9311112                                } 
    9321113                                found_matching_codec = 1; 
     
    9351116                            } 
    9361117 
    937                             pt_answer[pt_answer_count++] = preanswer->desc.fmt[j]; 
     1118                            pt_answer[pt_answer_count++] =  
     1119                                                prefer_remote_codec_order?  
     1120                                                preanswer->desc.fmt[j]: 
     1121                                                preanswer->desc.fmt[i]; 
    9381122                            break; 
    9391123                        } 
     
    9491133             *  - m=x-ms-message 5060 sip null 
    9501134             */ 
    951             offer_has_other = 1; 
     1135            master_has_other = 1; 
    9521136            if (found_matching_other) 
    9531137                continue; 
    9541138 
    955             for (j=0; j<preanswer->desc.fmt_count; ++j) { 
    956                 if (!pj_strcmp(&offer->desc.fmt[i], &preanswer->desc.fmt[j])) { 
     1139            for (j=0; j<slave->desc.fmt_count; ++j) { 
     1140                if (!pj_strcmp(&master->desc.fmt[i], &slave->desc.fmt[j])) { 
    9571141                    /* Match */ 
    9581142                    found_matching_other = 1; 
    959                     pt_answer[pt_answer_count++] = preanswer->desc.fmt[j]; 
     1143                    pt_answer[pt_answer_count++] = prefer_remote_codec_order?  
     1144                                                   preanswer->desc.fmt[j]: 
     1145                                                   preanswer->desc.fmt[i]; 
    9601146                    break; 
    9611147                } 
     
    9641150    } 
    9651151 
    966     /* See if all types of offer can be matched. */ 
    967     if (offer_has_codec && !found_matching_codec) { 
     1152    /* See if all types of master can be matched. */ 
     1153    if (master_has_codec && !found_matching_codec) { 
    9681154        return PJMEDIA_SDPNEG_NOANSCODEC; 
    9691155    } 
     
    9771163    */ 
    9781164 
    979     if (offer_has_other && !found_matching_other) { 
     1165    if (master_has_other && !found_matching_other) { 
    9801166        return PJMEDIA_SDPNEG_NOANSUNKNOWN; 
    9811167    } 
    9821168 
    9831169    /* Seems like everything is in order. 
    984      * Build the answer by cloning from local media, but rearrange the payload 
     1170     * Build the answer by cloning from preanswer, but rearrange the payload 
    9851171     * to suit the offer. 
    9861172     */ 
    987     answer = pjmedia_sdp_media_clone(pool, orig_local); 
     1173    answer = pjmedia_sdp_media_clone(pool, preanswer); 
    9881174    for (i=0; i<pt_answer_count; ++i) { 
    9891175        unsigned j; 
     
    9941180        pj_assert(j != answer->desc.fmt_count); 
    9951181        str_swap(&answer->desc.fmt[i], &answer->desc.fmt[j]); 
     1182         
     1183        /* For AMR/AMRWB format, adapt octet-align setting if required. */ 
     1184        if (!pj_strcmp(&pt_amr_need_adapt, &pt_answer[i])) 
     1185            amr_toggle_octet_align(pool, answer, i); 
    9961186    } 
    9971187     
     
    10171207 
    10181208    /* If offer has zero port, set our answer with zero port too */ 
    1019     if (offer->desc.port==0) 
     1209    if (offer->desc.port == 0) 
    10201210        answer->desc.port = 0; 
    10211211 
     
    10771267            { 
    10781268                /* See if it has matching codec. */ 
    1079                 if (prefer_remote_codec_order) { 
    1080                     status = match_offer(pool, om, im, im, &am); 
    1081                 } else { 
    1082                     status = match_offer(pool, im, om, im, &am); 
    1083                 } 
    1084  
     1269                status = match_offer(pool, prefer_remote_codec_order,  
     1270                                     om, im, &am); 
    10851271                if (status == PJ_SUCCESS) { 
    10861272                    /* Mark media as used. */ 
Note: See TracChangeset for help on using the changeset viewer.