mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-14 00:24:05 +00:00
Merge "rtp: Add support for RTP extension negotiation and abs-send-time."
This commit is contained in:
@@ -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
|
||||||
*
|
*
|
||||||
|
@@ -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;
|
||||||
|
@@ -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);
|
||||||
|
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user