mirror of
https://github.com/asterisk/asterisk.git
synced 2026-07-21 19:37:13 -07: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 */
|
||||
};
|
||||
|
||||
/*!
|
||||
* \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 */
|
||||
@@ -654,6 +666,8 @@ struct ast_rtp_engine {
|
||||
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;
|
||||
};
|
||||
@@ -720,6 +734,22 @@ struct ast_rtp_glue {
|
||||
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
|
||||
*
|
||||
@@ -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);
|
||||
|
||||
/*!
|
||||
* \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
|
||||
*
|
||||
|
||||
@@ -177,6 +177,14 @@
|
||||
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 */
|
||||
@@ -213,6 +221,20 @@ struct ast_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 */
|
||||
@@ -408,6 +430,10 @@ static void instance_destructor(void *obj)
|
||||
|
||||
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);
|
||||
|
||||
@@ -474,6 +500,14 @@ struct ast_rtp_instance *ast_rtp_instance_new(const char *engine_name,
|
||||
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);
|
||||
|
||||
/*
|
||||
@@ -680,6 +714,232 @@ struct ast_rtp_codecs *ast_rtp_instance_get_codecs(struct ast_rtp_instance *inst
|
||||
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;
|
||||
|
||||
+164
-4
@@ -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);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \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;
|
||||
@@ -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_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");
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -1139,11 +1296,12 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session,
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
@@ -1377,7 +1535,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!session_media->rtp && create_rtp(session, session_media)) {
|
||||
if (!session_media->rtp && create_rtp(session, session_media, sdp)) {
|
||||
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_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;
|
||||
@@ -1676,11 +1835,12 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session,
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
|
||||
+79
-4
@@ -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 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
|
||||
@@ -2249,6 +2250,7 @@ static struct ast_rtp_engine asterisk_rtp_engine = {
|
||||
.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,
|
||||
};
|
||||
|
||||
@@ -4282,6 +4284,20 @@ static int ast_rtcp_write(const void *data)
|
||||
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)
|
||||
{
|
||||
@@ -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 (!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;
|
||||
@@ -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 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);
|
||||
|
||||
/*
|
||||
@@ -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. */
|
||||
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);
|
||||
@@ -5299,6 +5361,9 @@ static int ast_rtp_rtcp_handle_nack(struct ast_rtp_instance *instance, unsigned
|
||||
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);
|
||||
@@ -7165,6 +7230,16 @@ static void ast_rtp_set_stream_num(struct ast_rtp_instance *instance, int stream
|
||||
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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user