From 920d97341f9c044fd012f6b41fff01e4c9563a16 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Mon, 25 Aug 2008 13:58:25 +0000 Subject: Ticket #599: - Added "dec_fmtp" and "enc_fmtp" fields to pjmedia_codec_param.setting. - Codec factory puts its default parameters in "dec_fmtp" field. - pjmedia_stream_info_from_sdp() puts the "fmtp" attribute in SDP to pjmedia_codec_param. - Special treatment for fmtp "bitrate" parameter (of G722.1) during SDP negotiation - Added maxptime field in stream_info. - Replaced iLBC's fmtp "mode" implementation to use general fmtp mechanism. - Added some test scripts for G722.1 bitrate negotiation. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2236 74dad513-b988-da41-8d7b-12977e46ad98 --- pjmedia/include/pjmedia-codec/types.h | 4 +- pjmedia/include/pjmedia/codec.h | 19 +++- pjmedia/include/pjmedia/stream.h | 1 + pjmedia/src/pjmedia-codec/ilbc.c | 60 +++++++--- pjmedia/src/pjmedia-codec/ipp_codecs.c | 86 ++++++++++++--- pjmedia/src/pjmedia/endpoint.c | 57 ++++++++-- pjmedia/src/pjmedia/sdp_neg.c | 67 +++++++++++- pjmedia/src/pjmedia/session.c | 121 +++++++++++++-------- .../scripts-sendto/400_fmtp_g7221_with_bitrate.py | 34 ++++++ .../401_fmtp_g7221_with_bitrate_24000.py | 35 ++++++ .../401_fmtp_g7221_with_bitrate_32000.py | 35 ++++++ 11 files changed, 420 insertions(+), 99 deletions(-) create mode 100644 pjsip-apps/src/test-pjsua/scripts-sendto/400_fmtp_g7221_with_bitrate.py create mode 100644 pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_24000.py create mode 100644 pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_32000.py diff --git a/pjmedia/include/pjmedia-codec/types.h b/pjmedia/include/pjmedia-codec/types.h index 32f55250..a749b576 100644 --- a/pjmedia/include/pjmedia-codec/types.h +++ b/pjmedia/include/pjmedia-codec/types.h @@ -61,7 +61,9 @@ enum PJMEDIA_RTP_PT_G726_24, /**< G726 @ 24Kbps */ /* PJMEDIA_RTP_PT_G726_32,*/ /**< G726 @ 32Kbps, static? */ PJMEDIA_RTP_PT_G726_40, /**< G726 @ 40Kbps */ - PJMEDIA_RTP_PT_G722_1, /**< G722.1 (16-32Kbps) */ + PJMEDIA_RTP_PT_G722_1_16, /**< G722.1 (16Kbps) */ + PJMEDIA_RTP_PT_G722_1_24, /**< G722.1 (24Kbps) */ + PJMEDIA_RTP_PT_G722_1_32, /**< G722.1 (32Kbps) */ }; diff --git a/pjmedia/include/pjmedia/codec.h b/pjmedia/include/pjmedia/codec.h index 55459f41..0241e229 100644 --- a/pjmedia/include/pjmedia/codec.h +++ b/pjmedia/include/pjmedia/codec.h @@ -238,6 +238,21 @@ typedef struct pjmedia_codec_info unsigned channel_cnt; /**< Channel count. */ } pjmedia_codec_info; +#define PJMEDIA_CODEC_MAX_FMTP_CNT 8 + +/** + * Structure of codec specific parameters which contains name=value pairs. + * The codec specific parameters are to be used with SDP according to + * the standards (e.g: RFC 3555). + */ +typedef struct pjmedia_codec_fmtp +{ + pj_uint8_t cnt; + struct param { + pj_str_t name; + pj_str_t val; + } param [PJMEDIA_CODEC_MAX_FMTP_CNT]; +} pjmedia_codec_fmtp; /** * Detailed codec attributes used both to configure a codec and to query @@ -277,8 +292,8 @@ typedef struct pjmedia_codec_param unsigned penh:1; /**< Perceptual Enhancement */ unsigned plc:1; /**< Packet loss concealment */ unsigned reserved:1; /**< Reserved, must be zero. */ - pj_uint8_t enc_fmtp_mode; /**< Mode param in fmtp (def:0) */ - pj_uint8_t dec_fmtp_mode; /**< Mode param in fmtp (def:0) */ + pjmedia_codec_fmtp enc_fmtp;/**< Encoder's fmtp params. */ + pjmedia_codec_fmtp dec_fmtp;/**< Decoder's fmtp params. */ } setting; } pjmedia_codec_param; diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h index 59ae0749..b14ef377 100644 --- a/pjmedia/include/pjmedia/stream.h +++ b/pjmedia/include/pjmedia/stream.h @@ -108,6 +108,7 @@ struct pjmedia_stream_info pjmedia_codec_info fmt; /**< Incoming codec format info. */ pjmedia_codec_param *param; /**< Optional codec param. */ unsigned tx_pt; /**< Outgoing codec paylaod type. */ + unsigned tx_maxptime;/**< Outgoing codec max ptime. */ int tx_event_pt;/**< Outgoing pt for telephone-events. */ int rx_event_pt;/**< Incoming pt for telephone-events. */ pj_uint32_t ssrc; /**< RTP SSRC. */ diff --git a/pjmedia/src/pjmedia-codec/ilbc.c b/pjmedia/src/pjmedia-codec/ilbc.c index 2b8e5fb6..65063167 100644 --- a/pjmedia/src/pjmedia-codec/ilbc.c +++ b/pjmedia/src/pjmedia-codec/ilbc.c @@ -143,7 +143,7 @@ struct ilbc_codec float dec_block[BLOCKL_MAX]; }; - +static pj_str_t STR_MODE = {"mode", 4}; /* * Initialize and register iLBC codec factory to pjmedia endpoint. @@ -273,7 +273,12 @@ static pj_status_t ilbc_default_attr (pjmedia_codec_factory *factory, attr->setting.vad = 1; attr->setting.plc = 1; attr->setting.penh = 1; - attr->setting.dec_fmtp_mode = (pj_uint8_t)ilbc_factory.mode; + attr->setting.dec_fmtp.cnt = 1; + attr->setting.dec_fmtp.param[0].name = STR_MODE; + if (ilbc_factory.mode == 30) + attr->setting.dec_fmtp.param[0].val = pj_str("30"); + else + attr->setting.dec_fmtp.param[0].val = pj_str("20"); return PJ_SUCCESS; } @@ -370,46 +375,65 @@ static pj_status_t ilbc_codec_open(pjmedia_codec *codec, { struct ilbc_codec *ilbc_codec = (struct ilbc_codec*)codec; pj_status_t status; + unsigned i, dec_fmtp_mode = 0, enc_fmtp_mode = 0; pj_assert(ilbc_codec != NULL); pj_assert(ilbc_codec->enc_ready == PJ_FALSE && ilbc_codec->dec_ready == PJ_FALSE); + /* Get decoder mode */ + for (i = 0; i < attr->setting.dec_fmtp.cnt; ++i) { + if (pj_stricmp(&attr->setting.dec_fmtp.param[i].name, &STR_MODE) == 0) + { + dec_fmtp_mode = (unsigned) + pj_strtoul(&attr->setting.dec_fmtp.param[i].val); + break; + } + } + /* Decoder mode must be set */ - PJ_ASSERT_RETURN(attr->setting.dec_fmtp_mode==20 || - attr->setting.dec_fmtp_mode==30, PJMEDIA_CODEC_EINMODE); + PJ_ASSERT_RETURN(dec_fmtp_mode == 20 || dec_fmtp_mode == 30, + PJMEDIA_CODEC_EINMODE); + + /* Get encoder mode */ + for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) { + if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_MODE) == 0) + { + enc_fmtp_mode = (unsigned) + pj_strtoul(&attr->setting.enc_fmtp.param[i].val); + break; + } + } /* The enc mode must be set in the attribute * (from the mode parameter in fmtp attribute in the SDP * received from remote) */ - if (attr->setting.enc_fmtp_mode == 0) - attr->setting.enc_fmtp_mode = attr->setting.dec_fmtp_mode; + if (enc_fmtp_mode == 0) + enc_fmtp_mode = dec_fmtp_mode; - PJ_ASSERT_RETURN(attr->setting.enc_fmtp_mode==20 || - attr->setting.enc_fmtp_mode==30, PJMEDIA_CODEC_EINMODE); + PJ_ASSERT_RETURN(enc_fmtp_mode==20 || + enc_fmtp_mode==30, PJMEDIA_CODEC_EINMODE); /* Update enc_ptime in the param */ - if (attr->setting.enc_fmtp_mode != attr->setting.dec_fmtp_mode) { - attr->info.enc_ptime = attr->setting.enc_fmtp_mode; + if (enc_fmtp_mode != dec_fmtp_mode) { + attr->info.enc_ptime = (pj_uint16_t)enc_fmtp_mode; } else { attr->info.enc_ptime = 0; } /* Create enc */ - ilbc_codec->enc_frame_size = initEncode(&ilbc_codec->enc, - attr->setting.enc_fmtp_mode); - ilbc_codec->enc_samples_per_frame = CLOCK_RATE*attr->setting.enc_fmtp_mode/ - 1000; + ilbc_codec->enc_frame_size = initEncode(&ilbc_codec->enc, enc_fmtp_mode); + ilbc_codec->enc_samples_per_frame = CLOCK_RATE * enc_fmtp_mode / 1000; ilbc_codec->enc_ready = PJ_TRUE; /* Create decoder */ ilbc_codec->dec_samples_per_frame = initDecode(&ilbc_codec->dec, - attr->setting.dec_fmtp_mode, + dec_fmtp_mode, attr->setting.penh); - if (attr->setting.dec_fmtp_mode == 20) + if (dec_fmtp_mode == 20) ilbc_codec->dec_frame_size = 38; - else if (attr->setting.dec_fmtp_mode == 30) + else if (dec_fmtp_mode == 30) ilbc_codec->dec_frame_size = 50; else { pj_assert(!"Invalid iLBC mode"); @@ -435,7 +459,7 @@ static pj_status_t ilbc_codec_open(pjmedia_codec *codec, PJ_LOG(5,(ilbc_codec->obj_name, "iLBC codec opened, encoder mode=%d, decoder mode=%d", - attr->setting.enc_fmtp_mode, attr->setting.dec_fmtp_mode)); + enc_fmtp_mode, dec_fmtp_mode)); return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia-codec/ipp_codecs.c b/pjmedia/src/pjmedia-codec/ipp_codecs.c index 1521b7c8..50926173 100644 --- a/pjmedia/src/pjmedia-codec/ipp_codecs.c +++ b/pjmedia/src/pjmedia-codec/ipp_codecs.c @@ -29,6 +29,7 @@ #include #include + /* * Only build this file if PJMEDIA_HAS_INTEL_IPP != 0 */ @@ -210,10 +211,12 @@ static struct ipp_codec { int has_native_plc; /* Codec has internal PLC? */ predecode_cb predecode; /* Callback to translate RTP frame - into USC frame */ - parse_cb parse; /* Callback to parse bitstream */ - pack_cb pack; /* Callback to pack bitstream */ -} + into USC frame. */ + parse_cb parse; /* Callback to parse bitstream. */ + pack_cb pack; /* Callback to pack bitstream. */ + + pjmedia_codec_fmtp dec_fmtp; /* Decoder's fmtp params. */ +} ipp_codec[] = { @@ -232,14 +235,8 @@ ipp_codec[] = # endif # if PJMEDIA_HAS_INTEL_IPP_CODEC_G729 - /* G.729 actually has internal VAD, but for now we need to disable it, - * since its RTP packaging (multiple frames per packet) requires - * SID frame to only be occured in the last frame, while controling - * encoder on each loop (to enable/disable VAD) is considered inefficient. - * This should still be interoperable with other implementations. - */ {1, "G729", PJMEDIA_RTP_PT_G729, &USC_G729AFP_Fxns, 8000, 1, 80, - 8000, 11800, 2, 0, 1, + 8000, 11800, 2, 1, 1, &predecode_g729, NULL, NULL }, # endif @@ -279,9 +276,20 @@ ipp_codec[] = # endif # if PJMEDIA_HAS_INTEL_IPP_CODEC_G722_1 - {0, "G7221", PJMEDIA_RTP_PT_G722_1, &USC_G722_Fxns, 16000, 1, 320, - 16000, 32000, 1, 0, 1, - NULL, NULL, NULL + {0, "G7221", PJMEDIA_RTP_PT_G722_1_16, &USC_G722_Fxns, 16000, 1, 320, + 16000, 16000, 1, 0, 1, + NULL, NULL, NULL, + {1, {{{"bitrate", 7}, {"16000", 5}}} } + }, + {1, "G7221", PJMEDIA_RTP_PT_G722_1_24, &USC_G722_Fxns, 16000, 1, 320, + 24000, 24000, 1, 0, 1, + NULL, NULL, NULL, + {1, {{{"bitrate", 7}, {"24000", 5}}} } + }, + {1, "G7221", PJMEDIA_RTP_PT_G722_1_32, &USC_G722_Fxns, 16000, 1, 320, + 32000, 32000, 1, 0, 1, + NULL, NULL, NULL, + {1, {{{"bitrate", 7}, {"32000", 5}}} } }, # endif }; @@ -422,7 +430,8 @@ static pj_status_t ipp_default_attr (pjmedia_codec_factory *factory, pj_str_t name = pj_str((char*)ipp_codec[i].name); if ((pj_stricmp(&id->encoding_name, &name) == 0) && (id->clock_rate == (unsigned)ipp_codec[i].clock_rate) && - (id->channel_cnt == (unsigned)ipp_codec[i].channel_count)) + (id->channel_cnt == (unsigned)ipp_codec[i].channel_count) && + (id->pt == (unsigned)ipp_codec[i].pt)) { attr->info.pt = (pj_uint8_t)id->pt; attr->info.channel_cnt = ipp_codec[i].channel_count; @@ -437,10 +446,22 @@ static pj_status_t ipp_default_attr (pjmedia_codec_factory *factory, attr->setting.frm_per_pkt = ipp_codec[i].frm_per_pkt; /* Default flags. */ - attr->setting.cng = 0; attr->setting.plc = 1; attr->setting.penh= 0; - attr->setting.vad = 1; /* Always disable for now */ + attr->setting.vad = 1; + attr->setting.cng = attr->setting.vad; + attr->setting.dec_fmtp = ipp_codec[i].dec_fmtp; + + if (attr->setting.vad == 0) { +#if PJMEDIA_HAS_INTEL_IPP_CODEC_G729 + if (id->pt == PJMEDIA_RTP_PT_G729) { + /* Signal G729 Annex B is being disabled */ + attr->setting.dec_fmtp.cnt = 1; + pj_strset2(&attr->setting.dec_fmtp.param[0].name, "annexb"); + pj_strset2(&attr->setting.dec_fmtp.param[0].val, "no"); + } +#endif + } return PJ_SUCCESS; } @@ -631,10 +652,24 @@ static pj_status_t ipp_codec_open( pjmedia_codec *codec, /* Setting the encoder params */ codec_data->info->params.direction = USC_ENCODE; codec_data->info->params.modes.vad = attr->setting.vad && - ippc->has_native_vad; + ippc->has_native_vad; codec_data->info->params.modes.bitrate = attr->info.avg_bps; codec_data->info->params.law = 0; /* Linear PCM input */ +#if PJMEDIA_HAS_INTEL_IPP_CODEC_G729 + if (ippc->pt == PJMEDIA_RTP_PT_G729) { + /* Check if G729 Annex B is signaled to be disabled */ + for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) { + if (pj_stricmp2(&attr->setting.enc_fmtp.param[i].name, "annexb")==0) + { + if (pj_stricmp2(&attr->setting.enc_fmtp.param[i].val, "no")==0) + codec_data->info->params.modes.vad = 0; + break; + } + } + } +#endif + /* Get number of memory blocks needed by the encoder */ if (USC_NoError != ippc->fxns->std.NumAlloc(&codec_data->info->params, &nb_membanks)) @@ -673,6 +708,9 @@ static pj_status_t ipp_codec_open( pjmedia_codec *codec, /* Setting the decoder params */ codec_data->info->params.direction = USC_DECODE; + /* Not sure if VAD affects decoder, just try to be safe */ + codec_data->info->params.modes.vad = ippc->has_native_vad; + /* Get number of memory blocks needed by the decoder */ if (USC_NoError != ippc->fxns->std.NumAlloc(&codec_data->info->params, &nb_membanks)) @@ -925,6 +963,18 @@ static pj_status_t ipp_codec_encode( pjmedia_codec *codec, nsamples -= samples_per_frame; tx += out.nbytes; bits_out += out.nbytes; + +#if PJMEDIA_HAS_INTEL_IPP_CODEC_G729 + if (out.frametype == 1) { + /* SID */ + break; + } else if (out.frametype == 0) { + /* Untransmitted */ + tx -= out.nbytes; + break; + } +#endif + } if (ippc->pack != NULL) { diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c index cac91e62..4f3388b2 100644 --- a/pjmedia/src/pjmedia/endpoint.c +++ b/pjmedia/src/pjmedia/endpoint.c @@ -424,18 +424,55 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt, m->attr[m->attr_count++] = attr; } - /* Add fmtp mode where applicable */ - if (codec_param.setting.dec_fmtp_mode != 0) { - const pj_str_t fmtp = { "fmtp", 4 }; + /* Add fmtp params */ + if (codec_param.setting.dec_fmtp.cnt > 0) { + enum { MAX_FMTP_STR_LEN = 160 }; + char buf[MAX_FMTP_STR_LEN]; + unsigned buf_len = 0, i; + pjmedia_codec_fmtp *dec_fmtp = &codec_param.setting.dec_fmtp; + + /* Print codec PT */ + buf_len += pj_ansi_snprintf(buf, + MAX_FMTP_STR_LEN - buf_len, + "%d", + codec_info->pt); + + for (i = 0; i < dec_fmtp->cnt; ++i) { + unsigned test_len = 2; + + /* Check if buf still available */ + test_len = dec_fmtp->param[i].val.slen + + dec_fmtp->param[i].name.slen; + if (test_len + buf_len >= MAX_FMTP_STR_LEN) + return PJ_ETOOBIG; + + /* Print delimiter */ + buf_len += pj_ansi_snprintf(&buf[buf_len], + MAX_FMTP_STR_LEN - buf_len, + (i == 0?" ":";")); + + /* Print an fmtp param */ + if (dec_fmtp->param[i].name.slen) + buf_len += pj_ansi_snprintf( + &buf[buf_len], + MAX_FMTP_STR_LEN - buf_len, + "%.*s=%.*s", + (int)dec_fmtp->param[i].name.slen, + dec_fmtp->param[i].name.ptr, + (int)dec_fmtp->param[i].val.slen, + dec_fmtp->param[i].val.ptr); + else + buf_len += pj_ansi_snprintf(&buf[buf_len], + MAX_FMTP_STR_LEN - buf_len, + "%.*s", + (int)dec_fmtp->param[i].val.slen, + dec_fmtp->param[i].val.ptr); + } + attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); - attr->name = fmtp; - attr->value.ptr = (char*) pj_pool_alloc(pool, 32); - attr->value.slen = - pj_ansi_snprintf( attr->value.ptr, 32, - "%d mode=%d", - codec_info->pt, - codec_param.setting.dec_fmtp_mode); + attr->name = pj_str("fmtp"); + attr->value = pj_strdup3(pool, buf); m->attr[m->attr_count++] = attr; } } diff --git a/pjmedia/src/pjmedia/sdp_neg.c b/pjmedia/src/pjmedia/sdp_neg.c index 6753e943..ef7815d4 100644 --- a/pjmedia/src/pjmedia/sdp_neg.c +++ b/pjmedia/src/pjmedia/sdp_neg.c @@ -533,6 +533,51 @@ static void update_media_direction(pj_pool_t *pool, } } +/* Matching G722.1 bitrates between offer and answer. + */ +static pj_bool_t match_g7221( const pjmedia_sdp_media *offer, + unsigned o_fmt_idx, + const pjmedia_sdp_media *answer, + unsigned a_fmt_idx) +{ + const pjmedia_sdp_attr *a_ans; + const pjmedia_sdp_attr *a_off; + pjmedia_sdp_fmtp fmtp; + unsigned a_bitrate = 0, o_bitrate = 0; + const pj_str_t bitrate = {"bitrate=", 8}; + const char *p; + + a_ans = pjmedia_sdp_media_find_attr2(answer, "fmtp", + &answer->desc.fmt[a_fmt_idx]); + if (!a_ans) + return PJ_FALSE; + + if (pjmedia_sdp_attr_get_fmtp(a_ans, &fmtp) != PJ_SUCCESS) + return PJ_FALSE; + + p = pj_stristr(&fmtp.fmt_param, &bitrate); + if (p == NULL) + return PJ_FALSE; + + a_bitrate = atoi(p + bitrate.slen); + + a_off = pjmedia_sdp_media_find_attr2(offer, "fmtp", + &offer->desc.fmt[o_fmt_idx]); + if (!a_off) + return PJ_FALSE; + + if (pjmedia_sdp_attr_get_fmtp(a_off, &fmtp) != PJ_SUCCESS) + return PJ_FALSE; + + p = pj_stristr(&fmtp.fmt_param, &bitrate); + if (p == NULL) + return PJ_FALSE; + + o_bitrate = atoi(p + bitrate.slen); + + return (a_bitrate == o_bitrate); +} + /* Update single local media description to after receiving answer * from remote. */ @@ -657,8 +702,14 @@ static pj_status_t process_m_answer( pj_pool_t *pool, (pj_stricmp(&or_.param, &ar.param)==0 || (ar.param.slen==1 && *ar.param.ptr=='1'))) { - /* Match! */ - break; + /* Further check for G7221, negotiate bitrate. */ + if (pj_strcmp2(&or_.enc_name, "G7221") == 0) { + if (match_g7221(offer, i, answer, j)) + break; + } else { + /* Match! */ + break; + } } } } @@ -871,10 +922,18 @@ static pj_status_t match_offer(pj_pool_t *pool, (or_.param.slen==1 && *or_.param.ptr=='1'))) { /* Match! */ - if (is_codec) + if (is_codec) { + /* Further check for G7221, negotiate bitrate. */ + if (pj_strcmp2(&or_.enc_name, "G7221") == 0 && + match_g7221(offer, i, preanswer, j) == 0) + { + continue; + } found_matching_codec = 1; - else + } else { found_matching_telephone_event = 1; + } + pt_answer[pt_answer_count++] = preanswer->desc.fmt[j]; break; } diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c index 2b2a3fbd..3ca91078 100644 --- a/pjmedia/src/pjmedia/session.c +++ b/pjmedia/src/pjmedia/session.c @@ -67,51 +67,76 @@ static const pj_str_t STR_RECVONLY = { "recvonly", 8 }; /* - * Get fmtp mode parameter associated with the codec. + * Parse fmtp for specified format/payload type. */ -static pj_status_t get_fmtp_mode(const pjmedia_sdp_media *m, - const pj_str_t *fmt, - int *p_mode) +static void parse_fmtp( pj_pool_t *pool, + const pjmedia_sdp_media *m, + unsigned pt, + pjmedia_codec_fmtp *fmtp) { const pjmedia_sdp_attr *attr; - pjmedia_sdp_fmtp fmtp; - const pj_str_t str_mode = { "mode=", 5 }; - char *pos; + pjmedia_sdp_fmtp sdp_fmtp; + char *p, *p_end, fmt_buf[8]; + pj_str_t fmt; + + pj_assert(m && fmtp); + + pj_bzero(fmtp, sizeof(pjmedia_codec_fmtp)); /* Get "fmtp" attribute for the format */ - attr = pjmedia_sdp_media_find_attr2(m, "fmtp", fmt); + pj_ansi_sprintf(fmt_buf, "%d", pt); + fmt = pj_str(fmt_buf); + attr = pjmedia_sdp_media_find_attr2(m, "fmtp", &fmt); if (attr == NULL) - return -1; + return; /* Parse "fmtp" attribute */ - if (pjmedia_sdp_attr_get_fmtp(attr, &fmtp) != PJ_SUCCESS) - return -1; + if (pjmedia_sdp_attr_get_fmtp(attr, &sdp_fmtp) != PJ_SUCCESS) + return; - /* Look for "mode=" string in the fmtp */ - while (fmtp.fmt_param.slen >= str_mode.slen + 1) { - if (pj_strnicmp(&fmtp.fmt_param, &str_mode, str_mode.slen)==0) { - /* Found "mode=" string */ - break; - } + /* Prepare parsing */ + p = sdp_fmtp.fmt_param.ptr; + p_end = p + sdp_fmtp.fmt_param.slen; - fmtp.fmt_param.ptr++; - fmtp.fmt_param.slen--; - } + /* Parse */ + while (p < p_end) { + char *token, *start, *end; - if (fmtp.fmt_param.slen < str_mode.slen + 1) { - /* "mode=" param not found */ - return -1; - } + /* Skip whitespaces */ + while (p < p_end && (*p == ' ' || *p == '\t')) ++p; + if (p == p_end) + break; - /* Get the mode */ - pos = fmtp.fmt_param.ptr + str_mode.slen; - *p_mode = 0; - while (pj_isdigit(*pos)) { - *p_mode = *p_mode * 10 + (*pos - '0'); - ++pos; - } + /* Get token */ + start = p; + while (p < p_end && *p != ';' && *p != '=') ++p; + end = p - 1; + + /* Right trim */ + while (end >= start && (*end == ' ' || *end == '\t' || + *end == '\r' || *end == '\n' )) + --end; + + /* Forward a char after trimming */ + ++end; + + /* Store token */ + if (end > start) { + token = (char*)pj_pool_alloc(pool, end - start); + pj_ansi_strncpy(token, start, end - start); + if (*p == '=') + /* Got param name */ + pj_strset(&fmtp->param[fmtp->cnt].name, token, end - start); + else + /* Got param value */ + pj_strset(&fmtp->param[fmtp->cnt++].val, token, end - start); + } else if (*p != '=') { + ++fmtp->cnt; + } - return PJ_SUCCESS; + /* Next */ + ++p; + } } @@ -135,7 +160,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( int rem_af, local_af; pj_sockaddr local_addr; pjmedia_sdp_rtpmap *rtpmap; - int local_fmtp_mode = 0, rem_fmtp_mode = 0; unsigned i, pt, fmti; pj_status_t status; @@ -400,7 +424,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( #if defined(PJMEDIA_HANDLE_G722_MPEG_BUG) && (PJMEDIA_HANDLE_G722_MPEG_BUG != 0) /* The session info should have the actual clock rate, because - * this info is used for calculationg buffer size, etc in stream */ + * this info is used for calculationg buffer size, etc in stream + */ if (si->fmt.pt == PJMEDIA_RTP_PT_G722) si->fmt.clock_rate = 16000; #endif @@ -463,9 +488,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( si->fmt.channel_cnt = 1; } - /* Get fmtp mode= param in local SDP, if any */ - get_fmtp_mode(local_m, &local_m->desc.fmt[fmti], &local_fmtp_mode); - /* Determine payload type for outgoing channel, by finding * dynamic payload type in remote SDP that matches the answer. */ @@ -493,9 +515,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( /* Found matched codec. */ si->tx_pt = rpt; - /* Get fmtp mode param in remote SDP, if any */ - get_fmtp_mode(rem_m, &rtpmap->pt, &rem_fmtp_mode); - break; } } @@ -509,6 +528,22 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( si->param = PJ_POOL_ALLOC_T(pool, pjmedia_codec_param); status = pjmedia_codec_mgr_get_default_param(mgr, &si->fmt, si->param); + /* Get remote fmtp for our encoder. */ + parse_fmtp(pool, rem_m, si->tx_pt, &si->param->setting.enc_fmtp); + + /* Get local fmtp for our decoder. */ + parse_fmtp(pool, local_m, si->fmt.pt, &si->param->setting.dec_fmtp); + + /* Get remote maxptime for our encoder. */ + attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr, + "maxptime", NULL); + if (attr) { + pj_str_t tmp_val = attr->value; + + pj_strltrim(&tmp_val); + si->tx_maxptime = pj_strtoul(&tmp_val); + } + /* When direction is NONE (it means SDP negotiation has failed) we don't * need to return a failure here, as returning failure will cause * the whole SDP to be rejected. See ticket #: @@ -519,12 +554,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp( if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE) return status; - /* Set fmtp mode for both local and remote */ - if (local_fmtp_mode != 0) - si->param->setting.dec_fmtp_mode = (pj_int8_t)local_fmtp_mode; - if (rem_fmtp_mode != 0) - si->param->setting.enc_fmtp_mode = (pj_int8_t)rem_fmtp_mode; - /* Get incomming payload type for telephone-events */ si->rx_event_pt = -1; diff --git a/pjsip-apps/src/test-pjsua/scripts-sendto/400_fmtp_g7221_with_bitrate.py b/pjsip-apps/src/test-pjsua/scripts-sendto/400_fmtp_g7221_with_bitrate.py new file mode 100644 index 00000000..f21b4e7c --- /dev/null +++ b/pjsip-apps/src/test-pjsua/scripts-sendto/400_fmtp_g7221_with_bitrate.py @@ -0,0 +1,34 @@ +# $Id $ +import inc_sip as sip +import inc_sdp as sdp + +# Answer for codec G722.1 should contain fmtp bitrate + +sdp = \ +""" +v=0 +o=- 3428650655 3428650655 IN IP4 192.168.1.9 +s=pjmedia +c=IN IP4 192.168.1.9 +t=0 0 +a=X-nat:0 +m=audio 4000 RTP/AVP 99 100 101 +a=rtcp:4001 IN IP4 192.168.1.9 +a=rtpmap:99 G7221/16000 +a=fmtp:99 bitrate=24000 +a=rtpmap:100 G7221/16000 +a=fmtp:100 bitrate=32000 +a=sendrecv +a=rtpmap:101 telephone-event/8000 +a=fmtp:101 0-15 +""" + +pjsua_args = "--null-audio --auto-answer 200 --add-codec G7221" +extra_headers = "" +include = ["fmtp:[\d]+ bitrate="] # response must include fmtp bitrate +exclude = [] + +sendto_cfg = sip.SendtoCfg("Answer should contain fmtp bitrate for codec G722.1", pjsua_args, sdp, 200, + extra_headers=extra_headers, + resp_inc=include, resp_exc=exclude) + diff --git a/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_24000.py b/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_24000.py new file mode 100644 index 00000000..53064ff1 --- /dev/null +++ b/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_24000.py @@ -0,0 +1,35 @@ +# $Id $ +import inc_sip as sip +import inc_sdp as sdp + +# Answer with codec G722.1 should choose the same bitrate +# which in this test is 24000 + +sdp = \ +""" +v=0 +o=- 3428650655 3428650655 IN IP4 192.168.1.9 +s=pjmedia +c=IN IP4 192.168.1.9 +t=0 0 +a=X-nat:0 +m=audio 4000 RTP/AVP 100 101 +a=rtcp:4001 IN IP4 192.168.1.9 +a=rtpmap:100 G7221/16000 +a=fmtp:100 bitrate=24000 +a=sendrecv +a=rtpmap:101 telephone-event/8000 +a=fmtp:101 0-15 +""" + +pjsua_args = "--null-audio --auto-answer 200 --add-codec G7221" +extra_headers = "" +include = ["a=rtpmap:[\d]+ G7221/16000", # response must choose G722.1 + "fmtp:[\d]+ bitrate=24000" # response must choose the same bitrate + ] +exclude = [] + +sendto_cfg = sip.SendtoCfg("Answer with G722.1 should choose bitrate 24000", pjsua_args, sdp, 200, + extra_headers=extra_headers, + resp_inc=include, resp_exc=exclude) + diff --git a/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_32000.py b/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_32000.py new file mode 100644 index 00000000..d60a4a44 --- /dev/null +++ b/pjsip-apps/src/test-pjsua/scripts-sendto/401_fmtp_g7221_with_bitrate_32000.py @@ -0,0 +1,35 @@ +# $Id $ +import inc_sip as sip +import inc_sdp as sdp + +# Answer with codec G722.1 should choose the same bitrate +# which in this test is 32000 + +sdp = \ +""" +v=0 +o=- 3428650655 3428650655 IN IP4 192.168.1.9 +s=pjmedia +c=IN IP4 192.168.1.9 +t=0 0 +a=X-nat:0 +m=audio 4000 RTP/AVP 100 101 +a=rtcp:4001 IN IP4 192.168.1.9 +a=rtpmap:100 G7221/16000 +a=fmtp:100 bitrate=32000 +a=sendrecv +a=rtpmap:101 telephone-event/8000 +a=fmtp:101 0-15 +""" + +pjsua_args = "--null-audio --auto-answer 200 --add-codec G7221" +extra_headers = "" +include = ["a=rtpmap:[\d]+ G7221/16000", # response must choose G722.1 + "fmtp:[\d]+ bitrate=32000" # response must choose the same bitrate + ] +exclude = [] + +sendto_cfg = sip.SendtoCfg("Answer with G722.1 should choose bitrate 32000", pjsua_args, sdp, 200, + extra_headers=extra_headers, + resp_inc=include, resp_exc=exclude) + -- cgit v1.2.3