From: Joshua Colp Date: Fri, 13 Jul 2012 16:49:40 +0000 (+0000) Subject: Add support for parsing SDP attributes, generating SDP attributes, and passing it... X-Git-Tag: 11.0.0-beta1~120 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a693fd1d8738a302082bb8e453c0fe69679f4c46;p=thirdparty%2Fasterisk.git Add support for parsing SDP attributes, generating SDP attributes, and passing it through. This support includes codecs such as H.263, H.264, SILK, and CELT. You are able to set up a call and have attribute information pass. This should help considerably with video calls. Review: https://reviewboard.asterisk.org/r/2005/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@370055 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- diff --git a/channels/chan_sip.c b/channels/chan_sip.c index b28c0eed92..e006f06f98 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -10175,7 +10175,7 @@ static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_ int found = FALSE; int codec; char mimeSubtype[128]; - char fmtp_string[64]; + char fmtp_string[256]; unsigned int sample_rate; int debug = sip_debug_test_pvt(p); @@ -10222,12 +10222,17 @@ static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_ if (debug) ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec); } - } else if (sscanf(a, "fmtp: %30u %63s", &codec, fmtp_string) == 2) { + } else if (sscanf(a, "fmtp: %30u %255s", &codec, fmtp_string) == 2) { struct ast_format *format; if ((format = ast_rtp_codecs_get_payload_format(newaudiortp, codec))) { unsigned int bit_rate; - int val = 0; + + if (!ast_format_sdp_parse(format, fmtp_string)) { + found = TRUE; + } else { + ast_rtp_codecs_payloads_unset(newaudiortp, NULL, codec); + } switch ((int) format->id) { case AST_FORMAT_SIREN7: @@ -10260,21 +10265,6 @@ static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_ } } break; - case AST_FORMAT_CELT: - if (sscanf(fmtp_string, "framesize=%30u", &val) == 1) { - ast_format_append(format, CELT_ATTR_KEY_FRAME_SIZE, val, AST_FORMAT_ATTR_END); - } - case AST_FORMAT_SILK: - if (sscanf(fmtp_string, "maxaveragebitrate=%30u", &val) == 1) { - ast_format_append(format, SILK_ATTR_KEY_MAX_BITRATE, val, AST_FORMAT_ATTR_END); - } - if (sscanf(fmtp_string, "usedtx=%30u", &val) == 1) { - ast_format_append(format, SILK_ATTR_KEY_DTX, val ? 1 : 0, AST_FORMAT_ATTR_END); - } - if (sscanf(fmtp_string, "useinbandfec=%30u", &val) == 1) { - ast_format_append(format, SILK_ATTR_KEY_FEC, val ? 1 : 0, AST_FORMAT_ATTR_END); - } - break; } } } @@ -10289,6 +10279,7 @@ static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp_ char mimeSubtype[128]; unsigned int sample_rate; int debug = sip_debug_test_pvt(p); + char fmtp_string[256]; if (sscanf(a, "rtpmap: %30u %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) { /* We have a rtpmap to handle */ @@ -10311,6 +10302,16 @@ static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp_ if (debug) ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec); } + } else if (sscanf(a, "fmtp: %30u %255s", &codec, fmtp_string) == 2) { + struct ast_format *format; + + if ((format = ast_rtp_codecs_get_payload_format(newvideortp, codec))) { + if (!ast_format_sdp_parse(format, fmtp_string)) { + found = TRUE; + } else { + ast_rtp_codecs_payloads_unset(newvideortp, NULL, codec); + } + } } return found; @@ -11747,7 +11748,6 @@ static void add_codec_to_sdp(const struct sip_pvt *p, { int rtp_code; struct ast_format_list fmt; - int val = 0; if (debug) ast_verbose("Adding codec %d (%s) to SDP\n", format->id, ast_getformatname(format)); @@ -11765,6 +11765,8 @@ static void add_codec_to_sdp(const struct sip_pvt *p, ast_rtp_lookup_mime_subtype2(1, format, 0, ast_test_flag(&p->flags[0], SIP_G726_NONSTANDARD) ? AST_RTP_OPT_G726_NONSTANDARD : 0), ast_rtp_lookup_sample_rate2(1, format, 0)); + ast_format_sdp_generate(format, rtp_code, a_buf); + switch ((int) format->id) { case AST_FORMAT_G729A: /* Indicate that we don't support VAD (G.729 annex B) */ @@ -11790,22 +11792,6 @@ static void add_codec_to_sdp(const struct sip_pvt *p, /* Indicate that we only expect 64Kbps */ ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=64000\r\n", rtp_code); break; - case AST_FORMAT_CELT: - if (!ast_format_get_value(format, CELT_ATTR_KEY_FRAME_SIZE, &val) && val > 0) { - ast_str_append(a_buf, 0, "a=fmtp:%d framesize=%u\r\n", rtp_code, val); - } - break; - case AST_FORMAT_SILK: - if (!ast_format_get_value(format, SILK_ATTR_KEY_MAX_BITRATE, &val) && val > 5000 && val < 40000) { - ast_str_append(a_buf, 0, "a=fmtp:%d maxaveragebitrate=%u\r\n", rtp_code, val); - } - if (!ast_format_get_value(format, SILK_ATTR_KEY_DTX, &val)) { - ast_str_append(a_buf, 0, "a=fmtp:%d usedtx=%u\r\n", rtp_code, val ? 1 : 0); - } - if (!ast_format_get_value(format, SILK_ATTR_KEY_FEC, &val)) { - ast_str_append(a_buf, 0, "a=fmtp:%d useinbandfec=%u\r\n", rtp_code, val ? 1 : 0); - } - break; } if (fmt.cur_ms && (fmt.cur_ms < *min_packet_size)) @@ -11837,7 +11823,8 @@ static void add_vcodec_to_sdp(const struct sip_pvt *p, struct ast_format *format ast_str_append(a_buf, 0, "a=rtpmap:%d %s/%d\r\n", rtp_code, ast_rtp_lookup_mime_subtype2(1, format, 0, 0), ast_rtp_lookup_sample_rate2(1, format, 0)); - /* Add fmtp code here */ + + ast_format_sdp_generate(format, rtp_code, a_buf); } /*! \brief Add text codec offer to SDP offer/answer body in INVITE or 200 OK */ @@ -12262,10 +12249,12 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int /* Start by sending our preferred audio/video codecs */ for (x = 0; x < AST_CODEC_PREF_SIZE; x++) { - if (!(ast_codec_pref_index(&p->prefs, x, &tmp_fmt))) + struct ast_format pref; + + if (!(ast_codec_pref_index(&p->prefs, x, &pref))) break; - if (!(ast_format_cap_iscompatible(tmpcap, &tmp_fmt))) + if (!ast_format_cap_get_compatible_format(tmpcap, &pref, &tmp_fmt)) continue; if (ast_format_cap_iscompatible(alreadysent, &tmp_fmt)) diff --git a/include/asterisk/format.h b/include/asterisk/format.h index d0f1021d5f..9dd6eb8b2d 100644 --- a/include/asterisk/format.h +++ b/include/asterisk/format.h @@ -131,7 +131,7 @@ enum ast_format_id { /*! \brief This structure contains the buffer used for format attributes */ struct ast_format_attr { /*! The buffer formats can use to represent attributes */ - uint8_t format_attr[AST_FORMAT_ATTR_SIZE]; + uint32_t format_attr[AST_FORMAT_ATTR_SIZE]; /*! If a format's payload needs to pass through that a new marker is required * for RTP, this variable will be set. */ uint8_t rtp_marker_bit; @@ -219,8 +219,46 @@ struct ast_format_attr_interface { * \retval -1 failure, Value was either not found, or not allowed to be accessed. */ int (* const format_attr_get_val)(const struct ast_format_attr *format_attr, int key, void *val); + + /* + * \brief Parse SDP attribute information, interpret it, and store it in ast_format_attr structure. + * + * \retval 0 Success, values were valid + * \retval -1 Failure, some values were not acceptable + */ + int (* const format_attr_sdp_parse)(struct ast_format_attr *format_attr, const char *attributes); + + /*! + * \brief Generate SDP attribute information from an ast_format_attr structure. + * + * \note This callback should generate a full fmtp line using the provided payload number. + */ + void (* const format_attr_sdp_generate)(const struct ast_format_attr *format_attr, unsigned int payload, struct ast_str **str); }; +/*! + * \brief This function is used to have a media format aware module parse and interpret + * SDP attribute information. Once interpreted this information is stored on the format + * itself using Asterisk format attributes. + * + * \param format to set + * \param attributes string containing the fmtp line from the SDP + * + * \retval 0 success, attribute values were valid + * \retval -1 failure, values were not acceptable + */ +int ast_format_sdp_parse(struct ast_format *format, const char *attributes); + +/*! + * \brief This function is used to produce an fmtp SDP line for an Asterisk format. The + * attributes present on the Asterisk format are translated into the SDP equivalent. + * + * \param format to generate an fmtp line for + * \param payload numerical payload for the fmtp line + * \param str structure that the fmtp line will be appended to + */ +void ast_format_sdp_generate(const struct ast_format *format, unsigned int payload, struct ast_str **str); + /*! * \brief This function is used to set an ast_format object to represent a media format * with optional format attributes represented by format specific key value pairs. diff --git a/main/format.c b/main/format.c index 96afd3e184..2d37eb458b 100644 --- a/main/format.c +++ b/main/format.c @@ -119,6 +119,51 @@ static int has_interface(const struct ast_format *format) return 1; } +int ast_format_sdp_parse(struct ast_format *format, const char *attributes) +{ + struct interface_ao2_wrapper *wrapper; + int res; + + if (!(wrapper = find_interface(format))) { + return 0; + } + + ao2_rdlock(wrapper); + if (!(wrapper->interface || !wrapper->interface->format_attr_sdp_parse)) { + ao2_unlock(wrapper); + ao2_ref(wrapper, -1); + return 0; + } + + res = wrapper->interface->format_attr_sdp_parse(&format->fattr, attributes); + + ao2_unlock(wrapper); + ao2_ref(wrapper, -1); + + return res; +} + +void ast_format_sdp_generate(const struct ast_format *format, unsigned int payload, struct ast_str **str) +{ + struct interface_ao2_wrapper *wrapper; + + if (!(wrapper = find_interface(format))) { + return; + } + + ao2_rdlock(wrapper); + if (!(wrapper->interface || !wrapper->interface->format_attr_sdp_generate)) { + ao2_unlock(wrapper); + ao2_ref(wrapper, -1); + return; + } + + wrapper->interface->format_attr_sdp_generate(&format->fattr, payload, str); + + ao2_unlock(wrapper); + ao2_ref(wrapper, -1); +} + /*! \internal * \brief set format attributes using an interface */ diff --git a/main/translate.c b/main/translate.c index 607fee77b4..ce430ce977 100644 --- a/main/translate.c +++ b/main/translate.c @@ -1286,12 +1286,22 @@ unsigned int ast_translate_path_steps(struct ast_format *dst_format, struct ast_ void ast_translate_available_formats(struct ast_format_cap *dest, struct ast_format_cap *src, struct ast_format_cap *result) { struct ast_format tmp_fmt; - struct ast_format cur_src; + struct ast_format cur_dest, cur_src; int src_audio = 0; int src_video = 0; int index; - ast_format_cap_copy(result, dest); + ast_format_cap_iter_start(dest); + while (!ast_format_cap_iter_next(dest, &cur_dest)) { + /* We give preference to a joint format structure if possible */ + if (ast_format_cap_get_compatible_format(src, &cur_dest, &tmp_fmt)) { + ast_format_cap_add(result, &tmp_fmt); + } else { + /* Otherwise we just use the destination format */ + ast_format_cap_add(result, &cur_dest); + } + } + ast_format_cap_iter_end(dest); /* if we don't have a source format, we just have to try all possible destination formats */ diff --git a/res/res_format_attr_celt.c b/res/res_format_attr_celt.c index bd38b29e2a..aea3eb482e 100644 --- a/res/res_format_attr_celt.c +++ b/res/res_format_attr_celt.c @@ -45,6 +45,29 @@ struct celt_attr { unsigned int framesize; }; +static int celt_sdp_parse(struct ast_format_attr *format_attr, const char *attributes) +{ + struct celt_attr *attr = (struct celt_attr *) format_attr; + unsigned int val; + + if (sscanf(attributes, "framesize=%30u", &val) == 1) { + attr->framesize = val; + } + + return 0; +} + +static void celt_sdp_generate(const struct ast_format_attr *format_attr, unsigned int payload, struct ast_str **str) +{ + struct celt_attr *attr = (struct celt_attr *) format_attr; + + if (!attr->framesize) { + return; + } + + ast_str_append(str, 0, "a=fmtp:%d framesize=%d\r\n", payload, attr->framesize); +} + static enum ast_format_cmp_res celt_cmp(const struct ast_format_attr *fattr1, const struct ast_format_attr *fattr2) { struct celt_attr *attr1 = (struct celt_attr *) fattr1; @@ -161,6 +184,8 @@ static struct ast_format_attr_interface celt_interface = { .format_attr_set = celt_set, .format_attr_isset = celt_isset, .format_attr_get_val = celt_get_val, + .format_attr_sdp_parse = celt_sdp_parse, + .format_attr_sdp_generate = celt_sdp_generate, }; static int load_module(void) diff --git a/res/res_format_attr_silk.c b/res/res_format_attr_silk.c index 4f9bae7391..7d4fa5bf84 100644 --- a/res/res_format_attr_silk.c +++ b/res/res_format_attr_silk.c @@ -47,6 +47,36 @@ struct silk_attr { unsigned int packetloss_percentage; }; +static int silk_sdp_parse(struct ast_format_attr *format_attr, const char *attributes) +{ + struct silk_attr *attr = (struct silk_attr *) format_attr; + unsigned int val; + + if (sscanf(attributes, "maxaveragebitrate=%30u", &val) == 1) { + attr->maxbitrate = val; + } + if (sscanf(attributes, "usedtx=%30u", &val) == 1) { + attr->dtx = val; + } + if (sscanf(attributes, "useinbandfec=%30u", &val) == 1) { + attr->fec = val; + } + + return 0; +} + +static void silk_sdp_generate(const struct ast_format_attr *format_attr, unsigned int payload, struct ast_str **str) +{ + struct silk_attr *attr = (struct silk_attr *) format_attr; + + if ((attr->maxbitrate > 5000) && (attr->maxbitrate < 40000)) { + ast_str_append(str, 0, "a=fmtp:%d maxaveragebitrate=%d\r\n", payload, attr->maxbitrate); + } + + ast_str_append(str, 0, "a=fmtp:%d usedtx=%d\r\n", payload, attr->dtx); + ast_str_append(str, 0, "a=fmtp:%d useinbandfec=%d\r\n", payload, attr->fec); +} + static enum ast_format_cmp_res silk_cmp(const struct ast_format_attr *fattr1, const struct ast_format_attr *fattr2) { struct silk_attr *attr1 = (struct silk_attr *) fattr1; @@ -195,6 +225,8 @@ static struct ast_format_attr_interface silk_interface = { .format_attr_set = silk_set, .format_attr_isset = silk_isset, .format_attr_get_val = silk_get_val, + .format_attr_sdp_parse = silk_sdp_parse, + .format_attr_sdp_generate = silk_sdp_generate, }; static int load_module(void)