summaryrefslogtreecommitdiff
path: root/main/sdp.c
diff options
context:
space:
mode:
authorMark Michelson <mmichelson@digium.com>2017-03-21 15:44:44 -0500
committerRichard Mudgett <rmudgett@digium.com>2017-04-25 13:03:33 -0500
commit32b3e36c683da0cea37a01c006037ff31f8a2b1d (patch)
tree24942142d9e821f99645c174ff626b49a0178f46 /main/sdp.c
parent59203c51cc6a9676ef1ab42aebe070a55f55ead2 (diff)
SDP: Ensure SDPs "merge" properly.
The gist of this work ensures that when a remote SDP is received, it is merged properly with the local capabilities. The remote SDP is converted into a stream topology. That topology is then merged with the current local topology on the SDP state. That new merged topology is then used to create an SDP. Finally, adjustments are made to RTP instances based on knowledge gained from the remote SDP. There are also a battery of tests in this commit that ensure that some basic SDP merges work as expected. While this may not sound like a big change, it has the property that it caused lots of ancillary changes. * The remote SDP is no longer stored on the SDP state. Biggest reason: there's no need for it. The remote SDP is used at the time it is being set and nowhere else. * Some new SDP APIs were added in order to find attributes and convert generic SDP attributes into rtpmap structures. * Writing tests made me realize that retrieving a value from an SDP options structure, the SDP options needs to be made const. * The SDP state machine was essentially gutted by a previous commit. Initially, I attempted to reinstate it, but I found that as it had been defined, it was not all that useful. What was more useful was knowing the role we play in SDP negotiation, so the SDP state machine has been transformed into an indicator of role. * Rather than storing separate local and joint stream state capabilities, it makes more sense to keep track of current stream state and update it as things change. Change-Id: I5938c2be3c6f0a003aa88a39a59e0880f8b2df3d
Diffstat (limited to 'main/sdp.c')
-rw-r--r--main/sdp.c365
1 files changed, 216 insertions, 149 deletions
diff --git a/main/sdp.c b/main/sdp.c
index 1ef6400b9..75a9da94d 100644
--- a/main/sdp.c
+++ b/main/sdp.c
@@ -500,201 +500,268 @@ int ast_sdp_m_add_format(struct ast_sdp_m_line *m_line, const struct ast_sdp_opt
return 0;
}
-int ast_sdp_add_m_from_rtp_stream(struct ast_sdp *sdp, const struct ast_sdp_state *sdp_state,
- const struct ast_sdp_options *options, int stream_index)
+static struct ast_sdp_a_line *sdp_find_attribute_common(const struct ast_sdp_a_lines *a_lines,
+ const char *attr_name, int payload)
{
- struct ast_stream *stream = ast_stream_topology_get_stream(ast_sdp_state_get_local_topology(sdp_state), stream_index);
- struct ast_sdp_m_line *m_line;
- struct ast_format_cap *caps;
- int i;
- int rtp_code;
- int min_packet_size = 0;
- int max_packet_size = 0;
- enum ast_media_type media_type;
- char tmp[64];
- struct ast_sockaddr address_rtp;
- struct ast_rtp_instance *rtp = ast_sdp_state_get_rtp_instance(sdp_state, stream_index);
struct ast_sdp_a_line *a_line;
+ int i;
- ast_assert(sdp && options && stream);
+ for (i = 0; i < AST_VECTOR_SIZE(a_lines); ++i) {
+ int a_line_payload;
- media_type = ast_stream_get_type(stream);
- if (ast_sdp_state_get_stream_connection_address(sdp_state, 0, &address_rtp)) {
- return -1;
- }
+ a_line = AST_VECTOR_GET(a_lines, i);
+ if (strcmp(a_line->name, attr_name)) {
+ continue;
+ }
- m_line = ast_sdp_m_alloc(
- ast_codec_media_type2str(ast_stream_get_type(stream)),
- ast_sockaddr_port(&address_rtp), 1,
- options->encryption != AST_SDP_ENCRYPTION_DISABLED ? "RTP/SAVP" : "RTP/AVP",
- NULL);
- if (!m_line) {
- return -1;
+ if (payload >= 0) {
+ int sscanf_res;
+ sscanf_res = sscanf(a_line->value, "%30d", &a_line_payload);
+ if (sscanf_res == 1 && payload == a_line_payload) {
+ return a_line;
+ }
+ } else {
+ return a_line;
+ }
}
- caps = ast_stream_get_formats(stream);
-
- for (i = 0; i < ast_format_cap_count(caps); i++) {
- struct ast_format *format = ast_format_cap_get_format(caps, i);
+ return NULL;
+}
- if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(rtp), 1, format, 0)) == -1) {
- ast_log(LOG_WARNING,"Unable to get rtp codec payload code for %s\n", ast_format_get_name(format));
- ao2_ref(format, -1);
- continue;
- }
+struct ast_sdp_a_line *ast_sdp_find_attribute(const struct ast_sdp *sdp,
+ const char *attr_name, int payload)
+{
+ return sdp_find_attribute_common(sdp->a_lines, attr_name, payload);
+}
- if (ast_sdp_m_add_format(m_line, options, rtp_code, 0, format, 0)) {
- ast_sdp_m_free(m_line);
- ao2_ref(format, -1);
- return -1;
- }
+struct ast_sdp_a_line *ast_sdp_m_find_attribute(const struct ast_sdp_m_line *m_line,
+ const char *attr_name, int payload)
+{
+ return sdp_find_attribute_common(m_line->a_lines, attr_name, payload);
+}
- if (ast_format_get_maximum_ms(format) &&
- ((ast_format_get_maximum_ms(format) < max_packet_size) || !max_packet_size)) {
- max_packet_size = ast_format_get_maximum_ms(format);
- }
+struct ast_sdp_rtpmap *ast_sdp_rtpmap_alloc(int payload, const char *encoding_name,
+ int clock_rate, const char *encoding_parameters)
+{
+ struct ast_sdp_rtpmap *rtpmap;
+ char *buf_pos;
- ao2_ref(format, -1);
+ rtpmap = ast_calloc(1, sizeof(*rtpmap) + strlen(encoding_name) + strlen(encoding_parameters) + 2);
+ if (!rtpmap) {
+ return NULL;
}
- if (media_type != AST_MEDIA_TYPE_VIDEO) {
- for (i = 1LL; i <= AST_RTP_MAX; i <<= 1) {
- if (!(options->telephone_event & i)) {
- continue;
- }
+ rtpmap->payload = payload;
+ rtpmap->clock_rate = clock_rate;
- rtp_code = ast_rtp_codecs_payload_code(
- ast_rtp_instance_get_codecs(rtp), 0, NULL, i);
+ buf_pos = rtpmap->buf;
+ COPY_STR_AND_ADVANCE(buf_pos, rtpmap->encoding_name, encoding_name);
+ COPY_STR_AND_ADVANCE(buf_pos, rtpmap->encoding_parameters, encoding_parameters);
- if (rtp_code == -1) {
- continue;
- }
+ return rtpmap;
+}
- if (sdp_m_add_rtpmap(m_line, options, rtp_code, 0, NULL, i)) {
- continue;
- }
+void ast_sdp_rtpmap_free(struct ast_sdp_rtpmap *rtpmap)
+{
+ ast_free(rtpmap);
+}
- if (i == AST_RTP_DTMF) {
- snprintf(tmp, sizeof(tmp), "%d 0-16", rtp_code);
- a_line = ast_sdp_a_alloc("fmtp", tmp);
- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {
- ast_sdp_a_free(a_line);
- ast_sdp_m_free(m_line);
- return -1;
- }
- }
- }
- }
+struct ast_sdp_rtpmap *ast_sdp_a_get_rtpmap(const struct ast_sdp_a_line *a_line)
+{
+ char *value_copy;
+ char *slash;
+ int payload;
+ char encoding_name[64];
+ int clock_rate;
+ char *encoding_parameters;
+ struct ast_sdp_rtpmap *rtpmap;
+ int clock_rate_len;
- if (ast_sdp_m_get_a_count(m_line) == 0) {
- return 0;
- }
+ value_copy = ast_strip(ast_strdupa(a_line->value));
- /* If ptime is set add it as an attribute */
- min_packet_size = ast_rtp_codecs_get_framing(ast_rtp_instance_get_codecs(rtp));
- if (!min_packet_size) {
- min_packet_size = ast_format_cap_get_framing(caps);
+ if (sscanf(value_copy, "%30d %63s", &payload, encoding_name) != 2) {
+ return NULL;
}
- if (min_packet_size) {
- snprintf(tmp, sizeof(tmp), "%d", min_packet_size);
- a_line = ast_sdp_a_alloc("ptime", tmp);
- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {
- ast_sdp_a_free(a_line);
- ast_sdp_m_free(m_line);
- return -1;
- }
+ slash = strchr(encoding_name, '/');
+ if (!slash) {
+ return NULL;
+ }
+ *slash++ = '\0';
+ if (ast_strlen_zero(encoding_name)) {
+ return NULL;
+ }
+ if (sscanf(slash, "%30d%n", &clock_rate, &clock_rate_len) < 1) {
+ return NULL;
}
- if (max_packet_size) {
- snprintf(tmp, sizeof(tmp), "%d", max_packet_size);
- a_line = ast_sdp_a_alloc("maxptime", tmp);
- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {
- ast_sdp_a_free(a_line);
- ast_sdp_m_free(m_line);
- return -1;
+ slash += clock_rate_len;
+ if (!ast_strlen_zero(slash)) {
+ if (*slash == '/') {
+ *slash++ = '\0';
+ encoding_parameters = slash;
+ if (ast_strlen_zero(encoding_parameters)) {
+ return NULL;
+ }
+ } else {
+ return NULL;
}
+ } else {
+ encoding_parameters = "";
}
- a_line = ast_sdp_a_alloc(ast_sdp_state_get_locally_held(sdp_state, stream_index) ? "sendonly" : "sendrecv", "");
- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {
- ast_sdp_a_free(a_line);
- ast_sdp_m_free(m_line);
- return -1;
- }
+ rtpmap = ast_sdp_rtpmap_alloc(payload, encoding_name, clock_rate,
+ encoding_parameters);
- if (ast_sdp_add_m(sdp, m_line)) {
- ast_sdp_m_free(m_line);
- return -1;
+ return rtpmap;
+}
+
+/*!
+ * \brief Turn an SDP attribute into an sdp_rtpmap structure
+ *
+ * \param m_line The media section where this attribute was found.
+ * \param payload The RTP payload to find an rtpmap for
+ * \param[out] rtpmap The rtpmap to fill in.
+ * \return Zero if successful, otherwise less than zero
+ */
+static struct ast_sdp_rtpmap *sdp_payload_get_rtpmap(const struct ast_sdp_m_line *m_line, int payload)
+{
+ struct ast_sdp_a_line *rtpmap_attr;
+
+ rtpmap_attr = ast_sdp_m_find_attribute(m_line, "rtpmap", payload);
+ if (!rtpmap_attr) {
+ return NULL;
}
- return 0;
+ return ast_sdp_a_get_rtpmap(rtpmap_attr);
}
-struct ast_sdp *ast_sdp_create_from_state(const struct ast_sdp_state *sdp_state)
+/*!
+ * \brief Find and process fmtp attributes for a given payload
+ *
+ * \param m_line The stream on which to search for the fmtp attribute
+ * \param payload The specific fmtp attribute to search for
+ * \param codecs The current RTP codecs that have been built up
+ */
+static void process_fmtp(const struct ast_sdp_m_line *m_line, int payload,
+ struct ast_rtp_codecs *codecs)
{
- const struct ast_sdp_options *options;
- RAII_VAR(struct ast_sdp *, sdp, NULL, ao2_cleanup);
- const const struct ast_stream_topology *topology;
- int stream_count;
- int stream_num;
- struct ast_sdp_o_line *o_line = NULL;
- struct ast_sdp_c_line *c_line = NULL;
- struct ast_sdp_s_line *s_line = NULL;
- struct ast_sdp_t_line *t_line = NULL;
- char *address_type;
- struct timeval tv = ast_tvnow();
- uint32_t t;
- ast_assert(!!sdp_state);
-
- options = ast_sdp_state_get_options(sdp_state);
- topology = ast_sdp_state_get_local_topology(sdp_state);
- stream_count = ast_stream_topology_get_count(topology);
+ struct ast_sdp_a_line *attr;
+ char *param;
+ char *param_start;
+ char *param_end;
+ size_t len;
+ struct ast_format *replace;
+ struct ast_format *format;
- t = tv.tv_sec + 2208988800UL;
- address_type = (strchr(options->media_address, ':') ? "IP6" : "IP4");
+ attr = ast_sdp_m_find_attribute(m_line, "fmtp", payload);
+ if (!attr) {
+ return;
+ }
- o_line = ast_sdp_o_alloc(options->sdpowner, t, t, address_type, options->media_address);
- if (!o_line) {
- goto error;
+ /* Extract the "a=fmtp:%d %s" attribute parameter string after the payload type. */
+ param_start = ast_skip_nonblanks(attr->value);/* Skip payload type */
+ param_start = ast_skip_blanks(param_start);
+ param_end = ast_skip_nonblanks(param_start);
+ if (param_end == param_start) {
+ /* There is no parameter string */
+ return;
}
- c_line = ast_sdp_c_alloc(address_type, options->media_address);
- if (!c_line) {
- goto error;
+ len = param_end - param_start;
+ param = ast_alloca(len + 1);
+ memcpy(param, param_start, len);
+ param[len] = '\0';
+
+ format = ast_rtp_codecs_get_payload_format(codecs, payload);
+ if (!format) {
+ return;
}
- s_line = ast_sdp_s_alloc(options->sdpsession);
- if (!s_line) {
- goto error;
+ replace = ast_format_parse_sdp_fmtp(format, param);
+ if (replace) {
+ ast_rtp_codecs_payload_replace_format(codecs, payload, replace);
+ ao2_ref(replace, -1);
}
+ ao2_ref(format, -1);
+}
- sdp = ast_sdp_alloc(o_line, c_line, s_line, NULL);
- if (!sdp) {
- goto error;
+/*!
+ * \brief Convert an SDP stream into an Asterisk stream
+ *
+ * Given an m-line from an SDP, convert it into an ast_stream structure.
+ * This takes formats, as well as clock-rate and fmtp attributes into account.
+ *
+ * \param m_line The SDP media section to convert
+ * \retval NULL An error occurred
+ * \retval non-NULL The converted stream
+ */
+static struct ast_stream *get_stream_from_m(const struct ast_sdp_m_line *m_line)
+{
+ int i;
+ int non_ast_fmts;
+ struct ast_rtp_codecs codecs;
+ struct ast_format_cap *caps;
+ struct ast_stream *stream;
+
+ caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+ if (!caps) {
+ return NULL;
+ }
+ stream = ast_stream_alloc(m_line->type, ast_media_type_from_str(m_line->type));
+ if (!stream) {
+ ao2_ref(caps, -1);
+ return NULL;
}
+ ast_rtp_codecs_payloads_initialize(&codecs);
- for (stream_num = 0; stream_num < stream_count; stream_num++) {
- enum ast_media_type type = ast_stream_get_type(ast_stream_topology_get_stream(topology, stream_num));
+ for (i = 0; i < ast_sdp_m_get_payload_count(m_line); ++i) {
+ struct ast_sdp_payload *payload_s;
+ struct ast_sdp_rtpmap *rtpmap;
+ int payload;
- if (type == AST_MEDIA_TYPE_AUDIO || type == AST_MEDIA_TYPE_VIDEO) {
- if (ast_sdp_add_m_from_rtp_stream(sdp, sdp_state, options, stream_num)) {
- goto error;
- }
+ payload_s = ast_sdp_m_get_payload(m_line, i);
+ sscanf(payload_s->fmt, "%30d", &payload);
+ ast_rtp_codecs_payloads_set_m_type(&codecs, NULL, payload);
+
+ rtpmap = sdp_payload_get_rtpmap(m_line, payload);
+ if (!rtpmap) {
+ continue;
}
+ ast_rtp_codecs_payloads_set_rtpmap_type_rate(&codecs, NULL,
+ payload, m_line->type, rtpmap->encoding_name, 0,
+ rtpmap->clock_rate);
+ ast_sdp_rtpmap_free(rtpmap);
+
+ process_fmtp(m_line, payload, &codecs);
}
- return sdp;
+ ast_rtp_codecs_payload_formats(&codecs, caps, &non_ast_fmts);
+ ast_stream_set_formats(stream, caps);
-error:
- if (sdp) {
- ast_sdp_free(sdp);
- } else {
- ast_sdp_t_free(t_line);
- ast_sdp_s_free(s_line);
- ast_sdp_c_free(c_line);
- ast_sdp_o_free(o_line);
+ ao2_ref(caps, -1);
+ ast_rtp_codecs_payloads_destroy(&codecs);
+ return stream;
+}
+
+struct ast_stream_topology *ast_get_topology_from_sdp(const struct ast_sdp *sdp)
+{
+ struct ast_stream_topology *topology;
+ int i;
+
+ topology = ast_stream_topology_alloc();
+ if (!topology) {
+ return NULL;
}
- return NULL;
-}
+ for (i = 0; i < ast_sdp_get_m_count(sdp); ++i) {
+ struct ast_stream *stream;
+
+ stream = get_stream_from_m(ast_sdp_get_m(sdp, i));
+ if (!stream) {
+ continue;
+ }
+ ast_stream_topology_append_stream(topology, stream);
+ }
+ return topology;
+}