<p>Mark Michelson has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/5824">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">chan_pjsip: Multistream: Modify use of ast_sip_session_media<br><br>This commit reworks the use of ast_sip_session media structures in order<br>to facilitate multistream behavior in chan_pjsip.<br><br>Prior to this commit the ast_sip_session structure contained an<br>ao2_container of ast_sip_session_media structures. This container was a<br>hash table keyed on the media type. At session creation, a session media<br>structure was pre-allocated for each supported media type and added to<br>the container. This effectively limited the allocation of session media<br>to being one per media type.<br><br>With this commit, this limitation is lifted. The following has been<br>changed:<br><br>* Rather than using an ao2_container, session medias are stored in a<br>  vector on the session. This allows for the index of the session media<br>  to be important, rather than the media type. The index of the session<br>  media vector corresponds to the index of our local SDP as well as the<br>  index of the stream topology on which the local SDP is based.<br><br>* Session media structures are not pre-allocated at session creation.<br>  Rather, they are created as needed due to the circumstances<br>  surrounding the session. Typically, session media structures are<br>  allocated based on a stream topology that will be represented by our<br>  local SDP. So typically the session media is allocated at the time of<br>  SDP creation. The big exception to this is during outbound session<br>  creation. We create the session media at that time due to assumptions<br>  made in chan_pjsip about session media already being allocated.<br><br>In addition to the primary changes, there are some changes that rippled<br>out of this change<br><br>* Because the index is important to have in the SDP handlers, some SDP<br>  handler functions have had their parameters changed. Rather than<br>  explicitly passing in SDP streams and a session media, we pass in a<br>  session, an SDP (or two), and an index. The index can be used to find<br>  the appropriate SDP stream, session media, and Asterisk stream this<br>  way, since the all correspond to the same index.<br><br>* Because a session media corresponds to an index in the SDP, this can<br>  result in a session media changing types. The most common cause of<br>  this would be for a session media changing from audio to T.38. In such<br>  a case, we need to be able to change the session media's type as<br>  necessary. Because of the type being variable, it means that we can no<br>  longer store the RTP instance and UDPTL as a union. They need to be<br>  distinct structures. This way, a session media could switch back and<br>  forth between types without causing memory problems.<br><br>NOTE: This change breaks some tests in the testsuite. Specifically, it<br>breaks the tests that make sure that Asterisk only accepts one stream of<br>each media type. The next change I will be working on will add<br>configuration values that govern the number of streams allowed on a<br>session. With that change in place, the default values will result in<br>those tests passing again. Therefore, it's not worth it for me to go to<br>special effort with this change to ensure those tests are passing.<br><br>Change-Id: I144b04f43633387b8e42a43ef3b25d7c5682b451<br>---<br>M channels/chan_pjsip.c<br>M include/asterisk/res_pjsip_session.h<br>M res/res_pjsip_sdp_rtp.c<br>M res/res_pjsip_session.c<br>M res/res_pjsip_t38.c<br>5 files changed, 377 insertions(+), 289 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/24/5824/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c<br>index f092383..bd76110 100644<br>--- a/channels/chan_pjsip.c<br>+++ b/channels/chan_pjsip.c<br>@@ -198,7 +198,7 @@<br>         struct chan_pjsip_pvt *pvt = channel->pvt;<br>         struct ast_sip_endpoint *endpoint;<br> <br>-        if (!pvt || !channel->session || !pvt->media[SIP_MEDIA_VIDEO]->rtp) {<br>+       if (!pvt || !channel->session || !pvt->media[SIP_MEDIA_VIDEO] || !pvt->media[SIP_MEDIA_VIDEO]->rtp) {<br>             return AST_RTP_GLUE_RESULT_FORBID;<br>    }<br> <br>@@ -461,6 +461,35 @@<br>    return ast_format_cap_iscompatible(cap1, cap2);<br> }<br> <br>+/*!<br>+ * \brief Get the first ast_sip_session_media of a certain media type.<br>+ *<br>+ * XXX For multistream, we'll likely want to change this to be a function<br>+ * to retrieve the nth session media instead of the first.<br>+ *<br>+ * \param session The session on which the session media lives<br>+ * \param type The type of the session media<br>+ */<br>+static struct ast_sip_session_media *get_first_session_media_by_type(<br>+   const struct ast_sip_session *session, enum ast_media_type type)<br>+{<br>+ int i;<br>+<br>+    for (i = 0; i < AST_VECTOR_SIZE(&session->media); ++i) {<br>+           struct ast_sip_session_media *session_media;<br>+<br>+              session_media = AST_VECTOR_GET(&session->media, i);<br>+<br>+                ast_assert(session_media != NULL);<br>+<br>+                if (session_media->type == type) {<br>+                        return ao2_bump(session_media);<br>+              }<br>+    }<br>+<br>+ return NULL;<br>+}<br>+<br> /*! \brief Function called to create a new PJSIP Asterisk channel */<br> static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int state, const char *exten, const char *title, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *cid_name)<br> {<br>@@ -569,11 +598,12 @@<br>    ast_channel_stage_snapshot_done(chan);<br>        ast_channel_unlock(chan);<br> <br>- /* If res_pjsip_session is ever updated to create/destroy ast_sip_session_media<br>-       * during a call such as if multiple same-type stream support is introduced,<br>-  * these will need to be recaptured as well */<br>-       pvt->media[SIP_MEDIA_AUDIO] = ao2_find(session->media, "audio", OBJ_KEY);<br>-    pvt->media[SIP_MEDIA_VIDEO] = ao2_find(session->media, "video", OBJ_KEY);<br>+    /* XXX For multistream, the chan_pjsip_pvt structure will be completely<br>+       * redone so it's not a fixed size array. It may have multiple audio / video<br>+      * streams<br>+    */<br>+  pvt->media[SIP_MEDIA_AUDIO] = get_first_session_media_by_type(session, AST_MEDIA_TYPE_AUDIO);<br>+     pvt->media[SIP_MEDIA_VIDEO] = get_first_session_media_by_type(session, AST_MEDIA_TYPE_VIDEO);<br>      set_channel_on_rtp_instance(pvt, ast_channel_uniqueid(chan));<br> <br>      return chan;<br>@@ -1340,20 +1370,15 @@<br> }<br> <br> /*! \brief Callback which changes the value of locally held on the media stream */<br>-static int local_hold_set_state(void *obj, void *arg, int flags)<br>+static void local_hold_set_state(struct ast_sip_session_media *session_media, unsigned int held)<br> {<br>-  struct ast_sip_session_media *session_media = obj;<br>-   unsigned int *held = arg;<br>-<br>- session_media->locally_held = *held;<br>-<br>-   return 0;<br>+    session_media->locally_held = held;<br> }<br> <br> /*! \brief Update local hold state and send a re-INVITE with the new SDP */<br> static int remote_send_hold_refresh(struct ast_sip_session *session, unsigned int held)<br> {<br>-    ao2_callback(session->media, OBJ_NODATA, local_hold_set_state, &held);<br>+        AST_VECTOR_CALLBACK_VOID(&session->media, local_hold_set_state, held);<br>         ast_sip_session_refresh(session, NULL, NULL, NULL, AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1);<br>         ao2_ref(session, -1);<br> <br>diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h<br>index 9bffa16..8829828 100644<br>--- a/include/asterisk/res_pjsip_session.h<br>+++ b/include/asterisk/res_pjsip_session.h<br>@@ -28,6 +28,8 @@<br> #include "asterisk/netsock2.h"<br> /* Needed for ast_sdp_srtp struct */<br> #include "asterisk/sdp_srtp.h"<br>+/* Needed for ast_media_type */<br>+#include "asterisk/codec.h"<br> <br> /* Forward declarations */<br> struct ast_sip_endpoint;<br>@@ -61,12 +63,10 @@<br>  * \brief A structure containing SIP session media information<br>  */<br> struct ast_sip_session_media {<br>-  union {<br>-              /*! \brief RTP instance itself */<br>-            struct ast_rtp_instance *rtp;<br>-                /*! \brief UDPTL instance itself */<br>-          struct ast_udptl *udptl;<br>-     };<br>+   /*! \brief RTP instance itself */<br>+    struct ast_rtp_instance *rtp;<br>+        /*! \brief UDPTL instance itself */<br>+  struct ast_udptl *udptl;<br>      /*! \brief Direct media address */<br>    struct ast_sockaddr direct_media_addr;<br>        /*! \brief SDP handler that setup the RTP */<br>@@ -87,8 +87,8 @@<br>       unsigned int locally_held:1;<br>  /*! \brief Does remote support rtcp_mux */<br>    unsigned int remote_rtcp_mux:1;<br>-      /*! \brief Stream type this session media handles */<br>- char stream_type[1];<br>+ /*! \brief Media type of this session media */<br>+       enum ast_media_type type;<br> };<br> <br> /*!<br>@@ -124,7 +124,7 @@<br>  /*! Datastores added to the session by supplements to the session */<br>  struct ao2_container *datastores;<br>     /*! Media streams */<br>- struct ao2_container *media;<br>+ AST_VECTOR(, struct ast_sip_session_media *) media;<br>   /*! Serializer for tasks relating to this SIP session */<br>      struct ast_taskprocessor *serializer;<br>         /*! Non-null if the session serializer is suspended or being suspended. */<br>@@ -311,14 +311,13 @@<br>     /*!<br>    * \brief Set session details based on a stream in an incoming SDP offer or answer<br>     * \param session The session for which the media is being negotiated<br>-         * \param session_media The media to be setup for this session<br>         * \param sdp The entire SDP. Useful for getting "global" information, such as connections or attributes<br>-    * \param stream The stream on which to operate<br>+       * \param index The index for the session media, Asterisk stream, and PJMEDIA stream being negotiated<br>          * \retval 0 The stream was not handled by this handler. If there are other registered handlers for this stream type, they will be called.<br>     * \retval <0 There was an error encountered. No further operation will take place and the current negotiation will be abandoned.<br>   * \retval >0 The stream was handled by this handler. No further handler of this stream type will be called.<br>        */<br>-  int (*negotiate_incoming_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream);<br>+   int (*negotiate_incoming_sdp_stream)(struct ast_sip_session *session, const struct pjmedia_sdp_session *sdp, int index);<br>      /*!<br>    * \brief Create an SDP media stream and add it to the outgoing SDP offer or answer<br>    * \param session The session for which media is being added<br>@@ -334,11 +333,12 @@<br>    * \param session The session for which media is being added<br>   * \param session_media The media to be setup for this session<br>         * \param sdp The entire SDP as currently built<br>+       * \param stream The stream that is to be added to the outgoing SDP<br>    * \retval 0 This handler has no stream to add. If there are other registered handlers for this stream type, they will be called.<br>      * \retval <0 There was an error encountered. No further operation will take place and the current SDP negotiation will be abandoned.<br>       * \retval >0 The handler has a stream to be added to the SDP. No further handler of this stream type will be called.<br>       */<br>-  int (*create_outgoing_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct pjmedia_sdp_session *sdp);<br>+    int (*create_outgoing_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct pjmedia_sdp_session *sdp, struct ast_stream *stream);<br>  /*!<br>    * \brief Update media stream with external address if applicable<br>      * \param tdata The outgoing message itself<br>@@ -349,17 +349,15 @@<br>    /*!<br>    * \brief Apply a negotiated SDP media stream<br>          * \param session The session for which media is being applied<br>-        * \param session_media The media to be setup for this session<br>         * \param local The entire local negotiated SDP<br>-       * \param local_stream The local stream which to apply<br>         * \param remote The entire remote negotiated SDP<br>-     * \param remote_stream The remote stream which to apply<br>+      * \param index The index of the session media, SDP streams, and Asterisk streams<br>      * \retval 0 The stream was not applied by this handler. If there are other registered handlers for this stream type, they will be called.<br>     * \retval <0 There was an error encountered. No further operation will take place and the current application will be abandoned.<br>   * \retval >0 The stream was handled by this handler. No further handler of this stream type will be called.<br>        */<br>-  int (*apply_negotiated_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,<br>-              const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream);<br>+    int (*apply_negotiated_sdp_stream)(struct ast_sip_session *session, const struct pjmedia_sdp_session *local,<br>+         const struct pjmedia_sdp_session *remote, int index);<br>         /*!<br>    * \brief Stop a session_media created by this handler but do not destroy resources<br>    * \param session The session for which media is being stopped<br>diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c<br>index 7b5f297..52245c8 100644<br>--- a/res/res_pjsip_sdp_rtp.c<br>+++ b/res/res_pjsip_sdp_rtp.c<br>@@ -68,18 +68,6 @@<br> static const char STR_VIDEO[] = "video";<br> static const int FD_VIDEO = 2;<br> <br>-/*! \brief Retrieves an ast_format_type based on the given stream_type */<br>-static enum ast_media_type stream_to_media_type(const char *stream_type)<br>-{<br>-  if (!strcasecmp(stream_type, STR_AUDIO)) {<br>-           return AST_MEDIA_TYPE_AUDIO;<br>- } else if (!strcasecmp(stream_type, STR_VIDEO)) {<br>-            return AST_MEDIA_TYPE_VIDEO;<br>- }<br>-<br>- return 0;<br>-}<br>-<br> /*! \brief Get the starting descriptor for a media type */<br> static int media_type_to_fdno(enum ast_media_type media_type)<br> {<br>@@ -242,11 +230,11 @@<br>              ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND);<br>      }<br> <br>- if (!strcmp(session_media->stream_type, STR_AUDIO) &&<br>+     if (session_media->type == AST_MEDIA_TYPE_AUDIO &&<br>                         (session->endpoint->media.tos_audio || session->endpoint->media.cos_audio)) {<br>             ast_rtp_instance_set_qos(session_media->rtp, session->endpoint->media.tos_audio,<br>                             session->endpoint->media.cos_audio, "SIP RTP Audio");<br>-        } else if (!strcmp(session_media->stream_type, STR_VIDEO) &&<br>+      } else if (session_media->type == AST_MEDIA_TYPE_VIDEO &&<br>                  (session->endpoint->media.tos_video || session->endpoint->media.cos_video)) {<br>             ast_rtp_instance_set_qos(session_media->rtp, session->endpoint->media.tos_video,<br>                             session->endpoint->media.cos_video, "SIP RTP Video");<br>@@ -336,34 +324,33 @@<br> static int set_caps(struct ast_sip_session *session,<br>   struct ast_sip_session_media *session_media,<br>  const struct pjmedia_sdp_media *stream,<br>-      int is_offer)<br>+        int is_offer, int index)<br> {<br>  RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);<br>   RAII_VAR(struct ast_format_cap *, peer, NULL, ao2_cleanup);<br>   RAII_VAR(struct ast_format_cap *, joint, NULL, ao2_cleanup);<br>- enum ast_media_type media_type = stream_to_media_type(session_media->stream_type);<br>+        RAII_VAR(struct ast_format_cap *, endpoint_caps, NULL, ao2_cleanup);<br>+ enum ast_media_type media_type = session_media->type;<br>      struct ast_rtp_codecs codecs = AST_RTP_CODECS_NULL_INIT;<br>      int fmts = 0;<br>         int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&<br>          ast_format_cap_count(session->direct_media_cap);<br>   int dsp_features = 0;<br>-        struct ast_stream *req_stream;<br>+       struct ast_stream *req_stream = NULL;<br>         struct ast_format_cap *req_caps;<br>-     struct ast_stream *endpoint_stream;<br>-  struct ast_format_cap *endpoint_caps;<br> <br>      if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||<br>        !(peer = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||<br>        !(joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {<br>-          ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", session_media->stream_type);<br>+         ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n",<br>+                 ast_codec_media_type2str(session_media->type));<br>            return -1;<br>    }<br> <br>- endpoint_stream = ast_stream_topology_get_first_stream_by_type(session->endpoint->media.codecs, media_type);<br>-   if (!endpoint_stream) {<br>+      endpoint_caps = ast_format_cap_from_stream_topology(session->endpoint->media.codecs);<br>+  if (!endpoint_caps) {<br>                 return -1;<br>    }<br>-    endpoint_caps = ast_stream_get_formats(endpoint_stream);<br> <br>   /* get the endpoint capabilities */<br>   if (direct_media_enabled) {<br>@@ -384,7 +371,7 @@<br> <br>           ast_rtp_codecs_payloads_destroy(&codecs);<br>                 ast_log(LOG_NOTICE, "No joint capabilities for '%s' media stream between our configuration(%s) and incoming SDP(%s)\n",<br>-                    session_media->stream_type,<br>+                       ast_codec_media_type2str(session_media->type),<br>                     ast_format_cap_get_names(caps, &usbuf),<br>                   ast_format_cap_get_names(peer, &thembuf));<br>                return -1;<br>@@ -400,14 +387,16 @@<br>     ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session_media->rtp),<br>                 session_media->rtp);<br> <br>-   req_stream = ast_stream_topology_get_first_stream_by_type(session->req_topology, media_type);<br>+     if (index < ast_stream_topology_get_count(session->req_topology)) {<br>+            req_stream = ast_stream_topology_get_stream(session->req_topology, index);<br>+        }<br>     if (!req_stream) {<br>            req_stream = ast_stream_alloc(ast_codec_media_type2str(media_type), media_type);<br>              if (!req_stream) {<br>                    return -1;<br>            }<br>             ast_stream_set_formats(req_stream, joint);<br>-           ast_stream_topology_append_stream(session->req_topology, req_stream);<br>+             ast_stream_topology_set_stream(session->req_topology, index, req_stream);<br>  } else {<br>              req_caps = ast_stream_get_formats(req_stream);<br>                ast_format_cap_replace_from_cap(req_caps, joint, AST_MEDIA_TYPE_UNKNOWN);<br>@@ -977,24 +966,28 @@<br> }<br> <br> /*! \brief Function which negotiates an incoming media stream */<br>-static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,<br>-                                   const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)<br>+static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,<br>+  const pjmedia_sdp_session *sdp, int index)<br> {<br>        char host[NI_MAXHOST];<br>        RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);<br>-      enum ast_media_type media_type = stream_to_media_type(session_media->stream_type);<br>+        struct ast_sip_session_media *session_media = AST_VECTOR_GET(&session->media, index);<br>+ pjmedia_sdp_media *stream = sdp->media[index];<br>+    enum ast_media_type media_type = session_media->type;<br>      enum ast_sip_session_media_encryption encryption = AST_SIP_MEDIA_ENCRYPT_NONE;<br>        int res;<br> <br>   /* If port is 0, ignore this media stream */<br>  if (!stream->desc.port) {<br>-         ast_debug(3, "Media stream '%s' is already declined\n", session_media->stream_type);<br>+            ast_debug(3, "Media stream '%s' is already declined\n",<br>+                    ast_codec_media_type2str(session_media->type));<br>            return 0;<br>     }<br> <br>  /* If no type formats have been configured reject this stream */<br>      if (!ast_stream_topology_get_first_stream_by_type(session->endpoint->media.codecs, media_type)) {<br>-              ast_debug(3, "Endpoint has no codecs for media type '%s', declining stream\n", session_media->stream_type);<br>+             ast_debug(3, "Endpoint has no codecs for media type '%s', declining stream\n",<br>+                     ast_codec_media_type2str(session_media->type));<br>            return 0;<br>     }<br> <br>@@ -1049,7 +1042,7 @@<br>           pj_strdup(session->inv_session->pool, &session_media->transport, &stream->desc.transport);<br>        }<br> <br>- if (set_caps(session, session_media, stream, 1)) {<br>+   if (set_caps(session, session_media, stream, 1, index)) {<br>             return 0;<br>     }<br>     return 1;<br>@@ -1170,7 +1163,7 @@<br> <br> /*! \brief Function which creates an outgoing stream */<br> static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,<br>-                                struct pjmedia_sdp_session *sdp)<br>+                                     struct pjmedia_sdp_session *sdp, struct ast_stream *stream)<br> {<br>         pj_pool_t *pool = session->inv_session->pool_prov;<br>      static const pj_str_t STR_IN = { "IN", 2 };<br>@@ -1189,27 +1182,14 @@<br>        int min_packet_size = 0, max_packet_size = 0;<br>         int rtp_code;<br>         RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);<br>-  enum ast_media_type media_type = stream_to_media_type(session_media->stream_type);<br>-        int use_override_prefs = ast_stream_topology_get_count(session->req_topology);<br>-    /* XXX Getting first stream by media type is not going to cut it when we're creating multiple streams of the same type */<br>-        struct ast_stream *req_stream = ast_stream_topology_get_first_stream_by_type(session->req_topology, media_type);<br>-  struct ast_stream *endpoint_config_stream = ast_stream_topology_get_first_stream_by_type(session->endpoint->media.codecs, media_type);<br>- struct ast_format_cap *req_caps;<br>-     struct ast_format_cap *endpoint_config_caps;<br>+ enum ast_media_type media_type = session_media->type;<br> <br>   int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&<br>          ast_format_cap_count(session->direct_media_cap);<br> <br>-       if ((use_override_prefs && !req_stream) ||<br>-       (!use_override_prefs && !endpoint_config_stream)) {<br>-              /* If no type formats are configured don't add a stream */<br>-               return 0;<br>-    } else if (!session_media->rtp && create_rtp(session, session_media)) {<br>+   if (!session_media->rtp && create_rtp(session, session_media)) {<br>           return -1;<br>    }<br>-<br>- req_caps = req_stream ? ast_stream_get_formats(req_stream) : NULL;<br>-   endpoint_config_caps = ast_stream_get_formats(endpoint_config_stream);<br> <br>     set_ice_components(session, session_media);<br>   enable_rtcp(session, session_media, NULL);<br>@@ -1223,7 +1203,7 @@<br>             return -1;<br>    }<br> <br>- media->desc.media = pj_str(session_media->stream_type);<br>+        pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(session_media->type));<br>        if (pj_strlen(&session_media->transport)) {<br>            /* If a transport has already been specified use it */<br>                media->desc.transport = session_media->transport;<br>@@ -1246,7 +1226,8 @@<br>        }<br> <br>  if (ast_strlen_zero(hostip)) {<br>-               ast_log(LOG_ERROR, "No local host IP available for stream %s\n", session_media->stream_type);<br>+           ast_log(LOG_ERROR, "No local host IP available for stream %s\n",<br>+                   ast_codec_media_type2str(session_media->type));<br>            return -1;<br>    }<br> <br>@@ -1272,17 +1253,22 @@<br>         add_ice_to_stream(session, session_media, pool, media);<br> <br>    if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {<br>-           ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", session_media->stream_type);<br>+         ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n",<br>+                 ast_codec_media_type2str(session_media->type));<br>            return -1;<br>    }<br> <br>  if (direct_media_enabled) {<br>+          struct ast_format_cap *endpoint_config_caps;<br>+<br>+              endpoint_config_caps = ast_format_cap_from_stream_topology(session->endpoint->media.codecs);<br>+           if (!endpoint_config_caps) {<br>+                 return -1;<br>+           }<br>             ast_format_cap_get_compatible(endpoint_config_caps, session->direct_media_cap, caps);<br>-     } else if (!req_caps || !ast_format_cap_count(req_caps) ||<br>-           !ast_format_cap_iscompatible(req_caps, endpoint_config_caps)) {<br>-              ast_format_cap_append_from_cap(caps, endpoint_config_caps, media_type);<br>+              ao2_ref(endpoint_config_caps, -1);<br>    } else {<br>-             ast_format_cap_append_from_cap(caps, req_caps, media_type);<br>+          ast_format_cap_append_from_cap(caps, ast_stream_get_formats(stream), media_type);<br>     }<br> <br>  for (index = 0; index < ast_format_cap_count(caps); ++index) {<br>@@ -1385,12 +1371,15 @@<br>    return 1;<br> }<br> <br>-static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,<br>-                                     const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,<br>-                                       const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream)<br>+static int apply_negotiated_sdp_stream(struct ast_sip_session *session,<br>+    const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_session *remote,<br>+   int index)<br> {<br>        RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);<br>-      enum ast_media_type media_type = stream_to_media_type(session_media->stream_type);<br>+        struct ast_sip_session_media *session_media = AST_VECTOR_GET(&session->media, index);<br>+ struct pjmedia_sdp_media *local_stream = local->media[index];<br>+     struct pjmedia_sdp_media *remote_stream = remote->media[index];<br>+   enum ast_media_type media_type = session_media->type;<br>      char host[NI_MAXHOST];<br>        int fdno, res;<br> <br>@@ -1441,7 +1430,7 @@<br>      /* Apply connection information to the RTP instance */<br>        ast_sockaddr_set_port(addrs, remote_stream->desc.port);<br>    ast_rtp_instance_set_remote_address(session_media->rtp, addrs);<br>-   if (set_caps(session, session_media, remote_stream, 0)) {<br>+    if (set_caps(session, session_media, remote_stream, 0, index)) {<br>              return 1;<br>     }<br> <br>@@ -1493,7 +1482,7 @@<br>   session_media->encryption = session->endpoint->media.rtp.encryption;<br> <br>      if (session->endpoint->media.rtp.keepalive > 0 &&<br>-                   stream_to_media_type(session_media->stream_type) == AST_MEDIA_TYPE_AUDIO) {<br>+                       session_media->type == AST_MEDIA_TYPE_AUDIO) {<br>             ast_rtp_instance_set_keepalive(session_media->rtp, session->endpoint->media.rtp.keepalive);<br>          /* Schedule the initial keepalive early in case this is being used to punch holes through<br>              * a NAT. This way there won't be an awkward delay before media starts flowing in some<br>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c<br>index 41ef0ce..56acbd3 100644<br>--- a/res/res_pjsip_session.c<br>+++ b/res/res_pjsip_session.c<br>@@ -54,6 +54,9 @@<br> #define MOD_DATA_ON_RESPONSE "on_response"<br> #define MOD_DATA_NAT_HOOK "nat_hook"<br> <br>+/* Most common case is one audio and one video stream */<br>+#define DEFAULT_NUM_SESSION_MEDIA 2<br>+<br> /* Some forward declarations */<br> static void handle_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata);<br> static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata,<br>@@ -102,23 +105,6 @@<br>         const char *stream_type2 = flags & OBJ_KEY ? arg : handler_list2->stream_type;<br> <br>      return strcmp(handler_list1->stream_type, stream_type2) ? 0 : CMP_MATCH | CMP_STOP;<br>-}<br>-<br>-static int session_media_hash(const void *obj, int flags)<br>-{<br>-        const struct ast_sip_session_media *session_media = obj;<br>-     const char *stream_type = flags & OBJ_KEY ? obj : session_media->stream_type;<br>-<br>-      return ast_str_hash(stream_type);<br>-}<br>-<br>-static int session_media_cmp(void *obj, void *arg, int flags)<br>-{<br>- struct ast_sip_session_media *session_media1 = obj;<br>-  struct ast_sip_session_media *session_media2 = arg;<br>-  const char *stream_type2 = flags & OBJ_KEY ? arg : session_media2->stream_type;<br>-<br>-    return strcmp(session_media1->stream_type, stream_type2) ? 0 : CMP_MATCH | CMP_STOP;<br> }<br> <br> int ast_sip_session_register_sdp_handler(struct ast_sip_session_sdp_handler *handler, const char *stream_type)<br>@@ -208,6 +194,71 @@<br>         session_media->handler = handler;<br> }<br> <br>+static int stream_destroy(void *obj, void *arg, int flags)<br>+{<br>+ struct sdp_handler_list *handler_list = obj;<br>+ struct ast_sip_session_media *session_media = arg;<br>+   struct ast_sip_session_sdp_handler *handler;<br>+<br>+      AST_LIST_TRAVERSE(&handler_list->list, handler, next) {<br>+               handler->stream_destroy(session_media);<br>+   }<br>+<br>+ return 0;<br>+}<br>+<br>+static void session_media_dtor(void *obj)<br>+{<br>+     struct ast_sip_session_media *session_media = obj;<br>+<br>+        /* It is possible for multiple handlers to have allocated memory on the<br>+       * session media (usually through a stream changing types). Therefore, we<br>+     * traverse all the SDP handlers and let them all call stream_destroy on<br>+      * the session_media<br>+  */<br>+  ao2_callback(sdp_handlers, 0, stream_destroy, session_media);<br>+<br>+     if (session_media->srtp) {<br>+                ast_sdp_srtp_destroy(session_media->srtp);<br>+        }<br>+}<br>+<br>+/*!<br>+ * \brief Allocate an ast_session_media and add it to the session's vector.<br>+ *<br>+ * This allocates a session media of the specified type. The position argument<br>+ * determines where in the vector that the new session media will be inserted.<br>+ * If the position is less than 0, then the session media is appended to the end<br>+ * of the vector. Otherwise, it is inserted at the given index.<br>+ *<br>+ * \note The returned ast_session_media is the reference held by the vector. Callers<br>+ * of this function must NOT decrement the refcount of the session media.<br>+ *<br>+ * \param session Session on which to add the session media<br>+ * \param type The type of the session media<br>+ * \param position Position at which to insert the new session media.<br>+ */<br>+static struct ast_sip_session_media *add_session_media(struct ast_sip_session *session,<br>+       enum ast_media_type type, int position)<br>+{<br>+  struct ast_sip_session_media *session_media;<br>+<br>+      session_media = ao2_alloc(sizeof(*session_media), session_media_dtor);<br>+       if (!session_media) {<br>+                return NULL;<br>+ }<br>+    session_media->encryption = session->endpoint->media.rtp.encryption;<br>+        session_media->keepalive_sched_id = -1;<br>+   session_media->timeout_sched_id = -1;<br>+     session_media->type = type;<br>+       if (position >= 0) {<br>+              AST_VECTOR_REPLACE(&session->media, position, session_media);<br>+ } else {<br>+             AST_VECTOR_APPEND(&session->media, session_media);<br>+    }<br>+    return session_media;<br>+}<br>+<br> static int handle_incoming_sdp(struct ast_sip_session *session, const pjmedia_sdp_session *sdp)<br> {<br>    int i;<br>@@ -223,32 +274,43 @@<br>                 char media[20];<br>               struct ast_sip_session_sdp_handler *handler;<br>          RAII_VAR(struct sdp_handler_list *, handler_list, NULL, ao2_cleanup);<br>-                RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup);<br>+          struct ast_sip_session_media *session_media = NULL;<br>           int res;<br> <br>           /* We need a null-terminated version of the media string */<br>           ast_copy_pj_str(media, &sdp->media[i]->desc.media, sizeof(media));<br> <br>-              session_media = ao2_find(session->media, media, OBJ_KEY);<br>+         if (i < AST_VECTOR_SIZE(&session->media)) {<br>+                        session_media = AST_VECTOR_GET(&session->media, i);<br>+                   if (ast_media_type_from_str(media) != session_media->type) {<br>+                              /* The stream may have changed types (e.g. from audio to T.38). In this<br>+                               * case, we can just correct the type and clear the cached SDP handler<br>+                                * on this session media. The session media destructor will ensure that<br>+                               * all SDP handlers get a chance to free the memory they allocated<br>+                            */<br>+                          session_media->type = ast_media_type_from_str(media);<br>+                             session_media->handler = NULL;<br>+                    }<br>+            }<br>             if (!session_media) {<br>-                        /* if the session_media doesn't exist, there weren't<br>-                  * any handlers at the time of its creation */<br>-                       continue;<br>+                    session_media = add_session_media(session, ast_media_type_from_str(media), i);<br>+                       if (!session_media) {<br>+                                return -1;<br>+                   }<br>             }<br> <br>          if (session_media->handler) {<br>                      handler = session_media->handler;<br>                  ast_debug(1, "Negotiating incoming SDP media stream '%s' using %s SDP handler\n",<br>-                          session_media->stream_type,<br>+                               ast_codec_media_type2str(session_media->type),<br>                             session_media->handler->id);<br>-                   res = handler->negotiate_incoming_sdp_stream(session, session_media, sdp,<br>-                         sdp->media[i]);<br>+                   res = handler->negotiate_incoming_sdp_stream(session, sdp, i);<br>                     if (res < 0) {<br>                             /* Catastrophic failure. Abort! */<br>                            return -1;<br>                    } else if (res > 0) {<br>                              ast_debug(1, "Media stream '%s' handled by %s\n",<br>-                                  session_media->stream_type,<br>+                                       ast_codec_media_type2str(session_media->type),<br>                                     session_media->handler->id);<br>                            /* Handled by this handler. Move to the next stream */<br>                                handled = 1;<br>@@ -266,17 +328,16 @@<br>                           continue;<br>                     }<br>                     ast_debug(1, "Negotiating incoming SDP media stream '%s' using %s SDP handler\n",<br>-                          session_media->stream_type,<br>+                               ast_codec_media_type2str(session_media->type),<br>                             handler->id);<br>-                     res = handler->negotiate_incoming_sdp_stream(session, session_media, sdp,<br>-                         sdp->media[i]);<br>+                   res = handler->negotiate_incoming_sdp_stream(session, sdp, i);<br>                     if (res < 0) {<br>                             /* Catastrophic failure. Abort! */<br>                            return -1;<br>                    }<br>                     if (res > 0) {<br>                             ast_debug(1, "Media stream '%s' handled by %s\n",<br>-                                  session_media->stream_type,<br>+                                       ast_codec_media_type2str(session_media->type),<br>                                     handler->id);<br>                              /* Handled by this handler. Move to the next stream */<br>                                session_media_set_handler(session_media, handler);<br>@@ -291,110 +352,93 @@<br>    return 0;<br> }<br> <br>-struct handle_negotiated_sdp_cb {<br>- struct ast_sip_session *session;<br>-     const pjmedia_sdp_session *local;<br>-    const pjmedia_sdp_session *remote;<br>-};<br>-<br>-static int handle_negotiated_sdp_session_media(void *obj, void *arg, int flags)<br>+static int handle_negotiated_sdp_session_media(struct ast_sip_session_media *session_media,<br>+           struct ast_sip_session *session, const pjmedia_sdp_session *local,<br>+           const pjmedia_sdp_session *remote, int index)<br> {<br>-    struct ast_sip_session_media *session_media = obj;<br>-   struct handle_negotiated_sdp_cb *callback_data = arg;<br>-        struct ast_sip_session *session = callback_data->session;<br>- const pjmedia_sdp_session *local = callback_data->local;<br>-  const pjmedia_sdp_session *remote = callback_data->remote;<br>-        int i;<br>+       /* See if there are registered handlers for this media stream type */<br>+        char media[20];<br>+      struct ast_sip_session_sdp_handler *handler;<br>+ RAII_VAR(struct sdp_handler_list *, handler_list, NULL, ao2_cleanup);<br>+        int res;<br> <br>-  for (i = 0; i < local->media_count; ++i) {<br>-             /* See if there are registered handlers for this media stream type */<br>-                char media[20];<br>-              struct ast_sip_session_sdp_handler *handler;<br>-         RAII_VAR(struct sdp_handler_list *, handler_list, NULL, ao2_cleanup);<br>-                int res;<br>+     /* We need a null-terminated version of the media string */<br>+  ast_copy_pj_str(media, &local->media[index]->desc.media, sizeof(media));<br> <br>-                if (!remote->media[i]) {<br>-                  continue;<br>-            }<br>-<br>-         /* We need a null-terminated version of the media string */<br>-          ast_copy_pj_str(media, &local->media[i]->desc.media, sizeof(media));<br>-<br>-            /* stream type doesn't match the one we're looking to fill */<br>-                if (strcasecmp(session_media->stream_type, media)) {<br>-                      continue;<br>-            }<br>-<br>-         handler = session_media->handler;<br>-         if (handler) {<br>-                       ast_debug(1, "Applying negotiated SDP media stream '%s' using %s SDP handler\n",<br>-                           session_media->stream_type,<br>+       handler = session_media->handler;<br>+ if (handler) {<br>+               ast_debug(1, "Applying negotiated SDP media stream '%s' using %s SDP handler\n",<br>+                   ast_codec_media_type2str(session_media->type),<br>+                    handler->id);<br>+             res = handler->apply_negotiated_sdp_stream(session, local, remote, index);<br>+                if (res >= 0) {<br>+                   ast_debug(1, "Applied negotiated SDP media stream '%s' using %s SDP handler\n",<br>+                            ast_codec_media_type2str(session_media->type),<br>                             handler->id);<br>-                     res = handler->apply_negotiated_sdp_stream(session, session_media, local,<br>-                         local->media[i], remote, remote->media[i]);<br>-                    if (res >= 0) {<br>-                           ast_debug(1, "Applied negotiated SDP media stream '%s' using %s SDP handler\n",<br>-                                    session_media->stream_type,<br>-                                       handler->id);<br>-                             return CMP_MATCH;<br>-                    }<br>                     return 0;<br>             }<br>+            return -1;<br>+   }<br> <br>-         handler_list = ao2_find(sdp_handlers, media, OBJ_KEY);<br>-               if (!handler_list) {<br>-                 ast_debug(1, "No registered SDP handlers for media type '%s'\n", media);<br>+   handler_list = ao2_find(sdp_handlers, media, OBJ_KEY);<br>+       if (!handler_list) {<br>+         ast_debug(1, "No registered SDP handlers for media type '%s'\n", media);<br>+           return -1;<br>+   }<br>+    AST_LIST_TRAVERSE(&handler_list->list, handler, next) {<br>+               if (handler == session_media->handler) {<br>                   continue;<br>             }<br>-            AST_LIST_TRAVERSE(&handler_list->list, handler, next) {<br>-                       if (handler == session_media->handler) {<br>-                          continue;<br>-                    }<br>-                    ast_debug(1, "Applying negotiated SDP media stream '%s' using %s SDP handler\n",<br>-                           session_media->stream_type,<br>+               ast_debug(1, "Applying negotiated SDP media stream '%s' using %s SDP handler\n",<br>+                   ast_codec_media_type2str(session_media->type),<br>+                    handler->id);<br>+             res = handler->apply_negotiated_sdp_stream(session, local, remote, index);<br>+                if (res < 0) {<br>+                    /* Catastrophic failure. Abort! */<br>+                   return -1;<br>+           }<br>+            if (res > 0) {<br>+                    ast_debug(1, "Applied negotiated SDP media stream '%s' using %s SDP handler\n",<br>+                            ast_codec_media_type2str(session_media->type),<br>                             handler->id);<br>-                     res = handler->apply_negotiated_sdp_stream(session, session_media, local,<br>-                         local->media[i], remote, remote->media[i]);<br>-                    if (res < 0) {<br>-                            /* Catastrophic failure. Abort! */<br>-                           return 0;<br>-                    }<br>-                    if (res > 0) {<br>-                            ast_debug(1, "Applied negotiated SDP media stream '%s' using %s SDP handler\n",<br>-                                    session_media->stream_type,<br>-                                       handler->id);<br>-                             /* Handled by this handler. Move to the next stream */<br>-                               session_media_set_handler(session_media, handler);<br>-                           return CMP_MATCH;<br>-                    }<br>+                    /* Handled by this handler. Move to the next stream */<br>+                       session_media_set_handler(session_media, handler);<br>+                   return 0;<br>             }<br>     }<br> <br>  if (session_media->handler && session_media->handler->stream_stop) {<br>                 ast_debug(1, "Stopping SDP media stream '%s' as it is not currently negotiated\n",<br>-                 session_media->stream_type);<br>+                      ast_codec_media_type2str(session_media->type));<br>            session_media->handler->stream_stop(session_media);<br>     }<br> <br>- return CMP_MATCH;<br>+    return 0;<br> }<br> <br> static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote)<br> {<br>-        RAII_VAR(struct ao2_iterator *, successful, NULL, ao2_iterator_cleanup);<br>-     struct handle_negotiated_sdp_cb callback_data = {<br>-            .session = session,<br>-          .local = local,<br>-              .remote = remote,<br>-    };<br>+   int i;<br> <br>-    successful = ao2_callback(session->media, OBJ_MULTIPLE, handle_negotiated_sdp_session_media, &callback_data);<br>- if (successful && ao2_iterator_count(successful) == ao2_container_count(session->media)) {<br>-                /* Nothing experienced a catastrophic failure */<br>-             ast_queue_frame(session->channel, &ast_null_frame);<br>-           return 0;<br>+    for (i = 0; i < local->media_count; ++i) {<br>+             struct ast_sip_session_media *session_media;<br>+         if (!remote->media[i]) {<br>+                  continue;<br>+            }<br>+<br>+         /* If we're handling negotiated streams, then we should already have set<br>+          * up session media instances that correspond to the local SDP, and there should<br>+              * be the same number of session medias as there are local streams<br>+            */<br>+          ast_assert(i < AST_VECTOR_SIZE(&session->media));<br>+<br>+               session_media = AST_VECTOR_GET(&session->media, i);<br>+           if (handle_negotiated_sdp_session_media(session_media, session, local, remote, i)) {<br>+                 return -1;<br>+           }<br>     }<br>-    return -1;<br>+<br>+        return 0;<br> }<br> <br> AST_RWLIST_HEAD_STATIC(session_supplements, ast_sip_session_supplement);<br>@@ -998,17 +1042,29 @@<br>           char media[20];<br>               struct ast_sip_session_sdp_handler *handler;<br>          RAII_VAR(struct sdp_handler_list *, handler_list, NULL, ao2_cleanup);<br>-                RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup);<br>+          struct ast_sip_session_media *session_media = NULL;<br>           enum ast_sip_session_sdp_stream_defer res;<br> <br>                 /* We need a null-terminated version of the media string */<br>           ast_copy_pj_str(media, &sdp->media[i]->desc.media, sizeof(media));<br> <br>-              session_media = ao2_find(session->media, media, OBJ_KEY);<br>+         if (i < AST_VECTOR_SIZE(&session->media)) {<br>+                        session_media = AST_VECTOR_GET(&session->media, i);<br>+                   if (ast_media_type_from_str(media) != session_media->type) {<br>+                              /* The stream may have changed types (e.g. from audio to T.38). In this<br>+                               * case, we can just correct the type and clear the cached SDP handler<br>+                                * on this session media. The session media destructor will ensure that<br>+                               * all SDP handlers get a chance to free the memory they allocated<br>+                            */<br>+                          session_media->type = ast_media_type_from_str(media);<br>+                             session_media->handler = NULL;<br>+                    }<br>+            }<br>             if (!session_media) {<br>-                        /* if the session_media doesn't exist, there weren't<br>-                  * any handlers at the time of its creation */<br>-                       continue;<br>+                    session_media = add_session_media(session, ast_media_type_from_str(media), i);<br>+                       if (!session_media) {<br>+                                return -1;<br>+                   }<br>             }<br> <br>          if (session_media->handler) {<br>@@ -1270,29 +1326,6 @@<br>      return strcmp(datastore1->uid, uid2) ? 0 : CMP_MATCH | CMP_STOP;<br> }<br> <br>-static void session_media_dtor(void *obj)<br>-{<br>-   struct ast_sip_session_media *session_media = obj;<br>-   struct sdp_handler_list *handler_list;<br>-       /* It is possible for SDP handlers to allocate memory on a session_media but<br>-  * not end up getting set as the handler for this session_media. This traversal<br>-       * ensures that all memory allocated by SDP handlers on the session_media is<br>-  * cleared (as well as file descriptors, etc.).<br>-       */<br>-  handler_list = ao2_find(sdp_handlers, session_media->stream_type, OBJ_KEY);<br>-       if (handler_list) {<br>-          struct ast_sip_session_sdp_handler *handler;<br>-<br>-              AST_LIST_TRAVERSE(&handler_list->list, handler, next) {<br>-                       handler->stream_destroy(session_media);<br>-           }<br>-    }<br>-    ao2_cleanup(handler_list);<br>-   if (session_media->srtp) {<br>-                ast_sdp_srtp_destroy(session_media->srtp);<br>-        }<br>-}<br>-<br> static void session_destructor(void *obj)<br> {<br>      struct ast_sip_session *session = obj;<br>@@ -1300,6 +1333,7 @@<br>         struct ast_sip_session_delayed_request *delay;<br>        const char *endpoint_name = session->endpoint ?<br>            ast_sorcery_object_get_id(session->endpoint) : "<none>";<br>+  int i;<br> <br>     ast_debug(3, "Destroying SIP session with endpoint %s\n", endpoint_name);<br> <br>@@ -1321,7 +1355,10 @@<br> <br>     ast_taskprocessor_unreference(session->serializer);<br>        ao2_cleanup(session->datastores);<br>- ao2_cleanup(session->media);<br>+      for (i = 0; i < AST_VECTOR_SIZE(&session->media); ++i) {<br>+           ao2_cleanup(AST_VECTOR_GET(&session->media, i));<br>+      }<br>+    AST_VECTOR_FREE(&session->media);<br> <br>   AST_LIST_HEAD_DESTROY(&session->supplements);<br>  while ((delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next))) {<br>@@ -1355,25 +1392,6 @@<br>             }<br>             AST_LIST_INSERT_TAIL(&session->supplements, copy, next);<br>       }<br>-    return 0;<br>-}<br>-<br>-static int add_session_media(void *obj, void *arg, int flags)<br>-{<br>- struct sdp_handler_list *handler_list = obj;<br>- struct ast_sip_session *session = arg;<br>-       RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup);<br>-<br>-       session_media = ao2_alloc(sizeof(*session_media) + strlen(handler_list->stream_type), session_media_dtor);<br>-        if (!session_media) {<br>-                return CMP_STOP;<br>-     }<br>-    session_media->encryption = session->endpoint->media.rtp.encryption;<br>-        session_media->keepalive_sched_id = -1;<br>-   session_media->timeout_sched_id = -1;<br>-     /* Safe use of strcpy */<br>-     strcpy(session_media->stream_type, handler_list->stream_type);<br>- ao2_link(session->media, session_media);<br>   return 0;<br> }<br> <br>@@ -1449,12 +1467,9 @@<br> <br>   session->endpoint = ao2_bump(endpoint);<br> <br>-        session->media = ao2_container_alloc(MEDIA_BUCKETS, session_media_hash, session_media_cmp);<br>-       if (!session->media) {<br>+    if (AST_VECTOR_INIT(&session->media, DEFAULT_NUM_SESSION_MEDIA) < 0) {<br>              return NULL;<br>  }<br>-    /* fill session->media with available types */<br>-    ao2_callback(sdp_handlers, OBJ_NODATA, add_session_media, session);<br> <br>        if (rdata) {<br>          /*<br>@@ -1811,6 +1826,9 @@<br>                             /* replace instances of joint caps equivalents in req_caps */<br>                                 ast_format_cap_replace_from_cap(req_cap, joint_cap,<br>                                           AST_MEDIA_TYPE_UNKNOWN);<br>+                             if (add_session_media(session, type, -1) == NULL) {<br>+                                  continue;<br>+                            }<br>                             clone_stream = ast_stream_clone(req_stream, NULL);<br>                            if (!clone_stream) {<br>                                  continue;<br>@@ -1819,6 +1837,21 @@<br>                                     continue;<br>                             }<br>                     }<br>+            }<br>+    }<br>+<br>+ if (ast_stream_topology_get_count(session->req_topology) == 0) {<br>+          /* We haven't added anything yet to the session->req_topology, either because<br>+          * 1) The passed in req_topology had no streams.<br>+              * 2) The passed in req_topology had no overlapping capabilities with the endpoint.<br>+           */<br>+          int i;<br>+<br>+            for (i = 0; i < ast_stream_topology_get_count(endpoint->media.codecs); ++i) {<br>+                  struct ast_stream *stream;<br>+<br>+                        stream = ast_stream_topology_get_stream(endpoint->media.codecs, i);<br>+                       add_session_media(session, ast_stream_get_type(stream), -1);<br>          }<br>     }<br> <br>@@ -2954,27 +2987,26 @@<br>         }<br> }<br> <br>-static int add_sdp_streams(void *obj, void *arg, void *data, int flags)<br>+static int add_sdp_streams(struct ast_sip_session_media *session_media,<br>+ struct ast_sip_session *session, pjmedia_sdp_session *answer,<br>+        struct ast_stream *stream)<br> {<br>-       struct ast_sip_session_media *session_media = obj;<br>-   pjmedia_sdp_session *answer = arg;<br>-   struct ast_sip_session *session = data;<br>       struct ast_sip_session_sdp_handler *handler = session_media->handler;<br>      RAII_VAR(struct sdp_handler_list *, handler_list, NULL, ao2_cleanup);<br>         int res;<br> <br>   if (handler) {<br>                /* if an already assigned handler reports a catastrophic error, fail */<br>-              res = handler->create_outgoing_sdp_stream(session, session_media, answer);<br>+                res = handler->create_outgoing_sdp_stream(session, session_media, answer, stream);<br>                 if (res < 0) {<br>-                    return 0;<br>+                    return -1;<br>            }<br>-            return CMP_MATCH;<br>+            return 0;<br>     }<br> <br>- handler_list = ao2_find(sdp_handlers, session_media->stream_type, OBJ_KEY);<br>+       handler_list = ao2_find(sdp_handlers, ast_codec_media_type2str(session_media->type), OBJ_KEY);<br>     if (!handler_list) {<br>-         return CMP_MATCH;<br>+            return 0;<br>     }<br> <br>  /* no handler for this stream type and we have a list to search */<br>@@ -2982,29 +3014,30 @@<br>           if (handler == session_media->handler) {<br>                   continue;<br>             }<br>-            res = handler->create_outgoing_sdp_stream(session, session_media, answer);<br>+                res = handler->create_outgoing_sdp_stream(session, session_media, answer, stream);<br>                 if (res < 0) {<br>                     /* catastrophic error */<br>-                     return 0;<br>+                    return -1;<br>            }<br>             if (res > 0) {<br>                     /* Handled by this handler. Move to the next stream */<br>                        session_media_set_handler(session_media, handler);<br>-                   return CMP_MATCH;<br>+                    return 0;<br>             }<br>     }<br> <br>  /* streams that weren't handled won't be included in generated outbound SDP */<br>-       return CMP_MATCH;<br>+    return 0;<br> }<br> <br> static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, struct ast_sip_session *session, const pjmedia_sdp_session *offer)<br> {<br>-        RAII_VAR(struct ao2_iterator *, successful, NULL, ao2_iterator_cleanup);<br>      static const pj_str_t STR_IN = { "IN", 2 };<br>         static const pj_str_t STR_IP4 = { "IP4", 3 };<br>       static const pj_str_t STR_IP6 = { "IP6", 3 };<br>       pjmedia_sdp_session *local;<br>+  struct ast_stream_topology *topology;<br>+        int i;<br> <br>     if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {<br>          ast_log(LOG_ERROR, "Failed to create session SDP. Session has been already disconnected\n");<br>@@ -3025,11 +3058,35 @@<br>       pj_strdup2(inv->pool_prov, &local->origin.user, session->endpoint->media.sdpowner);<br>   pj_strdup2(inv->pool_prov, &local->name, session->endpoint->media.sdpsession);<br> <br>-    /* Now let the handlers add streams of various types, pjmedia will automatically reorder the media streams for us */<br>- successful = ao2_callback_data(session->media, OBJ_MULTIPLE, add_sdp_streams, local, session);<br>-    if (!successful || ao2_iterator_count(successful) != ao2_container_count(session->media)) {<br>-               /* Something experienced a catastrophic failure */<br>-           return NULL;<br>+ if (session->req_topology && ast_stream_topology_get_count(session->req_topology)) {<br>+           /* The most common case. An incoming call, an incoming SDP offer, or an application<br>+           * has dictated what streams are requested on this call.<br>+              */<br>+          topology = session->req_topology;<br>+ } else {<br>+             /* Weird case. We're originating a call where the codecs were not specified, or we're<br>+                 * answering an incoming call that had no SDP offer<br>+           */<br>+          topology = session->endpoint->media.codecs;<br>+    }<br>+<br>+ for (i = 0; i < ast_stream_topology_get_count(topology); ++i) {<br>+           struct ast_sip_session_media *session_media;<br>+         struct ast_stream *stream;<br>+<br>+                stream = ast_stream_topology_get_stream(topology, i);<br>+                if (i < AST_VECTOR_SIZE(&session->media)) {<br>+                        session_media = AST_VECTOR_GET(&session->media, i);<br>+           } else {<br>+                     session_media = add_session_media(session, ast_stream_get_type(stream), i);<br>+          }<br>+<br>+         if (!session_media) {<br>+                        return NULL;<br>+         }<br>+            if (add_sdp_streams(session_media, session, local, stream)) {<br>+                        return NULL;<br>+         }<br>     }<br> <br>  /* Use the connection details of the first media stream if possible for SDP level */<br>diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c<br>index bb1641a..96043d8 100644<br>--- a/res/res_pjsip_t38.c<br>+++ b/res/res_pjsip_t38.c<br>@@ -190,12 +190,29 @@<br>      }<br> }<br> <br>+/*! \brief Find the first instance of a session media of type "image" */<br>+static struct ast_sip_session_media *find_image_session_media(struct ast_sip_session *session)<br>+{<br>+   int i;<br>+<br>+    for (i = 0; i < AST_VECTOR_SIZE(&session->media); ++i) {<br>+           struct ast_sip_session_media *session_media;<br>+<br>+              session_media = AST_VECTOR_GET(&session->media, i);<br>+           if (session_media->type == AST_MEDIA_TYPE_IMAGE) {<br>+                        return session_media;<br>+                }<br>+    }<br>+<br>+ return NULL;<br>+}<br>+<br> /*! \brief Task function which rejects a T.38 re-invite and resumes handling it */<br> static int t38_automatic_reject(void *obj)<br> {<br>     RAII_VAR(struct ast_sip_session *, session, obj, ao2_cleanup);<br>        RAII_VAR(struct ast_datastore *, datastore, ast_sip_session_get_datastore(session, "t38"), ao2_cleanup);<br>-   RAII_VAR(struct ast_sip_session_media *, session_media, ao2_find(session->media, "image", OBJ_KEY), ao2_cleanup);<br>+       struct ast_sip_session_media *session_media = find_image_session_media(session);<br> <br>   if (!datastore) {<br>             return 0;<br>@@ -292,14 +309,14 @@<br> {<br>  struct pjsip_status_line status = rdata->msg_info.msg->line.status;<br>     struct t38_state *state;<br>-     RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup);<br>+  struct ast_sip_session_media *session_media = NULL;<br> <br>        if (status.code == 100) {<br>             return 0;<br>     }<br> <br>  if (!(state = t38_state_get_or_alloc(session)) ||<br>-            !(session_media = ao2_find(session->media, "image", OBJ_KEY))) {<br>+                !(session_media = find_image_session_media(session))) {<br>               ast_log(LOG_WARNING, "Received response to T.38 re-invite on '%s' but state unavailable\n",<br>                         ast_channel_name(session->channel));<br>               return 0;<br>@@ -316,7 +333,7 @@<br>        RAII_VAR(struct t38_parameters_task_data *, data, obj, ao2_cleanup);<br>  const struct ast_control_t38_parameters *parameters = data->frame->data.ptr;<br>    struct t38_state *state = t38_state_get_or_alloc(data->session);<br>-  RAII_VAR(struct ast_sip_session_media *, session_media, ao2_find(data->session->media, "image", OBJ_KEY), ao2_cleanup);<br>+      struct ast_sip_session_media *session_media = find_image_session_media(data->session);<br> <br>  /* Without session media or state we can't interpret parameters */<br>        if (!session_media || !state) {<br>@@ -417,12 +434,11 @@<br> <br>             /* Avoid deadlock between chan and the session->media container lock */<br>            ast_channel_unlock(chan);<br>-            session_media = ao2_find(session->media, "image", OBJ_SEARCH_KEY);<br>+              session_media = find_image_session_media(session);<br>            ast_channel_lock(chan);<br>               if (session_media && session_media->udptl) {<br>                       ast_udptl_write(session_media->udptl, f);<br>          }<br>-            ao2_cleanup(session_media);<br>   }<br> <br>  return f;<br>@@ -437,12 +453,11 @@<br> <br>           /* Avoid deadlock between chan and the session->media container lock */<br>            ast_channel_unlock(chan);<br>-            session_media = ao2_find(session->media, "image", OBJ_SEARCH_KEY);<br>+              session_media = find_image_session_media(session);<br>            ast_channel_lock(chan);<br>               if (session_media && session_media->udptl) {<br>                       f = ast_udptl_read(session_media->udptl);<br>          }<br>-            ao2_cleanup(session_media);<br>   }<br> <br>  return f;<br>@@ -676,11 +691,13 @@<br> }<br> <br> /*! \brief Function which negotiates an incoming media stream */<br>-static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,<br>-                                   const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)<br>+static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,<br>+  const struct pjmedia_sdp_session *sdp, int index)<br> {<br>         struct t38_state *state;<br>      char host[NI_MAXHOST];<br>+       struct ast_sip_session_media *session_media = AST_VECTOR_GET(&session->media, index);<br>+ pjmedia_sdp_media *stream = sdp->media[index];<br>     RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);<br> <br>    if (!session->endpoint->media.t38.enabled) {<br>@@ -720,7 +737,7 @@<br> <br> /*! \brief Function which creates an outgoing stream */<br> static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,<br>-                                 struct pjmedia_sdp_session *sdp)<br>+                                     struct pjmedia_sdp_session *sdp, struct ast_stream *stream)<br> {<br>         pj_pool_t *pool = session->inv_session->pool_prov;<br>      static const pj_str_t STR_IN = { "IN", 2 };<br>@@ -758,7 +775,7 @@<br>            return -1;<br>    }<br> <br>- media->desc.media = pj_str(session_media->stream_type);<br>+        pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(session_media->type));<br>        media->desc.transport = STR_UDPTL;<br> <br>      if (ast_strlen_zero(session->endpoint->media.address)) {<br>@@ -827,11 +844,13 @@<br> }<br> <br> /*! \brief Function which applies a negotiated stream */<br>-static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,<br>-                                      const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,<br>-                                       const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream)<br>+static int apply_negotiated_sdp_stream(struct ast_sip_session *session, <br>+   const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_session *remote,<br>+   int index)<br> {<br>        RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);<br>+      struct ast_sip_session_media *session_media = AST_VECTOR_GET(&session->media, index);<br>+ pjmedia_sdp_media *remote_stream = remote->media[index];<br>   char host[NI_MAXHOST];<br>        struct t38_state *state;<br> <br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/5824">change 5824</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/5824"/><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: newchange </div>
<div style="display:none"> Gerrit-Change-Id: I144b04f43633387b8e42a43ef3b25d7c5682b451 </div>
<div style="display:none"> Gerrit-Change-Number: 5824 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Mark Michelson <mmichelson@digium.com> </div>