<p>Joshua Colp <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/5896">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Joshua Colp: Looks good to me, but someone else must approve; Approved for Submit
George Joseph: Looks good to me, approved
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">SDP: Create declined m= SDP lines using remote SDP if applicable.<br><br>* Update SDP unit tests to test negotiating with declined streams.<br>Generation of declined m= lines created and responded tested.<br><br>Change-Id: I5cb99f5010994ab0c7d9cf2d395eca23fab37b98<br>---<br>M main/sdp_state.c<br>M tests/test_sdp.c<br>2 files changed, 432 insertions(+), 236 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/main/sdp_state.c b/main/sdp_state.c<br>index 330140c..a77d96d 100644<br>--- a/main/sdp_state.c<br>+++ b/main/sdp_state.c<br>@@ -2813,8 +2813,161 @@<br> ast_sdp_m_add_a(m_line, a_line);<br> }<br> <br>+/*!<br>+ * \internal<br>+ * \brief Create a declined m-line from a remote requested stream.<br>+ * \since 15.0.0<br>+ *<br>+ * \details<br>+ * Using the last received remote SDP create a declined stream<br>+ * m-line for the requested stream. The stream may be unsupported.<br>+ *<br>+ * \param sdp Our SDP under construction to append the declined stream.<br>+ * \param sdp_state<br>+ * \param stream_index Which remote SDP stream we are declining.<br>+ *<br>+ * \retval 0 on success.<br>+ * \retval -1 on failure.<br>+ */<br>+static int sdp_add_m_from_declined_remote_stream(struct ast_sdp *sdp,<br>+ const struct ast_sdp_state *sdp_state, int stream_index)<br>+{<br>+ const struct ast_sdp_m_line *m_line_remote;<br>+ struct ast_sdp_m_line *m_line;<br>+ int idx;<br>+<br>+ ast_assert(sdp && sdp_state && sdp_state->remote_sdp);<br>+ ast_assert(stream_index < ast_sdp_get_m_count(sdp_state->remote_sdp));<br>+<br>+ /*<br>+ * The only way we can generate a declined unsupported stream<br>+ * m-line is if the remote offered it to us.<br>+ */<br>+ m_line_remote = ast_sdp_get_m(sdp_state->remote_sdp, stream_index);<br>+<br>+ /* Copy remote SDP stream m-line except for port number. */<br>+ m_line = ast_sdp_m_alloc(m_line_remote->type, 0, m_line_remote->port_count,<br>+ m_line_remote->proto, NULL);<br>+ if (!m_line) {<br>+ return -1;<br>+ }<br>+<br>+ /* Copy any m-line payload strings from the remote SDP */<br>+ for (idx = 0; idx < ast_sdp_m_get_payload_count(m_line_remote); ++idx) {<br>+ const struct ast_sdp_payload *payload_remote;<br>+ struct ast_sdp_payload *payload;<br>+<br>+ payload_remote = ast_sdp_m_get_payload(m_line_remote, idx);<br>+ payload = ast_sdp_payload_alloc(payload_remote->fmt);<br>+ if (!payload) {<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+ if (ast_sdp_m_add_payload(m_line, payload)) {<br>+ ast_sdp_payload_free(payload);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+ }<br>+<br>+ if (ast_sdp_add_m(sdp, m_line)) {<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+<br>+ return 0;<br>+}<br>+<br>+/*!<br>+ * \internal<br>+ * \brief Create a declined m-line for our SDP stream.<br>+ * \since 15.0.0<br>+ *<br>+ * \param sdp Our SDP under construction to append the declined stream.<br>+ * \param sdp_state<br>+ * \param type Stream type we are declining.<br>+ * \param stream_index Which remote SDP stream we are declining.<br>+ *<br>+ * \retval 0 on success.<br>+ * \retval -1 on failure.<br>+ */<br>+static int sdp_add_m_from_declined_stream(struct ast_sdp *sdp,<br>+ const struct ast_sdp_state *sdp_state, enum ast_media_type type, int stream_index)<br>+{<br>+ struct ast_sdp_m_line *m_line;<br>+ const char *proto;<br>+ const char *fmt;<br>+ struct ast_sdp_payload *payload;<br>+<br>+ if (sdp_state->role == SDP_ROLE_ANSWERER) {<br>+ /* We are declining the remote stream or it is still declined. */<br>+ return sdp_add_m_from_declined_remote_stream(sdp, sdp_state, stream_index);<br>+ }<br>+<br>+ /* Send declined remote stream in our offer if the type matches. */<br>+ if (sdp_state->remote_sdp<br>+ && stream_index < ast_sdp_get_m_count(sdp_state->remote_sdp)) {<br>+ if (!sdp_is_stream_type_supported(type)<br>+ || !strcasecmp(ast_sdp_get_m(sdp_state->remote_sdp, stream_index)->type,<br>+ ast_codec_media_type2str(type))) {<br>+ /* Stream is still declined */<br>+ return sdp_add_m_from_declined_remote_stream(sdp, sdp_state, stream_index);<br>+ }<br>+ }<br>+<br>+ /* Build a new declined stream in our offer. */<br>+ switch (type) {<br>+ case AST_MEDIA_TYPE_AUDIO:<br>+ case AST_MEDIA_TYPE_VIDEO:<br>+ proto = "RTP/AVP";<br>+ break;<br>+ case AST_MEDIA_TYPE_IMAGE:<br>+ proto = "udptl";<br>+ break;<br>+ default:<br>+ /* Stream type not supported */<br>+ ast_assert(0);<br>+ return -1;<br>+ }<br>+ m_line = ast_sdp_m_alloc(ast_codec_media_type2str(type), 0, 1, proto, NULL);<br>+ if (!m_line) {<br>+ return -1;<br>+ }<br>+<br>+ /* Add a dummy static payload type */<br>+ switch (type) {<br>+ case AST_MEDIA_TYPE_AUDIO:<br>+ fmt = "0"; /* ulaw */<br>+ break;<br>+ case AST_MEDIA_TYPE_VIDEO:<br>+ fmt = "31"; /* H.261 */<br>+ break;<br>+ case AST_MEDIA_TYPE_IMAGE:<br>+ fmt = "t38"; /* T.38 */<br>+ break;<br>+ default:<br>+ /* Stream type not supported */<br>+ ast_assert(0);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+ payload = ast_sdp_payload_alloc(fmt);<br>+ if (!payload || ast_sdp_m_add_payload(m_line, payload)) {<br>+ ast_sdp_payload_free(payload);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+<br>+ if (ast_sdp_add_m(sdp, m_line)) {<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+<br>+ return 0;<br>+}<br>+<br> static int sdp_add_m_from_rtp_stream(struct ast_sdp *sdp, const struct ast_sdp_state *sdp_state,<br>- const struct ast_sdp_options *options, const struct sdp_state_capabilities *capabilities, int stream_index)<br>+ const struct sdp_state_capabilities *capabilities, int stream_index)<br> {<br> struct ast_stream *stream;<br> struct ast_sdp_m_line *m_line;<br>@@ -2829,11 +2982,14 @@<br> struct sdp_state_stream *stream_state;<br> struct ast_rtp_instance *rtp;<br> struct ast_sdp_a_line *a_line;<br>+ const struct ast_sdp_options *options;<br>+ const char *direction;<br> <br> stream = ast_stream_topology_get_stream(capabilities->topology, stream_index);<br> <br>- ast_assert(sdp && options && stream);<br>+ ast_assert(sdp && sdp_state && stream);<br> <br>+ options = sdp_state->options;<br> caps = ast_stream_get_formats(stream);<br> <br> stream_state = AST_VECTOR_GET(&capabilities->streams, stream_index);<br>@@ -2856,145 +3012,118 @@<br> rtp_port = 0;<br> }<br> <br>- m_line = ast_sdp_m_alloc(<br>- ast_codec_media_type2str(ast_stream_get_type(stream)),<br>- rtp_port, 1,<br>+ media_type = ast_stream_get_type(stream);<br>+ if (!rtp_port) {<br>+ /* Declined/disabled stream */<br>+ return sdp_add_m_from_declined_stream(sdp, sdp_state, media_type, stream_index);<br>+ }<br>+<br>+ /* Stream is not declined/disabled */<br>+ m_line = ast_sdp_m_alloc(ast_codec_media_type2str(media_type), rtp_port, 1,<br> options->encryption != AST_SDP_ENCRYPTION_DISABLED ? "RTP/SAVP" : "RTP/AVP",<br> NULL);<br> if (!m_line) {<br> return -1;<br> }<br> <br>- if (rtp_port) {<br>- const char *direction;<br>+ for (i = 0; i < ast_format_cap_count(caps); i++) {<br>+ struct ast_format *format = ast_format_cap_get_format(caps, i);<br> <br>- /* Stream is not declined/disabled */<br>- for (i = 0; i < ast_format_cap_count(caps); i++) {<br>- struct ast_format *format = ast_format_cap_get_format(caps, i);<br>-<br>- rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(rtp), 1,<br>- format, 0);<br>- if (rtp_code == -1) {<br>- ast_log(LOG_WARNING,"Unable to get rtp codec payload code for %s\n",<br>- ast_format_get_name(format));<br>- ao2_ref(format, -1);<br>- continue;<br>- }<br>-<br>- if (ast_sdp_m_add_format(m_line, options, rtp_code, 1, format, 0)) {<br>- ast_sdp_m_free(m_line);<br>- ao2_ref(format, -1);<br>- return -1;<br>- }<br>-<br>- if (ast_format_get_maximum_ms(format)<br>- && ((ast_format_get_maximum_ms(format) < max_packet_size)<br>- || !max_packet_size)) {<br>- max_packet_size = ast_format_get_maximum_ms(format);<br>- }<br>-<br>+ rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(rtp), 1,<br>+ format, 0);<br>+ if (rtp_code == -1) {<br>+ ast_log(LOG_WARNING,"Unable to get rtp codec payload code for %s\n",<br>+ ast_format_get_name(format));<br> ao2_ref(format, -1);<br>+ continue;<br> }<br> <br>- media_type = ast_stream_get_type(stream);<br>- if (media_type != AST_MEDIA_TYPE_VIDEO<br>- && (options->dtmf == AST_SDP_DTMF_RFC_4733 || options->dtmf == AST_SDP_DTMF_AUTO)) {<br>- i = AST_RTP_DTMF;<br>- rtp_code = ast_rtp_codecs_payload_code(<br>- ast_rtp_instance_get_codecs(rtp), 0, NULL, i);<br>- if (-1 < rtp_code) {<br>- if (ast_sdp_m_add_format(m_line, options, rtp_code, 0, NULL, i)) {<br>- ast_sdp_m_free(m_line);<br>- return -1;<br>- }<br>+ if (ast_sdp_m_add_format(m_line, options, rtp_code, 1, format, 0)) {<br>+ ast_sdp_m_free(m_line);<br>+ ao2_ref(format, -1);<br>+ return -1;<br>+ }<br> <br>- snprintf(tmp, sizeof(tmp), "%d 0-16", rtp_code);<br>- a_line = ast_sdp_a_alloc("fmtp", tmp);<br>- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>- ast_sdp_a_free(a_line);<br>- ast_sdp_m_free(m_line);<br>- return -1;<br>- }<br>+ if (ast_format_get_maximum_ms(format)<br>+ && ((ast_format_get_maximum_ms(format) < max_packet_size)<br>+ || !max_packet_size)) {<br>+ max_packet_size = ast_format_get_maximum_ms(format);<br>+ }<br>+<br>+ ao2_ref(format, -1);<br>+ }<br>+<br>+ if (media_type != AST_MEDIA_TYPE_VIDEO<br>+ && (options->dtmf == AST_SDP_DTMF_RFC_4733 || options->dtmf == AST_SDP_DTMF_AUTO)) {<br>+ i = AST_RTP_DTMF;<br>+ rtp_code = ast_rtp_codecs_payload_code(<br>+ ast_rtp_instance_get_codecs(rtp), 0, NULL, i);<br>+ if (-1 < rtp_code) {<br>+ if (ast_sdp_m_add_format(m_line, options, rtp_code, 0, NULL, i)) {<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br> }<br>- }<br> <br>- /* If ptime is set add it as an attribute */<br>- min_packet_size = ast_rtp_codecs_get_framing(ast_rtp_instance_get_codecs(rtp));<br>- if (!min_packet_size) {<br>- min_packet_size = ast_format_cap_get_framing(caps);<br>- }<br>- if (min_packet_size) {<br>- snprintf(tmp, sizeof(tmp), "%d", min_packet_size);<br>-<br>- a_line = ast_sdp_a_alloc("ptime", tmp);<br>+ snprintf(tmp, sizeof(tmp), "%d 0-16", rtp_code);<br>+ a_line = ast_sdp_a_alloc("fmtp", tmp);<br> if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br> ast_sdp_a_free(a_line);<br> ast_sdp_m_free(m_line);<br> return -1;<br> }<br> }<br>+ }<br> <br>- if (max_packet_size) {<br>- snprintf(tmp, sizeof(tmp), "%d", max_packet_size);<br>- a_line = ast_sdp_a_alloc("maxptime", tmp);<br>- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>- ast_sdp_a_free(a_line);<br>- ast_sdp_m_free(m_line);<br>- return -1;<br>- }<br>- }<br>+ /* If ptime is set add it as an attribute */<br>+ min_packet_size = ast_rtp_codecs_get_framing(ast_rtp_instance_get_codecs(rtp));<br>+ if (!min_packet_size) {<br>+ min_packet_size = ast_format_cap_get_framing(caps);<br>+ }<br>+ if (min_packet_size) {<br>+ snprintf(tmp, sizeof(tmp), "%d", min_packet_size);<br> <br>- if (sdp_state->locally_held || stream_state->locally_held) {<br>- if (stream_state->remotely_held) {<br>- direction = "inactive";<br>- } else {<br>- direction = "sendonly";<br>- }<br>- } else {<br>- if (stream_state->remotely_held) {<br>- direction = "recvonly";<br>- } else {<br>- /* Default is "sendrecv" */<br>- direction = NULL;<br>- }<br>- }<br>- if (direction) {<br>- a_line = ast_sdp_a_alloc(direction, "");<br>- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>- ast_sdp_a_free(a_line);<br>- ast_sdp_m_free(m_line);<br>- return -1;<br>- }<br>- }<br>-<br>- add_ssrc_attributes(m_line, options, rtp);<br>- } else {<br>- /* Declined/disabled stream */<br>- struct ast_sdp_payload *payload;<br>- const char *fmt;<br>-<br>- /*<br>- * Add a static payload type placeholder to the declined/disabled stream.<br>- *<br>- * XXX We should use the default payload type in the received offer but<br>- * we don't have that available.<br>- */<br>- switch (ast_stream_get_type(stream)) {<br>- default:<br>- case AST_MEDIA_TYPE_AUDIO:<br>- fmt = "0"; /* ulaw */<br>- break;<br>- case AST_MEDIA_TYPE_VIDEO:<br>- fmt = "31"; /* H.261 */<br>- break;<br>- }<br>- payload = ast_sdp_payload_alloc(fmt);<br>- if (!payload || ast_sdp_m_add_payload(m_line, payload)) {<br>- ast_sdp_payload_free(payload);<br>+ a_line = ast_sdp_a_alloc("ptime", tmp);<br>+ if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>+ ast_sdp_a_free(a_line);<br> ast_sdp_m_free(m_line);<br> return -1;<br> }<br> }<br>+<br>+ if (max_packet_size) {<br>+ snprintf(tmp, sizeof(tmp), "%d", max_packet_size);<br>+ a_line = ast_sdp_a_alloc("maxptime", tmp);<br>+ if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>+ ast_sdp_a_free(a_line);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+ }<br>+<br>+ if (sdp_state->locally_held || stream_state->locally_held) {<br>+ if (stream_state->remotely_held) {<br>+ direction = "inactive";<br>+ } else {<br>+ direction = "sendonly";<br>+ }<br>+ } else {<br>+ if (stream_state->remotely_held) {<br>+ direction = "recvonly";<br>+ } else {<br>+ /* Default is "sendrecv" */<br>+ direction = NULL;<br>+ }<br>+ }<br>+ if (direction) {<br>+ a_line = ast_sdp_a_alloc(direction, "");<br>+ if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>+ ast_sdp_a_free(a_line);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+ }<br>+<br>+ add_ssrc_attributes(m_line, options, rtp);<br> <br> if (ast_sdp_add_m(sdp, m_line)) {<br> ast_sdp_m_free(m_line);<br>@@ -3026,11 +3155,12 @@<br> }<br> <br> static int sdp_add_m_from_udptl_stream(struct ast_sdp *sdp, const struct ast_sdp_state *sdp_state,<br>- const struct ast_sdp_options *options, const struct sdp_state_capabilities *capabilities, int stream_index)<br>+ const struct sdp_state_capabilities *capabilities, int stream_index)<br> {<br> struct ast_stream *stream;<br> struct ast_sdp_m_line *m_line;<br> struct ast_sdp_payload *payload;<br>+ enum ast_media_type media_type;<br> char tmp[64];<br> struct sdp_state_udptl *udptl;<br> struct ast_sdp_a_line *a_line;<br>@@ -3039,7 +3169,7 @@<br> <br> stream = ast_stream_topology_get_stream(capabilities->topology, stream_index);<br> <br>- ast_assert(sdp && options && stream);<br>+ ast_assert(sdp && sdp_state && stream);<br> <br> stream_state = AST_VECTOR_GET(&capabilities->streams, stream_index);<br> if (stream_state->udptl<br>@@ -3061,9 +3191,15 @@<br> udptl_port = 0;<br> }<br> <br>- m_line = ast_sdp_m_alloc(<br>- ast_codec_media_type2str(ast_stream_get_type(stream)),<br>- udptl_port, 1, "udptl", NULL);<br>+ media_type = ast_stream_get_type(stream);<br>+ if (!udptl_port) {<br>+ /* Declined/disabled stream */<br>+ return sdp_add_m_from_declined_stream(sdp, sdp_state, media_type, stream_index);<br>+ }<br>+<br>+ /* Stream is not declined/disabled */<br>+ m_line = ast_sdp_m_alloc(ast_codec_media_type2str(media_type), udptl_port, 1,<br>+ "udptl", NULL);<br> if (!m_line) {<br> return -1;<br> }<br>@@ -3075,98 +3211,95 @@<br> return -1;<br> }<br> <br>- if (udptl_port) {<br>- /* Stream is not declined/disabled */<br>- snprintf(tmp, sizeof(tmp), "%u", stream_state->t38_local_params.version);<br>- a_line = ast_sdp_a_alloc("T38FaxVersion", tmp);<br>+ snprintf(tmp, sizeof(tmp), "%u", stream_state->t38_local_params.version);<br>+ a_line = ast_sdp_a_alloc("T38FaxVersion", tmp);<br>+ if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>+ ast_sdp_a_free(a_line);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+<br>+ snprintf(tmp, sizeof(tmp), "%u", t38_get_rate(stream_state->t38_local_params.rate));<br>+ a_line = ast_sdp_a_alloc("T38FaxMaxBitRate", tmp);<br>+ if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>+ ast_sdp_a_free(a_line);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+<br>+ if (stream_state->t38_local_params.fill_bit_removal) {<br>+ a_line = ast_sdp_a_alloc("T38FaxFillBitRemoval", "");<br> if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br> ast_sdp_a_free(a_line);<br> ast_sdp_m_free(m_line);<br> return -1;<br> }<br>+ }<br> <br>- snprintf(tmp, sizeof(tmp), "%u", t38_get_rate(stream_state->t38_local_params.rate));<br>- a_line = ast_sdp_a_alloc("T38FaxMaxBitRate", tmp);<br>+ if (stream_state->t38_local_params.transcoding_mmr) {<br>+ a_line = ast_sdp_a_alloc("T38FaxTranscodingMMR", "");<br> if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br> ast_sdp_a_free(a_line);<br> ast_sdp_m_free(m_line);<br> return -1;<br> }<br>+ }<br> <br>- if (stream_state->t38_local_params.fill_bit_removal) {<br>- a_line = ast_sdp_a_alloc("T38FaxFillBitRemoval", "");<br>- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>- ast_sdp_a_free(a_line);<br>- ast_sdp_m_free(m_line);<br>- return -1;<br>- }<br>- }<br>-<br>- if (stream_state->t38_local_params.transcoding_mmr) {<br>- a_line = ast_sdp_a_alloc("T38FaxTranscodingMMR", "");<br>- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>- ast_sdp_a_free(a_line);<br>- ast_sdp_m_free(m_line);<br>- return -1;<br>- }<br>- }<br>-<br>- if (stream_state->t38_local_params.transcoding_jbig) {<br>- a_line = ast_sdp_a_alloc("T38FaxTranscodingJBIG", "");<br>- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>- ast_sdp_a_free(a_line);<br>- ast_sdp_m_free(m_line);<br>- return -1;<br>- }<br>- }<br>-<br>- switch (stream_state->t38_local_params.rate_management) {<br>- case AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF:<br>- a_line = ast_sdp_a_alloc("T38FaxRateManagement", "transferredTCF");<br>- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>- ast_sdp_a_free(a_line);<br>- ast_sdp_m_free(m_line);<br>- return -1;<br>- }<br>- break;<br>- case AST_T38_RATE_MANAGEMENT_LOCAL_TCF:<br>- a_line = ast_sdp_a_alloc("T38FaxRateManagement", "localTCF");<br>- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>- ast_sdp_a_free(a_line);<br>- ast_sdp_m_free(m_line);<br>- return -1;<br>- }<br>- break;<br>- }<br>-<br>- snprintf(tmp, sizeof(tmp), "%u", ast_udptl_get_local_max_datagram(udptl->instance));<br>- a_line = ast_sdp_a_alloc("T38FaxMaxDatagram", tmp);<br>+ if (stream_state->t38_local_params.transcoding_jbig) {<br>+ a_line = ast_sdp_a_alloc("T38FaxTranscodingJBIG", "");<br> if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br> ast_sdp_a_free(a_line);<br> ast_sdp_m_free(m_line);<br> return -1;<br> }<br>+ }<br> <br>- switch (ast_udptl_get_error_correction_scheme(udptl->instance)) {<br>- case UDPTL_ERROR_CORRECTION_NONE:<br>- break;<br>- case UDPTL_ERROR_CORRECTION_FEC:<br>- a_line = ast_sdp_a_alloc("T38FaxUdpEC", "t38UDPFEC");<br>- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>- ast_sdp_a_free(a_line);<br>- ast_sdp_m_free(m_line);<br>- return -1;<br>- }<br>- break;<br>- case UDPTL_ERROR_CORRECTION_REDUNDANCY:<br>- a_line = ast_sdp_a_alloc("T38FaxUdpEC", "t38UDPRedundancy");<br>- if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>- ast_sdp_a_free(a_line);<br>- ast_sdp_m_free(m_line);<br>- return -1;<br>- }<br>- break;<br>+ switch (stream_state->t38_local_params.rate_management) {<br>+ case AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF:<br>+ a_line = ast_sdp_a_alloc("T38FaxRateManagement", "transferredTCF");<br>+ if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>+ ast_sdp_a_free(a_line);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br> }<br>+ break;<br>+ case AST_T38_RATE_MANAGEMENT_LOCAL_TCF:<br>+ a_line = ast_sdp_a_alloc("T38FaxRateManagement", "localTCF");<br>+ if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>+ ast_sdp_a_free(a_line);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+ break;<br>+ }<br>+<br>+ snprintf(tmp, sizeof(tmp), "%u", ast_udptl_get_local_max_datagram(udptl->instance));<br>+ a_line = ast_sdp_a_alloc("T38FaxMaxDatagram", tmp);<br>+ if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>+ ast_sdp_a_free(a_line);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+<br>+ switch (ast_udptl_get_error_correction_scheme(udptl->instance)) {<br>+ case UDPTL_ERROR_CORRECTION_NONE:<br>+ break;<br>+ case UDPTL_ERROR_CORRECTION_FEC:<br>+ a_line = ast_sdp_a_alloc("T38FaxUdpEC", "t38UDPFEC");<br>+ if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>+ ast_sdp_a_free(a_line);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+ break;<br>+ case UDPTL_ERROR_CORRECTION_REDUNDANCY:<br>+ a_line = ast_sdp_a_alloc("T38FaxUdpEC", "t38UDPRedundancy");<br>+ if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {<br>+ ast_sdp_a_free(a_line);<br>+ ast_sdp_m_free(m_line);<br>+ return -1;<br>+ }<br>+ break;<br> }<br> <br> if (ast_sdp_add_m(sdp, m_line)) {<br>@@ -3200,7 +3333,7 @@<br> uint32_t t;<br> int stream_count;<br> <br>- options = ast_sdp_state_get_options(sdp_state);<br>+ options = sdp_state->options;<br> topology = capabilities->topology;<br> <br> t = tv.tv_sec + 2208988800UL;<br>@@ -3233,18 +3366,22 @@<br> switch (ast_stream_get_type(ast_stream_topology_get_stream(topology, stream_num))) {<br> case AST_MEDIA_TYPE_AUDIO:<br> case AST_MEDIA_TYPE_VIDEO:<br>- if (sdp_add_m_from_rtp_stream(sdp, sdp_state, options, capabilities, stream_num)) {<br>+ if (sdp_add_m_from_rtp_stream(sdp, sdp_state, capabilities, stream_num)) {<br> goto error;<br> }<br> break;<br> case AST_MEDIA_TYPE_IMAGE:<br>- if (sdp_add_m_from_udptl_stream(sdp, sdp_state, options, capabilities, stream_num)) {<br>+ if (sdp_add_m_from_udptl_stream(sdp, sdp_state, capabilities, stream_num)) {<br> goto error;<br> }<br> break;<br> case AST_MEDIA_TYPE_UNKNOWN:<br> case AST_MEDIA_TYPE_TEXT:<br> case AST_MEDIA_TYPE_END:<br>+ /* Decline any of these streams from the remote. */<br>+ if (sdp_add_m_from_declined_remote_stream(sdp, sdp_state, stream_num)) {<br>+ goto error;<br>+ }<br> break;<br> }<br> }<br>diff --git a/tests/test_sdp.c b/tests/test_sdp.c<br>index 662e2aa..0ab8ec8 100644<br>--- a/tests/test_sdp.c<br>+++ b/tests/test_sdp.c<br>@@ -88,6 +88,12 @@<br> return -1;<br> }<br> <br>+ if (m_line->port == 0) {<br>+ ast_test_status_update(test, "Expected %s m-line to not be declined\n",<br>+ media_type);<br>+ return -1;<br>+ }<br>+<br> if (ast_sdp_m_get_payload_count(m_line) != num_payloads) {<br> ast_test_status_update(test, "Expected %s m-line payload count %d but got %d\n",<br> media_type, num_payloads, ast_sdp_m_get_payload_count(m_line));<br>@@ -462,17 +468,20 @@<br> int idx;<br> <br> for (idx = 0; idx < num_streams; ++idx) {<br>- RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);<br>+ struct ast_format_cap *caps;<br> <br>- caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);<br>- if (!caps) {<br>- return -1;<br>+ if (ast_strlen_zero(formats[idx].formats)) {<br>+ continue;<br> }<br> <br>- if (ast_format_cap_update_by_allow_disallow(caps, formats[idx].formats, 1) < 0) {<br>+ caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);<br>+ if (!caps<br>+ || ast_format_cap_update_by_allow_disallow(caps, formats[idx].formats, 1) < 0) {<br>+ ao2_cleanup(caps);<br> return -1;<br> }<br> ast_sdp_options_set_format_cap_type(options, formats[idx].type, caps);<br>+ ao2_cleanup(caps);<br> }<br> return 0;<br> }<br>@@ -494,10 +503,12 @@<br> * \param opt_formats Array of new stream media types and formats allowed to create.<br> * NULL if use a default stream creation.<br> * Not used if test_options provided.<br>+ * \param max_streams 0 if set max to max(3, num_streams) else max(max_streams, num_streams)<br>+ * Not used if test_options provided.<br> * \param test_options Optional SDP options.<br> */<br> static struct ast_sdp_state *build_sdp_state(int num_streams, const struct sdp_format *formats,<br>- int opt_num_streams, const struct sdp_format *opt_formats,<br>+ int opt_num_streams, const struct sdp_format *opt_formats, unsigned int max_streams,<br> struct ast_sdp_options *test_options)<br> {<br> struct ast_stream_topology *topology = NULL;<br>@@ -506,8 +517,6 @@<br> int i;<br> <br> if (!test_options) {<br>- unsigned int max_streams;<br>-<br> static const struct sdp_format sdp_formats[] = {<br> { AST_MEDIA_TYPE_AUDIO, "ulaw" },<br> { AST_MEDIA_TYPE_VIDEO, "vp8" },<br>@@ -520,8 +529,10 @@<br> }<br> <br> /* Determine max_streams to allow */<br>- max_streams = ARRAY_LEN(sdp_formats);<br>- if (ARRAY_LEN(sdp_formats) < num_streams) {<br>+ if (!max_streams) {<br>+ max_streams = ARRAY_LEN(sdp_formats);<br>+ }<br>+ if (max_streams < num_streams) {<br> max_streams = num_streams;<br> }<br> ast_sdp_options_set_max_streams(options, max_streams);<br>@@ -544,21 +555,27 @@<br> }<br> <br> for (i = 0; i < num_streams; ++i) {<br>- RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);<br> struct ast_stream *stream;<br> <br>- caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);<br>- if (!caps) {<br>- goto end;<br>- }<br>- if (ast_format_cap_update_by_allow_disallow(caps, formats[i].formats, 1) < 0) {<br>- goto end;<br>- }<br> stream = ast_stream_alloc("sure_thing", formats[i].type);<br> if (!stream) {<br> goto end;<br> }<br>- ast_stream_set_formats(stream, caps);<br>+ if (!ast_strlen_zero(formats[i].formats)) {<br>+ struct ast_format_cap *caps;<br>+<br>+ caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);<br>+ if (!caps<br>+ || ast_format_cap_update_by_allow_disallow(caps, formats[i].formats, 1) < 0) {<br>+ ao2_cleanup(caps);<br>+ ast_stream_free(stream);<br>+ goto end;<br>+ }<br>+ ast_stream_set_formats(stream, caps);<br>+ ao2_cleanup(caps);<br>+ } else {<br>+ ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED);<br>+ }<br> if (ast_stream_topology_append_stream(topology, stream) < 0) {<br> ast_stream_free(stream);<br> goto end;<br>@@ -604,7 +621,7 @@<br> }<br> <br> sdp_state = build_sdp_state(ARRAY_LEN(formats), formats,<br>- ARRAY_LEN(formats), formats, NULL);<br>+ ARRAY_LEN(formats), formats, 0, NULL);<br> if (!sdp_state) {<br> goto end;<br> }<br>@@ -749,7 +766,7 @@<br> }<br> <br> sdp_state = build_sdp_state(ARRAY_LEN(sdp_formats), sdp_formats,<br>- ARRAY_LEN(sdp_formats), sdp_formats, NULL);<br>+ ARRAY_LEN(sdp_formats), sdp_formats, 0, NULL);<br> if (!sdp_state) {<br> res = AST_TEST_FAIL;<br> goto end;<br>@@ -848,6 +865,7 @@<br> int offer_num_streams, const struct sdp_format *offer_formats,<br> int answer_num_streams, const struct sdp_format *answer_formats,<br> int allowed_ans_num_streams, const struct sdp_format *allowed_ans_formats,<br>+ unsigned int max_streams,<br> int (*validate_sdp)(struct ast_test *test, const struct ast_sdp *sdp))<br> {<br> enum ast_test_result_state res = AST_TEST_PASS;<br>@@ -857,36 +875,42 @@<br> const struct ast_sdp *answerer_sdp;<br> <br> sdp_state_offerer = build_sdp_state(offer_num_streams, offer_formats,<br>- offer_num_streams, offer_formats, NULL);<br>+ offer_num_streams, offer_formats, max_streams, NULL);<br> if (!sdp_state_offerer) {<br>+ ast_test_status_update(test, "Building offerer SDP state failed\n");<br> res = AST_TEST_FAIL;<br> goto end;<br> }<br> <br> sdp_state_answerer = build_sdp_state(answer_num_streams, answer_formats,<br>- allowed_ans_num_streams, allowed_ans_formats, NULL);<br>+ allowed_ans_num_streams, allowed_ans_formats, max_streams, NULL);<br> if (!sdp_state_answerer) {<br>+ ast_test_status_update(test, "Building answerer SDP state failed\n");<br> res = AST_TEST_FAIL;<br> goto end;<br> }<br> <br> offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);<br> if (!offerer_sdp) {<br>+ ast_test_status_update(test, "Building offerer offer failed\n");<br> res = AST_TEST_FAIL;<br> goto end;<br> }<br> <br> if (ast_sdp_state_set_remote_sdp(sdp_state_answerer, offerer_sdp)) {<br>+ ast_test_status_update(test, "Setting answerer offer failed\n");<br> res = AST_TEST_FAIL;<br> goto end;<br> }<br> answerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_answerer);<br> if (!answerer_sdp) {<br>+ ast_test_status_update(test, "Building answerer answer failed\n");<br> res = AST_TEST_FAIL;<br> goto end;<br> }<br> <br> if (ast_sdp_state_set_remote_sdp(sdp_state_offerer, answerer_sdp)) {<br>+ ast_test_status_update(test, "Setting offerer answer failed\n");<br> res = AST_TEST_FAIL;<br> goto end;<br> }<br>@@ -902,6 +926,11 @@<br> goto end;<br> }<br> offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);<br>+ if (!offerer_sdp) {<br>+ ast_test_status_update(test, "Building offerer current sdp failed\n");<br>+ res = AST_TEST_FAIL;<br>+ goto end;<br>+ }<br> if (validate_sdp(test, offerer_sdp)) {<br> res = AST_TEST_FAIL;<br> goto end;<br>@@ -944,6 +973,7 @@<br> ARRAY_LEN(offerer_formats), offerer_formats,<br> 0, NULL,<br> 0, NULL,<br>+ 0,<br> validate_avi_sdp_streams);<br> }<br> <br>@@ -978,10 +1008,11 @@<br> ARRAY_LEN(offerer_formats), offerer_formats,<br> ARRAY_LEN(answerer_formats), answerer_formats,<br> 0, NULL,<br>+ 0,<br> validate_avi_sdp_streams);<br> }<br> <br>-static int validate_ava_declined_sdp_streams(struct ast_test *test, const struct ast_sdp *sdp)<br>+static int validate_aviavia_declined_sdp_streams(struct ast_test *test, const struct ast_sdp *sdp)<br> {<br> struct ast_sdp_m_line *m_line;<br> <br>@@ -1000,6 +1031,26 @@<br> }<br> <br> m_line = ast_sdp_get_m(sdp, 2);<br>+ if (validate_m_line_declined(test, m_line, "image")) {<br>+ return -1;<br>+ }<br>+<br>+ m_line = ast_sdp_get_m(sdp, 3);<br>+ if (validate_m_line_declined(test, m_line, "audio")) {<br>+ return -1;<br>+ }<br>+<br>+ m_line = ast_sdp_get_m(sdp, 4);<br>+ if (validate_m_line_declined(test, m_line, "video")) {<br>+ return -1;<br>+ }<br>+<br>+ m_line = ast_sdp_get_m(sdp, 5);<br>+ if (validate_m_line_declined(test, m_line, "image")) {<br>+ return -1;<br>+ }<br>+<br>+ m_line = ast_sdp_get_m(sdp, 6);<br> if (validate_m_line(test, m_line, "audio", 1)) {<br> return -1;<br> }<br>@@ -1018,11 +1069,18 @@<br> AST_TEST_DEFINE(sdp_negotiation_decline_incompatible)<br> {<br> static const struct sdp_format offerer_formats[] = {<br>+ /* Incompatible declined streams */<br> { AST_MEDIA_TYPE_AUDIO, "alaw" },<br> { AST_MEDIA_TYPE_VIDEO, "vp8" },<br>+ { AST_MEDIA_TYPE_IMAGE, "t38" },<br>+ /* Initially declined streams */<br>+ { AST_MEDIA_TYPE_AUDIO, "" },<br>+ { AST_MEDIA_TYPE_VIDEO, "" },<br>+ { AST_MEDIA_TYPE_IMAGE, "" },<br>+ /* Compatible stream so not all are declined */<br> { AST_MEDIA_TYPE_AUDIO, "ulaw,alaw" },<br> };<br>- static const struct sdp_format answerer_formats[] = {<br>+ static const struct sdp_format allowed_formats[] = {<br> { AST_MEDIA_TYPE_AUDIO, "ulaw" },<br> };<br> <br>@@ -1032,9 +1090,9 @@<br> info->category = "/main/sdp/";<br> info->summary = "Simulate an initial negotiation declining streams";<br> info->description =<br>- "Initial negotiation tests declining incompatible streams on the answering side.\n"<br>- "After negotiation both offerer and answerer sides should have the same\n"<br>- "expected stream types and formats.";<br>+ "Initial negotiation tests declining incompatible streams.\n"<br>+ "After negotiation both offerer and answerer sides should have\n"<br>+ "the same expected stream types and formats.";<br> return AST_TEST_NOT_RUN;<br> case TEST_EXECUTE:<br> break;<br>@@ -1042,9 +1100,10 @@<br> <br> return sdp_negotiation_completed_tests(test,<br> ARRAY_LEN(offerer_formats), offerer_formats,<br>- ARRAY_LEN(answerer_formats), answerer_formats,<br>- ARRAY_LEN(answerer_formats), answerer_formats,<br>- validate_ava_declined_sdp_streams);<br>+ 0, NULL,<br>+ ARRAY_LEN(allowed_formats), allowed_formats,<br>+ ARRAY_LEN(offerer_formats),<br>+ validate_aviavia_declined_sdp_streams);<br> }<br> <br> static int validate_aaaa_declined_sdp_streams(struct ast_test *test, const struct ast_sdp *sdp)<br>@@ -1114,6 +1173,7 @@<br> ARRAY_LEN(offerer_formats), offerer_formats,<br> 0, NULL,<br> 0, NULL,<br>+ 0,<br> validate_aaaa_declined_sdp_streams);<br> }<br> <br>@@ -1143,13 +1203,13 @@<br> }<br> <br> sdp_state_offerer = build_sdp_state(ARRAY_LEN(offerer_formats), offerer_formats,<br>- ARRAY_LEN(offerer_formats), offerer_formats, NULL);<br>+ ARRAY_LEN(offerer_formats), offerer_formats, 0, NULL);<br> if (!sdp_state_offerer) {<br> res = AST_TEST_FAIL;<br> goto end;<br> }<br> <br>- sdp_state_answerer = build_sdp_state(0, NULL, 0, NULL, NULL);<br>+ sdp_state_answerer = build_sdp_state(0, NULL, 0, NULL, 0, NULL);<br> if (!sdp_state_answerer) {<br> res = AST_TEST_FAIL;<br> goto end;<br>@@ -1245,7 +1305,7 @@<br> }<br> ast_sdp_options_set_ssrc(options, 1);<br> <br>- test_state = build_sdp_state(ARRAY_LEN(formats), formats, 0, NULL, options);<br>+ test_state = build_sdp_state(ARRAY_LEN(formats), formats, 0, NULL, 0, options);<br> if (!test_state) {<br> ast_test_status_update(test, "Failed to create SDP state\n");<br> goto end;<br>@@ -1335,11 +1395,10 @@<br> struct ast_format_cap *caps;<br> <br> caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);<br>- if (!caps) {<br>- goto fail;<br>- }<br>- if (ast_format_cap_update_by_allow_disallow(caps, desc->formats, 1) < 0) {<br>- ao2_ref(caps, -1);<br>+ if (!caps<br>+ || ast_format_cap_update_by_allow_disallow(caps, desc->formats, 1) < 0) {<br>+ ao2_cleanup(caps);<br>+ ast_stream_free(stream);<br> goto fail;<br> }<br> ast_stream_set_formats(stream, caps);<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/5896">change 5896</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/5896"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I5cb99f5010994ab0c7d9cf2d395eca23fab37b98 </div>
<div style="display:none"> Gerrit-Change-Number: 5896 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Richard Mudgett <rmudgett@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins2 </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@digium.com> </div>