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