<p>Kevin Harwell has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/5988">View Change</a></p><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 equal enabled<br>  if media_encryption=unset/none, then media_encryption=dtls<br>  if media_encryption=dtls and dtls_verify=unset/none,<br>      then dtls_verify=fingerprint<br>  if media_encryption=dtls, then dtls_setup=actpass (Note, overwrites<br>      any already specified value)<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, 165 insertions(+), 1 deletion(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/88/5988/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 fc59c90..6cfbe91 100644<br>--- a/channels/chan_pjsip.c<br>+++ b/channels/chan_pjsip.c<br>@@ -1597,7 +1597,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_vp8) != 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..aa412f9 100644<br>--- a/configs/samples/pjsip.conf.sample<br>+++ b/configs/samples/pjsip.conf.sample<br>@@ -790,6 +790,15 @@<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>+         ;     if media_encryption=unset/none, then media_encryption=dtls<br>+         ;     if media_encryption=dtls and dtls_verify=unset/none,<br>+         ;         then dtls_verify=fingerprint<br>+         ;     if media_encryption=dtls, then dtls_setup=actpass (Note,<br>+         ;         overwrites any already specified value)<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 2cd27d3..5dcc6c4 100644<br>--- a/include/asterisk/res_pjsip.h<br>+++ b/include/asterisk/res_pjsip.h<br>@@ -688,6 +688,8 @@<br>      unsigned int max_audio_streams;<br>       /*! Maximum number of video streams to offer/accept */<br>        unsigned int max_video_streams;<br>+      /*! Enable webrtc settings and defaults */<br>+   unsigned int webrtc;<br> };<br> <br> /*!<br>@@ -2059,6 +2061,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 e298e1f..62f5391 100644<br>--- a/include/asterisk/res_pjsip_session.h<br>+++ b/include/asterisk/res_pjsip_session.h<br>@@ -99,6 +99,8 @@<br>    ast_sip_session_media_write_cb write_callback;<br>        /*! \brief The stream number to place into any resulting frames */<br>    int stream_num;<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 8923540..b061b8f 100644<br>--- a/res/res_pjsip.c<br>+++ b/res/res_pjsip.c<br>@@ -1002,6 +1002,18 @@<br>                                                streams allowed for the endpoint.<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>if media_encryption=unset/none, then media_encryption=dtls</para><br>+                                          <para>if media_encryption=dtls and dtls_verify=unset/none, then dtls_verify=fingerprint</para><br>+                                           <para>if media_encryption=dtls, then dtls_setup=actpass (Note, overwrites any already specified value)</para><br>+                                    </description><br>+                         </configOption><br>                         </configObject><br>                         <configObject name="auth"><br>                            <synopsis>Authentication type</synopsis><br>@@ -4228,6 +4240,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 372b01b..00c9755 100644<br>--- a/res/res_pjsip/pjsip_configuration.c<br>+++ b/res/res_pjsip/pjsip_configuration.c<br>@@ -1332,6 +1332,28 @@<br>               return -1;<br>    }<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.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>+             if (endpoint->media.rtp.encryption == AST_SIP_MEDIA_ENCRYPT_NONE) {<br>+                       endpoint->media.rtp.encryption = AST_SIP_MEDIA_ENCRYPT_DTLS;<br>+                      endpoint->media.rtp.dtls_cfg.enabled = 1;<br>+         }<br>+            if (endpoint->media.rtp.dtls_cfg.enabled) {<br>+                       endpoint->media.rtp.dtls_cfg.default_setup = AST_RTP_DTLS_SETUP_ACTPASS;<br>+                  if (endpoint->media.rtp.dtls_cfg.verify == AST_RTP_DTLS_VERIFY_NONE) {<br>+                            endpoint->media.rtp.dtls_cfg.verify = AST_RTP_DTLS_VERIFY_FINGERPRINT;<br>+                    }<br>+            }<br>+    }<br>+<br>  return 0;<br> }<br> <br>@@ -1954,6 +1976,7 @@<br>       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "notify_early_inuse_ringing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, notify_early_inuse_ringing));<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", "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 a491308..4599194 100644<br>--- a/res/res_pjsip_sdp_rtp.c<br>+++ b/res/res_pjsip_sdp_rtp.c<br>@@ -940,6 +940,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>@@ -982,9 +1041,12 @@<br>   }<br> <br>  session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(stream, "rtcp-mux", NULL) != NULL);<br>+<br>    set_ice_components(session, session_media);<br> <br>        enable_rtcp(session, session_media, stream);<br>+<br>+      process_msid_attribute(session, session_media, stream);<br> <br>    res = setup_media_encryption(session, session_media, sdp, stream);<br>    if (res) {<br>@@ -1013,6 +1075,7 @@<br>     if (set_caps(session, session_media, stream, 1, asterisk_stream)) {<br>           return 0;<br>     }<br>+<br>  return 1;<br> }<br> <br>@@ -1365,6 +1428,9 @@<br>               pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);<br>        }<br> <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> <br>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c<br>index ecda499..8692ddd 100644<br>--- a/res/res_pjsip_session.c<br>+++ b/res/res_pjsip_session.c<br>@@ -371,6 +371,8 @@<br>    if (session_media->srtp) {<br>                 ast_sdp_srtp_destroy(session_media->srtp);<br>         }<br>+<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>@@ -3482,6 +3484,21 @@<br>             }<br>     }<br> <br>+ /*<br>+    * Add session level attributes. TODO This functionality should not be done here, but<br>+         * instead should probably be done through something like a registered callback handler<br>+       * at the SDP parsing level.<br>+  */<br>+  if (session->endpoint->media.webrtc) {<br>+         pj_str_t stmp;<br>+               pjmedia_sdp_attr *attr;<br>+<br>+           attr = pjmedia_sdp_attr_create(inv->pool_prov, "msid-semantic", pj_cstr(&stmp, "WMS *"));<br>+         if (attr) {<br>+                  pjmedia_sdp_attr_add(&local->attr_count, local->attr, attr);<br>+               }<br>+    }<br>+<br>  for (i = 0; i < ast_stream_topology_get_count(session->pending_media_state->topology); ++i) {<br>                struct ast_sip_session_media *session_media;<br>          struct ast_stream *stream;<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: newchange </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: 1 </div>
<div style="display:none"> Gerrit-Owner: Kevin Harwell <kharwell@digium.com> </div>