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
This commit is contained in:
Joshua Colp
2012-07-13 16:49:40 +00:00
parent 2603707f30
commit a693fd1d87
6 changed files with 180 additions and 41 deletions

View File

@@ -10175,7 +10175,7 @@ static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_
int found = FALSE; int found = FALSE;
int codec; int codec;
char mimeSubtype[128]; char mimeSubtype[128];
char fmtp_string[64]; char fmtp_string[256];
unsigned int sample_rate; unsigned int sample_rate;
int debug = sip_debug_test_pvt(p); 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) if (debug)
ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec); 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; struct ast_format *format;
if ((format = ast_rtp_codecs_get_payload_format(newaudiortp, codec))) { if ((format = ast_rtp_codecs_get_payload_format(newaudiortp, codec))) {
unsigned int bit_rate; 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) { switch ((int) format->id) {
case AST_FORMAT_SIREN7: 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; 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]; char mimeSubtype[128];
unsigned int sample_rate; unsigned int sample_rate;
int debug = sip_debug_test_pvt(p); int debug = sip_debug_test_pvt(p);
char fmtp_string[256];
if (sscanf(a, "rtpmap: %30u %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) { if (sscanf(a, "rtpmap: %30u %127[^/]/%30u", &codec, mimeSubtype, &sample_rate) == 3) {
/* We have a rtpmap to handle */ /* 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) if (debug)
ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec); 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; return found;
@@ -11747,7 +11748,6 @@ static void add_codec_to_sdp(const struct sip_pvt *p,
{ {
int rtp_code; int rtp_code;
struct ast_format_list fmt; struct ast_format_list fmt;
int val = 0;
if (debug) if (debug)
ast_verbose("Adding codec %d (%s) to SDP\n", format->id, ast_getformatname(format)); 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_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_rtp_lookup_sample_rate2(1, format, 0));
ast_format_sdp_generate(format, rtp_code, a_buf);
switch ((int) format->id) { switch ((int) format->id) {
case AST_FORMAT_G729A: case AST_FORMAT_G729A:
/* Indicate that we don't support VAD (G.729 annex B) */ /* 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 */ /* Indicate that we only expect 64Kbps */
ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=64000\r\n", rtp_code); ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=64000\r\n", rtp_code);
break; 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)) 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_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_mime_subtype2(1, format, 0, 0),
ast_rtp_lookup_sample_rate2(1, format, 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 */ /*! \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 */ /* Start by sending our preferred audio/video codecs */
for (x = 0; x < AST_CODEC_PREF_SIZE; x++) { 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; break;
if (!(ast_format_cap_iscompatible(tmpcap, &tmp_fmt))) if (!ast_format_cap_get_compatible_format(tmpcap, &pref, &tmp_fmt))
continue; continue;
if (ast_format_cap_iscompatible(alreadysent, &tmp_fmt)) if (ast_format_cap_iscompatible(alreadysent, &tmp_fmt))

View File

@@ -131,7 +131,7 @@ enum ast_format_id {
/*! \brief This structure contains the buffer used for format attributes */ /*! \brief This structure contains the buffer used for format attributes */
struct ast_format_attr { struct ast_format_attr {
/*! The buffer formats can use to represent attributes */ /*! 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 /*! If a format's payload needs to pass through that a new marker is required
* for RTP, this variable will be set. */ * for RTP, this variable will be set. */
uint8_t rtp_marker_bit; 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. * \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); 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 * \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. * with optional format attributes represented by format specific key value pairs.

View File

@@ -119,6 +119,51 @@ static int has_interface(const struct ast_format *format)
return 1; 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 /*! \internal
* \brief set format attributes using an interface * \brief set format attributes using an interface
*/ */

View File

@@ -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) 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 tmp_fmt;
struct ast_format cur_src; struct ast_format cur_dest, cur_src;
int src_audio = 0; int src_audio = 0;
int src_video = 0; int src_video = 0;
int index; 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 /* if we don't have a source format, we just have to try all
possible destination formats */ possible destination formats */

View File

@@ -45,6 +45,29 @@ struct celt_attr {
unsigned int framesize; 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) 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; 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_set = celt_set,
.format_attr_isset = celt_isset, .format_attr_isset = celt_isset,
.format_attr_get_val = celt_get_val, .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) static int load_module(void)

View File

@@ -47,6 +47,36 @@ struct silk_attr {
unsigned int packetloss_percentage; 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) 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; 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_set = silk_set,
.format_attr_isset = silk_isset, .format_attr_isset = silk_isset,
.format_attr_get_val = silk_get_val, .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) static int load_module(void)