<p>Jenkins2 <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/5988">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Benjamin Keith Ford: Looks good to me, but someone else must approve
  Joshua Colp: Looks good to me, but someone else must approve
  Matthew Fredrickson: Looks good to me, approved
  Jenkins2: Approved for Submit

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip: Add "webrtc" configuration option<br><br>This patch creates a new configuration option called "webrtc". When enabled it<br>defaults and enables the following options that are needed in order for webrtc<br>to work in Asterisk:<br><br>  rtcp-mux, use_avpf, ice_support, and use_received_transport=enabled<br>  media_encryption=dtls<br>  dtls_verify=fingerprint<br>  dtls_setup=actpass<br><br>When "webrtc" is enabled, this patch also parses the "msid" media level<br>attribute from an SDP. It will also appropriately add it onto the outgoing<br>session when applicable.<br><br>Lastly, when "webrtc" is enabled h264 RTCP FIR feedback frames are now sent.<br><br>ASTERISK-27119 #close<br><br>Change-Id: I5ec02e07c5d5b9ad86a34fdf31bf2f9da9aac6fd<br>---<br>M channels/chan_pjsip.c<br>M configs/samples/pjsip.conf.sample<br>M include/asterisk/res_pjsip.h<br>M include/asterisk/res_pjsip_session.h<br>M res/res_pjsip.c<br>M res/res_pjsip.exports.in<br>M res/res_pjsip/pjsip_configuration.c<br>M res/res_pjsip_sdp_rtp.c<br>M res/res_pjsip_session.c<br>9 files changed, 151 insertions(+), 7 deletions(-)<br><br></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 931b608..f009943 100644<br>--- a/channels/chan_pjsip.c<br>+++ b/channels/chan_pjsip.c<br>@@ -1595,7 +1595,9 @@<br>                              /* FIXME: Only use this for VP8. Additional work would have to be done to<br>                              * fully support other video codecs */<br> <br>-                            if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), ast_format_vp8) != AST_FORMAT_CMP_NOT_EQUAL) {<br>+                                if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), ast_format_vp8) != AST_FORMAT_CMP_NOT_EQUAL ||<br>+                                        (channel->session->endpoint->media.webrtc &&<br>+                                         ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), ast_format_h264) != AST_FORMAT_CMP_NOT_EQUAL)) {<br>                                  /* FIXME Fake RTP write, this will be sent as an RTCP packet. Ideally the<br>                                      * RTP engine would provide a way to externally write/schedule RTCP<br>                                    * packets */<br>diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample<br>index c05938e..3c3e52a 100644<br>--- a/configs/samples/pjsip.conf.sample<br>+++ b/configs/samples/pjsip.conf.sample<br>@@ -790,6 +790,14 @@<br>                     ; (default: 1)<br> ;max_video_streams= ; The maximum number of allowed negotiated video streams<br>                     ; (default: 1)<br>+;webrtc= ; When set to "yes" this also enables the following values that are needed<br>+         ; for webrtc: rtcp_mux, use_avpf, ice_support, and use_received_transport.<br>+         ; The following configuration settings also get defaulted as follows:<br>+         ;     media_encryption=dtls<br>+         ;     dtls_verify=fingerprint<br>+         ;     dtls_setup=actpass<br>+         ; A dtls_cert_file and a dtls_ca_file still need to be specified.<br>+         ; Default for this option is "no"<br> <br> ;==========================AUTH SECTION OPTIONS=========================<br> ;[auth]<br>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h<br>index d499d55..cf366cb 100644<br>--- a/include/asterisk/res_pjsip.h<br>+++ b/include/asterisk/res_pjsip.h<br>@@ -690,6 +690,8 @@<br>        unsigned int max_video_streams;<br>       /*! Use BUNDLE */<br>     unsigned int bundle;<br>+ /*! Enable webrtc settings and defaults */<br>+   unsigned int webrtc;<br> };<br> <br> /*!<br>@@ -2061,6 +2063,24 @@<br> void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size);<br> <br> /*!<br>+ * \brief Create and copy a pj_str_t into a standard character buffer.<br>+ *<br>+ * pj_str_t is not NULL-terminated. Any place that expects a NULL-<br>+ * terminated string needs to have the pj_str_t copied into a separate<br>+ * buffer.<br>+ *<br>+ * Copies the pj_str_t contents into a newly allocated buffer pointed to<br>+ * by dest. NULL-terminates the buffer.<br>+ *<br>+ * \note Caller is responsible for freeing the allocated memory.<br>+ *<br>+ * \param dest [out] The destination buffer<br>+ * \param src The pj_str_t to copy<br>+ * \retval Number of characters copied or negative value on error<br>+ */<br>+int ast_copy_pj_str2(char **dest, const pj_str_t *src);<br>+<br>+/*!<br>  * \brief Get the looked-up endpoint on an out-of dialog request or response<br>  *<br>  * The function may ONLY be called on out-of-dialog requests or responses. For<br>diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h<br>index eae29de..eae11af 100644<br>--- a/include/asterisk/res_pjsip_session.h<br>+++ b/include/asterisk/res_pjsip_session.h<br>@@ -105,6 +105,8 @@<br>  int bundle_group;<br>     /*! \brief Whether this stream is currently bundled or not */<br>         unsigned int bundled;<br>+        /*! \brief RTP/Media streams association identifier */<br>+       char *msid;<br> };<br> <br> /*!<br>diff --git a/res/res_pjsip.c b/res/res_pjsip.c<br>index ee5c5fe..0211211 100644<br>--- a/res/res_pjsip.c<br>+++ b/res/res_pjsip.c<br>@@ -1010,6 +1010,18 @@<br>                                                underlying transport. Note that enabling bundle will also enable the rtcp_mux option.<br>                                         </para></description><br>                             </configOption><br>+                                <configOption name="webrtc" default="no"><br>+                                  <synopsis>Defaults and enables some options that are relevant to WebRTC</synopsis><br>+                                       <description><para><br>+                                              When set to "yes" this also enables the following values that are needed in<br>+                                                order for basic WebRTC support to work: rtcp_mux, use_avpf, ice_support, and<br>+                                         use_received_transport. The following configuration settings also get defaulted<br>+                                              as follows:</para><br>+                                             <para>media_encryption=dtls</para><br>+                                               <para>dtls_verify=fingerprint</para><br>+                                             <para>dtls_setup=actpass</para><br>+                                  </description><br>+                         </configOption><br>                         </configObject><br>                         <configObject name="auth"><br>                            <synopsis>Authentication type</synopsis><br>@@ -4244,6 +4256,18 @@<br>  dest[chars_to_copy] = '\0';<br> }<br> <br>+int ast_copy_pj_str2(char **dest, const pj_str_t *src)<br>+{<br>+      int res = ast_asprintf(dest, "%.*s", (int)pj_strlen(src), pj_strbuf(src));<br>+<br>+      if (res < 0) {<br>+            *dest = NULL;<br>+        }<br>+<br>+ return res;<br>+}<br>+<br>+<br> int ast_sip_is_content_type(pjsip_media_type *content_type, char *type, char *subtype)<br> {<br>    pjsip_media_type compare;<br>diff --git a/res/res_pjsip.exports.in b/res/res_pjsip.exports.in<br>index 8b62abb..4adecd4 100644<br>--- a/res/res_pjsip.exports.in<br>+++ b/res/res_pjsip.exports.in<br>@@ -2,6 +2,7 @@<br>   global:<br>               LINKER_SYMBOL_PREFIXast_sip_*;<br>                LINKER_SYMBOL_PREFIXast_copy_pj_str;<br>+         LINKER_SYMBOL_PREFIXast_copy_pj_str2;<br>                 LINKER_SYMBOL_PREFIXast_pjsip_rdata_get_endpoint;<br>     local:<br>                *;<br>diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c<br>index c601737..9f9de36 100644<br>--- a/res/res_pjsip/pjsip_configuration.c<br>+++ b/res/res_pjsip/pjsip_configuration.c<br>@@ -1363,8 +1363,30 @@<br>               return -1;<br>    }<br> <br>- if (endpoint->media.bundle) {<br>-             endpoint->media.rtcp_mux = 1;<br>+     endpoint->media.rtcp_mux |= endpoint->media.bundle;<br>+<br>+ /*<br>+    * If webrtc has been enabled then enable those attributes, and default<br>+       * some, that are needed in order for webrtc to work.<br>+         */<br>+  endpoint->media.bundle |= endpoint->media.webrtc;<br>+      endpoint->media.rtcp_mux |= endpoint->media.webrtc;<br>+    endpoint->media.rtp.use_avpf |= endpoint->media.webrtc;<br>+        endpoint->media.rtp.ice_support |= endpoint->media.webrtc;<br>+     endpoint->media.rtp.use_received_transport |= endpoint->media.webrtc;<br>+<br>+       if (endpoint->media.webrtc) {<br>+             endpoint->media.rtp.encryption = AST_SIP_MEDIA_ENCRYPT_DTLS;<br>+              endpoint->media.rtp.dtls_cfg.enabled = 1;<br>+         endpoint->media.rtp.dtls_cfg.default_setup = AST_RTP_DTLS_SETUP_ACTPASS;<br>+          endpoint->media.rtp.dtls_cfg.verify = AST_RTP_DTLS_VERIFY_FINGERPRINT;<br>+<br>+         if (ast_strlen_zero(endpoint->media.rtp.dtls_cfg.certfile) ||<br>+                     (ast_strlen_zero(endpoint->media.rtp.dtls_cfg.cafile))) {<br>+                 ast_log(LOG_ERROR, "WebRTC can't be enabled on endpoint '%s' - a DTLS cert "<br>+                           "or ca file has not been specified", ast_sorcery_object_get_id(endpoint));<br>+                 return -1;<br>+           }<br>     }<br> <br>  return 0;<br>@@ -1990,6 +2012,7 @@<br>      ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_audio_streams", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.max_audio_streams));<br>  ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_video_streams", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.max_video_streams));<br>  ast_sorcery_object_field_register(sip_sorcery, "endpoint", "bundle", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.bundle));<br>+      ast_sorcery_object_field_register(sip_sorcery, "endpoint", "webrtc", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.webrtc));<br> <br>    if (ast_sip_initialize_sorcery_transport()) {<br>                 ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");<br>diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c<br>index 4ec8115..a2e7f8f 100644<br>--- a/res/res_pjsip_sdp_rtp.c<br>+++ b/res/res_pjsip_sdp_rtp.c<br>@@ -1025,6 +1025,65 @@<br>         }<br> }<br> <br>+static void process_msid_attribute(struct ast_sip_session *session,<br>+       struct ast_sip_session_media *session_media, pjmedia_sdp_media *media)<br>+{<br>+   pjmedia_sdp_attr *attr;<br>+<br>+   if (!session->endpoint->media.webrtc) {<br>+                return;<br>+      }<br>+<br>+ attr = pjmedia_sdp_media_find_attr2(media, "msid", NULL);<br>+  if (attr) {<br>+          ast_free(session_media->msid);<br>+            ast_copy_pj_str2(&session_media->msid, &attr->value);<br>+  }<br>+}<br>+<br>+static void add_msid_to_stream(struct ast_sip_session *session,<br>+   struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media)<br>+{<br>+  pj_str_t stmp;<br>+       pjmedia_sdp_attr *attr;<br>+<br>+   if (!session->endpoint->media.webrtc) {<br>+                return;<br>+      }<br>+<br>+ if (ast_strlen_zero(session_media->msid)) {<br>+               char uuid1[AST_UUID_STR_LEN], uuid2[AST_UUID_STR_LEN];<br>+<br>+            if (ast_asprintf(&session_media->msid, "{%s} {%s}",<br>+                 ast_uuid_generate_str(uuid1, sizeof(uuid1)),<br>+                 ast_uuid_generate_str(uuid2, sizeof(uuid2))) < 0) {<br>+                       session_media->msid = NULL;<br>+                       return;<br>+              }<br>+    }<br>+<br>+ attr = pjmedia_sdp_attr_create(pool, "msid", pj_cstr(&stmp, session_media->msid));<br>+  pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);<br>+}<br>+<br>+static void add_rtcp_fb_to_stream(struct ast_sip_session *session,<br>+   struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media)<br>+{<br>+  pj_str_t stmp;<br>+       pjmedia_sdp_attr *attr;<br>+<br>+   if (!session->endpoint->media.webrtc || session_media->type != AST_MEDIA_TYPE_VIDEO) {<br>+              return;<br>+      }<br>+<br>+ /*<br>+    * For now just automatically add it the stream even though it hasn't<br>+     * necessarily been negotiated.<br>+       */<br>+  attr = pjmedia_sdp_attr_create(pool, "rtcp-fb", pj_cstr(&stmp, "* ccm fir"));<br>+        pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);<br>+}<br>+<br> /*! \brief Function which negotiates an incoming media stream */<br> static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,<br>  struct ast_sip_session_media *session_media, const pjmedia_sdp_session *sdp,<br>@@ -1068,7 +1127,7 @@<br>   }<br> <br>  process_ssrc_attributes(session, session_media, stream);<br>-<br>+  process_msid_attribute(session, session_media, stream);<br>       session_media_transport = ast_sip_session_media_get_transport(session, session_media);<br> <br>     if (session_media_transport == session_media || !session_media->bundled) {<br>@@ -1527,6 +1586,8 @@<br>  }<br> <br>  add_ssrc_to_stream(session, session_media, pool, media);<br>+     add_msid_to_stream(session, session_media, pool, media);<br>+     add_rtcp_fb_to_stream(session, session_media, pool, media);<br> <br>        /* Add the media stream to the SDP */<br>         sdp->media[sdp->media_count++] = media;<br>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c<br>index 315db6d..fe3680f 100644<br>--- a/res/res_pjsip_session.c<br>+++ b/res/res_pjsip_session.c<br>@@ -395,6 +395,7 @@<br>       }<br> <br>  ast_free(session_media->mid);<br>+     ast_free(session_media->msid);<br> }<br> <br> struct ast_sip_session_media *ast_sip_session_media_state_add(struct ast_sip_session *session,<br>@@ -3573,14 +3574,16 @@<br>    int index, mid_id;<br>    struct sip_session_media_bundle_group *bundle_group;<br> <br>+      if (session->endpoint->media.webrtc) {<br>+         attr = pjmedia_sdp_attr_create(pool, "msid-semantic", pj_cstr(&stmp, "WMS *"));<br>+              pjmedia_sdp_attr_add(&answer->attr_count, answer->attr, attr);<br>+     }<br>+<br>  if (!session->endpoint->media.bundle) {<br>                 return 0;<br>     }<br> <br>  memset(bundle_groups, 0, sizeof(bundle_groups));<br>-<br>-  attr = pjmedia_sdp_attr_create(pool, "msid-semantic", pj_cstr(&stmp, "WMS *"));<br>-      pjmedia_sdp_attr_add(&answer->attr_count, answer->attr, attr);<br> <br>   /* Build the bundle group layout so we can then add it to the SDP */<br>  for (index = 0; index < AST_VECTOR_SIZE(&session->pending_media_state->sessions); ++index) {<br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/5988">change 5988</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/5988"/><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: I5ec02e07c5d5b9ad86a34fdf31bf2f9da9aac6fd </div>
<div style="display:none"> Gerrit-Change-Number: 5988 </div>
<div style="display:none"> Gerrit-PatchSet: 5 </div>
<div style="display:none"> Gerrit-Owner: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Benjamin Keith Ford <bford@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: Matthew Fredrickson <creslin@digium.com> </div>