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