]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_pjsip: Add "webrtc" configuration option
authorKevin Harwell <kharwell@digium.com>
Mon, 10 Jul 2017 23:17:44 +0000 (18:17 -0500)
committerKevin Harwell <kharwell@digium.com>
Thu, 13 Jul 2017 23:19:35 +0000 (18:19 -0500)
This patch creates a new configuration option called "webrtc". When enabled it
defaults and enables the following options that are needed in order for webrtc
to work in Asterisk:

  rtcp-mux, use_avpf, ice_support, and use_received_transport=enabled
  media_encryption=dtls
  dtls_verify=fingerprint
  dtls_setup=actpass

When "webrtc" is enabled, this patch also parses the "msid" media level
attribute from an SDP. It will also appropriately add it onto the outgoing
session when applicable.

Lastly, when "webrtc" is enabled h264 RTCP FIR feedback frames are now sent.

ASTERISK-27119 #close

Change-Id: I5ec02e07c5d5b9ad86a34fdf31bf2f9da9aac6fd

channels/chan_pjsip.c
configs/samples/pjsip.conf.sample
include/asterisk/res_pjsip.h
include/asterisk/res_pjsip_session.h
res/res_pjsip.c
res/res_pjsip.exports.in
res/res_pjsip/pjsip_configuration.c
res/res_pjsip_sdp_rtp.c
res/res_pjsip_session.c

index 931b608390584ebbba4b0d0935712ec6e31e32d5..f009943ed9a719ad12088b3f43213cb6186ee13d 100644 (file)
@@ -1595,7 +1595,9 @@ static int chan_pjsip_indicate(struct ast_channel *ast, int condition, const voi
                                /* FIXME: Only use this for VP8. Additional work would have to be done to
                                 * fully support other video codecs */
 
-                               if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), ast_format_vp8) != AST_FORMAT_CMP_NOT_EQUAL) {
+                               if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), ast_format_vp8) != AST_FORMAT_CMP_NOT_EQUAL ||
+                                       (channel->session->endpoint->media.webrtc &&
+                                        ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), ast_format_h264) != AST_FORMAT_CMP_NOT_EQUAL)) {
                                        /* FIXME Fake RTP write, this will be sent as an RTCP packet. Ideally the
                                         * RTP engine would provide a way to externally write/schedule RTCP
                                         * packets */
index c05938ea5fdcc00ae26a029831b17a002ed169e2..3c3e52a053f73335ae8ab34c1feece5c6f4b7918 100644 (file)
                     ; (default: 1)
 ;max_video_streams= ; The maximum number of allowed negotiated video streams
                     ; (default: 1)
+;webrtc= ; When set to "yes" this also enables the following values that are needed
+         ; for webrtc: rtcp_mux, use_avpf, ice_support, and use_received_transport.
+         ; The following configuration settings also get defaulted as follows:
+         ;     media_encryption=dtls
+         ;     dtls_verify=fingerprint
+         ;     dtls_setup=actpass
+         ; A dtls_cert_file and a dtls_ca_file still need to be specified.
+         ; Default for this option is "no"
 
 ;==========================AUTH SECTION OPTIONS=========================
 ;[auth]
index d499d5514bfca5fa97e835adb1573724b809ea4b..cf366cbab36ba7ed2743f9c9aedd1c179358583e 100644 (file)
@@ -690,6 +690,8 @@ struct ast_sip_endpoint_media_configuration {
        unsigned int max_video_streams;
        /*! Use BUNDLE */
        unsigned int bundle;
+       /*! Enable webrtc settings and defaults */
+       unsigned int webrtc;
 };
 
 /*!
@@ -2060,6 +2062,24 @@ int ast_sip_append_body(pjsip_tx_data *tdata, const char *body_text);
  */
 void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size);
 
+/*!
+ * \brief Create and copy a pj_str_t into a standard character buffer.
+ *
+ * pj_str_t is not NULL-terminated. Any place that expects a NULL-
+ * terminated string needs to have the pj_str_t copied into a separate
+ * buffer.
+ *
+ * Copies the pj_str_t contents into a newly allocated buffer pointed to
+ * by dest. NULL-terminates the buffer.
+ *
+ * \note Caller is responsible for freeing the allocated memory.
+ *
+ * \param dest [out] The destination buffer
+ * \param src The pj_str_t to copy
+ * \retval Number of characters copied or negative value on error
+ */
+int ast_copy_pj_str2(char **dest, const pj_str_t *src);
+
 /*!
  * \brief Get the looked-up endpoint on an out-of dialog request or response
  *
index eae29de046753d9825377d48636be83e9ed93911..eae11af43488dd43692f6c742d6d0899e78b0203 100644 (file)
@@ -105,6 +105,8 @@ struct ast_sip_session_media {
        int bundle_group;
        /*! \brief Whether this stream is currently bundled or not */
        unsigned int bundled;
+       /*! \brief RTP/Media streams association identifier */
+       char *msid;
 };
 
 /*!
index ee5c5fe5e90dbeff63782e19eeb5db874b09cf16..02112113cd7d8ca31a6eab2abf80e8eab4082bbf 100644 (file)
                                                underlying transport. Note that enabling bundle will also enable the rtcp_mux option.
                                        </para></description>
                                </configOption>
+                               <configOption name="webrtc" default="no">
+                                       <synopsis>Defaults and enables some options that are relevant to WebRTC</synopsis>
+                                       <description><para>
+                                               When set to "yes" this also enables the following values that are needed in
+                                               order for basic WebRTC support to work: rtcp_mux, use_avpf, ice_support, and
+                                               use_received_transport. The following configuration settings also get defaulted
+                                               as follows:</para>
+                                               <para>media_encryption=dtls</para>
+                                               <para>dtls_verify=fingerprint</para>
+                                               <para>dtls_setup=actpass</para>
+                                       </description>
+                               </configOption>
                        </configObject>
                        <configObject name="auth">
                                <synopsis>Authentication type</synopsis>
@@ -4244,6 +4256,18 @@ void ast_copy_pj_str(char *dest, const pj_str_t *src, size_t size)
        dest[chars_to_copy] = '\0';
 }
 
+int ast_copy_pj_str2(char **dest, const pj_str_t *src)
+{
+       int res = ast_asprintf(dest, "%.*s", (int)pj_strlen(src), pj_strbuf(src));
+
+       if (res < 0) {
+               *dest = NULL;
+       }
+
+       return res;
+}
+
+
 int ast_sip_is_content_type(pjsip_media_type *content_type, char *type, char *subtype)
 {
        pjsip_media_type compare;
index 8b62abbfe45b84b19d89f93f71ac332a76eecf08..4adecd419c225ead7709d147fd25f3eb70186e3e 100644 (file)
@@ -2,6 +2,7 @@
        global:
                LINKER_SYMBOL_PREFIXast_sip_*;
                LINKER_SYMBOL_PREFIXast_copy_pj_str;
+               LINKER_SYMBOL_PREFIXast_copy_pj_str2;
                LINKER_SYMBOL_PREFIXast_pjsip_rdata_get_endpoint;
        local:
                *;
index c60173721b94e29729bd86ebee93d9f3278961fc..9f9de36faa3dfb854ab6aa1a32a641b4d554eea7 100644 (file)
@@ -1363,8 +1363,30 @@ static int sip_endpoint_apply_handler(const struct ast_sorcery *sorcery, void *o
                return -1;
        }
 
-       if (endpoint->media.bundle) {
-               endpoint->media.rtcp_mux = 1;
+       endpoint->media.rtcp_mux |= endpoint->media.bundle;
+
+       /*
+        * If webrtc has been enabled then enable those attributes, and default
+        * some, that are needed in order for webrtc to work.
+        */
+       endpoint->media.bundle |= endpoint->media.webrtc;
+       endpoint->media.rtcp_mux |= endpoint->media.webrtc;
+       endpoint->media.rtp.use_avpf |= endpoint->media.webrtc;
+       endpoint->media.rtp.ice_support |= endpoint->media.webrtc;
+       endpoint->media.rtp.use_received_transport |= endpoint->media.webrtc;
+
+       if (endpoint->media.webrtc) {
+               endpoint->media.rtp.encryption = AST_SIP_MEDIA_ENCRYPT_DTLS;
+               endpoint->media.rtp.dtls_cfg.enabled = 1;
+               endpoint->media.rtp.dtls_cfg.default_setup = AST_RTP_DTLS_SETUP_ACTPASS;
+               endpoint->media.rtp.dtls_cfg.verify = AST_RTP_DTLS_VERIFY_FINGERPRINT;
+
+               if (ast_strlen_zero(endpoint->media.rtp.dtls_cfg.certfile) ||
+                       (ast_strlen_zero(endpoint->media.rtp.dtls_cfg.cafile))) {
+                       ast_log(LOG_ERROR, "WebRTC can't be enabled on endpoint '%s' - a DTLS cert "
+                               "or ca file has not been specified", ast_sorcery_object_get_id(endpoint));
+                       return -1;
+               }
        }
 
        return 0;
@@ -1990,6 +2012,7 @@ int ast_res_pjsip_initialize_configuration(void)
        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));
        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));
        ast_sorcery_object_field_register(sip_sorcery, "endpoint", "bundle", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.bundle));
+       ast_sorcery_object_field_register(sip_sorcery, "endpoint", "webrtc", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.webrtc));
 
        if (ast_sip_initialize_sorcery_transport()) {
                ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
index 4ec811528807ad0958a9ebe4ba84ea95a75d5cf6..a2e7f8f922326076e9fcec88b304da049ccb01d5 100644 (file)
@@ -1025,6 +1025,65 @@ static void process_ssrc_attributes(struct ast_sip_session *session, struct ast_
        }
 }
 
+static void process_msid_attribute(struct ast_sip_session *session,
+       struct ast_sip_session_media *session_media, pjmedia_sdp_media *media)
+{
+       pjmedia_sdp_attr *attr;
+
+       if (!session->endpoint->media.webrtc) {
+               return;
+       }
+
+       attr = pjmedia_sdp_media_find_attr2(media, "msid", NULL);
+       if (attr) {
+               ast_free(session_media->msid);
+               ast_copy_pj_str2(&session_media->msid, &attr->value);
+       }
+}
+
+static void add_msid_to_stream(struct ast_sip_session *session,
+       struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media)
+{
+       pj_str_t stmp;
+       pjmedia_sdp_attr *attr;
+
+       if (!session->endpoint->media.webrtc) {
+               return;
+       }
+
+       if (ast_strlen_zero(session_media->msid)) {
+               char uuid1[AST_UUID_STR_LEN], uuid2[AST_UUID_STR_LEN];
+
+               if (ast_asprintf(&session_media->msid, "{%s} {%s}",
+                       ast_uuid_generate_str(uuid1, sizeof(uuid1)),
+                       ast_uuid_generate_str(uuid2, sizeof(uuid2))) < 0) {
+                       session_media->msid = NULL;
+                       return;
+               }
+       }
+
+       attr = pjmedia_sdp_attr_create(pool, "msid", pj_cstr(&stmp, session_media->msid));
+       pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);
+}
+
+static void add_rtcp_fb_to_stream(struct ast_sip_session *session,
+       struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media)
+{
+       pj_str_t stmp;
+       pjmedia_sdp_attr *attr;
+
+       if (!session->endpoint->media.webrtc || session_media->type != AST_MEDIA_TYPE_VIDEO) {
+               return;
+       }
+
+       /*
+        * For now just automatically add it the stream even though it hasn't
+        * necessarily been negotiated.
+        */
+       attr = pjmedia_sdp_attr_create(pool, "rtcp-fb", pj_cstr(&stmp, "* ccm fir"));
+       pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);
+}
+
 /*! \brief Function which negotiates an incoming media stream */
 static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,
        struct ast_sip_session_media *session_media, const pjmedia_sdp_session *sdp,
@@ -1068,7 +1127,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,
        }
 
        process_ssrc_attributes(session, session_media, stream);
-
+       process_msid_attribute(session, session_media, stream);
        session_media_transport = ast_sip_session_media_get_transport(session, session_media);
 
        if (session_media_transport == session_media || !session_media->bundled) {
@@ -1527,6 +1586,8 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
        }
 
        add_ssrc_to_stream(session, session_media, pool, media);
+       add_msid_to_stream(session, session_media, pool, media);
+       add_rtcp_fb_to_stream(session, session_media, pool, media);
 
        /* Add the media stream to the SDP */
        sdp->media[sdp->media_count++] = media;
index 315db6df5db1c30ded0b08fdd61fbe022c772b66..fe3680f3b785eaf0f159afc0ee13c8032953f35d 100644 (file)
@@ -395,6 +395,7 @@ static void session_media_dtor(void *obj)
        }
 
        ast_free(session_media->mid);
+       ast_free(session_media->msid);
 }
 
 struct ast_sip_session_media *ast_sip_session_media_state_add(struct ast_sip_session *session,
@@ -3573,15 +3574,17 @@ static int add_bundle_groups(struct ast_sip_session *session, pj_pool_t *pool, p
        int index, mid_id;
        struct sip_session_media_bundle_group *bundle_group;
 
+       if (session->endpoint->media.webrtc) {
+               attr = pjmedia_sdp_attr_create(pool, "msid-semantic", pj_cstr(&stmp, "WMS *"));
+               pjmedia_sdp_attr_add(&answer->attr_count, answer->attr, attr);
+       }
+
        if (!session->endpoint->media.bundle) {
                return 0;
        }
 
        memset(bundle_groups, 0, sizeof(bundle_groups));
 
-       attr = pjmedia_sdp_attr_create(pool, "msid-semantic", pj_cstr(&stmp, "WMS *"));
-       pjmedia_sdp_attr_add(&answer->attr_count, answer->attr, attr);
-
        /* Build the bundle group layout so we can then add it to the SDP */
        for (index = 0; index < AST_VECTOR_SIZE(&session->pending_media_state->sessions); ++index) {
                struct ast_sip_session_media *session_media = AST_VECTOR_GET(&session->pending_media_state->sessions, index);