AST_RTP_DTLS_VERIFY_CERTIFICATE = (1 << 1), /*!< Verify the certificate */
};
+/*!
+ * \brief Known RTP extensions
+ */
+enum ast_rtp_extension {
+ /*! Per the RFC 0 should not be used, so we treat it as an unsupported extension placeholder */
+ AST_RTP_EXTENSION_UNSUPPORTED = 0,
+ /*! abs-send-time from https://tools.ietf.org/html/draft-alvestrand-rmcat-remb-03 */
+ AST_RTP_EXTENSION_ABS_SEND_TIME,
+ /*! The maximum number of known RTP extensions */
+ AST_RTP_EXTENSION_MAX,
+};
+
/*! \brief DTLS configuration structure */
struct ast_rtp_dtls_cfg {
unsigned int enabled:1; /*!< Whether DTLS support is enabled or not */
struct ast_rtp_engine_ice *ice;
/*! Callback to pointer for optional DTLS SRTP support */
struct ast_rtp_engine_dtls *dtls;
+ /*! Callback to enable an RTP extension (returns non-zero if supported) */
+ int (*extension_enable)(struct ast_rtp_instance *instance, enum ast_rtp_extension extension);
/*! Linked list information */
AST_RWLIST_ENTRY(ast_rtp_engine) entry;
};
AST_RWLIST_ENTRY(ast_rtp_glue) entry;
};
+/*!
+ * \brief Directions for RTP extensions
+ */
+enum ast_rtp_extension_direction {
+ /*! The extension is not negotiated and is not flowing */
+ AST_RTP_EXTENSION_DIRECTION_NONE,
+ /*! Send and receive */
+ AST_RTP_EXTENSION_DIRECTION_SENDRECV,
+ /*! Send only */
+ AST_RTP_EXTENSION_DIRECTION_SENDONLY,
+ /*! Receive only */
+ AST_RTP_EXTENSION_DIRECTION_RECVONLY,
+ /*! Negotiated but not sending or receiving */
+ AST_RTP_EXTENSION_DIRECTION_INACTIVE,
+};
+
/*!
* \brief Allocation routine for \ref ast_rtp_payload_type
*
*/
struct ast_rtp_codecs *ast_rtp_instance_get_codecs(struct ast_rtp_instance *instance);
+/*!
+ * \brief Enable support for an RTP extension on an instance
+ *
+ * \param instance The RTP instance to enable the extension on
+ * \param id The unique local identifier to use for this extension (-1 to have one auto selected)
+ * \param extension The RTP extension
+ * \param direction The initial direction that the RTP extension should be used in
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \since 15.5.0
+ */
+int ast_rtp_instance_extmap_enable(struct ast_rtp_instance *instance, int id, enum ast_rtp_extension extension,
+ enum ast_rtp_extension_direction direction);
+
+/*!
+ * \brief Negotiate received RTP extension information
+ *
+ * \param instance The RTP instance to set the extension on
+ * \param id The local identifier for the extension
+ * \param direction The direction that the extension should be used in
+ * \param uri The unique URI for the extension
+ * \param attributes Attributes specific to this extension (if NULL or empty then no attributes)
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \since 15.5.0
+ */
+int ast_rtp_instance_extmap_negotiate(struct ast_rtp_instance *instance, int id, enum ast_rtp_extension_direction direction,
+ const char *uri, const char *attributes);
+
+/*!
+ * \brief Clear negotiated RTP extension information
+ *
+ * \param instance The RTP instance to clear negotiated extension information on
+ */
+void ast_rtp_instance_extmap_clear(struct ast_rtp_instance *instance);
+
+/*!
+ * \brief Retrieve the id for an RTP extension
+ *
+ * \param instance The RTP instance to retrieve the id from
+ * \param extension The RTP extension
+ *
+ * \retval -1 not negotiated
+ * \retval id if negotiated
+ *
+ * \since 15.5.0
+ */
+int ast_rtp_instance_extmap_get_id(struct ast_rtp_instance *instance, enum ast_rtp_extension extension);
+
+/*!
+ * \brief Get the number of known unique identifiers
+ *
+ * \param instance The RTP instance to retrieve the count from
+ *
+ * \return the number of known unique identifiers
+ *
+ * \since 15.5.0
+ */
+size_t ast_rtp_instance_extmap_count(struct ast_rtp_instance *instance);
+
+/*!
+ * \brief Retrieve the extension for an RTP extension id
+ *
+ * \param instance The RTP instance to retrieve the extension from
+ * \param id The negotiated RTP extension id
+ *
+ * \retval extension the extension that maps to the id
+ *
+ * \since 15.5.0
+ *
+ * \note This will return AST_RTP_EXTENSION_UNSUPPORTED if an extension was proposed for this unique identifier
+ * but it is not supported or if the unique identifier is unused.
+ */
+enum ast_rtp_extension ast_rtp_instance_extmap_get_extension(struct ast_rtp_instance *instance, int id);
+
+/*!
+ * \brief Retrieve the negotiated direction for an RTP extension id
+ *
+ * \param instance The RTP instance to retrieve the direction from
+ * \param id The negotiated RTP extension id
+ *
+ * \retval direction the direction that has been negotiated
+ *
+ * \since 15.5.0
+ */
+enum ast_rtp_extension_direction ast_rtp_instance_extmap_get_direction(struct ast_rtp_instance *instance, int id);
+
+/*!
+ * \brief Retrieve the URI for an RTP extension id
+ *
+ * \param instance The RTP instance to retrieve the direction from
+ * \param id The negotiated RTP extension id
+ *
+ * \retval uri The URI for the RTP extension
+ *
+ * \since 15.5.0
+ */
+const char *ast_rtp_instance_extmap_get_uri(struct ast_rtp_instance *instance, int id);
+
/*!
* \brief Initialize an RTP codecs structure
*
struct ast_srtp_res *res_srtp = NULL;
struct ast_srtp_policy_res *res_srtp_policy = NULL;
+/*! Structure that contains extmap negotiation information */
+struct rtp_extmap {
+ /*! The RTP extension */
+ enum ast_rtp_extension extension;
+ /*! The current negotiated direction */
+ enum ast_rtp_extension_direction direction;
+};
+
/*! Structure that represents an RTP session (instance) */
struct ast_rtp_instance {
/*! Engine that is handling this RTP instance */
time_t last_tx;
/*! Time of last packet received */
time_t last_rx;
+ /*! Enabled RTP extensions */
+ AST_VECTOR(, enum ast_rtp_extension_direction) extmap_enabled;
+ /*! Negotiated RTP extensions (using index based on extension) */
+ AST_VECTOR(, int) extmap_negotiated;
+ /*! Negotiated RTP extensions (using index based on unique id) */
+ AST_VECTOR(, struct rtp_extmap) extmap_unique_ids;
+};
+
+/*!
+ * \brief URIs for known RTP extensions
+ */
+static const char * const rtp_extension_uris[AST_RTP_EXTENSION_MAX] = {
+ [AST_RTP_EXTENSION_UNSUPPORTED] = "",
+ [AST_RTP_EXTENSION_ABS_SEND_TIME] = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
};
/*! List of RTP engines that are currently registered */
ast_rtp_codecs_payloads_destroy(&instance->codecs);
+ AST_VECTOR_FREE(&instance->extmap_enabled);
+ AST_VECTOR_FREE(&instance->extmap_negotiated);
+ AST_VECTOR_FREE(&instance->extmap_unique_ids);
+
/* Drop our engine reference */
ast_module_unref(instance->engine->mod);
return NULL;
}
+ /* Initialize RTP extension support */
+ if (AST_VECTOR_INIT(&instance->extmap_enabled, 0) ||
+ AST_VECTOR_INIT(&instance->extmap_negotiated, 0) ||
+ AST_VECTOR_INIT(&instance->extmap_unique_ids, 0)) {
+ ao2_ref(instance, -1);
+ return NULL;
+ }
+
ast_debug(1, "Using engine '%s' for RTP instance '%p'\n", engine->name, instance);
/*
return &instance->codecs;
}
+int ast_rtp_instance_extmap_enable(struct ast_rtp_instance *instance, int id, enum ast_rtp_extension extension,
+ enum ast_rtp_extension_direction direction)
+{
+ struct rtp_extmap extmap = {
+ .extension = extension,
+ .direction = direction,
+ };
+
+ ao2_lock(instance);
+
+ if (!instance->engine->extension_enable || !instance->engine->extension_enable(instance, extension)) {
+ ao2_unlock(instance);
+ return 0;
+ }
+
+ /* We store enabled extensions separately so we can easily do negotiation */
+ if (AST_VECTOR_REPLACE(&instance->extmap_enabled, extension, direction)) {
+ ao2_unlock(instance);
+ return -1;
+ }
+
+ if (id <= 0) {
+ /* We find a free unique identifier for this extension by just appending it to the
+ * vector of unique ids. The size of the vector will become its unique identifier.
+ * As well when we are asking for information on the extensions it will be returned,
+ * allowing it to be added to the SDP offer.
+ */
+ if (AST_VECTOR_APPEND(&instance->extmap_unique_ids, extmap)) {
+ AST_VECTOR_REPLACE(&instance->extmap_enabled, extension, AST_RTP_EXTENSION_DIRECTION_NONE);
+ ao2_unlock(instance);
+ return -1;
+ }
+ id = AST_VECTOR_SIZE(&instance->extmap_unique_ids);
+ } else {
+ /* Otherwise we put it precisely where they want it */
+ if (AST_VECTOR_REPLACE(&instance->extmap_unique_ids, id - 1, extmap)) {
+ AST_VECTOR_REPLACE(&instance->extmap_enabled, extension, AST_RTP_EXTENSION_DIRECTION_NONE);
+ ao2_unlock(instance);
+ return -1;
+ }
+ }
+
+ /* Now that we have an id add the extension to here */
+ if (AST_VECTOR_REPLACE(&instance->extmap_negotiated, extension, id)) {
+ extmap.extension = AST_RTP_EXTENSION_UNSUPPORTED;
+ extmap.direction = AST_RTP_EXTENSION_DIRECTION_NONE;
+ AST_VECTOR_REPLACE(&instance->extmap_enabled, extension, AST_RTP_EXTENSION_DIRECTION_NONE);
+ AST_VECTOR_REPLACE(&instance->extmap_unique_ids, id - 1, extmap);
+ ao2_unlock(instance);
+ return -1;
+ }
+
+ ao2_unlock(instance);
+
+ return 0;
+}
+
+/*! \brief Helper function which negotiates two RTP extension directions to get our current direction */
+static enum ast_rtp_extension_direction rtp_extmap_negotiate_direction(enum ast_rtp_extension_direction ours,
+ enum ast_rtp_extension_direction theirs)
+{
+ if (theirs == AST_RTP_EXTENSION_DIRECTION_NONE || ours == AST_RTP_EXTENSION_DIRECTION_NONE) {
+ /* This should not occur but if it does tolerate either side not having this extension
+ * in use.
+ */
+ return AST_RTP_EXTENSION_DIRECTION_NONE;
+ } else if (theirs == AST_RTP_EXTENSION_DIRECTION_INACTIVE) {
+ /* Inactive is always inactive on our side */
+ return AST_RTP_EXTENSION_DIRECTION_INACTIVE;
+ } else if (theirs == AST_RTP_EXTENSION_DIRECTION_SENDRECV) {
+ return ours;
+ } else if (theirs == AST_RTP_EXTENSION_DIRECTION_SENDONLY) {
+ /* If they are send only then we become recvonly if we are configured as sendrecv or recvonly */
+ if (ours == AST_RTP_EXTENSION_DIRECTION_SENDRECV || ours == AST_RTP_EXTENSION_DIRECTION_RECVONLY) {
+ return AST_RTP_EXTENSION_DIRECTION_RECVONLY;
+ }
+ } else if (theirs == AST_RTP_EXTENSION_DIRECTION_RECVONLY) {
+ /* If they are recv only then we become sendonly if we are configured as sendrecv or sendonly */
+ if (ours == AST_RTP_EXTENSION_DIRECTION_SENDRECV || ours == AST_RTP_EXTENSION_DIRECTION_SENDONLY) {
+ return AST_RTP_EXTENSION_DIRECTION_SENDONLY;
+ }
+ }
+
+ return AST_RTP_EXTENSION_DIRECTION_NONE;
+}
+
+int ast_rtp_instance_extmap_negotiate(struct ast_rtp_instance *instance, int id, enum ast_rtp_extension_direction direction,
+ const char *uri, const char *attributes)
+{
+ /* 'attributes' is currently unused but exists in the API to ensure it does not need to be altered
+ * in the future in case we need to use it.
+ */
+ int idx;
+ enum ast_rtp_extension extension = AST_RTP_EXTENSION_UNSUPPORTED;
+
+ /* Per the RFC the identifier has to be 1 or above */
+ if (id < 1) {
+ return -1;
+ }
+
+ /* Convert the provided URI to the internal representation */
+ for (idx = 0; idx < ARRAY_LEN(rtp_extension_uris); ++idx) {
+ if (!strcasecmp(rtp_extension_uris[idx], uri)) {
+ extension = idx;
+ break;
+ }
+ }
+
+ ao2_lock(instance);
+ /* We only accept the extension if it is enabled */
+ if (extension < AST_VECTOR_SIZE(&instance->extmap_enabled) &&
+ AST_VECTOR_GET(&instance->extmap_enabled, extension) != AST_RTP_EXTENSION_DIRECTION_NONE) {
+ struct rtp_extmap extmap = {
+ .extension = extension,
+ .direction = rtp_extmap_negotiate_direction(AST_VECTOR_GET(&instance->extmap_enabled, extension), direction),
+ };
+
+ /* If the direction negotiation failed then don't accept or use this extension */
+ if (extmap.direction != AST_RTP_EXTENSION_DIRECTION_NONE) {
+ if (extension != AST_RTP_EXTENSION_UNSUPPORTED) {
+ AST_VECTOR_REPLACE(&instance->extmap_negotiated, extension, id);
+ }
+ AST_VECTOR_REPLACE(&instance->extmap_unique_ids, id - 1, extmap);
+ }
+ }
+ ao2_unlock(instance);
+
+ return 0;
+}
+
+void ast_rtp_instance_extmap_clear(struct ast_rtp_instance *instance)
+{
+ static const struct rtp_extmap extmap_none = {
+ .extension = AST_RTP_EXTENSION_UNSUPPORTED,
+ .direction = AST_RTP_EXTENSION_DIRECTION_NONE,
+ };
+ int idx;
+
+ ao2_lock(instance);
+
+ /* Clear both the known unique ids and the negotiated extensions as we are about to have
+ * new results set on us.
+ */
+ for (idx = 0; idx < AST_VECTOR_SIZE(&instance->extmap_unique_ids); ++idx) {
+ AST_VECTOR_REPLACE(&instance->extmap_unique_ids, idx, extmap_none);
+ }
+
+ for (idx = 0; idx < AST_VECTOR_SIZE(&instance->extmap_negotiated); ++idx) {
+ AST_VECTOR_REPLACE(&instance->extmap_negotiated, idx, -1);
+ }
+
+ ao2_unlock(instance);
+}
+
+int ast_rtp_instance_extmap_get_id(struct ast_rtp_instance *instance, enum ast_rtp_extension extension)
+{
+ int id = -1;
+
+ ao2_lock(instance);
+ if (extension < AST_VECTOR_SIZE(&instance->extmap_negotiated)) {
+ id = AST_VECTOR_GET(&instance->extmap_negotiated, extension);
+ }
+ ao2_unlock(instance);
+
+ return id;
+}
+
+size_t ast_rtp_instance_extmap_count(struct ast_rtp_instance *instance)
+{
+ size_t count;
+
+ ao2_lock(instance);
+ count = AST_VECTOR_SIZE(&instance->extmap_unique_ids);
+ ao2_unlock(instance);
+
+ return count;
+}
+
+enum ast_rtp_extension ast_rtp_instance_extmap_get_extension(struct ast_rtp_instance *instance, int id)
+{
+ enum ast_rtp_extension extension = AST_RTP_EXTENSION_UNSUPPORTED;
+
+ ao2_lock(instance);
+
+ /* The local unique identifier starts at '1' so the highest unique identifier
+ * can be the actual size of the vector. We compensate (as it is 0 index based)
+ * by dropping it down to 1 to get the correct information.
+ */
+ if (0 < id && id <= AST_VECTOR_SIZE(&instance->extmap_unique_ids)) {
+ struct rtp_extmap *extmap = AST_VECTOR_GET_ADDR(&instance->extmap_unique_ids, id - 1);
+
+ extension = extmap->extension;
+ }
+ ao2_unlock(instance);
+
+ return extension;
+}
+
+enum ast_rtp_extension_direction ast_rtp_instance_extmap_get_direction(struct ast_rtp_instance *instance, int id)
+{
+ enum ast_rtp_extension_direction direction = AST_RTP_EXTENSION_DIRECTION_NONE;
+
+ ao2_lock(instance);
+
+ if (0 < id && id <= AST_VECTOR_SIZE(&instance->extmap_unique_ids)) {
+ struct rtp_extmap *extmap = AST_VECTOR_GET_ADDR(&instance->extmap_unique_ids, id - 1);
+
+ direction = extmap->direction;
+ }
+ ao2_unlock(instance);
+
+ return direction;
+}
+
+const char *ast_rtp_instance_extmap_get_uri(struct ast_rtp_instance *instance, int id)
+{
+ enum ast_rtp_extension extension = ast_rtp_instance_extmap_get_extension(instance, id);
+
+ if (extension == AST_RTP_EXTENSION_UNSUPPORTED ||
+ (unsigned int)extension >= ARRAY_LEN(rtp_extension_uris)) {
+ return NULL;
+ }
+
+ return rtp_extension_uris[extension];
+}
+
int ast_rtp_codecs_payloads_initialize(struct ast_rtp_codecs *codecs)
{
int res;
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, rtcp_type);
}
+/*!
+ * \brief Enable an RTP extension on an RTP session.
+ */
+static void enable_rtp_extension(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
+ enum ast_rtp_extension extension, enum ast_rtp_extension_direction direction,
+ const pjmedia_sdp_session *sdp)
+{
+ int id = -1;
+
+ /* For a bundle group the local unique identifier space is shared across all streams within
+ * it.
+ */
+ if (session_media->bundle_group != -1) {
+ int index;
+
+ for (index = 0; index < sdp->media_count; ++index) {
+ struct ast_sip_session_media *other_session_media;
+ int other_id;
+
+ if (index >= AST_VECTOR_SIZE(&session->pending_media_state->sessions)) {
+ break;
+ }
+
+ 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;
+ }
+
+ other_id = ast_rtp_instance_extmap_get_id(other_session_media->rtp, extension);
+ if (other_id == -1) {
+ /* Worst case we have to fall back to the highest available free local unique identifier
+ * for the bundle group.
+ */
+ other_id = ast_rtp_instance_extmap_count(other_session_media->rtp) + 1;
+ if (id < other_id) {
+ id = other_id;
+ }
+ continue;
+ }
+
+ id = other_id;
+ break;
+ }
+ }
+
+ ast_rtp_instance_extmap_enable(session_media->rtp, id, extension, direction);
+}
+
/*! \brief Internal function which creates an RTP instance */
-static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media)
+static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
+ const pjmedia_sdp_session *sdp)
{
struct ast_rtp_engine_ice *ice;
struct ast_sockaddr temp_media_address;
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RETRANS_RECV, session->endpoint->media.webrtc);
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RETRANS_SEND, session->endpoint->media.webrtc);
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_REMB, session->endpoint->media.webrtc);
+ enable_rtp_extension(session, session_media, AST_RTP_EXTENSION_ABS_SEND_TIME, AST_RTP_EXTENSION_DIRECTION_SENDRECV, sdp);
if (session->endpoint->media.tos_video || session->endpoint->media.cos_video) {
ast_rtp_instance_set_qos(session_media->rtp, session->endpoint->media.tos_video,
session->endpoint->media.cos_video, "SIP RTP Video");
pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);
}
+static void add_extmap_to_stream(struct ast_sip_session *session,
+ struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media)
+{
+ int idx;
+ char extmap_value[256];
+
+ if (!session->endpoint->media.webrtc || session_media->type != AST_MEDIA_TYPE_VIDEO) {
+ return;
+ }
+
+ /* RTP extension local unique identifiers start at '1' */
+ for (idx = 1; idx <= ast_rtp_instance_extmap_count(session_media->rtp); ++idx) {
+ enum ast_rtp_extension extension = ast_rtp_instance_extmap_get_extension(session_media->rtp, idx);
+ const char *direction_str = "";
+ pj_str_t stmp;
+ pjmedia_sdp_attr *attr;
+
+ /* If this is an unsupported RTP extension we can't place it into the SDP */
+ if (extension == AST_RTP_EXTENSION_UNSUPPORTED) {
+ continue;
+ }
+
+ switch (ast_rtp_instance_extmap_get_direction(session_media->rtp, idx)) {
+ case AST_RTP_EXTENSION_DIRECTION_SENDRECV:
+ /* Lack of a direction indicates sendrecv, so we leave it out */
+ direction_str = "";
+ break;
+ case AST_RTP_EXTENSION_DIRECTION_SENDONLY:
+ direction_str = "/sendonly";
+ break;
+ case AST_RTP_EXTENSION_DIRECTION_RECVONLY:
+ direction_str = "/recvonly";
+ break;
+ case AST_RTP_EXTENSION_DIRECTION_NONE:
+ /* It is impossible for a "none" direction extension to be negotiated but just in case
+ * we treat it as inactive.
+ */
+ case AST_RTP_EXTENSION_DIRECTION_INACTIVE:
+ direction_str = "/inactive";
+ break;
+ }
+
+ snprintf(extmap_value, sizeof(extmap_value), "%d%s %s", idx, direction_str,
+ ast_rtp_instance_extmap_get_uri(session_media->rtp, idx));
+ attr = pjmedia_sdp_attr_create(pool, "extmap", pj_cstr(&stmp, extmap_value));
+ pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);
+ }
+}
+
+/*! \brief Function which processes extmap attributes in a stream */
+static void process_extmap_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.webrtc || session_media->type != AST_MEDIA_TYPE_VIDEO) {
+ return;
+ }
+
+ ast_rtp_instance_extmap_clear(session_media->rtp);
+
+ 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 *uri;
+ int id;
+ char direction_str[10] = "";
+ char *attributes;
+ enum ast_rtp_extension_direction direction = AST_RTP_EXTENSION_DIRECTION_SENDRECV;
+
+ /* We only care about extmap attributes */
+ if (pj_strcmp2(&attr->name, "extmap")) {
+ continue;
+ }
+
+ ast_copy_pj_str(attr_value, &attr->value, sizeof(attr_value));
+
+ /* Split the combined unique identifier and direction away from the URI and attributes for easier parsing */
+ uri = strchr(attr_value, ' ');
+ if (ast_strlen_zero(uri)) {
+ continue;
+ }
+ *uri++ = '\0';
+
+ if ((sscanf(attr_value, "%30d%9s", &id, direction_str) < 1) || (id < 1)) {
+ /* We require at a minimum the unique identifier */
+ continue;
+ }
+
+ /* Convert from the string to the internal representation */
+ if (!strcasecmp(direction_str, "/sendonly")) {
+ direction = AST_RTP_EXTENSION_DIRECTION_SENDONLY;
+ } else if (!strcasecmp(direction_str, "/recvonly")) {
+ direction = AST_RTP_EXTENSION_DIRECTION_RECVONLY;
+ } else if (!strcasecmp(direction_str, "/inactive")) {
+ direction = AST_RTP_EXTENSION_DIRECTION_INACTIVE;
+ }
+
+ attributes = strchr(uri, ' ');
+ if (!ast_strlen_zero(attributes)) {
+ *attributes++ = '\0';
+ }
+
+ ast_rtp_instance_extmap_negotiate(session_media->rtp, id, direction, uri, attributes);
+ }
+}
+
/*! \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,
}
/* Using the connection information create an appropriate RTP instance */
- if (!session_media->rtp && create_rtp(session, session_media)) {
+ if (!session_media->rtp && create_rtp(session, session_media, sdp)) {
return -1;
}
process_ssrc_attributes(session, session_media, stream);
+ process_extmap_attributes(session, session_media, stream);
session_media_transport = ast_sip_session_media_get_transport(session, session_media);
if (session_media_transport == session_media || !session_media->bundled) {
return 1;
}
- if (!session_media->rtp && create_rtp(session, session_media)) {
+ if (!session_media->rtp && create_rtp(session, session_media, sdp)) {
return -1;
}
add_ssrc_to_stream(session, session_media, pool, media);
add_msid_to_stream(session, session_media, pool, media, stream);
add_rtcp_fb_to_stream(session, session_media, pool, media);
+ add_extmap_to_stream(session, session_media, pool, media);
/* Add the media stream to the SDP */
sdp->media[sdp->media_count++] = media;
}
/* Create an RTP instance if need be */
- if (!session_media->rtp && create_rtp(session, session_media)) {
+ if (!session_media->rtp && create_rtp(session, session_media, local)) {
return -1;
}
process_ssrc_attributes(session, session_media, remote_stream);
+ process_extmap_attributes(session, session_media, remote_stream);
session_media_transport = ast_sip_session_media_get_transport(session, session_media);
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_extension_enable(struct ast_rtp_instance *instance, enum ast_rtp_extension extension);
static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent);
#ifdef HAVE_OPENSSL_SRTP
.cname_get = ast_rtp_get_cname,
.set_remote_ssrc = ast_rtp_set_remote_ssrc,
.set_stream_num = ast_rtp_set_stream_num,
+ .extension_enable = ast_rtp_extension_enable,
.bundle = ast_rtp_bundle,
};
return res;
}
+static void put_unaligned_time24(void *p, uint32_t time_msw, uint32_t time_lsw)
+{
+ unsigned char *cp = p;
+ uint32_t datum;
+
+ /* Convert the time to 6.18 format */
+ datum = (time_msw << 18) & 0x00fc0000;
+ datum |= (time_lsw >> 14) & 0x0003ffff;
+
+ cp[0] = datum >> 16;
+ cp[1] = datum >> 8;
+ cp[2] = datum;
+}
+
/*! \pre instance is locked */
static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec)
{
/* If we know the remote address construct a packet and send it out */
if (!ast_sockaddr_isnull(&remote_address)) {
- int hdrlen = 12, res, ice;
- int packet_len = frame->datalen + hdrlen;
- unsigned char *rtpheader = (unsigned char *)(frame->data.ptr - hdrlen);
+ int hdrlen = 12;
+ int res;
+ int ice;
+ int ext = 0;
+ int abs_send_time_id;
+ int packet_len;
+ unsigned char *rtpheader;
- put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (seqno) | (mark << 23)));
+ /* If the abs-send-time extension has been negotiated determine how much space we need */
+ abs_send_time_id = ast_rtp_instance_extmap_get_id(instance, AST_RTP_EXTENSION_ABS_SEND_TIME);
+ if (abs_send_time_id != -1) {
+ /* 4 bytes for the shared information, 1 byte for identifier, 3 bytes for abs-send-time */
+ hdrlen += 8;
+ ext = 1;
+ }
+
+ packet_len = frame->datalen + hdrlen;
+ rtpheader = (unsigned char *)(frame->data.ptr - hdrlen);
+
+ put_unaligned_uint32(rtpheader, htonl((2 << 30) | (ext << 28) | (codec << 16) | (seqno) | (mark << 23)));
put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts));
put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc));
+ /* We assume right now that we will only ever have the abs-send-time extension in the packet
+ * which simplifies things a bit.
+ */
+ if (abs_send_time_id != -1) {
+ unsigned int now_msw;
+ unsigned int now_lsw;
+
+ /* This happens before being placed into the retransmission buffer so that when we
+ * retransmit we only have to update the timestamp, not everything else.
+ */
+ put_unaligned_uint32(rtpheader + 12, htonl((0xBEDE << 16) | 1));
+ rtpheader[16] = (abs_send_time_id << 4) | 2;
+
+ timeval2ntp(ast_tvnow(), &now_msw, &now_lsw);
+ put_unaligned_time24(rtpheader + 17, now_msw, now_lsw);
+ }
+
/* If retransmissions are enabled, we need to store this packet for future use */
if (rtp->send_buffer) {
struct ast_rtp_rtcp_nack_payload *payload;
unsigned int pid; /* Packet ID which refers to seqno of lost packet */
unsigned int blp; /* Bitmask of following lost packets */
struct ast_sockaddr remote_address = { {0,} };
+ int abs_send_time_id;
+ unsigned int now_msw = 0;
+ unsigned int now_lsw = 0;
if (!rtp->send_buffer) {
ast_debug(1, "Tried to handle NACK request, but we don't have a RTP packet storage!\n");
return res;
}
+ abs_send_time_id = ast_rtp_instance_extmap_get_id(instance, AST_RTP_EXTENSION_ABS_SEND_TIME);
+ if (abs_send_time_id != -1) {
+ timeval2ntp(ast_tvnow(), &now_msw, &now_lsw);
+ }
+
ast_rtp_instance_get_remote_address(instance, &remote_address);
/*
/* We know the remote end is missing this packet. Go ahead and send it if we still have it. */
payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_get(rtp->send_buffer, pid);
if (payload) {
+ if (abs_send_time_id != -1) {
+ /* On retransmission we need to update the timestamp within the packet, as it
+ * is supposed to contain when the packet was actually sent.
+ */
+ put_unaligned_time24(payload->buf + 17, now_msw, now_lsw);
+ }
res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
} else {
ast_debug(1, "Received NACK request for RTP packet with seqno %d, but we don't have it\n", pid);
unsigned int seqno = (pid + blp_index) % 65536;
payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_get(rtp->send_buffer, seqno);
if (payload) {
+ if (abs_send_time_id != -1) {
+ put_unaligned_time24(payload->buf + 17, now_msw, now_lsw);
+ }
res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
} else {
ast_debug(1, "Remote end also requested RTP packet with seqno %d, but we don't have it\n", seqno);
rtp->stream_num = stream_num;
}
+static int ast_rtp_extension_enable(struct ast_rtp_instance *instance, enum ast_rtp_extension extension)
+{
+ switch (extension) {
+ case AST_RTP_EXTENSION_ABS_SEND_TIME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
/*! \pre child is locked */
static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent)
{