<p>Jenkins2 <strong>merged</strong> this change.</p><p><a href="https://gerrit.asterisk.org/5981">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Kevin Harwell: 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_rtp_asterisk / res_pjsip: Add support for BUNDLE.<br><br>BUNDLE is a specification used in WebRTC to allow multiple<br>streams to use the same underlying transport. This reduces<br>the number of ICE and DTLS negotiations that has to occur<br>to 1 normally.<br><br>This change implements this by adding support for it to<br>the RTP SDP module in PJSIP. BUNDLE can be turned on using<br>the "bundle" option and on an offer we will offer to<br>bundle streams together. On an answer we will accept any<br>bundle groups provided. Once accepted each stream is bundled<br>to another RTP instance for transport.<br><br>For the res_rtp_asterisk changes the ability to bundle<br>an RTP instance to another based on the SSRC received<br>from the remote side has been added. For outgoing traffic<br>if an RTP instance is bundled to another we will use the<br>other RTP instance for any transport related things. For<br>incoming traffic received from the transport instance we<br>look up the correct instance based on the SSRC and use it<br>for any non-transport related data.<br><br>ASTERISK-27118<br><br>Change-Id: I96c0920b9f9aca7382256484765a239017973c11<br>---<br>M channels/chan_pjsip.c<br>M include/asterisk/res_pjsip.h<br>M include/asterisk/res_pjsip_session.h<br>M include/asterisk/rtp_engine.h<br>M main/rtp_engine.c<br>M res/res_pjsip.c<br>M res/res_pjsip/pjsip_configuration.c<br>M res/res_pjsip_sdp_rtp.c<br>M res/res_pjsip_session.c<br>M res/res_pjsip_t38.c<br>M res/res_rtp_asterisk.c<br>11 files changed, 1,170 insertions(+), 393 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 0e4468c..3de980a 100644<br>--- a/channels/chan_pjsip.c<br>+++ b/channels/chan_pjsip.c<br>@@ -792,8 +792,6 @@<br> return f;<br> }<br> <br>- f->stream_num = callback_state->session->stream_num;<br>-<br> if (f->frametype != AST_FRAME_VOICE ||<br> callback_state->session != session->active_media_state->default_session[callback_state->session->type]) {<br> return f;<br>diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h<br>index 2cd27d3..d499d55 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>+ /*! Use BUNDLE */<br>+ unsigned int bundle;<br> };<br> <br> /*!<br>diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h<br>index e298e1f..eae29de 100644<br>--- a/include/asterisk/res_pjsip_session.h<br>+++ b/include/asterisk/res_pjsip_session.h<br>@@ -99,6 +99,12 @@<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 Media identifier for this stream (may be shared across multiple streams) */<br>+ char *mid;<br>+ /*! \brief The bundle group the stream belongs to */<br>+ int bundle_group;<br>+ /*! \brief Whether this stream is currently bundled or not */<br>+ unsigned int bundled;<br> };<br> <br> /*!<br>@@ -833,6 +839,19 @@<br> int ast_sip_session_media_set_write_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media,<br> ast_sip_session_media_write_cb callback);<br> <br>+/*!<br>+ * \brief Retrieve the underlying media session that is acting as transport for a media session<br>+ * \since 15.0.0<br>+ *<br>+ * \param session The session<br>+ * \param session_media The media session to retrieve the transport for<br>+ *<br>+ * \note This operates on the pending media state<br>+ *<br>+ * \note This function is guaranteed to return non-NULL<br>+ */<br>+struct ast_sip_session_media *ast_sip_session_media_get_transport(struct ast_sip_session *session, struct ast_sip_session_media *session_media);<br>+<br> /*! \brief Determines whether the res_pjsip_session module is loaded */<br> #define CHECK_PJSIP_SESSION_MODULE_LOADED() \<br> do { \<br>diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h<br>index 5f43916..20ae959 100644<br>--- a/include/asterisk/rtp_engine.h<br>+++ b/include/asterisk/rtp_engine.h<br>@@ -603,6 +603,12 @@<br> unsigned int (*ssrc_get)(struct ast_rtp_instance *instance);<br> /*! Callback to retrieve RTCP SDES CNAME */<br> const char *(*cname_get)(struct ast_rtp_instance *instance);<br>+ /*! Callback to bundle an RTP instance to another */<br>+ int (*bundle)(struct ast_rtp_instance *child, struct ast_rtp_instance *parent);<br>+ /*! Callback to set remote SSRC information */<br>+ void (*set_remote_ssrc)(struct ast_rtp_instance *instance, unsigned int ssrc);<br>+ /*! Callback to set the stream identifier */<br>+ void (*set_stream_num)(struct ast_rtp_instance *instance, int stream_num);<br> /*! Callback to pointer for optional ICE support */<br> struct ast_rtp_engine_ice *ice;<br> /*! Callback to pointer for optional DTLS SRTP support */<br>@@ -1507,6 +1513,20 @@<br> int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, struct ast_format *format, int code);<br> <br> /*!<br>+ * \brief Set a payload code for use with a specific Asterisk format<br>+ *<br>+ * \param codecs Codecs structure to manipulate<br>+ * \param code The payload code<br>+ * \param format Asterisk format<br>+ *<br>+ * \retval 0 Payload was set to the given format<br>+ * \retval -1 Payload was in use or could not be set<br>+ *<br>+ * \since 15.0.0<br>+ */<br>+int ast_rtp_codecs_payload_set_rx(struct ast_rtp_codecs *codecs, int code, struct ast_format *format);<br>+<br>+/*!<br> * \brief Retrieve a tx mapped payload type based on whether it is an Asterisk format and the code<br> * \since 14.0.0<br> *<br>@@ -2266,6 +2286,8 @@<br> *<br> * \retval 0 Success<br> * \retval non-zero Failure<br>+ *<br>+ * \note If no remote policy is provided any existing SRTP policies are left and the new local policy is added<br> */<br> int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy* remote_policy, struct ast_srtp_policy *local_policy, int rtcp);<br> <br>@@ -2411,6 +2433,36 @@<br> */<br> const char *ast_rtp_instance_get_cname(struct ast_rtp_instance *rtp);<br> <br>+/*!<br>+ * \brief Request that an RTP instance be bundled with another<br>+ * \since 15.0.0<br>+ *<br>+ * \param child The child RTP instance<br>+ * \param parent The parent RTP instance the child should be bundled with<br>+ *<br>+ * \retval 0 success<br>+ * \retval -1 failure<br>+ */<br>+int ast_rtp_instance_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent);<br>+<br>+/*!<br>+ * \brief Set the remote SSRC for an RTP instance<br>+ * \since 15.0.0<br>+ *<br>+ * \param rtp The RTP instance<br>+ * \param ssrc The remote SSRC<br>+ */<br>+void ast_rtp_instance_set_remote_ssrc(struct ast_rtp_instance *rtp, unsigned int ssrc);<br>+<br>+/*!<br>+ * \brief Set the stream number for an RTP instance<br>+ * \since 15.0.0<br>+ *<br>+ * \param rtp The RTP instance<br>+ * \param stream_num The stream identifier number<br>+ */<br>+void ast_rtp_instance_set_stream_num(struct ast_rtp_instance *instance, int stream_num);<br>+<br> /*! \addtogroup StasisTopicsAndMessages<br> * @{<br> */<br>diff --git a/main/rtp_engine.c b/main/rtp_engine.c<br>index 9cfae09..abd4b1f 100644<br>--- a/main/rtp_engine.c<br>+++ b/main/rtp_engine.c<br>@@ -1495,21 +1495,24 @@<br> * \param asterisk_format Non-zero if the given Asterisk format is present<br> * \param format Asterisk format to look for<br> * \param code The format to look for<br>+ * \param explicit Require the provided code to be explicitly used<br> *<br> * \note It is assumed that static_RTP_PT_lock is at least read locked before calling.<br> *<br> * \retval Numerical payload type<br> * \retval -1 if could not assign.<br> */<br>-static int rtp_codecs_assign_payload_code_rx(struct ast_rtp_codecs *codecs, int asterisk_format, struct ast_format *format, int code)<br>+static int rtp_codecs_assign_payload_code_rx(struct ast_rtp_codecs *codecs, int asterisk_format, struct ast_format *format, int code, int explicit)<br> {<br>- int payload;<br>+ int payload = code;<br> struct ast_rtp_payload_type *new_type;<br> <br>- payload = find_static_payload_type(asterisk_format, format, code);<br>+ if (!explicit) {<br>+ payload = find_static_payload_type(asterisk_format, format, code);<br> <br>- if (payload < 0 && (!asterisk_format || ast_option_rtpusedynamic)) {<br>- return payload;<br>+ if (payload < 0 && (!asterisk_format || ast_option_rtpusedynamic)) {<br>+ return payload;<br>+ }<br> }<br> <br> new_type = rtp_payload_type_alloc(format, payload, code, 1);<br>@@ -1525,9 +1528,9 @@<br> * The payload type is a static assignment<br> * or our default dynamic position is available.<br> */<br>- rtp_codecs_payload_replace_rx(codecs, payload, new_type);<br>- } else if (-1 < (payload = find_unused_payload(codecs))<br>- || -1 < (payload = rtp_codecs_find_non_primary_dynamic_rx(codecs))) {<br>+ rtp_codecs_payload_replace_rx(codecs, payload, new_type);<br>+ } else if (!explicit && (-1 < (payload = find_unused_payload(codecs))<br>+ || -1 < (payload = rtp_codecs_find_non_primary_dynamic_rx(codecs)))) {<br> /*<br> * We found the first available empty dynamic position<br> * or we found a mapping that should no longer be<br>@@ -1535,6 +1538,11 @@<br> */<br> new_type->payload = payload;<br> rtp_codecs_payload_replace_rx(codecs, payload, new_type);<br>+ } else if (explicit) {<br>+ /*<br>+ * They explicitly requested this payload number be used but it couldn't be<br>+ */<br>+ payload = -1;<br> } else {<br> /*<br> * There are no empty or non-primary dynamic positions<br>@@ -1595,11 +1603,16 @@<br> <br> if (payload < 0) {<br> payload = rtp_codecs_assign_payload_code_rx(codecs, asterisk_format, format,<br>- code);<br>+ code, 0);<br> }<br> ast_rwlock_unlock(&static_RTP_PT_lock);<br> <br> return payload;<br>+}<br>+<br>+int ast_rtp_codecs_payload_set_rx(struct ast_rtp_codecs *codecs, int code, struct ast_format *format)<br>+{<br>+ return rtp_codecs_assign_payload_code_rx(codecs, 1, format, code, 1);<br> }<br> <br> int ast_rtp_codecs_payload_code_tx(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code)<br>@@ -2424,7 +2437,7 @@<br> <br> if (!*srtp) {<br> res = res_srtp->create(srtp, instance, remote_policy);<br>- } else {<br>+ } else if (remote_policy) {<br> res = res_srtp->replace(srtp, instance, remote_policy);<br> }<br> if (!res) {<br>@@ -3366,3 +3379,38 @@<br> <br> return cname;<br> }<br>+<br>+int ast_rtp_instance_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent)<br>+{<br>+ int res = -1;<br>+<br>+ if (child->engine != parent->engine) {<br>+ return -1;<br>+ }<br>+<br>+ ao2_lock(child);<br>+ if (child->engine->bundle) {<br>+ res = child->engine->bundle(child, parent);<br>+ }<br>+ ao2_unlock(child);<br>+<br>+ return res;<br>+}<br>+<br>+void ast_rtp_instance_set_remote_ssrc(struct ast_rtp_instance *rtp, unsigned int ssrc)<br>+{<br>+ ao2_lock(rtp);<br>+ if (rtp->engine->set_remote_ssrc) {<br>+ rtp->engine->set_remote_ssrc(rtp, ssrc);<br>+ }<br>+ ao2_unlock(rtp);<br>+}<br>+<br>+void ast_rtp_instance_set_stream_num(struct ast_rtp_instance *rtp, int stream_num)<br>+{<br>+ ao2_lock(rtp);<br>+ if (rtp->engine->set_stream_num) {<br>+ rtp->engine->set_stream_num(rtp, stream_num);<br>+ }<br>+ ao2_unlock(rtp);<br>+}<br>\ No newline at end of file<br>diff --git a/res/res_pjsip.c b/res/res_pjsip.c<br>index 0cf0343..1b546ef 100644<br>--- a/res/res_pjsip.c<br>+++ b/res/res_pjsip.c<br>@@ -995,6 +995,14 @@<br> streams allowed for the endpoint.<br> </para></description><br> </configOption><br>+ <configOption name="bundle" default="no"><br>+ <synopsis>Enable RTP bundling</synopsis><br>+ <description><para><br>+ With this option enabled, Asterisk will attempt to negotiate the use of bundle.<br>+ If negotiated this will result in multiple RTP streams being carried over the same<br>+ underlying transport. Note that enabling bundle will also enable the rtcp_mux option.<br>+ </para></description><br>+ </configOption><br> </configObject><br> <configObject name="auth"><br> <synopsis>Authentication type</synopsis><br>diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c<br>index 372b01b..d56ff5d 100644<br>--- a/res/res_pjsip/pjsip_configuration.c<br>+++ b/res/res_pjsip/pjsip_configuration.c<br>@@ -1332,6 +1332,10 @@<br> return -1;<br> }<br> <br>+ if (endpoint->media.bundle) {<br>+ endpoint->media.rtcp_mux = 1;<br>+ }<br>+<br> return 0;<br> }<br> <br>@@ -1954,6 +1958,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", "bundle", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.bundle));<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..4ec8115 100644<br>--- a/res/res_pjsip_sdp_rtp.c<br>+++ b/res/res_pjsip_sdp_rtp.c<br>@@ -317,6 +317,7 @@<br> <br> static int set_caps(struct ast_sip_session *session,<br> struct ast_sip_session_media *session_media,<br>+ struct ast_sip_session_media *session_media_transport,<br> const struct pjmedia_sdp_media *stream,<br> int is_offer, struct ast_stream *asterisk_stream)<br> {<br>@@ -375,6 +376,24 @@<br> session_media->rtp);<br> <br> ast_stream_set_formats(asterisk_stream, joint);<br>+<br>+ /* If this is a bundled stream then apply the payloads to RTP instance acting as transport to prevent conflicts */<br>+ if (session_media_transport != session_media && session_media->bundled) {<br>+ int index;<br>+<br>+ for (index = 0; index < ast_format_cap_count(joint); ++index) {<br>+ struct ast_format *format = ast_format_cap_get_format(joint, index);<br>+ int rtp_code;<br>+<br>+ /* Ensure this payload is in the bundle group transport codecs, this purposely doesn't check the return value for<br>+ * things as the format is guaranteed to have a payload already.<br>+ */<br>+ rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media->rtp), 1, format, 0);<br>+ ast_rtp_codecs_payload_set_rx(ast_rtp_instance_get_codecs(session_media_transport->rtp), rtp_code, format);<br>+<br>+ ao2_ref(format, -1);<br>+ }<br>+ }<br> <br> if (session->channel && ast_sip_session_is_pending_stream_default(session, asterisk_stream)) {<br> ast_channel_lock(session->channel);<br>@@ -496,7 +515,8 @@<br> }<br> <br> /*! \brief Function which adds ICE attributes to a media stream */<br>-static void add_ice_to_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media)<br>+static void add_ice_to_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media,<br>+ unsigned int include_candidates)<br> {<br> struct ast_rtp_engine_ice *ice;<br> struct ao2_container *candidates;<br>@@ -506,8 +526,7 @@<br> struct ao2_iterator it_candidates;<br> struct ast_rtp_engine_ice_candidate *candidate;<br> <br>- if (!session->endpoint->media.rtp.ice_support || !(ice = ast_rtp_instance_get_ice(session_media->rtp)) ||<br>- !(candidates = ice->get_local_candidates(session_media->rtp))) {<br>+ if (!session->endpoint->media.rtp.ice_support || !(ice = ast_rtp_instance_get_ice(session_media->rtp))) {<br> return;<br> }<br> <br>@@ -519,6 +538,15 @@<br> if ((password = ice->get_password(session_media->rtp))) {<br> attr = pjmedia_sdp_attr_create(pool, "ice-pwd", pj_cstr(&stmp, password));<br> media->attr[media->attr_count++] = attr;<br>+ }<br>+<br>+ if (!include_candidates) {<br>+ return;<br>+ }<br>+<br>+ candidates = ice->get_local_candidates(session_media->rtp);<br>+ if (!candidates) {<br>+ return;<br> }<br> <br> it_candidates = ao2_iterator_init(candidates, 0);<br>@@ -940,6 +968,63 @@<br> }<br> }<br> <br>+/*! \brief Function which adds ssrc attributes to a media stream */<br>+static void add_ssrc_to_stream(struct ast_sip_session *session, 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>+ char tmp[128];<br>+<br>+ if (!session->endpoint->media.bundle || session_media->bundle_group == -1) {<br>+ return;<br>+ }<br>+<br>+ snprintf(tmp, sizeof(tmp), "%u cname:%s", ast_rtp_instance_get_ssrc(session_media->rtp), ast_rtp_instance_get_cname(session_media->rtp));<br>+ attr = pjmedia_sdp_attr_create(pool, "ssrc", pj_cstr(&stmp, tmp));<br>+ media->attr[media->attr_count++] = attr;<br>+}<br>+<br>+/*! \brief Function which processes ssrc attributes in a stream */<br>+static void process_ssrc_attributes(struct ast_sip_session *session, struct ast_sip_session_media *session_media,<br>+ const struct pjmedia_sdp_media *remote_stream)<br>+{<br>+ int index;<br>+<br>+ if (!session->endpoint->media.bundle) {<br>+ return;<br>+ }<br>+<br>+ for (index = 0; index < remote_stream->attr_count; ++index) {<br>+ pjmedia_sdp_attr *attr = remote_stream->attr[index];<br>+ char attr_value[pj_strlen(&attr->value) + 1];<br>+ char *ssrc_attribute_name, *ssrc_attribute_value = NULL;<br>+ unsigned int ssrc;<br>+<br>+ /* We only care about ssrc attributes */<br>+ if (pj_strcmp2(&attr->name, "ssrc")) {<br>+ continue;<br>+ }<br>+<br>+ ast_copy_pj_str(attr_value, &attr->value, sizeof(attr_value));<br>+<br>+ if ((ssrc_attribute_name = strchr(attr_value, ' '))) {<br>+ /* This has an actual attribute */<br>+ *ssrc_attribute_name++ = '\0';<br>+ ssrc_attribute_value = strchr(ssrc_attribute_name, ':');<br>+ if (ssrc_attribute_value) {<br>+ /* Values are actually optional according to the spec */<br>+ *ssrc_attribute_value++ = '\0';<br>+ }<br>+ }<br>+<br>+ if (sscanf(attr_value, "%30u", &ssrc) < 1) {<br>+ continue;<br>+ }<br>+<br>+ ast_rtp_instance_set_remote_ssrc(session_media->rtp, ssrc);<br>+ }<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>@@ -948,6 +1033,7 @@<br> char host[NI_MAXHOST];<br> RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);<br> pjmedia_sdp_media *stream = sdp->media[index];<br>+ struct ast_sip_session_media *session_media_transport;<br> enum ast_media_type media_type = session_media->type;<br> enum ast_sip_session_media_encryption encryption = AST_SIP_MEDIA_ENCRYPT_NONE;<br> int res;<br>@@ -981,38 +1067,51 @@<br> return -1;<br> }<br> <br>- session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(stream, "rtcp-mux", NULL) != NULL);<br>- set_ice_components(session, session_media);<br>+ process_ssrc_attributes(session, session_media, stream);<br> <br>- enable_rtcp(session, session_media, stream);<br>+ session_media_transport = ast_sip_session_media_get_transport(session, session_media);<br> <br>- res = setup_media_encryption(session, session_media, sdp, stream);<br>- if (res) {<br>- if (!session->endpoint->media.rtp.encryption_optimistic ||<br>- !pj_strncmp2(&stream->desc.transport, "RTP/SAVP", 8)) {<br>- /* If optimistic encryption is disabled and crypto should have been enabled<br>- * but was not this session must fail. This must also fail if crypto was<br>- * required in the offer but could not be set up.<br>- */<br>- return -1;<br>+ if (session_media_transport == session_media || !session_media->bundled) {<br>+ /* If this media session is carrying actual traffic then set up those aspects */<br>+ session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(stream, "rtcp-mux", NULL) != NULL);<br>+ set_ice_components(session, session_media);<br>+<br>+ enable_rtcp(session, session_media, stream);<br>+<br>+ res = setup_media_encryption(session, session_media, sdp, stream);<br>+ if (res) {<br>+ if (!session->endpoint->media.rtp.encryption_optimistic ||<br>+ !pj_strncmp2(&stream->desc.transport, "RTP/SAVP", 8)) {<br>+ /* If optimistic encryption is disabled and crypto should have been enabled<br>+ * but was not this session must fail. This must also fail if crypto was<br>+ * required in the offer but could not be set up.<br>+ */<br>+ return -1;<br>+ }<br>+ /* There is no encryption, sad. */<br>+ session_media->encryption = AST_SIP_MEDIA_ENCRYPT_NONE;<br> }<br>- /* There is no encryption, sad. */<br>- session_media->encryption = AST_SIP_MEDIA_ENCRYPT_NONE;<br>- }<br> <br>- /* If we've been explicitly configured to use the received transport OR if<br>- * encryption is on and crypto is present use the received transport.<br>- * This is done in case of optimistic because it may come in as RTP/AVP or RTP/SAVP depending<br>- * on the configuration of the remote endpoint (optimistic themselves or mandatory).<br>- */<br>- if ((session->endpoint->media.rtp.use_received_transport) ||<br>- ((encryption == AST_SIP_MEDIA_ENCRYPT_SDES) && !res)) {<br>- pj_strdup(session->inv_session->pool, &session_media->transport, &stream->desc.transport);<br>- }<br>+ /* If we've been explicitly configured to use the received transport OR if<br>+ * encryption is on and crypto is present use the received transport.<br>+ * This is done in case of optimistic because it may come in as RTP/AVP or RTP/SAVP depending<br>+ * on the configuration of the remote endpoint (optimistic themselves or mandatory).<br>+ */<br>+ if ((session->endpoint->media.rtp.use_received_transport) ||<br>+ ((encryption == AST_SIP_MEDIA_ENCRYPT_SDES) && !res)) {<br>+ pj_strdup(session->inv_session->pool, &session_media->transport, &stream->desc.transport);<br>+ }<br>+ } else {<br>+ /* This is bundled with another session, so mark it as such */<br>+ ast_rtp_instance_bundle(session_media->rtp, session_media_transport->rtp);<br> <br>- if (set_caps(session, session_media, stream, 1, asterisk_stream)) {<br>+ enable_rtcp(session, session_media, stream);<br>+ }<br>+<br>+ if (set_caps(session, session_media, session_media_transport, stream, 1, asterisk_stream)) {<br> return 0;<br> }<br>+<br> return 1;<br> }<br> <br>@@ -1032,6 +1131,7 @@<br> static const pj_str_t STR_PASSIVE = { "passive", 7 };<br> static const pj_str_t STR_ACTPASS = { "actpass", 7 };<br> static const pj_str_t STR_HOLDCONN = { "holdconn", 8 };<br>+ enum ast_rtp_dtls_setup setup;<br> <br> switch (session_media->encryption) {<br> case AST_SIP_MEDIA_ENCRYPT_NONE:<br>@@ -1085,7 +1185,16 @@<br> break;<br> }<br> <br>- switch (dtls->get_setup(session_media->rtp)) {<br>+ /* If this is an answer we need to use our current state, if it's an offer we need to use<br>+ * the configured value.<br>+ */<br>+ if (pjmedia_sdp_neg_get_state(session->inv_session->neg) != PJMEDIA_SDP_NEG_STATE_DONE) {<br>+ setup = dtls->get_setup(session_media->rtp);<br>+ } else {<br>+ setup = session->endpoint->media.rtp.dtls_cfg.default_setup;<br>+ }<br>+<br>+ switch (setup) {<br> case AST_RTP_DTLS_SETUP_ACTIVE:<br> attr = pjmedia_sdp_attr_create(pool, "setup", &STR_ACTIVE);<br> media->attr[media->attr_count++] = attr;<br>@@ -1100,7 +1209,6 @@<br> break;<br> case AST_RTP_DTLS_SETUP_HOLDCONN:<br> attr = pjmedia_sdp_attr_create(pool, "setup", &STR_HOLDCONN);<br>- media->attr[media->attr_count++] = attr;<br> break;<br> default:<br> break;<br>@@ -1152,6 +1260,7 @@<br> int rtp_code;<br> RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);<br> enum ast_media_type media_type = session_media->type;<br>+ struct ast_sip_session_media *session_media_transport;<br> <br> int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&<br> ast_format_cap_count(session->direct_media_cap);<br>@@ -1195,68 +1304,106 @@<br> return -1;<br> }<br> <br>- set_ice_components(session, session_media);<br>- enable_rtcp(session, session_media, NULL);<br>+ /* If this stream has not been bundled already it is new and we need to ensure there is no SSRC conflict */<br>+ if (session_media->bundle_group != -1 && !session_media->bundled) {<br>+ for (index = 0; index < sdp->media_count; ++index) {<br>+ struct ast_sip_session_media *other_session_media;<br> <br>- /* Crypto has to be added before setting the media transport so that SRTP is properly<br>- * set up according to the configuration. This ends up changing the media transport.<br>- */<br>- if (add_crypto_to_stream(session, session_media, pool, media)) {<br>- return -1;<br>- }<br>+ other_session_media = AST_VECTOR_GET(&session->pending_media_state->sessions, index);<br>+ if (!other_session_media->rtp || other_session_media->bundle_group != session_media->bundle_group) {<br>+ continue;<br>+ }<br> <br>- if (pj_strlen(&session_media->transport)) {<br>- /* If a transport has already been specified use it */<br>- media->desc.transport = session_media->transport;<br>- } else {<br>- media->desc.transport = pj_str(ast_sdp_get_rtp_profile(<br>- /* Optimistic encryption places crypto in the normal RTP/AVP profile */<br>- !session->endpoint->media.rtp.encryption_optimistic &&<br>- (session_media->encryption == AST_SIP_MEDIA_ENCRYPT_SDES),<br>- session_media->rtp, session->endpoint->media.rtp.use_avpf,<br>- session->endpoint->media.rtp.force_avp));<br>- }<br>-<br>- media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn));<br>- if (!media->conn) {<br>- return -1;<br>- }<br>-<br>- /* Add connection level details */<br>- if (direct_media_enabled) {<br>- hostip = ast_sockaddr_stringify_fmt(&session_media->direct_media_addr, AST_SOCKADDR_STR_ADDR);<br>- } else if (ast_strlen_zero(session->endpoint->media.address)) {<br>- hostip = ast_sip_get_host_ip_string(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET());<br>- } else {<br>- hostip = session->endpoint->media.address;<br>- }<br>-<br>- if (ast_strlen_zero(hostip)) {<br>- ast_log(LOG_ERROR, "No local host IP available for stream %s\n",<br>- ast_codec_media_type2str(session_media->type));<br>- return -1;<br>- }<br>-<br>- media->conn->net_type = STR_IN;<br>- /* Assume that the connection will use IPv4 until proven otherwise */<br>- media->conn->addr_type = STR_IP4;<br>- pj_strdup2(pool, &media->conn->addr, hostip);<br>-<br>- if (!ast_strlen_zero(session->endpoint->media.address)) {<br>- pj_sockaddr ip;<br>-<br>- if ((pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &media->conn->addr, &ip) == PJ_SUCCESS) &&<br>- (ip.addr.sa_family == pj_AF_INET6())) {<br>- media->conn->addr_type = STR_IP6;<br>+ if (ast_rtp_instance_get_ssrc(session_media->rtp) == ast_rtp_instance_get_ssrc(other_session_media->rtp)) {<br>+ ast_rtp_instance_change_source(session_media->rtp);<br>+ /* Start the conflict check over again */<br>+ index = -1;<br>+ continue;<br>+ }<br> }<br> }<br> <br>- /* Add ICE attributes and candidates */<br>- add_ice_to_stream(session, session_media, pool, media);<br>+ session_media_transport = ast_sip_session_media_get_transport(session, session_media);<br> <br>- ast_rtp_instance_get_local_address(session_media->rtp, &addr);<br>- media->desc.port = direct_media_enabled ? ast_sockaddr_port(&session_media->direct_media_addr) : (pj_uint16_t) ast_sockaddr_port(&addr);<br>- media->desc.port_count = 1;<br>+ if (session_media_transport == session_media || !session_media->bundled) {<br>+ set_ice_components(session, session_media);<br>+ enable_rtcp(session, session_media, NULL);<br>+<br>+ /* Crypto has to be added before setting the media transport so that SRTP is properly<br>+ * set up according to the configuration. This ends up changing the media transport.<br>+ */<br>+ if (add_crypto_to_stream(session, session_media, pool, media)) {<br>+ return -1;<br>+ }<br>+<br>+ if (pj_strlen(&session_media->transport)) {<br>+ /* If a transport has already been specified use it */<br>+ media->desc.transport = session_media->transport;<br>+ } else {<br>+ media->desc.transport = pj_str(ast_sdp_get_rtp_profile(<br>+ /* Optimistic encryption places crypto in the normal RTP/AVP profile */<br>+ !session->endpoint->media.rtp.encryption_optimistic &&<br>+ (session_media->encryption == AST_SIP_MEDIA_ENCRYPT_SDES),<br>+ session_media->rtp, session->endpoint->media.rtp.use_avpf,<br>+ session->endpoint->media.rtp.force_avp));<br>+ }<br>+<br>+ media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn));<br>+ if (!media->conn) {<br>+ return -1;<br>+ }<br>+<br>+ /* Add connection level details */<br>+ if (direct_media_enabled) {<br>+ hostip = ast_sockaddr_stringify_fmt(&session_media->direct_media_addr, AST_SOCKADDR_STR_ADDR);<br>+ } else if (ast_strlen_zero(session->endpoint->media.address)) {<br>+ hostip = ast_sip_get_host_ip_string(session->endpoint->media.rtp.ipv6 ? pj_AF_INET6() : pj_AF_INET());<br>+ } else {<br>+ hostip = session->endpoint->media.address;<br>+ }<br>+<br>+ if (ast_strlen_zero(hostip)) {<br>+ ast_log(LOG_ERROR, "No local host IP available for stream %s\n",<br>+ ast_codec_media_type2str(session_media->type));<br>+ return -1;<br>+ }<br>+<br>+ media->conn->net_type = STR_IN;<br>+ /* Assume that the connection will use IPv4 until proven otherwise */<br>+ media->conn->addr_type = STR_IP4;<br>+ pj_strdup2(pool, &media->conn->addr, hostip);<br>+<br>+ if (!ast_strlen_zero(session->endpoint->media.address)) {<br>+ pj_sockaddr ip;<br>+<br>+ if ((pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &media->conn->addr, &ip) == PJ_SUCCESS) &&<br>+ (ip.addr.sa_family == pj_AF_INET6())) {<br>+ media->conn->addr_type = STR_IP6;<br>+ }<br>+ }<br>+<br>+ /* Add ICE attributes and candidates */<br>+ add_ice_to_stream(session, session_media, pool, media, 1);<br>+<br>+ ast_rtp_instance_get_local_address(session_media->rtp, &addr);<br>+ media->desc.port = direct_media_enabled ? ast_sockaddr_port(&session_media->direct_media_addr) : (pj_uint16_t) ast_sockaddr_port(&addr);<br>+ media->desc.port_count = 1;<br>+ } else {<br>+ pjmedia_sdp_media *bundle_group_stream = sdp->media[session_media_transport->stream_num];<br>+<br>+ /* As this is in a bundle group it shares the same details as the group instance */<br>+ media->desc.transport = bundle_group_stream->desc.transport;<br>+ media->conn = bundle_group_stream->conn;<br>+ media->desc.port = bundle_group_stream->desc.port;<br>+<br>+ if (add_crypto_to_stream(session, session_media_transport, pool, media)) {<br>+ return -1;<br>+ }<br>+<br>+ add_ice_to_stream(session, session_media_transport, pool, media, 0);<br>+<br>+ enable_rtcp(session, session_media, NULL);<br>+ }<br> <br> if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {<br> ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n",<br>@@ -1278,10 +1425,23 @@<br> continue;<br> }<br> <br>- if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media->rtp), 1, format, 0)) == -1) {<br>- ast_log(LOG_WARNING,"Unable to get rtp codec payload code for %s\n", ast_format_get_name(format));<br>- ao2_ref(format, -1);<br>- continue;<br>+ /* If this stream is not a transport we need to use the transport codecs structure for payload management to prevent<br>+ * conflicts.<br>+ */<br>+ if (session_media_transport != session_media) {<br>+ if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media_transport->rtp), 1, format, 0)) == -1) {<br>+ ast_log(LOG_WARNING,"Unable to get rtp codec payload code for %s\n", ast_format_get_name(format));<br>+ ao2_ref(format, -1);<br>+ continue;<br>+ }<br>+ /* Our instance has to match the payload number though */<br>+ ast_rtp_codecs_payload_set_rx(ast_rtp_instance_get_codecs(session_media->rtp), rtp_code, format);<br>+ } else {<br>+ if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media->rtp), 1, format, 0)) == -1) {<br>+ ast_log(LOG_WARNING,"Unable to get rtp codec payload code for %s\n", ast_format_get_name(format));<br>+ ao2_ref(format, -1);<br>+ continue;<br>+ }<br> }<br> <br> if ((attr = generate_rtpmap_attr(session, media, pool, rtp_code, 1, format, 0))) {<br>@@ -1332,6 +1492,7 @@<br> }<br> }<br> <br>+<br> /* If no formats were actually added to the media stream don't add it to the SDP */<br> if (!media->desc.fmt_count) {<br> return 1;<br>@@ -1364,6 +1525,8 @@<br> attr = pjmedia_sdp_attr_create(pool, "rtcp-mux", NULL);<br> pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);<br> }<br>+<br>+ add_ssrc_to_stream(session, session_media, pool, media);<br> <br> /* Add the media stream to the SDP */<br> sdp->media[sdp->media_count++] = media;<br>@@ -1425,6 +1588,7 @@<br> enum ast_media_type media_type = session_media->type;<br> char host[NI_MAXHOST];<br> int res;<br>+ struct ast_sip_session_media *session_media_transport;<br> <br> if (!session->channel) {<br> return 1;<br>@@ -1441,48 +1605,60 @@<br> return -1;<br> }<br> <br>- session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(remote_stream, "rtcp-mux", NULL) != NULL);<br>- set_ice_components(session, session_media);<br>+ process_ssrc_attributes(session, session_media, remote_stream);<br> <br>- enable_rtcp(session, session_media, remote_stream);<br>+ session_media_transport = ast_sip_session_media_get_transport(session, session_media);<br> <br>- res = setup_media_encryption(session, session_media, remote, remote_stream);<br>- if (!session->endpoint->media.rtp.encryption_optimistic && res) {<br>- /* If optimistic encryption is disabled and crypto should have been enabled but was not<br>- * this session must fail.<br>- */<br>- return -1;<br>+ if (session_media_transport == session_media || !session_media->bundled) {<br>+ session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(remote_stream, "rtcp-mux", NULL) != NULL);<br>+ set_ice_components(session, session_media);<br>+<br>+ enable_rtcp(session, session_media, remote_stream);<br>+<br>+ res = setup_media_encryption(session, session_media, remote, remote_stream);<br>+ if (!session->endpoint->media.rtp.encryption_optimistic && res) {<br>+ /* If optimistic encryption is disabled and crypto should have been enabled but was not<br>+ * this session must fail.<br>+ */<br>+ return -1;<br>+ }<br>+<br>+ if (!remote_stream->conn && !remote->conn) {<br>+ return 1;<br>+ }<br>+<br>+ ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host));<br>+<br>+ /* Ensure that the address provided is valid */<br>+ if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) {<br>+ /* The provided host was actually invalid so we error out this negotiation */<br>+ return -1;<br>+ }<br>+<br>+ /* Apply connection information to the RTP instance */<br>+ ast_sockaddr_set_port(addrs, remote_stream->desc.port);<br>+ ast_rtp_instance_set_remote_address(session_media->rtp, addrs);<br>+<br>+ ast_sip_session_media_set_write_callback(session, session_media, media_session_rtp_write_callback);<br>+ ast_sip_session_media_add_read_callback(session, session_media, ast_rtp_instance_fd(session_media->rtp, 0),<br>+ media_session_rtp_read_callback);<br>+ if (!session->endpoint->media.rtcp_mux || !session_media->remote_rtcp_mux) {<br>+ ast_sip_session_media_add_read_callback(session, session_media, ast_rtp_instance_fd(session_media->rtp, 1),<br>+ media_session_rtcp_read_callback);<br>+ }<br>+<br>+ /* If ICE support is enabled find all the needed attributes */<br>+ process_ice_attributes(session, session_media, remote, remote_stream);<br>+ } else {<br>+ /* This is bundled with another session, so mark it as such */<br>+ ast_rtp_instance_bundle(session_media->rtp, session_media_transport->rtp);<br>+ ast_sip_session_media_set_write_callback(session, session_media, media_session_rtp_write_callback);<br>+ enable_rtcp(session, session_media, remote_stream);<br> }<br> <br>- if (!remote_stream->conn && !remote->conn) {<br>+ if (set_caps(session, session_media, session_media_transport, remote_stream, 0, asterisk_stream)) {<br> return 1;<br> }<br>-<br>- ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host));<br>-<br>- /* Ensure that the address provided is valid */<br>- if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) {<br>- /* The provided host was actually invalid so we error out this negotiation */<br>- return -1;<br>- }<br>-<br>- /* Apply connection information to the RTP instance */<br>- ast_sockaddr_set_port(addrs, remote_stream->desc.port);<br>- ast_rtp_instance_set_remote_address(session_media->rtp, addrs);<br>- if (set_caps(session, session_media, remote_stream, 0, asterisk_stream)) {<br>- return 1;<br>- }<br>-<br>- ast_sip_session_media_set_write_callback(session, session_media, media_session_rtp_write_callback);<br>- ast_sip_session_media_add_read_callback(session, session_media, ast_rtp_instance_fd(session_media->rtp, 0),<br>- media_session_rtp_read_callback);<br>- if (!session->endpoint->media.rtcp_mux || !session_media->remote_rtcp_mux) {<br>- ast_sip_session_media_add_read_callback(session, session_media, ast_rtp_instance_fd(session_media->rtp, 1),<br>- media_session_rtcp_read_callback);<br>- }<br>-<br>- /* If ICE support is enabled find all the needed attributes */<br>- process_ice_attributes(session, session_media, remote, remote_stream);<br> <br> /* Set the channel uniqueid on the RTP instance now that it is becoming active */<br> ast_channel_lock(session->channel);<br>@@ -1490,6 +1666,7 @@<br> ast_channel_unlock(session->channel);<br> <br> /* Ensure the RTP instance is active */<br>+ ast_rtp_instance_set_stream_num(session_media->rtp, ast_stream_get_position(asterisk_stream));<br> ast_rtp_instance_activate(session_media->rtp);<br> <br> /* audio stream handles music on hold */<br>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c<br>index ecda499..315db6d 100644<br>--- a/res/res_pjsip_session.c<br>+++ b/res/res_pjsip_session.c<br>@@ -324,6 +324,28 @@<br> return 0;<br> }<br> <br>+struct ast_sip_session_media *ast_sip_session_media_get_transport(struct ast_sip_session *session, struct ast_sip_session_media *session_media)<br>+{<br>+ int index;<br>+<br>+ if (!session->endpoint->media.bundle || ast_strlen_zero(session_media->mid)) {<br>+ return session_media;<br>+ }<br>+<br>+ for (index = 0; index < AST_VECTOR_SIZE(&session->pending_media_state->sessions); ++index) {<br>+ struct ast_sip_session_media *bundle_group_session_media;<br>+<br>+ bundle_group_session_media = AST_VECTOR_GET(&session->pending_media_state->sessions, index);<br>+<br>+ /* The first session which is in the bundle group is considered the authoritative session for transport */<br>+ if (bundle_group_session_media->bundle_group == session_media->bundle_group) {<br>+ return bundle_group_session_media;<br>+ }<br>+ }<br>+<br>+ return session_media;<br>+}<br>+<br> /*!<br> * \brief Set an SDP stream handler for a corresponding session media.<br> *<br>@@ -371,6 +393,8 @@<br> if (session_media->srtp) {<br> ast_sdp_srtp_destroy(session_media->srtp);<br> }<br>+<br>+ ast_free(session_media->mid);<br> }<br> <br> struct ast_sip_session_media *ast_sip_session_media_state_add(struct ast_sip_session *session,<br>@@ -408,13 +432,25 @@<br> session_media->timeout_sched_id = -1;<br> session_media->type = type;<br> session_media->stream_num = position;<br>+<br>+ if (session->endpoint->media.bundle) {<br>+ /* This is a new stream so create a new mid based on media type and position, which makes it unique.<br>+ * If this is the result of an offer the mid will just end up getting replaced.<br>+ */<br>+ if (ast_asprintf(&session_media->mid, "%s-%d", ast_codec_media_type2str(type), position) < 0) {<br>+ ao2_ref(session_media, -1);<br>+ return NULL;<br>+ }<br>+ session_media->bundle_group = 0;<br>+ } else {<br>+ session_media->bundle_group = -1;<br>+ }<br> }<br> <br> AST_VECTOR_REPLACE(&media_state->sessions, position, session_media);<br> <br> /* If this stream will be active in some way and it is the first of this type then consider this the default media session to match */<br>- if (!media_state->default_session[type] &&<br>- ast_stream_get_state(ast_stream_topology_get_stream(media_state->topology, position)) != AST_STREAM_STATE_REMOVED) {<br>+ if (!media_state->default_session[type] && ast_stream_get_state(ast_stream_topology_get_stream(media_state->topology, position)) != AST_STREAM_STATE_REMOVED) {<br> media_state->default_session[type] = session_media;<br> }<br> <br>@@ -439,6 +475,78 @@<br> */<br> return 1;<br> }<br>+}<br>+<br>+static int get_mid_bundle_group(const pjmedia_sdp_session *sdp, const char *mid)<br>+{<br>+ int bundle_group = 0;<br>+ int index;<br>+<br>+ for (index = 0; index < sdp->attr_count; ++index) {<br>+ pjmedia_sdp_attr *attr = sdp->attr[index];<br>+ char value[pj_strlen(&attr->value) + 1], *mids = value, *attr_mid;<br>+<br>+ if (pj_strcmp2(&attr->name, "group") || pj_strncmp2(&attr->value, "BUNDLE", 6)) {<br>+ continue;<br>+ }<br>+<br>+ ast_copy_pj_str(value, &attr->value, sizeof(value));<br>+<br>+ /* Skip the BUNDLE at the front */<br>+ mids += 7;<br>+<br>+ while ((attr_mid = strsep(&mids, " "))) {<br>+ if (!strcmp(attr_mid, mid)) {<br>+ /* The ordering of attributes determines our internal identification of the bundle group based on number,<br>+ * with -1 being not in a bundle group. Since this is only exposed internally for response purposes it's<br>+ * actually even fine if things move around.<br>+ */<br>+ return bundle_group;<br>+ }<br>+ }<br>+<br>+ bundle_group++;<br>+ }<br>+<br>+ return -1;<br>+}<br>+<br>+static int set_mid_and_bundle_group(struct ast_sip_session *session,<br>+ struct ast_sip_session_media *session_media,<br>+ const pjmedia_sdp_session *sdp,<br>+ const struct pjmedia_sdp_media *stream)<br>+{<br>+ pjmedia_sdp_attr *attr;<br>+<br>+ if (!session->endpoint->media.bundle) {<br>+ return 0;<br>+ }<br>+<br>+ /* By default on an incoming negotiation we assume no mid and bundle group is present */<br>+ ast_free(session_media->mid);<br>+ session_media->mid = NULL;<br>+ session_media->bundle_group = -1;<br>+ session_media->bundled = 0;<br>+<br>+ /* Grab the media identifier for the stream */<br>+ attr = pjmedia_sdp_media_find_attr2(stream, "mid", NULL);<br>+ if (!attr) {<br>+ return 0;<br>+ }<br>+<br>+ session_media->mid = ast_calloc(1, attr->value.slen + 1);<br>+ if (!session_media->mid) {<br>+ return 0;<br>+ }<br>+ ast_copy_pj_str(session_media->mid, &attr->value, attr->value.slen + 1);<br>+<br>+ /* Determine what bundle group this is part of */<br>+ session_media->bundle_group = get_mid_bundle_group(sdp, session_media->mid);<br>+<br>+ /* If this is actually part of a bundle group then the other side requested or accepted the bundle request */<br>+ session_media->bundled = session_media->bundle_group != -1;<br>+<br>+ return 0;<br> }<br> <br> static int handle_incoming_sdp(struct ast_sip_session *session, const pjmedia_sdp_session *sdp)<br>@@ -497,8 +605,12 @@<br> ast_debug(1, "Declining incoming SDP media stream '%s' at position '%d'\n",<br> ast_codec_media_type2str(type), i);<br> ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED);<br>+ session_media->bundle_group = -1;<br>+ session_media->bundled = 0;<br> continue;<br> }<br>+<br>+ set_mid_and_bundle_group(session, session_media, sdp, remote_stream);<br> <br> if (session_media->handler) {<br> handler = session_media->handler;<br>@@ -588,6 +700,8 @@<br> <br> /* We need a null-terminated version of the media string */<br> ast_copy_pj_str(media, &local->media[index]->desc.media, sizeof(media));<br>+<br>+ set_mid_and_bundle_group(session, session_media, remote, remote->media[index]);<br> <br> handler = session_media->handler;<br> if (handler) {<br>@@ -3443,6 +3557,82 @@<br> return 0;<br> }<br> <br>+/*! \brief Bundle group building structure */<br>+struct sip_session_media_bundle_group {<br>+ /*! \brief The media identifiers in this bundle group */<br>+ char *mids[PJMEDIA_MAX_SDP_MEDIA];<br>+ /*! \brief SDP attribute string */<br>+ struct ast_str *attr_string;<br>+};<br>+<br>+static int add_bundle_groups(struct ast_sip_session *session, pj_pool_t *pool, pjmedia_sdp_session *answer)<br>+{<br>+ pj_str_t stmp;<br>+ pjmedia_sdp_attr *attr;<br>+ struct sip_session_media_bundle_group bundle_groups[PJMEDIA_MAX_SDP_MEDIA];<br>+ int index, mid_id;<br>+ struct sip_session_media_bundle_group *bundle_group;<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>+ struct ast_sip_session_media *session_media = AST_VECTOR_GET(&session->pending_media_state->sessions, index);<br>+<br>+ /* If this stream is not part of a bundle group we can't add it */<br>+ if (session_media->bundle_group == -1) {<br>+ continue;<br>+ }<br>+<br>+ bundle_group = &bundle_groups[session_media->bundle_group];<br>+<br>+ /* If this is the first mid then we need to allocate the attribute string and place BUNDLE in front */<br>+ if (!bundle_group->mids[0]) {<br>+ bundle_group->mids[0] = session_media->mid;<br>+ bundle_group->attr_string = ast_str_create(64);<br>+ if (!bundle_group->attr_string) {<br>+ continue;<br>+ }<br>+<br>+ ast_str_set(&bundle_group->attr_string, -1, "BUNDLE %s", session_media->mid);<br>+ continue;<br>+ }<br>+<br>+ for (mid_id = 1; mid_id < PJMEDIA_MAX_SDP_MEDIA; ++mid_id) {<br>+ if (!bundle_group->mids[mid_id]) {<br>+ bundle_group->mids[mid_id] = session_media->mid;<br>+ ast_str_append(&bundle_group->attr_string, -1, " %s", session_media->mid);<br>+ break;<br>+ } else if (!strcmp(bundle_group->mids[mid_id], session_media->mid)) {<br>+ break;<br>+ }<br>+ }<br>+ }<br>+<br>+ /* Add all bundle groups that have mids to the SDP */<br>+ for (index = 0; index < PJMEDIA_MAX_SDP_MEDIA; ++index) {<br>+ bundle_group = &bundle_groups[index];<br>+<br>+ if (!bundle_group->attr_string) {<br>+ continue;<br>+ }<br>+<br>+ attr = pjmedia_sdp_attr_create(pool, "group", pj_cstr(&stmp, ast_str_buffer(bundle_group->attr_string)));<br>+ pjmedia_sdp_attr_add(&answer->attr_count, answer->attr, attr);<br>+<br>+ ast_free(bundle_group->attr_string);<br>+ }<br>+<br>+ return 0;<br>+}<br>+<br> static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, struct ast_sip_session *session, const pjmedia_sdp_session *offer)<br> {<br> static const pj_str_t STR_IN = { "IN", 2 };<br>@@ -3485,6 +3675,7 @@<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>+ unsigned int streams = local->media_count;<br> <br> /* This code does not enforce any maximum stream count limitations as that is done on either<br> * the handling of an incoming SDP offer or on the handling of a session refresh.<br>@@ -3501,12 +3692,30 @@<br> return NULL;<br> }<br> <br>+ /* If a stream was actually added then add any additional details */<br>+ if (streams != local->media_count) {<br>+ pjmedia_sdp_media *media = local->media[streams];<br>+ pj_str_t stmp;<br>+ pjmedia_sdp_attr *attr;<br>+<br>+ /* Add the media identifier if present */<br>+ if (!ast_strlen_zero(session_media->mid)) {<br>+ attr = pjmedia_sdp_attr_create(inv->pool_prov, "mid", pj_cstr(&stmp, session_media->mid));<br>+ pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);<br>+ }<br>+ }<br>+<br> /* Ensure that we never exceed the maximum number of streams PJMEDIA will allow. */<br> if (local->media_count == PJMEDIA_MAX_SDP_MEDIA) {<br> break;<br> }<br> }<br> <br>+ /* Add any bundle groups that are present on the media state */<br>+ if (add_bundle_groups(session, inv->pool_prov, local)) {<br>+ return NULL;<br>+ }<br>+<br> /* Use the connection details of an available media if possible for SDP level */<br> for (stream = 0; stream < local->media_count; stream++) {<br> if (!local->media[stream]->conn) {<br>diff --git a/res/res_pjsip_t38.c b/res/res_pjsip_t38.c<br>index a032bb1..877d48f 100644<br>--- a/res/res_pjsip_t38.c<br>+++ b/res/res_pjsip_t38.c<br>@@ -880,11 +880,20 @@<br> <br> static struct ast_frame *media_session_udptl_read_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media)<br> {<br>+ struct ast_frame *frame;<br>+<br> if (!session_media->udptl) {<br> return &ast_null_frame;<br> }<br> <br>- return ast_udptl_read(session_media->udptl);<br>+ frame = ast_udptl_read(session_media->udptl);<br>+ if (!frame) {<br>+ return NULL;<br>+ }<br>+<br>+ frame->stream_num = session_media->stream_num;<br>+<br>+ return frame;<br> }<br> <br> static int media_session_udptl_write_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct ast_frame *frame)<br>diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c<br>index 01dfe76..4bfbf9b 100644<br>--- a/res/res_rtp_asterisk.c<br>+++ b/res/res_rtp_asterisk.c<br>@@ -68,6 +68,7 @@<br> #include "asterisk/module.h"<br> #include "asterisk/rtp_engine.h"<br> #include "asterisk/smoother.h"<br>+#include "asterisk/uuid.h"<br> #include "asterisk/test.h"<br> <br> #define MAX_TIMESTAMP_SKEW 640<br>@@ -238,6 +239,14 @@<br> };<br> #endif<br> <br>+/*! \brief Structure used for mapping an incoming SSRC to an RTP instance */<br>+struct rtp_ssrc_mapping {<br>+ /*! \brief The received SSRC */<br>+ unsigned int ssrc;<br>+ /*! \brief The RTP instance this SSRC belongs to*/<br>+ struct ast_rtp_instance *instance;<br>+};<br>+<br> /*! \brief RTP session description */<br> struct ast_rtp {<br> int s;<br>@@ -245,6 +254,7 @@<br> struct ast_frame f;<br> unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];<br> unsigned int ssrc; /*!< Synchronization source, RFC 3550, page 10. */<br>+ char cname[AST_UUID_STR_LEN]; /*!< Our local CNAME */<br> unsigned int themssrc; /*!< Their SSRC */<br> unsigned int rxssrc;<br> unsigned int lastts;<br>@@ -300,6 +310,11 @@<br> void *data;<br> struct ast_rtcp *rtcp;<br> struct ast_rtp *bridged; /*!< Who we are Packet bridged to */<br>+<br>+ struct ast_rtp_instance *bundled; /*!< The RTP instance we are bundled to */<br>+ int stream_num; /*!< Stream num for this RTP instance */<br>+ AST_VECTOR(, struct rtp_ssrc_mapping) ssrc_mapping; /*!< Mappings of SSRC to RTP instances */<br>+ struct ast_sockaddr bind_address; /*!< Requested bind address for the sockets */<br> <br> enum strict_rtp_state strict_rtp_state; /*!< Current state that strict RTP protection is in */<br> struct ast_sockaddr strict_rtp_address; /*!< Remote address information for strict RTP purposes */<br>@@ -477,6 +492,9 @@<br> static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);<br> static unsigned int ast_rtp_get_ssrc(struct ast_rtp_instance *instance);<br> static const char *ast_rtp_get_cname(struct ast_rtp_instance *instance);<br>+static void ast_rtp_set_remote_ssrc(struct ast_rtp_instance *instance, unsigned int ssrc);<br>+static void ast_rtp_set_stream_num(struct ast_rtp_instance *instance, int stream_num);<br>+static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent);<br> <br> #ifdef HAVE_OPENSSL_SRTP<br> static int ast_rtp_activate(struct ast_rtp_instance *instance);<br>@@ -1907,6 +1925,9 @@<br> #endif<br> .ssrc_get = ast_rtp_get_ssrc,<br> .cname_get = ast_rtp_get_cname,<br>+ .set_remote_ssrc = ast_rtp_set_remote_ssrc,<br>+ .set_stream_num = ast_rtp_set_stream_num,<br>+ .bundle = ast_rtp_bundle,<br> };<br> <br> #ifdef HAVE_OPENSSL_SRTP<br>@@ -1943,6 +1964,23 @@<br> }<br> #endif<br> <br>+#ifdef HAVE_OPENSSL_SRTP<br>+static void dtls_perform_setup(struct dtls_details *dtls)<br>+{<br>+ if (!dtls->ssl || !SSL_is_init_finished(dtls->ssl)) {<br>+ return;<br>+ }<br>+<br>+ SSL_clear(dtls->ssl);<br>+ if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {<br>+ SSL_set_accept_state(dtls->ssl);<br>+ } else {<br>+ SSL_set_connect_state(dtls->ssl);<br>+ }<br>+ dtls->connection = AST_RTP_DTLS_CONNECTION_NEW;<br>+}<br>+#endif<br>+<br> #ifdef HAVE_PJPROJECT<br> static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq);<br> <br>@@ -1971,9 +2009,12 @@<br> }<br> <br> #ifdef HAVE_OPENSSL_SRTP<br>+<br>+ dtls_perform_setup(&rtp->dtls);<br> dtls_perform_handshake(instance, &rtp->dtls, 0);<br> <br> if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {<br>+ dtls_perform_setup(&rtp->rtcp->dtls);<br> dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);<br> }<br> #endif<br>@@ -2241,14 +2282,90 @@<br> return 0;<br> }<br> <br>-static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance, int rtcp)<br>+static int dtls_srtp_add_local_ssrc(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance, int rtcp, unsigned int ssrc, int set_remote_policy)<br> {<br> unsigned char material[SRTP_MASTER_LEN * 2];<br> unsigned char *local_key, *local_salt, *remote_key, *remote_salt;<br> struct ast_srtp_policy *local_policy, *remote_policy = NULL;<br>- struct ast_rtp_instance_stats stats = { 0, };<br> int res = -1;<br> struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;<br>+<br>+ /* Produce key information and set up SRTP */<br>+ if (!SSL_export_keying_material(dtls->ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {<br>+ ast_log(LOG_WARNING, "Unable to extract SRTP keying material from DTLS-SRTP negotiation on RTP instance '%p'\n",<br>+ instance);<br>+ return -1;<br>+ }<br>+<br>+ /* Whether we are acting as a server or client determines where the keys/salts are */<br>+ if (rtp->dtls.dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) {<br>+ local_key = material;<br>+ remote_key = local_key + SRTP_MASTER_KEY_LEN;<br>+ local_salt = remote_key + SRTP_MASTER_KEY_LEN;<br>+ remote_salt = local_salt + SRTP_MASTER_SALT_LEN;<br>+ } else {<br>+ remote_key = material;<br>+ local_key = remote_key + SRTP_MASTER_KEY_LEN;<br>+ remote_salt = local_key + SRTP_MASTER_KEY_LEN;<br>+ local_salt = remote_salt + SRTP_MASTER_SALT_LEN;<br>+ }<br>+<br>+ if (!(local_policy = res_srtp_policy->alloc())) {<br>+ return -1;<br>+ }<br>+<br>+ if (res_srtp_policy->set_master_key(local_policy, local_key, SRTP_MASTER_KEY_LEN, local_salt, SRTP_MASTER_SALT_LEN) < 0) {<br>+ ast_log(LOG_WARNING, "Could not set key/salt information on local policy of '%p' when setting up DTLS-SRTP\n", rtp);<br>+ goto error;<br>+ }<br>+<br>+ if (res_srtp_policy->set_suite(local_policy, rtp->suite)) {<br>+ ast_log(LOG_WARNING, "Could not set suite to '%u' on local policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp);<br>+ goto error;<br>+ }<br>+<br>+ res_srtp_policy->set_ssrc(local_policy, ssrc, 0);<br>+<br>+ if (set_remote_policy) {<br>+ if (!(remote_policy = res_srtp_policy->alloc())) {<br>+ goto error;<br>+ }<br>+<br>+ if (res_srtp_policy->set_master_key(remote_policy, remote_key, SRTP_MASTER_KEY_LEN, remote_salt, SRTP_MASTER_SALT_LEN) < 0) {<br>+ ast_log(LOG_WARNING, "Could not set key/salt information on remote policy of '%p' when setting up DTLS-SRTP\n", rtp);<br>+ goto error;<br>+ }<br>+<br>+ if (res_srtp_policy->set_suite(remote_policy, rtp->suite)) {<br>+ ast_log(LOG_WARNING, "Could not set suite to '%u' on remote policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp);<br>+ goto error;<br>+ }<br>+<br>+ res_srtp_policy->set_ssrc(remote_policy, 0, 1);<br>+ }<br>+<br>+ if (ast_rtp_instance_add_srtp_policy(instance, remote_policy, local_policy, rtcp)) {<br>+ ast_log(LOG_WARNING, "Could not set policies when setting up DTLS-SRTP on '%p'\n", rtp);<br>+ goto error;<br>+ }<br>+<br>+ res = 0;<br>+<br>+error:<br>+ /* policy->destroy() called even on success to release local reference to these resources */<br>+ res_srtp_policy->destroy(local_policy);<br>+<br>+ if (remote_policy) {<br>+ res_srtp_policy->destroy(remote_policy);<br>+ }<br>+<br>+ return res;<br>+}<br>+<br>+static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance, int rtcp)<br>+{<br>+ struct dtls_details *dtls = !rtcp ? &rtp->dtls : &rtp->rtcp->dtls;<br>+ int index;<br> <br> /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */<br> if (rtp->dtls_verify & AST_RTP_DTLS_VERIFY_FINGERPRINT) {<br>@@ -2287,93 +2404,27 @@<br> X509_free(certificate);<br> }<br> <br>- /* Ensure that certificate verification was successful */<br>- if ((rtp->dtls_verify & AST_RTP_DTLS_VERIFY_CERTIFICATE) && SSL_get_verify_result(dtls->ssl) != X509_V_OK) {<br>- ast_log(LOG_WARNING, "Peer certificate on RTP instance '%p' failed verification test\n",<br>- instance);<br>+ if (dtls_srtp_add_local_ssrc(rtp, srtp, instance, rtcp, ast_rtp_instance_get_ssrc(instance), 1)) {<br> return -1;<br> }<br> <br>- /* Produce key information and set up SRTP */<br>- if (!SSL_export_keying_material(dtls->ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) {<br>- ast_log(LOG_WARNING, "Unable to extract SRTP keying material from DTLS-SRTP negotiation on RTP instance '%p'\n",<br>- instance);<br>- return -1;<br>- }<br>+ for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) {<br>+ struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index);<br> <br>- /* Whether we are acting as a server or client determines where the keys/salts are */<br>- if (rtp->dtls.dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) {<br>- local_key = material;<br>- remote_key = local_key + SRTP_MASTER_KEY_LEN;<br>- local_salt = remote_key + SRTP_MASTER_KEY_LEN;<br>- remote_salt = local_salt + SRTP_MASTER_SALT_LEN;<br>- } else {<br>- remote_key = material;<br>- local_key = remote_key + SRTP_MASTER_KEY_LEN;<br>- remote_salt = local_key + SRTP_MASTER_KEY_LEN;<br>- local_salt = remote_salt + SRTP_MASTER_SALT_LEN;<br>- }<br>-<br>- if (!(local_policy = res_srtp_policy->alloc())) {<br>- return -1;<br>- }<br>-<br>- if (res_srtp_policy->set_master_key(local_policy, local_key, SRTP_MASTER_KEY_LEN, local_salt, SRTP_MASTER_SALT_LEN) < 0) {<br>- ast_log(LOG_WARNING, "Could not set key/salt information on local policy of '%p' when setting up DTLS-SRTP\n", rtp);<br>- goto error;<br>- }<br>-<br>- if (res_srtp_policy->set_suite(local_policy, rtp->suite)) {<br>- ast_log(LOG_WARNING, "Could not set suite to '%u' on local policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp);<br>- goto error;<br>- }<br>-<br>- if (ast_rtp_instance_get_stats(instance, &stats, AST_RTP_INSTANCE_STAT_LOCAL_SSRC)) {<br>- goto error;<br>- }<br>-<br>- res_srtp_policy->set_ssrc(local_policy, stats.local_ssrc, 0);<br>-<br>- if (!(remote_policy = res_srtp_policy->alloc())) {<br>- goto error;<br>- }<br>-<br>- if (res_srtp_policy->set_master_key(remote_policy, remote_key, SRTP_MASTER_KEY_LEN, remote_salt, SRTP_MASTER_SALT_LEN) < 0) {<br>- ast_log(LOG_WARNING, "Could not set key/salt information on remote policy of '%p' when setting up DTLS-SRTP\n", rtp);<br>- goto error;<br>- }<br>-<br>- if (res_srtp_policy->set_suite(remote_policy, rtp->suite)) {<br>- ast_log(LOG_WARNING, "Could not set suite to '%u' on remote policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp);<br>- goto error;<br>- }<br>-<br>- res_srtp_policy->set_ssrc(remote_policy, 0, 1);<br>-<br>- if (ast_rtp_instance_add_srtp_policy(instance, remote_policy, local_policy, rtcp)) {<br>- ast_log(LOG_WARNING, "Could not set policies when setting up DTLS-SRTP on '%p'\n", rtp);<br>- goto error;<br>+ if (dtls_srtp_add_local_ssrc(rtp, srtp, instance, rtcp, ast_rtp_instance_get_ssrc(mapping->instance), 0)) {<br>+ return -1;<br>+ }<br> }<br> <br> if (rtp->rekey) {<br> ao2_ref(instance, +1);<br> if ((rtp->rekeyid = ast_sched_add(rtp->sched, rtp->rekey * 1000, dtls_srtp_renegotiate, instance)) < 0) {<br> ao2_ref(instance, -1);<br>- goto error;<br>+ return -1;<br> }<br> }<br> <br>- res = 0;<br>-<br>-error:<br>- /* policy->destroy() called even on success to release local reference to these resources */<br>- res_srtp_policy->destroy(local_policy);<br>-<br>- if (remote_policy) {<br>- res_srtp_policy->destroy(remote_policy);<br>- }<br>-<br>- return res;<br>+ return 0;<br> }<br> #endif<br> <br>@@ -2569,7 +2620,9 @@<br> int len = size;<br> void *temp = buf;<br> struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>- struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance, rtcp);<br>+ struct ast_rtp_instance *transport = rtp->bundled ? rtp->bundled : instance;<br>+ struct ast_rtp *transport_rtp = ast_rtp_instance_get_data(transport);<br>+ struct ast_srtp *srtp = ast_rtp_instance_get_srtp(transport, rtcp);<br> int res;<br> <br> *via_ice = 0;<br>@@ -2579,20 +2632,24 @@<br> }<br> <br> #ifdef HAVE_PJPROJECT<br>- if (rtp->ice) {<br>+ if (transport_rtp->ice) {<br> pj_status_t status;<br> struct ice_wrap *ice;<br> <br> pj_thread_register_check();<br> <br> /* Release the instance lock to avoid deadlock with PJPROJECT group lock */<br>- ice = rtp->ice;<br>+ ice = transport_rtp->ice;<br> ao2_ref(ice, +1);<br>- ao2_unlock(instance);<br>+ if (instance == transport) {<br>+ ao2_unlock(instance);<br>+ }<br> status = pj_ice_sess_send_data(ice->real_ice,<br> rtcp ? AST_RTP_ICE_COMPONENT_RTCP : AST_RTP_ICE_COMPONENT_RTP, temp, len);<br> ao2_ref(ice, -1);<br>- ao2_lock(instance);<br>+ if (instance == transport) {<br>+ ao2_lock(instance);<br>+ }<br> if (status == PJ_SUCCESS) {<br> *via_ice = 1;<br> return len;<br>@@ -2600,7 +2657,7 @@<br> }<br> #endif<br> <br>- res = ast_sendto(rtcp ? rtp->rtcp->s : rtp->s, temp, len, flags, sa);<br>+ res = ast_sendto(rtcp ? transport_rtp->rtcp->s : transport_rtp->s, temp, len, flags, sa);<br> if (res > 0) {<br> ast_rtp_instance_set_last_tx(instance, time(NULL));<br> }<br>@@ -2990,22 +3047,10 @@<br> }<br> #endif<br> <br>-/*! \pre instance is locked */<br>-static int ast_rtp_new(struct ast_rtp_instance *instance,<br>- struct ast_sched_context *sched, struct ast_sockaddr *addr,<br>- void *data)<br>+static int rtp_allocate_transport(struct ast_rtp_instance *instance, struct ast_rtp *rtp)<br> {<br>- struct ast_rtp *rtp = NULL;<br> int x, startplace;<br> <br>- /* Create a new RTP structure to hold all of our data */<br>- if (!(rtp = ast_calloc(1, sizeof(*rtp)))) {<br>- return -1;<br>- }<br>-<br>- /* Set default parameters on the newly created RTP structure */<br>- rtp->ssrc = ast_random();<br>- rtp->seqno = ast_random() & 0x7fff;<br> rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN);<br> if (strictrtp) {<br> rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno);<br>@@ -3015,10 +3060,9 @@<br> /* Create a new socket for us to listen on and use */<br> if ((rtp->s =<br> create_new_socket("RTP",<br>- ast_sockaddr_is_ipv4(addr) ? AF_INET :<br>- ast_sockaddr_is_ipv6(addr) ? AF_INET6 : -1)) < 0) {<br>+ ast_sockaddr_is_ipv4(&rtp->bind_address) ? AF_INET :<br>+ ast_sockaddr_is_ipv6(&rtp->bind_address) ? AF_INET6 : -1)) < 0) {<br> ast_log(LOG_WARNING, "Failed to create a new socket for RTP instance '%p'\n", instance);<br>- ast_free(rtp);<br> return -1;<br> }<br> <br>@@ -3028,11 +3072,11 @@<br> startplace = x;<br> <br> for (;;) {<br>- ast_sockaddr_set_port(addr, x);<br>+ ast_sockaddr_set_port(&rtp->bind_address, x);<br> /* Try to bind, this will tell us whether the port is available or not */<br>- if (!ast_bind(rtp->s, addr)) {<br>+ if (!ast_bind(rtp->s, &rtp->bind_address)) {<br> ast_debug(1, "Allocated port %d for RTP instance '%p'\n", x, instance);<br>- ast_rtp_instance_set_local_address(instance, addr);<br>+ ast_rtp_instance_set_local_address(instance, &rtp->bind_address);<br> break;<br> }<br> <br>@@ -3045,7 +3089,6 @@<br> if (x == startplace || (errno != EADDRINUSE && errno != EACCES)) {<br> ast_log(LOG_ERROR, "Oh dear... we couldn't allocate a port for RTP instance '%p'\n", instance);<br> close(rtp->s);<br>- ast_free(rtp);<br> return -1;<br> }<br> }<br>@@ -3056,40 +3099,30 @@<br> <br> generate_random_string(rtp->local_ufrag, sizeof(rtp->local_ufrag));<br> generate_random_string(rtp->local_passwd, sizeof(rtp->local_passwd));<br>-#endif<br>- ast_rtp_instance_set_data(instance, rtp);<br>-#ifdef HAVE_PJPROJECT<br>+<br> /* Create an ICE session for ICE negotiation */<br> if (icesupport) {<br> rtp->ice_num_components = 2;<br>- ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(addr), x, instance);<br>- if (ice_create(instance, addr, x, 0)) {<br>+ ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(&rtp->bind_address), x, instance);<br>+ if (ice_create(instance, &rtp->bind_address, x, 0)) {<br> ast_log(LOG_NOTICE, "Failed to create ICE session\n");<br> } else {<br> rtp->ice_port = x;<br>- ast_sockaddr_copy(&rtp->ice_original_rtp_addr, addr);<br>+ ast_sockaddr_copy(&rtp->ice_original_rtp_addr, &rtp->bind_address);<br> }<br> }<br> #endif<br>- /* Record any information we may need */<br>- rtp->sched = sched;<br> <br> #ifdef HAVE_OPENSSL_SRTP<br> rtp->rekeyid = -1;<br> rtp->dtls.timeout_timer = -1;<br> #endif<br> <br>- rtp->f.subclass.format = ao2_bump(ast_format_none);<br>- rtp->lastrxformat = ao2_bump(ast_format_none);<br>- rtp->lasttxformat = ao2_bump(ast_format_none);<br>-<br> return 0;<br> }<br> <br>-/*! \pre instance is locked */<br>-static int ast_rtp_destroy(struct ast_rtp_instance *instance)<br>+static void rtp_deallocate_transport(struct ast_rtp_instance *instance, struct ast_rtp *rtp)<br> {<br>- struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br> #ifdef HAVE_PJPROJECT<br> struct timeval wait = ast_tvadd(ast_tvnow(), ast_samp2tv(TURN_STATE_WAIT_TIME, 1000));<br> struct timespec ts = { .tv_sec = wait.tv_sec, .tv_nsec = wait.tv_usec * 1000, };<br>@@ -3099,35 +3132,16 @@<br> ast_rtp_dtls_stop(instance);<br> #endif<br> <br>- /* Destroy the smoother that was smoothing out audio if present */<br>- if (rtp->smoother) {<br>- ast_smoother_free(rtp->smoother);<br>- }<br>-<br> /* Close our own socket so we no longer get packets */<br> if (rtp->s > -1) {<br> close(rtp->s);<br>+ rtp->s = -1;<br> }<br> <br> /* Destroy RTCP if it was being used */<br>- if (rtp->rtcp) {<br>- /*<br>- * It is not possible for there to be an active RTCP scheduler<br>- * entry at this point since it holds a reference to the<br>- * RTP instance while it's active.<br>- */<br>+ if (rtp->rtcp && rtp->rtcp->s > -1) {<br> close(rtp->rtcp->s);<br>- ast_free(rtp->rtcp->local_addr_str);<br>- ast_free(rtp->rtcp);<br>- }<br>-<br>- /* Destroy RED if it was being used */<br>- if (rtp->red) {<br>- ao2_unlock(instance);<br>- AST_SCHED_DEL(rtp->sched, rtp->red->schedid);<br>- ao2_lock(instance);<br>- ast_free(rtp->red);<br>- rtp->red = NULL;<br>+ rtp->rtcp->s = -1;<br> }<br> <br> #ifdef HAVE_PJPROJECT<br>@@ -3148,6 +3162,7 @@<br> while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) {<br> ast_cond_timedwait(&rtp->cond, ao2_object_get_lockaddr(instance), &ts);<br> }<br>+ rtp->turn_rtp = NULL;<br> }<br> <br> /* Destroy the RTCP TURN relay if being used */<br>@@ -3161,6 +3176,7 @@<br> while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) {<br> ast_cond_timedwait(&rtp->cond, ao2_object_get_lockaddr(instance), &ts);<br> }<br>+ rtp->turn_rtcp = NULL;<br> }<br> <br> /* Destroy any ICE session */<br>@@ -3169,10 +3185,12 @@<br> /* Destroy any candidates */<br> if (rtp->ice_local_candidates) {<br> ao2_ref(rtp->ice_local_candidates, -1);<br>+ rtp->ice_local_candidates = NULL;<br> }<br> <br> if (rtp->ice_active_remote_candidates) {<br> ao2_ref(rtp->ice_active_remote_candidates, -1);<br>+ rtp->ice_active_remote_candidates = NULL;<br> }<br> <br> if (rtp->ioqueue) {<br>@@ -3184,17 +3202,109 @@<br> ao2_unlock(instance);<br> rtp_ioqueue_thread_remove(rtp->ioqueue);<br> ao2_lock(instance);<br>+ rtp->ioqueue = NULL;<br> }<br> #endif<br>+}<br>+<br>+/*! \pre instance is locked */<br>+static int ast_rtp_new(struct ast_rtp_instance *instance,<br>+ struct ast_sched_context *sched, struct ast_sockaddr *addr,<br>+ void *data)<br>+{<br>+ struct ast_rtp *rtp = NULL;<br>+<br>+ /* Create a new RTP structure to hold all of our data */<br>+ if (!(rtp = ast_calloc(1, sizeof(*rtp)))) {<br>+ return -1;<br>+ }<br>+<br>+ /* Set default parameters on the newly created RTP structure */<br>+ rtp->ssrc = ast_random();<br>+ ast_uuid_generate_str(rtp->cname, sizeof(rtp->cname));<br>+ rtp->seqno = ast_random() & 0x7fff;<br>+ rtp->sched = sched;<br>+ ast_sockaddr_copy(&rtp->bind_address, addr);<br>+<br>+ /* Transport creation operations can grab the RTP data from the instance, so set it */<br>+ ast_rtp_instance_set_data(instance, rtp);<br>+<br>+ if (rtp_allocate_transport(instance, rtp)) {<br>+ ast_free(rtp);<br>+ return -1;<br>+ }<br>+<br>+ rtp->f.subclass.format = ao2_bump(ast_format_none);<br>+ rtp->lastrxformat = ao2_bump(ast_format_none);<br>+ rtp->lasttxformat = ao2_bump(ast_format_none);<br>+ rtp->stream_num = -1;<br>+ AST_VECTOR_INIT(&rtp->ssrc_mapping, 1);<br>+<br>+ return 0;<br>+}<br>+<br>+/*!<br>+ * \brief SSRC mapping comparator for AST_VECTOR_REMOVE_CMP_UNORDERED()<br>+ *<br>+ * \param elem Element to compare against<br>+ * \param value Value to compare with the vector element.<br>+ *<br>+ * \return 0 if element does not match.<br>+ * \return Non-zero if element matches.<br>+ */<br>+#define SSRC_MAPPING_ELEM_CMP(elem, value) ((elem).ssrc == (value))<br>+<br>+/*! \pre instance is locked */<br>+static int ast_rtp_destroy(struct ast_rtp_instance *instance)<br>+{<br>+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>+<br>+ if (rtp->bundled) {<br>+ struct ast_rtp *bundled_rtp;<br>+<br>+ /* We can't hold our instance lock while removing ourselves from the parent */<br>+ ao2_unlock(instance);<br>+<br>+ ao2_lock(rtp->bundled);<br>+ bundled_rtp = ast_rtp_instance_get_data(rtp->bundled);<br>+ AST_VECTOR_REMOVE_CMP_UNORDERED(&bundled_rtp->ssrc_mapping, rtp->themssrc, SSRC_MAPPING_ELEM_CMP, AST_VECTOR_ELEM_CLEANUP_NOOP);<br>+ ao2_unlock(rtp->bundled);<br>+<br>+ ao2_lock(instance);<br>+ ao2_ref(rtp->bundled, -1);<br>+ }<br>+<br>+ rtp_deallocate_transport(instance, rtp);<br>+<br>+ /* Destroy the smoother that was smoothing out audio if present */<br>+ if (rtp->smoother) {<br>+ ast_smoother_free(rtp->smoother);<br>+ }<br>+<br>+ /* Destroy RTCP if it was being used */<br>+ if (rtp->rtcp) {<br>+ /*<br>+ * It is not possible for there to be an active RTCP scheduler<br>+ * entry at this point since it holds a reference to the<br>+ * RTP instance while it's active.<br>+ */<br>+ ast_free(rtp->rtcp->local_addr_str);<br>+ ast_free(rtp->rtcp);<br>+ }<br>+<br>+ /* Destroy RED if it was being used */<br>+ if (rtp->red) {<br>+ ao2_unlock(instance);<br>+ AST_SCHED_DEL(rtp->sched, rtp->red->schedid);<br>+ ao2_lock(instance);<br>+ ast_free(rtp->red);<br>+ rtp->red = NULL;<br>+ }<br> <br> ao2_cleanup(rtp->lasttxformat);<br> ao2_cleanup(rtp->lastrxformat);<br> ao2_cleanup(rtp->f.subclass.format);<br>-<br>-#ifdef HAVE_PJPROJECT<br>- /* Destroy synchronization items */<br>- ast_cond_destroy(&rtp->cond);<br>-#endif<br>+ AST_VECTOR_FREE(&rtp->ssrc_mapping);<br> <br> /* Finally destroy ourselves */<br> ast_free(rtp);<br>@@ -3444,21 +3554,18 @@<br> struct ast_srtp *rtcp_srtp = ast_rtp_instance_get_srtp(instance, 1);<br> unsigned int ssrc = ast_random();<br> <br>- if (!rtp->lastts) {<br>- ast_debug(3, "Not changing SSRC since we haven't sent any RTP yet\n");<br>- return;<br>- }<br>+ if (rtp->lastts) {<br>+ /* We simply set this bit so that the next packet sent will have the marker bit turned on */<br>+ ast_set_flag(rtp, FLAG_NEED_MARKER_BIT);<br> <br>- /* We simply set this bit so that the next packet sent will have the marker bit turned on */<br>- ast_set_flag(rtp, FLAG_NEED_MARKER_BIT);<br>+ ast_debug(3, "Changing ssrc from %u to %u due to a source change\n", rtp->ssrc, ssrc);<br> <br>- ast_debug(3, "Changing ssrc from %u to %u due to a source change\n", rtp->ssrc, ssrc);<br>-<br>- if (srtp) {<br>- ast_debug(3, "Changing ssrc for SRTP from %u to %u\n", rtp->ssrc, ssrc);<br>- res_srtp->change_source(srtp, rtp->ssrc, ssrc);<br>- if (rtcp_srtp != srtp) {<br>- res_srtp->change_source(rtcp_srtp, rtp->ssrc, ssrc);<br>+ if (srtp) {<br>+ ast_debug(3, "Changing ssrc for SRTP from %u to %u\n", rtp->ssrc, ssrc);<br>+ res_srtp->change_source(srtp, rtp->ssrc, ssrc);<br>+ if (rtcp_srtp != srtp) {<br>+ res_srtp->change_source(rtcp_srtp, rtp->ssrc, ssrc);<br>+ }<br> }<br> }<br> <br>@@ -3573,14 +3680,13 @@<br> struct timeval now;<br> unsigned int now_lsw;<br> unsigned int now_msw;<br>- unsigned int *rtcpheader;<br>+ unsigned char *rtcpheader;<br> unsigned int lost_packets;<br> int fraction_lost;<br> struct timeval dlsr = { 0, };<br>- char bdata[512];<br>+ unsigned char bdata[512] = "";<br> int rate = rtp_get_rate(rtp->f.subclass.format);<br> int ice;<br>- int header_offset = 0;<br> struct ast_sockaddr remote_address = { { 0, } };<br> struct ast_rtp_rtcp_report_block *report_block = NULL;<br> RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,<br>@@ -3634,38 +3740,42 @@<br> }<br> }<br> timeval2ntp(rtcp_report->sender_information.ntp_timestamp, &now_msw, &now_lsw);<br>- rtcpheader = (unsigned int *)bdata;<br>- rtcpheader[1] = htonl(rtcp_report->ssrc); /* Our SSRC */<br>+ rtcpheader = bdata;<br>+ put_unaligned_uint32(rtcpheader + 4, htonl(rtcp_report->ssrc)); /* Our SSRC */<br> len += 8;<br> if (sr) {<br>- header_offset = 5;<br>- rtcpheader[2] = htonl(now_msw); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/<br>- rtcpheader[3] = htonl(now_lsw); /* now, LSW */<br>- rtcpheader[4] = htonl(rtcp_report->sender_information.rtp_timestamp);<br>- rtcpheader[5] = htonl(rtcp_report->sender_information.packet_count);<br>- rtcpheader[6] = htonl(rtcp_report->sender_information.octet_count);<br>+ put_unaligned_uint32(rtcpheader + len, htonl(now_msw)); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/<br>+ put_unaligned_uint32(rtcpheader + len + 4, htonl(now_lsw)); /* now, LSW */<br>+ put_unaligned_uint32(rtcpheader + len + 8, htonl(rtcp_report->sender_information.rtp_timestamp));<br>+ put_unaligned_uint32(rtcpheader + len + 12, htonl(rtcp_report->sender_information.packet_count));<br>+ put_unaligned_uint32(rtcpheader + len + 16, htonl(rtcp_report->sender_information.octet_count));<br> len += 20;<br> }<br> if (report_block) {<br>- rtcpheader[2 + header_offset] = htonl(report_block->source_ssrc); /* Their SSRC */<br>- rtcpheader[3 + header_offset] = htonl((report_block->lost_count.fraction << 24) | report_block->lost_count.packets);<br>- rtcpheader[4 + header_offset] = htonl(report_block->highest_seq_no);<br>- rtcpheader[5 + header_offset] = htonl(report_block->ia_jitter);<br>- rtcpheader[6 + header_offset] = htonl(report_block->lsr);<br>- rtcpheader[7 + header_offset] = htonl(report_block->dlsr);<br>+ put_unaligned_uint32(rtcpheader + len, htonl(report_block->source_ssrc)); /* Their SSRC */<br>+ put_unaligned_uint32(rtcpheader + len + 4, htonl((report_block->lost_count.fraction << 24) | report_block->lost_count.packets));<br>+ put_unaligned_uint32(rtcpheader + len + 8, htonl(report_block->highest_seq_no));<br>+ put_unaligned_uint32(rtcpheader + len + 12, htonl(report_block->ia_jitter));<br>+ put_unaligned_uint32(rtcpheader + len + 16, htonl(report_block->lsr));<br>+ put_unaligned_uint32(rtcpheader + len + 20, htonl(report_block->dlsr));<br> len += 24;<br> }<br>- rtcpheader[0] = htonl((2 << 30) | (rtcp_report->reception_report_count << 24)<br>- | ((sr ? RTCP_PT_SR : RTCP_PT_RR) << 16) | ((len/4)-1));<br> <br>- /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */<br>- /* it can change mid call, and SDES can't) */<br>- rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2);<br>- rtcpheader[(len/4)+1] = htonl(rtcp_report->ssrc);<br>- rtcpheader[(len/4)+2] = htonl(0x01 << 24);<br>- len += 12;<br>+ put_unaligned_uint32(rtcpheader, htonl((2 << 30) | (rtcp_report->reception_report_count << 24)<br>+ | ((sr ? RTCP_PT_SR : RTCP_PT_RR) << 16) | ((len/4)-1)));<br> <br>- ast_sockaddr_copy(&remote_address, &rtp->rtcp->them);<br>+ put_unaligned_uint32(rtcpheader + len, htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | (2 + (AST_UUID_STR_LEN / 4))));<br>+ put_unaligned_uint32(rtcpheader + len + 4, htonl(rtcp_report->ssrc));<br>+ put_unaligned_uint16(rtcpheader + len + 8, htonl(0x01 << 24));<br>+ put_unaligned_uint16(rtcpheader + len + 9, htonl(AST_UUID_STR_LEN << 24));<br>+ memcpy(rtcpheader + len + 10, rtp->cname, AST_UUID_STR_LEN);<br>+ len += 12 + AST_UUID_STR_LEN;<br>+<br>+ if (rtp->bundled) {<br>+ ast_rtp_instance_get_remote_address(instance, &remote_address);<br>+ } else {<br>+ ast_sockaddr_copy(&remote_address, &rtp->rtcp->them);<br>+ }<br> res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, &remote_address, &ice);<br> if (res < 0) {<br> ast_log(LOG_ERROR, "RTCP %s transmission error to %s, rtcp halted %s\n",<br>@@ -3942,7 +4052,6 @@<br> <br> /* VP8: is this a request to send a RTCP FIR? */<br> if (frame->frametype == AST_FRAME_CONTROL && frame->subclass.integer == AST_CONTROL_VIDUPDATE) {<br>- struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br> unsigned int *rtcpheader;<br> char bdata[1024];<br> int len = 20;<br>@@ -3972,7 +4081,7 @@<br> rtcpheader[2] = htonl(rtp->themssrc);<br> rtcpheader[3] = htonl(rtp->themssrc); /* FCI: SSRC */<br> rtcpheader[4] = htonl(rtp->rtcp->firseq << 24); /* FCI: Sequence number */<br>- res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, &rtp->rtcp->them, &ice);<br>+ res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, rtp->bundled ? &remote_address : &rtp->rtcp->them, &ice);<br> if (res < 0) {<br> ast_log(LOG_ERROR, "RTCP FIR transmission error: %s\n", strerror(errno));<br> }<br>@@ -4537,9 +4646,29 @@<br> rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current;<br> }<br> <br>+/*! \pre instance is locked */<br>+static struct ast_rtp_instance *rtp_find_instance_by_ssrc(struct ast_rtp_instance *instance,<br>+ struct ast_rtp *rtp, unsigned int ssrc)<br>+{<br>+ int index;<br>+ struct ast_rtp_instance *found = instance;<br>+<br>+ for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) {<br>+ struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index);<br>+<br>+ if (mapping->ssrc == ssrc) {<br>+ found = mapping->instance;<br>+ break;<br>+ }<br>+ }<br>+<br>+ return found;<br>+}<br>+<br> static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr)<br> {<br>- struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>+ struct ast_rtp_instance *transport = instance;<br>+ struct ast_rtp *transport_rtp = ast_rtp_instance_get_data(instance);<br> unsigned int *rtcpheader = (unsigned int *)(rtcpdata);<br> int packetwords, position = 0;<br> int report_counter = 0;<br>@@ -4548,13 +4677,13 @@<br> <br> packetwords = size / 4;<br> <br>- if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {<br>+ if (ast_rtp_instance_get_prop(transport, AST_RTP_PROPERTY_NAT)) {<br> /* Send to whoever sent to us */<br>- if (ast_sockaddr_cmp(&rtp->rtcp->them, addr)) {<br>- ast_sockaddr_copy(&rtp->rtcp->them, addr);<br>+ if (ast_sockaddr_cmp(&transport_rtp->rtcp->them, addr)) {<br>+ ast_sockaddr_copy(&transport_rtp->rtcp->them, addr);<br> if (rtpdebug) {<br> ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",<br>- ast_sockaddr_stringify(&rtp->rtcp->them));<br>+ ast_sockaddr_stringify(&transport_rtp->rtcp->them));<br> }<br> }<br> }<br>@@ -4566,6 +4695,8 @@<br> unsigned int length;<br> struct ast_json *message_blob;<br> RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report, NULL, ao2_cleanup);<br>+ struct ast_rtp_instance *child;<br>+ struct ast_rtp *rtp;<br> <br> i = position;<br> length = ntohl(rtcpheader[i]);<br>@@ -4595,6 +4726,21 @@<br> (pt == RTCP_PT_FUR) ? "H.261 FUR" : "Unknown");<br> ast_verbose("Reception reports: %d\n", rc);<br> ast_verbose("SSRC of sender: %u\n", rtcp_report->ssrc);<br>+ }<br>+<br>+ /* Determine the appropriate instance for this */<br>+ child = rtp_find_instance_by_ssrc(transport, transport_rtp, rtcp_report->ssrc);<br>+ if (child != transport) {<br>+ /* It is safe to hold the child lock while holding the parent lock, we guarantee that the locking order<br>+ * is always parent->child or that the child lock is not held when acquiring the parent lock.<br>+ */<br>+ ao2_lock(child);<br>+ instance = child;<br>+ rtp = ast_rtp_instance_get_data(instance);<br>+ } else {<br>+ /* The child is the parent! We don't need to unlock it. */<br>+ child = NULL;<br>+ rtp = transport_rtp;<br> }<br> <br> i += 2; /* Advance past header and ssrc */<br>@@ -4632,6 +4778,9 @@<br> /* Don't handle multiple reception reports (rc > 1) yet */<br> report_block = ast_calloc(1, sizeof(*report_block));<br> if (!report_block) {<br>+ if (child) {<br>+ ao2_unlock(child);<br>+ }<br> return &ast_null_frame;<br> }<br> rtcp_report->report_block[report_counter] = report_block;<br>@@ -4678,8 +4827,8 @@<br> */<br> <br> message_blob = ast_json_pack("{s: s, s: s, s: f}",<br>- "from", ast_sockaddr_stringify(&rtp->rtcp->them),<br>- "to", rtp->rtcp->local_addr_str,<br>+ "from", ast_sockaddr_stringify(&transport_rtp->rtcp->them),<br>+ "to", transport_rtp->rtcp->local_addr_str,<br> "rtt", rtp->rtcp->rtt);<br> ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_received_type(),<br> rtcp_report,<br>@@ -4688,26 +4837,26 @@<br> <br> /* Return an AST_FRAME_RTCP frame with the ast_rtp_rtcp_report<br> * object as a its data */<br>- rtp->f.frametype = AST_FRAME_RTCP;<br>- rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET;<br>- memcpy(rtp->f.data.ptr, rtcp_report, sizeof(struct ast_rtp_rtcp_report));<br>- rtp->f.datalen = sizeof(struct ast_rtp_rtcp_report);<br>+ transport_rtp->f.frametype = AST_FRAME_RTCP;<br>+ transport_rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET;<br>+ memcpy(transport_rtp->f.data.ptr, rtcp_report, sizeof(struct ast_rtp_rtcp_report));<br>+ transport_rtp->f.datalen = sizeof(struct ast_rtp_rtcp_report);<br> if (rc > 0) {<br> /* There's always a single report block stored, here */<br> struct ast_rtp_rtcp_report *rtcp_report2;<br>- report_block = rtp->f.data.ptr + rtp->f.datalen + sizeof(struct ast_rtp_rtcp_report_block *);<br>+ report_block = transport_rtp->f.data.ptr + transport_rtp->f.datalen + sizeof(struct ast_rtp_rtcp_report_block *);<br> memcpy(report_block, rtcp_report->report_block[report_counter-1], sizeof(struct ast_rtp_rtcp_report_block));<br>- rtcp_report2 = (struct ast_rtp_rtcp_report *)rtp->f.data.ptr;<br>+ rtcp_report2 = (struct ast_rtp_rtcp_report *)transport_rtp->f.data.ptr;<br> rtcp_report2->report_block[report_counter-1] = report_block;<br>- rtp->f.datalen += sizeof(struct ast_rtp_rtcp_report_block);<br>+ transport_rtp->f.datalen += sizeof(struct ast_rtp_rtcp_report_block);<br> }<br>- rtp->f.offset = AST_FRIENDLY_OFFSET;<br>- rtp->f.samples = 0;<br>- rtp->f.mallocd = 0;<br>- rtp->f.delivery.tv_sec = 0;<br>- rtp->f.delivery.tv_usec = 0;<br>- rtp->f.src = "RTP";<br>- f = &rtp->f;<br>+ transport_rtp->f.offset = AST_FRIENDLY_OFFSET;<br>+ transport_rtp->f.samples = 0;<br>+ transport_rtp->f.mallocd = 0;<br>+ transport_rtp->f.delivery.tv_sec = 0;<br>+ transport_rtp->f.delivery.tv_usec = 0;<br>+ transport_rtp->f.src = "RTP";<br>+ f = &transport_rtp->f;<br> break;<br> case RTCP_PT_FUR:<br> /* Handle RTCP FIR as FUR */<br>@@ -4715,34 +4864,38 @@<br> if (rtcp_debug_test_addr(addr)) {<br> ast_verbose("Received an RTCP Fast Update Request\n");<br> }<br>- rtp->f.frametype = AST_FRAME_CONTROL;<br>- rtp->f.subclass.integer = AST_CONTROL_VIDUPDATE;<br>- rtp->f.datalen = 0;<br>- rtp->f.samples = 0;<br>- rtp->f.mallocd = 0;<br>- rtp->f.src = "RTP";<br>- f = &rtp->f;<br>+ transport_rtp->f.frametype = AST_FRAME_CONTROL;<br>+ transport_rtp->f.subclass.integer = AST_CONTROL_VIDUPDATE;<br>+ transport_rtp->f.datalen = 0;<br>+ transport_rtp->f.samples = 0;<br>+ transport_rtp->f.mallocd = 0;<br>+ transport_rtp->f.src = "RTP";<br>+ f = &transport_rtp->f;<br> break;<br> case RTCP_PT_SDES:<br> if (rtcp_debug_test_addr(addr)) {<br> ast_verbose("Received an SDES from %s\n",<br>- ast_sockaddr_stringify(&rtp->rtcp->them));<br>+ ast_sockaddr_stringify(&transport_rtp->rtcp->them));<br> }<br> break;<br> case RTCP_PT_BYE:<br> if (rtcp_debug_test_addr(addr)) {<br> ast_verbose("Received a BYE from %s\n",<br>- ast_sockaddr_stringify(&rtp->rtcp->them));<br>+ ast_sockaddr_stringify(&transport_rtp->rtcp->them));<br> }<br> break;<br> default:<br> ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s\n",<br>- pt, ast_sockaddr_stringify(&rtp->rtcp->them));<br>+ pt, ast_sockaddr_stringify(&transport_rtp->rtcp->them));<br> break;<br> }<br> position += (length + 1);<br>+ rtp->rtcp->rtcp_info = 1;<br>+<br>+ if (child) {<br>+ ao2_unlock(child);<br>+ }<br> }<br>- rtp->rtcp->rtcp_info = 1;<br> <br> return f;<br> <br>@@ -4928,11 +5081,19 @@<br> return 0;<br> }<br> <br>+static void rtp_instance_unlock(struct ast_rtp_instance *instance)<br>+{<br>+ if (instance) {<br>+ ao2_unlock(instance);<br>+ }<br>+}<br>+<br> /*! \pre instance is locked */<br> static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp)<br> {<br> struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br> struct ast_rtp_instance *instance1;<br>+ RAII_VAR(struct ast_rtp_instance *, child, NULL, rtp_instance_unlock);<br> struct ast_sockaddr addr;<br> int res, hdrlen = 12, version, payloadtype, padding, mark, ext, cc, prev_seqno;<br> unsigned char *read_area = rtp->rawdata + AST_FRIENDLY_OFFSET;<br>@@ -4948,11 +5109,6 @@<br> return ast_rtcp_read(instance);<br> }<br> return &ast_null_frame;<br>- }<br>-<br>- /* If we are currently sending DTMF to the remote party send a continuation packet */<br>- if (rtp->sending_digit) {<br>- ast_rtp_dtmf_continuation(instance);<br> }<br> <br> /* Actually read in the data from the socket */<br>@@ -5070,15 +5226,37 @@<br> }<br> }<br> <br>+ /* If the version is not what we expected by this point then just drop the packet */<br>+ if (version != 2) {<br>+ return &ast_null_frame;<br>+ }<br>+<br>+ /* We use the SSRC to determine what RTP instance this packet is actually for */<br>+ ssrc = ntohl(rtpheader[2]);<br>+<br>+ /* Determine the appropriate instance for this */<br>+ child = rtp_find_instance_by_ssrc(instance, rtp, ssrc);<br>+ if (child != instance) {<br>+ /* It is safe to hold the child lock while holding the parent lock, we guarantee that the locking order<br>+ * is always parent->child or that the child lock is not held when acquiring the parent lock.<br>+ */<br>+ ao2_lock(child);<br>+ instance = child;<br>+ rtp = ast_rtp_instance_get_data(instance);<br>+ } else {<br>+ /* The child is the parent! We don't need to unlock it. */<br>+ child = NULL;<br>+ }<br>+<br>+ /* If we are currently sending DTMF to the remote party send a continuation packet */<br>+ if (rtp->sending_digit) {<br>+ ast_rtp_dtmf_continuation(instance);<br>+ }<br>+<br> /* If we are directly bridged to another instance send the audio directly out */<br> instance1 = ast_rtp_instance_get_bridged(instance);<br> if (instance1<br> && !bridge_p2p_rtp_write(instance, instance1, rtpheader, res, hdrlen)) {<br>- return &ast_null_frame;<br>- }<br>-<br>- /* If the version is not what we expected by this point then just drop the packet */<br>- if (version != 2) {<br> return &ast_null_frame;<br> }<br> <br>@@ -5090,7 +5268,6 @@<br> cc = (seqno & 0xF000000) >> 24;<br> seqno &= 0xffff;<br> timestamp = ntohl(rtpheader[1]);<br>- ssrc = ntohl(rtpheader[2]);<br> <br> AST_LIST_HEAD_INIT_NOLOCK(&frames);<br> /* Force a marker bit and change SSRC if the SSRC changes */<br>@@ -5264,6 +5441,7 @@<br> rtp->f.data.ptr = read_area + hdrlen;<br> rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET;<br> rtp->f.seqno = seqno;<br>+ rtp->f.stream_num = rtp->stream_num;<br> <br> if ((ast_format_cmp(rtp->f.subclass.format, ast_format_t140) == AST_FORMAT_CMP_EQUAL)<br> && ((int)seqno - (prev_seqno + 1) > 0)<br>@@ -5525,6 +5703,7 @@<br> {<br> struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br> struct ast_sockaddr local;<br>+ int index;<br> <br> ast_rtp_instance_get_local_address(instance, &local);<br> if (!ast_sockaddr_isnull(addr)) {<br>@@ -5551,6 +5730,13 @@<br> <br> ast_free(rtp->rtcp->local_addr_str);<br> rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&local));<br>+ }<br>+<br>+ /* Update any bundled RTP instances */<br>+ for (index = 0; index < AST_VECTOR_SIZE(&rtp->ssrc_mapping); ++index) {<br>+ struct rtp_ssrc_mapping *mapping = AST_VECTOR_GET_ADDR(&rtp->ssrc_mapping, index);<br>+<br>+ ast_rtp_instance_set_remote_address(mapping->instance, addr);<br> }<br> <br> rtp->rxseqno = 0;<br>@@ -5836,42 +6022,104 @@<br> /*! \pre instance is locked */<br> static const char *ast_rtp_get_cname(struct ast_rtp_instance *instance)<br> {<br>- /* XXX<br>- *<br>- * Asterisk currently puts a zero-length CNAME value in RTCP SDES items,<br>- * meaning our CNAME will always be an empty string. In future, should<br>- * Asterisk actually start using meaningful CNAMEs, this function will<br>- * need to return that instead of an empty string<br>- */<br>- return "";<br>+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>+<br>+ return rtp->cname;<br> }<br> <br>-#ifdef HAVE_OPENSSL_SRTP<br>-static void dtls_perform_setup(struct dtls_details *dtls)<br>+static void ast_rtp_set_remote_ssrc(struct ast_rtp_instance *instance, unsigned int ssrc)<br> {<br>- if (!dtls->ssl || !SSL_is_init_finished(dtls->ssl)) {<br>+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>+<br>+ if (rtp->themssrc) {<br> return;<br> }<br> <br>- SSL_clear(dtls->ssl);<br>- if (dtls->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) {<br>- SSL_set_accept_state(dtls->ssl);<br>- } else {<br>- SSL_set_connect_state(dtls->ssl);<br>- }<br>- dtls->connection = AST_RTP_DTLS_CONNECTION_NEW;<br>+ rtp->themssrc = ssrc;<br> }<br> <br>+static void ast_rtp_set_stream_num(struct ast_rtp_instance *instance, int stream_num)<br>+{<br>+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>+<br>+ rtp->stream_num = stream_num;<br>+}<br>+<br>+static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent)<br>+{<br>+ struct ast_rtp *child_rtp = ast_rtp_instance_get_data(child);<br>+ struct ast_rtp *parent_rtp = ast_rtp_instance_get_data(parent);<br>+ struct rtp_ssrc_mapping mapping;<br>+ struct ast_sockaddr them = { { 0, } };<br>+<br>+ if (child_rtp->bundled == parent) {<br>+ return 0;<br>+ }<br>+<br>+ /* If this instance was already bundled then remove the SSRC mapping */<br>+ if (child_rtp->bundled) {<br>+ struct ast_rtp *bundled_rtp;<br>+<br>+ ao2_unlock(child);<br>+<br>+ /* The child lock can't be held while accessing the parent */<br>+ ao2_lock(child_rtp->bundled);<br>+ bundled_rtp = ast_rtp_instance_get_data(child_rtp->bundled);<br>+ AST_VECTOR_REMOVE_CMP_UNORDERED(&bundled_rtp->ssrc_mapping, child_rtp->themssrc, SSRC_MAPPING_ELEM_CMP, AST_VECTOR_ELEM_CLEANUP_NOOP);<br>+ ao2_unlock(child_rtp->bundled);<br>+<br>+ ao2_lock(child);<br>+ ao2_ref(child_rtp->bundled, -1);<br>+ child_rtp->bundled = NULL;<br>+ }<br>+<br>+ if (!parent) {<br>+ /* We transitioned away from bundle so we need our own transport resources once again */<br>+ rtp_allocate_transport(child, child_rtp);<br>+ return 0;<br>+ }<br>+<br>+ /* We no longer need any transport related resources as we will use our parent RTP instance instead */<br>+ rtp_deallocate_transport(child, child_rtp);<br>+<br>+ /* Children maintain a reference to the parent to guarantee that the transport doesn't go away on them */<br>+ child_rtp->bundled = ao2_bump(parent);<br>+<br>+ mapping.ssrc = child_rtp->themssrc;<br>+ mapping.instance = child;<br>+<br>+ ao2_unlock(child);<br>+<br>+ ao2_lock(parent);<br>+<br>+ AST_VECTOR_APPEND(&parent_rtp->ssrc_mapping, mapping);<br>+<br>+#ifdef HAVE_OPENSSL_SRTP<br>+ /* If DTLS-SRTP is already in use then add the local SSRC to it, otherwise it will get added once DTLS<br>+ * negotiation has been completed.<br>+ */<br>+ if (parent_rtp->dtls.connection == AST_RTP_DTLS_CONNECTION_EXISTING) {<br>+ dtls_srtp_add_local_ssrc(parent_rtp, ast_rtp_instance_get_srtp(parent, 0), parent, 0, child_rtp->ssrc, 0);<br>+ }<br>+#endif<br>+<br>+ /* Bundle requires that RTCP-MUX be in use so only the main remote address needs to match */<br>+ ast_rtp_instance_get_remote_address(parent, &them);<br>+<br>+ ao2_unlock(parent);<br>+<br>+ ao2_lock(child);<br>+<br>+ ast_rtp_instance_set_remote_address(child, &them);<br>+<br>+ return 0;<br>+}<br>+<br>+#ifdef HAVE_OPENSSL_SRTP<br> /*! \pre instance is locked */<br> static int ast_rtp_activate(struct ast_rtp_instance *instance)<br> {<br> struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>-<br>- dtls_perform_setup(&rtp->dtls);<br>-<br>- if (rtp->rtcp) {<br>- dtls_perform_setup(&rtp->rtcp->dtls);<br>- }<br> <br> /* If ICE negotiation is enabled the DTLS Handshake will be performed upon completion of it */<br> #ifdef HAVE_PJPROJECT<br>@@ -5880,9 +6128,11 @@<br> }<br> #endif<br> <br>+ dtls_perform_setup(&rtp->dtls);<br> dtls_perform_handshake(instance, &rtp->dtls, 0);<br> <br> if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {<br>+ dtls_perform_setup(&rtp->rtcp->dtls);<br> dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);<br> }<br> <br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/5981">change 5981</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/5981"/><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: I96c0920b9f9aca7382256484765a239017973c11 </div>
<div style="display:none"> Gerrit-Change-Number: 5981 </div>
<div style="display:none"> Gerrit-PatchSet: 5 </div>
<div style="display:none"> Gerrit-Owner: Joshua Colp <jcolp@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>