diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index 931b608390584ebbba4b0d0935712ec6e31e32d5..f009943ed9a719ad12088b3f43213cb6186ee13d 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -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 */ diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample index c05938ea5fdcc00ae26a029831b17a002ed169e2..3c3e52a053f73335ae8ab34c1feece5c6f4b7918 100644 --- a/configs/samples/pjsip.conf.sample +++ b/configs/samples/pjsip.conf.sample @@ -790,6 +790,14 @@ ; (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] diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index d499d5514bfca5fa97e835adb1573724b809ea4b..cf366cbab36ba7ed2743f9c9aedd1c179358583e 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -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 * diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h index eae29de046753d9825377d48636be83e9ed93911..eae11af43488dd43692f6c742d6d0899e78b0203 100644 --- a/include/asterisk/res_pjsip_session.h +++ b/include/asterisk/res_pjsip_session.h @@ -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; }; /*! diff --git a/res/res_pjsip.c b/res/res_pjsip.c index ee5c5fe5e90dbeff63782e19eeb5db874b09cf16..02112113cd7d8ca31a6eab2abf80e8eab4082bbf 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -1010,6 +1010,18 @@ 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; diff --git a/res/res_pjsip.exports.in b/res/res_pjsip.exports.in index 8b62abbfe45b84b19d89f93f71ac332a76eecf08..4adecd419c225ead7709d147fd25f3eb70186e3e 100644 --- a/res/res_pjsip.exports.in +++ b/res/res_pjsip.exports.in @@ -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: *; diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index c60173721b94e29729bd86ebee93d9f3278961fc..9f9de36faa3dfb854ab6aa1a32a641b4d554eea7 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -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"); diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index 4ec811528807ad0958a9ebe4ba84ea95a75d5cf6..a2e7f8f922326076e9fcec88b304da049ccb01d5 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -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; diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 315db6df5db1c30ded0b08fdd61fbe022c772b66..fe3680f3b785eaf0f159afc0ee13c8032953f35d 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -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);