<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>