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