<p>Joshua Colp <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/6937">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  George Joseph: Looks good to me, but someone else must approve
  Kevin Harwell: Looks good to me, approved
  Joshua Colp: Approved for Submit

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">core / pjsip: Add support for grouping streams together.<br><br>In WebRTC streams (or media tracks in their world) can be grouped<br>together using the mslabel. This informs the browser that each<br>should be synchronized with each other.<br><br>This change extends the stream API so this information can<br>be stored with streams. The PJSIP support has been extended<br>to use the mslabel to determine grouped streams and store<br>this association on the streams. Finally when creating the<br>SDP the group information is used to cause each media stream<br>to use the same mslabel.<br><br>ASTERISK-27379<br><br>Change-Id: Id6299aa031efe46254edbdc7973c534d54d641ad<br>---<br>M include/asterisk/res_pjsip_session.h<br>M include/asterisk/stream.h<br>M main/cli.c<br>M main/stream.c<br>M res/res_pjsip_sdp_rtp.c<br>M res/res_pjsip_session.c<br>6 files changed, 121 insertions(+), 15 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h<br>index b7a22b93..a0fc965 100644<br>--- a/include/asterisk/res_pjsip_session.h<br>+++ b/include/asterisk/res_pjsip_session.h<br>@@ -111,6 +111,8 @@<br>   char label[AST_UUID_STR_LEN];<br>         /*! \brief The underlying session has been changed in some fashion */<br>         unsigned int changed;<br>+        /*! \brief Remote media stream label */<br>+      char *remote_mslabel;<br> };<br> <br> /*!<br>diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h<br>index 2c1053a..c2d5a88 100644<br>--- a/include/asterisk/stream.h<br>+++ b/include/asterisk/stream.h<br>@@ -476,4 +476,25 @@<br> void ast_stream_topology_map(const struct ast_stream_topology *topology,<br>     struct ast_vector_int *types, struct ast_vector_int *v0, struct ast_vector_int *v1);<br> <br>+/*!<br>+ * \brief Get the stream group that a stream is part of<br>+ *<br>+ * \param stream The stream<br>+ *<br>+ * \return the numerical stream group (-1 if not in a group)<br>+ *<br>+ * \since 15.2.0<br>+ */<br>+int ast_stream_get_group(const struct ast_stream *stream);<br>+<br>+/*!<br>+ * \brief Set the stream group for a stream<br>+ *<br>+ * \param stream The stream<br>+ * \param group The group the stream is part of<br>+ *<br>+ * \since 15.2.0<br>+ */<br>+void ast_stream_set_group(struct ast_stream *stream, int group);<br>+<br> #endif /* _AST_STREAM_H */<br>diff --git a/main/cli.c b/main/cli.c<br>index ef86e25..d9aab85 100644<br>--- a/main/cli.c<br>+++ b/main/cli.c<br>@@ -1690,10 +1690,12 @@<br>                      "Name: %s\n"<br>                        "    Type: %s\n"<br>                    "    State: %s\n"<br>+                  "    Group: %d\n"<br>                   "    Formats: %s\n",<br>                        ast_stream_get_name(stream),<br>                  ast_codec_media_type2str(ast_stream_get_type(stream)),<br>                        ast_stream_state2str(ast_stream_get_state(stream)),<br>+                  ast_stream_get_group(stream),<br>                         ast_format_cap_get_names(ast_stream_get_formats(stream), &codec_buf)<br>                      );<br>    }<br>diff --git a/main/stream.c b/main/stream.c<br>index 89ed0dc..c233b2f 100644<br>--- a/main/stream.c<br>+++ b/main/stream.c<br>@@ -67,6 +67,11 @@<br>    ast_stream_data_free_fn data_free_fn[AST_STREAM_DATA_SLOT_MAX];<br> <br>    /*!<br>+   * \brief The group that the stream is part of<br>+        */<br>+  int group;<br>+<br>+        /*!<br>    * \brief Name for the stream within the context of the channel it is on<br>       */<br>   char name[0];<br>@@ -90,6 +95,7 @@<br> <br>   stream->type = type;<br>       stream->state = AST_STREAM_STATE_INACTIVE;<br>+        stream->group = -1;<br>        strcpy(stream->name, S_OR(name, "")); /* Safe */<br> <br>      return stream;<br>@@ -115,6 +121,7 @@<br> <br>        memcpy(new_stream, stream, sizeof(*new_stream));<br>      strcpy(new_stream->name, stream_name); /* Safe */<br>+ new_stream->group = -1;<br>    if (new_stream->formats) {<br>                 ao2_ref(new_stream->formats, +1);<br>  }<br>@@ -288,14 +295,16 @@<br>      }<br> <br>  for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {<br>-                struct ast_stream *stream =<br>-                  ast_stream_clone(AST_VECTOR_GET(&topology->streams, i), NULL);<br>+                struct ast_stream *existing = AST_VECTOR_GET(&topology->streams, i);<br>+          struct ast_stream *stream = ast_stream_clone(existing, NULL);<br> <br>              if (!stream || AST_VECTOR_APPEND(&new_topology->streams, stream)) {<br>                    ast_stream_free(stream);<br>                      ast_stream_topology_free(new_topology);<br>                       return NULL;<br>          }<br>+<br>+         ast_stream_set_group(stream, ast_stream_get_group(existing));<br>         }<br> <br>  return new_topology;<br>@@ -580,3 +589,17 @@<br>            AST_VECTOR_REPLACE(v1, index, i);<br>     }<br> }<br>+<br>+int ast_stream_get_group(const struct ast_stream *stream)<br>+{<br>+     ast_assert(stream != NULL);<br>+<br>+       return stream->group;<br>+}<br>+<br>+void ast_stream_set_group(struct ast_stream *stream, int group)<br>+{<br>+        ast_assert(stream != NULL);<br>+<br>+       stream->group = group;<br>+}<br>diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c<br>index 604fd42..a877582 100644<br>--- a/res/res_pjsip_sdp_rtp.c<br>+++ b/res/res_pjsip_sdp_rtp.c<br>@@ -1052,20 +1052,11 @@<br>   }<br> <br>  if (ast_strlen_zero(session_media->mslabel)) {<br>-            if (ast_sip_session_is_pending_stream_default(session, stream)) {<br>-                    int index;<br>+           /* If this stream is grouped with another then use its media stream label if possible */<br>+             if (ast_stream_get_group(stream) != -1) {<br>+                    struct ast_sip_session_media *group_session_media = AST_VECTOR_GET(&session->pending_media_state->sessions, ast_stream_get_group(stream));<br> <br>-                      /* If this is a default stream we group them together under the same stream, but as different tracks */<br>-                      for (index = 0; index < AST_VECTOR_SIZE(&session->pending_media_state->sessions); ++index) {<br>-                            struct ast_sip_session_media *other_session_media = AST_VECTOR_GET(&session->pending_media_state->sessions, index);<br>-<br>-                             if (session_media == other_session_media) {<br>-                                  continue;<br>-                            }<br>-<br>-                         ast_copy_string(session_media->mslabel, other_session_media->mslabel, sizeof(session_media->mslabel));<br>-                              break;<br>-                       }<br>+                    ast_copy_string(session_media->mslabel, group_session_media->mslabel, sizeof(session_media->mslabel));<br>               }<br> <br>          if (ast_strlen_zero(session_media->mslabel)) {<br>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c<br>index 37ff531..4724d46 100644<br>--- a/res/res_pjsip_session.c<br>+++ b/res/res_pjsip_session.c<br>@@ -399,6 +399,7 @@<br>   }<br> <br>  ast_free(session_media->mid);<br>+     ast_free(session_media->remote_mslabel);<br> }<br> <br> struct ast_sip_session_media *ast_sip_session_media_state_add(struct ast_sip_session *session,<br>@@ -553,6 +554,70 @@<br>     return 0;<br> }<br> <br>+static void set_remote_mslabel_and_stream_group(struct ast_sip_session *session,<br>+  struct ast_sip_session_media *session_media,<br>+ const pjmedia_sdp_session *sdp,<br>+      const struct pjmedia_sdp_media *stream,<br>+      struct ast_stream *asterisk_stream)<br>+{<br>+      int index;<br>+<br>+        ast_free(session_media->remote_mslabel);<br>+  session_media->remote_mslabel = NULL;<br>+<br>+  for (index = 0; index < stream->attr_count; ++index) {<br>+         pjmedia_sdp_attr *attr = stream->attr[index];<br>+             char attr_value[pj_strlen(&attr->value) + 1];<br>+         char *ssrc_attribute_name, *ssrc_attribute_value = NULL;<br>+             char *msid, *tmp = attr_value;<br>+               static const pj_str_t STR_msid = { "msid", 4 };<br>+            static const pj_str_t STR_ssrc = { "ssrc", 4 };<br>+<br>+         if (!pj_strcmp(&attr->name, &STR_msid)) {<br>+                 ast_copy_pj_str(attr_value, &attr->value, sizeof(attr_value));<br>+                        msid = strsep(&tmp, " ");<br>+                      session_media->remote_mslabel = ast_strdup(msid);<br>+                 break;<br>+               } else if (!pj_strcmp(&attr->name, &STR_ssrc)) {<br>+                  ast_copy_pj_str(attr_value, &attr->value, sizeof(attr_value));<br>+<br>+                     if ((ssrc_attribute_name = strchr(attr_value, ' '))) {<br>+                               /* This has an actual attribute */<br>+                           *ssrc_attribute_name++ = '\0';<br>+                               ssrc_attribute_value = strchr(ssrc_attribute_name, ':');<br>+                             if (ssrc_attribute_value) {<br>+                                  /* Values are actually optional according to the spec */<br>+                                     *ssrc_attribute_value++ = '\0';<br>+                              }<br>+<br>+                         if (!strcasecmp(ssrc_attribute_name, "mslabel") && !ast_strlen_zero(ssrc_attribute_value)) {<br>+                                       session_media->remote_mslabel = ast_strdup(ssrc_attribute_value);<br>+                                 break;<br>+                               }<br>+                    }<br>+            }<br>+    }<br>+<br>+ if (ast_strlen_zero(session_media->remote_mslabel)) {<br>+             return;<br>+      }<br>+<br>+ /* Iterate through the existing streams looking for a match and if so then group this with it */<br>+     for (index = 0; index < AST_VECTOR_SIZE(&session->pending_media_state->sessions); ++index) {<br>+            struct ast_sip_session_media *group_session_media;<br>+<br>+                group_session_media = AST_VECTOR_GET(&session->pending_media_state->sessions, index);<br>+<br>+           if (ast_strlen_zero(group_session_media->remote_mslabel) ||<br>+                       strcmp(group_session_media->remote_mslabel, session_media->remote_mslabel)) {<br>+                  continue;<br>+            }<br>+<br>+         ast_stream_set_group(asterisk_stream, index);<br>+                break;<br>+       }<br>+}<br>+<br> static void remove_stream_from_bundle(struct ast_sip_session_media *session_media,<br>         struct ast_stream *stream)<br> {<br>@@ -630,6 +695,7 @@<br>           }<br> <br>          set_mid_and_bundle_group(session, session_media, sdp, remote_stream);<br>+                set_remote_mslabel_and_stream_group(session, session_media, sdp, remote_stream, stream);<br> <br>           if (session_media->handler) {<br>                      handler = session_media->handler;<br>@@ -730,6 +796,7 @@<br>     ast_copy_pj_str(media, &local->media[index]->desc.media, sizeof(media));<br> <br>         set_mid_and_bundle_group(session, session_media, remote, remote->media[index]);<br>+   set_remote_mslabel_and_stream_group(session, session_media, remote, remote->media[index], asterisk_stream);<br> <br>     handler = session_media->handler;<br>  if (handler) {<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/6937">change 6937</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/6937"/><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: Id6299aa031efe46254edbdc7973c534d54d641ad </div>
<div style="display:none"> Gerrit-Change-Number: 6937 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </div>
<div style="display:none"> Gerrit-Owner: Joshua Colp <jcolp@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>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Sean Bright <sean.bright@gmail.com> </div>