Merge "rtp: Add support for RTP extension negotiation and abs-send-time."

This commit is contained in:
Joshua Colp
2018-05-24 15:26:57 -05:00
committed by Gerrit Code Review
4 changed files with 636 additions and 8 deletions

View File

@@ -533,6 +533,18 @@ enum ast_rtp_dtls_verify {
AST_RTP_DTLS_VERIFY_CERTIFICATE = (1 << 1), /*!< Verify the certificate */ 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 */ /*! \brief DTLS configuration structure */
struct ast_rtp_dtls_cfg { struct ast_rtp_dtls_cfg {
unsigned int enabled:1; /*!< Whether DTLS support is enabled or not */ unsigned int enabled:1; /*!< Whether DTLS support is enabled or not */
@@ -654,6 +666,8 @@ struct ast_rtp_engine {
struct ast_rtp_engine_ice *ice; struct ast_rtp_engine_ice *ice;
/*! Callback to pointer for optional DTLS SRTP support */ /*! Callback to pointer for optional DTLS SRTP support */
struct ast_rtp_engine_dtls *dtls; 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 */ /*! Linked list information */
AST_RWLIST_ENTRY(ast_rtp_engine) entry; AST_RWLIST_ENTRY(ast_rtp_engine) entry;
}; };
@@ -720,6 +734,22 @@ struct ast_rtp_glue {
AST_RWLIST_ENTRY(ast_rtp_glue) 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 * \brief Allocation routine for \ref ast_rtp_payload_type
* *
@@ -1246,6 +1276,109 @@ int ast_rtp_instance_get_prop(struct ast_rtp_instance *instance, enum ast_rtp_pr
*/ */
struct ast_rtp_codecs *ast_rtp_instance_get_codecs(struct ast_rtp_instance *instance); 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 * \brief Initialize an RTP codecs structure
* *

View File

@@ -177,6 +177,14 @@
struct ast_srtp_res *res_srtp = NULL; struct ast_srtp_res *res_srtp = NULL;
struct ast_srtp_policy_res *res_srtp_policy = 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) */ /*! Structure that represents an RTP session (instance) */
struct ast_rtp_instance { struct ast_rtp_instance {
/*! Engine that is handling this RTP instance */ /*! Engine that is handling this RTP instance */
@@ -213,6 +221,20 @@ struct ast_rtp_instance {
time_t last_tx; time_t last_tx;
/*! Time of last packet received */ /*! Time of last packet received */
time_t last_rx; 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 */ /*! List of RTP engines that are currently registered */
@@ -408,6 +430,10 @@ static void instance_destructor(void *obj)
ast_rtp_codecs_payloads_destroy(&instance->codecs); 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 */ /* Drop our engine reference */
ast_module_unref(instance->engine->mod); ast_module_unref(instance->engine->mod);
@@ -474,6 +500,14 @@ struct ast_rtp_instance *ast_rtp_instance_new(const char *engine_name,
return NULL; 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); ast_debug(1, "Using engine '%s' for RTP instance '%p'\n", engine->name, instance);
/* /*
@@ -680,6 +714,232 @@ struct ast_rtp_codecs *ast_rtp_instance_get_codecs(struct ast_rtp_instance *inst
return &instance->codecs; 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 ast_rtp_codecs_payloads_initialize(struct ast_rtp_codecs *codecs)
{ {
int res; int res;

View File

@@ -151,8 +151,57 @@ static void enable_rtcp(struct ast_sip_session *session, struct ast_sip_session_
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, rtcp_type); 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 */ /*! \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_rtp_engine_ice *ice;
struct ast_sockaddr temp_media_address; struct ast_sockaddr temp_media_address;
@@ -223,6 +272,7 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
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_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_RETRANS_SEND, session->endpoint->media.webrtc);
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_REMB, 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) { if (session->endpoint->media.tos_video || session->endpoint->media.cos_video) {
ast_rtp_instance_set_qos(session_media->rtp, session->endpoint->media.tos_video, ast_rtp_instance_set_qos(session_media->rtp, session->endpoint->media.tos_video,
session->endpoint->media.cos_video, "SIP RTP Video"); session->endpoint->media.cos_video, "SIP RTP Video");
@@ -1101,6 +1151,113 @@ static void add_rtcp_fb_to_stream(struct ast_sip_session *session,
pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr); 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 */ /*! \brief Function which negotiates an incoming media stream */
static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,
struct ast_sip_session_media *session_media, const pjmedia_sdp_session *sdp, struct ast_sip_session_media *session_media, const pjmedia_sdp_session *sdp,
@@ -1139,11 +1296,12 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,
} }
/* Using the connection information create an appropriate RTP instance */ /* 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; return -1;
} }
process_ssrc_attributes(session, session_media, stream); 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); session_media_transport = ast_sip_session_media_get_transport(session, session_media);
if (session_media_transport == session_media || !session_media->bundled) { if (session_media_transport == session_media || !session_media->bundled) {
@@ -1377,7 +1535,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
return 1; return 1;
} }
if (!session_media->rtp && create_rtp(session, session_media)) { if (!session_media->rtp && create_rtp(session, session_media, sdp)) {
return -1; return -1;
} }
@@ -1602,6 +1760,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
add_ssrc_to_stream(session, session_media, pool, media); add_ssrc_to_stream(session, session_media, pool, media);
add_msid_to_stream(session, session_media, pool, media, stream); add_msid_to_stream(session, session_media, pool, media, stream);
add_rtcp_fb_to_stream(session, session_media, pool, media); 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 */ /* Add the media stream to the SDP */
sdp->media[sdp->media_count++] = media; sdp->media[sdp->media_count++] = media;
@@ -1676,11 +1835,12 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session,
} }
/* Create an RTP instance if need be */ /* 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; return -1;
} }
process_ssrc_attributes(session, session_media, remote_stream); 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); session_media_transport = ast_sip_session_media_get_transport(session, session_media);

View File

@@ -551,6 +551,7 @@ 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 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_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 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); static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent);
#ifdef HAVE_OPENSSL_SRTP #ifdef HAVE_OPENSSL_SRTP
@@ -2249,6 +2250,7 @@ static struct ast_rtp_engine asterisk_rtp_engine = {
.cname_get = ast_rtp_get_cname, .cname_get = ast_rtp_get_cname,
.set_remote_ssrc = ast_rtp_set_remote_ssrc, .set_remote_ssrc = ast_rtp_set_remote_ssrc,
.set_stream_num = ast_rtp_set_stream_num, .set_stream_num = ast_rtp_set_stream_num,
.extension_enable = ast_rtp_extension_enable,
.bundle = ast_rtp_bundle, .bundle = ast_rtp_bundle,
}; };
@@ -4282,6 +4284,20 @@ static int ast_rtcp_write(const void *data)
return res; 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 */ /*! \pre instance is locked */
static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec) static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *frame, int codec)
{ {
@@ -4399,14 +4415,46 @@ static int rtp_raw_write(struct ast_rtp_instance *instance, struct ast_frame *fr
/* If we know the remote address construct a packet and send it out */ /* If we know the remote address construct a packet and send it out */
if (!ast_sockaddr_isnull(&remote_address)) { if (!ast_sockaddr_isnull(&remote_address)) {
int hdrlen = 12, res, ice; int hdrlen = 12;
int packet_len = frame->datalen + hdrlen; int res;
unsigned char *rtpheader = (unsigned char *)(frame->data.ptr - hdrlen); 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 + 4, htonl(rtp->lastts));
put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc)); 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 retransmissions are enabled, we need to store this packet for future use */
if (rtp->send_buffer) { if (rtp->send_buffer) {
struct ast_rtp_rtcp_nack_payload *payload; struct ast_rtp_rtcp_nack_payload *payload;
@@ -5263,12 +5311,20 @@ static int ast_rtp_rtcp_handle_nack(struct ast_rtp_instance *instance, unsigned
unsigned int pid; /* Packet ID which refers to seqno of lost packet */ unsigned int pid; /* Packet ID which refers to seqno of lost packet */
unsigned int blp; /* Bitmask of following lost packets */ unsigned int blp; /* Bitmask of following lost packets */
struct ast_sockaddr remote_address = { {0,} }; 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) { if (!rtp->send_buffer) {
ast_debug(1, "Tried to handle NACK request, but we don't have a RTP packet storage!\n"); ast_debug(1, "Tried to handle NACK request, but we don't have a RTP packet storage!\n");
return res; 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); ast_rtp_instance_get_remote_address(instance, &remote_address);
/* /*
@@ -5281,6 +5337,12 @@ static int ast_rtp_rtcp_handle_nack(struct ast_rtp_instance *instance, unsigned
/* We know the remote end is missing this packet. Go ahead and send it if we still have it. */ /* 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); payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_get(rtp->send_buffer, pid);
if (payload) { 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); res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
} else { } else {
ast_debug(1, "Received NACK request for RTP packet with seqno %d, but we don't have it\n", pid); ast_debug(1, "Received NACK request for RTP packet with seqno %d, but we don't have it\n", pid);
@@ -5299,6 +5361,9 @@ static int ast_rtp_rtcp_handle_nack(struct ast_rtp_instance *instance, unsigned
unsigned int seqno = (pid + blp_index) % 65536; unsigned int seqno = (pid + blp_index) % 65536;
payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_get(rtp->send_buffer, seqno); payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_get(rtp->send_buffer, seqno);
if (payload) { 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); res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
} else { } else {
ast_debug(1, "Remote end also requested RTP packet with seqno %d, but we don't have it\n", seqno); ast_debug(1, "Remote end also requested RTP packet with seqno %d, but we don't have it\n", seqno);
@@ -7165,6 +7230,16 @@ static void ast_rtp_set_stream_num(struct ast_rtp_instance *instance, int stream
rtp->stream_num = stream_num; 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 */ /*! \pre child is locked */
static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent) static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent)
{ {